detached c0865ed

Merge pull request #130 from bnema/fix/omnibox-domain-boost

fix: boost domain matches in omnibox search results
commit
c0865ed65b4599463b7470efb278a2e5c00dae8b
author
bnema · · 2026-01-23 17:57 UTC
committer
GitHub · · 2026-01-23 17:57 UTC
merge of
9bf4c8a + 27c9c62 · diff against the first parent
signature
GPG UnverifiedB5690EEEBB952194 The key is unknown
15 files · +765 -531
A .github/CODEOWNERS +2 -0
@@ -0,0 +1,2 @@
1+# Default owner for everything
2+* @bnema
@@ -0,0 +1,2 @@
1+# Default owner for everything
2+* @bnema
A .github/dependabot.yml +28 -0
@@ -0,0 +1,28 @@
1+version: 2
2+updates:
3+ - package-ecosystem: gomod
4+ directory: /
5+ schedule:
6+ interval: weekly
7+ groups:
8+ go-dependencies:
9+ patterns:
10+ - "*"
11+
12+ - package-ecosystem: npm
13+ directory: /webui
14+ schedule:
15+ interval: weekly
16+ groups:
17+ npm-dependencies:
18+ patterns:
19+ - "*"
20+
21+ - package-ecosystem: github-actions
22+ directory: /
23+ schedule:
24+ interval: weekly
25+ groups:
26+ github-actions:
27+ patterns:
28+ - "*"
@@ -0,0 +1,28 @@
1+version: 2
2+updates:
3+ - package-ecosystem: gomod
4+ directory: /
5+ schedule:
6+ interval: weekly
7+ groups:
8+ go-dependencies:
9+ patterns:
10+ - "*"
11+
12+ - package-ecosystem: npm
13+ directory: /webui
14+ schedule:
15+ interval: weekly
16+ groups:
17+ npm-dependencies:
18+ patterns:
19+ - "*"
20+
21+ - package-ecosystem: github-actions
22+ directory: /
23+ schedule:
24+ interval: weekly
25+ groups:
26+ github-actions:
27+ patterns:
28+ - "*"
M .github/workflows/aur.yml +2 -2
@@ -23,7 +23,7 @@ jobs:
2323 cancel-in-progress: true
2424
2525 steps:
26- - uses: actions/checkout@v4
26+ - uses: actions/checkout@v6
2727 with:
2828 ref: ${{ github.event.workflow_run.head_sha }}
2929 fetch-depth: 0
@@ -119,7 +119,7 @@ jobs:
119119 cancel-in-progress: true
120120
121121 steps:
122- - uses: actions/checkout@v4
122+ - uses: actions/checkout@v6
123123 with:
124124 fetch-depth: 0
125125
@@ -23,7 +23,7 @@ jobs:
23 cancel-in-progress: true23 cancel-in-progress: true
24 24
25 steps:25 steps:
26- - uses: actions/checkout@v426+ - uses: actions/checkout@v6
27 with:27 with:
28 ref: ${{ github.event.workflow_run.head_sha }}28 ref: ${{ github.event.workflow_run.head_sha }}
29 fetch-depth: 029 fetch-depth: 0
@@ -119,7 +119,7 @@ jobs:
119 cancel-in-progress: true119 cancel-in-progress: true
120 120
121 steps:121 steps:
122- - uses: actions/checkout@v4122+ - uses: actions/checkout@v6
123 with:123 with:
124 fetch-depth: 0124 fetch-depth: 0
125 125
M .github/workflows/ci.yml +6 -6
@@ -41,21 +41,21 @@ jobs:
4141 pacman -S --noconfirm git go nodejs npm webkitgtk-6.0 gtk4 gobject-introspection base-devel
4242 - uses: actions/checkout@v6
4343 - name: Cache Go modules
44- uses: actions/cache@v4
44+ uses: actions/cache@v5
4545 with:
4646 path: ~/go/pkg/mod
4747 key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}
4848 restore-keys: |
4949 ${{ runner.os }}-go-mod-
5050 - name: Cache Go build cache
51- uses: actions/cache@v4
51+ uses: actions/cache@v5
5252 with:
5353 path: ~/.cache/go-build
5454 key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}
5555 restore-keys: |
5656 ${{ runner.os }}-go-build-
5757 - name: Cache npm
58- uses: actions/cache@v4
58+ uses: actions/cache@v5
5959 with:
6060 path: ~/.npm
6161 key: ${{ runner.os }}-npm-${{ hashFiles('webui/package-lock.json') }}
@@ -82,21 +82,21 @@ jobs:
8282 pacman -S --noconfirm git go nodejs npm webkitgtk-6.0 gtk4 libadwaita gobject-introspection base-devel
8383 - uses: actions/checkout@v6
8484 - name: Cache Go modules
85- uses: actions/cache@v4
85+ uses: actions/cache@v5
8686 with:
8787 path: ~/go/pkg/mod
8888 key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}
8989 restore-keys: |
9090 ${{ runner.os }}-go-mod-
9191 - name: Cache Go build cache
92- uses: actions/cache@v4
92+ uses: actions/cache@v5
9393 with:
9494 path: ~/.cache/go-build
9595 key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}
9696 restore-keys: |
9797 ${{ runner.os }}-go-build-
9898 - name: Cache npm
99- uses: actions/cache@v4
99+ uses: actions/cache@v5
100100 with:
101101 path: ~/.npm
102102 key: ${{ runner.os }}-npm-${{ hashFiles('webui/package-lock.json') }}
@@ -41,21 +41,21 @@ jobs:
41 pacman -S --noconfirm git go nodejs npm webkitgtk-6.0 gtk4 gobject-introspection base-devel41 pacman -S --noconfirm git go nodejs npm webkitgtk-6.0 gtk4 gobject-introspection base-devel
42 - uses: actions/checkout@v642 - uses: actions/checkout@v6
43 - name: Cache Go modules43 - name: Cache Go modules
44- uses: actions/cache@v444+ uses: actions/cache@v5
45 with:45 with:
46 path: ~/go/pkg/mod46 path: ~/go/pkg/mod
47 key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}47 key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}
48 restore-keys: |48 restore-keys: |
49 ${{ runner.os }}-go-mod-49 ${{ runner.os }}-go-mod-
50 - name: Cache Go build cache50 - name: Cache Go build cache
51- uses: actions/cache@v451+ uses: actions/cache@v5
52 with:52 with:
53 path: ~/.cache/go-build53 path: ~/.cache/go-build
54 key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}54 key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}
55 restore-keys: |55 restore-keys: |
56 ${{ runner.os }}-go-build-56 ${{ runner.os }}-go-build-
57 - name: Cache npm57 - name: Cache npm
58- uses: actions/cache@v458+ uses: actions/cache@v5
59 with:59 with:
60 path: ~/.npm60 path: ~/.npm
61 key: ${{ runner.os }}-npm-${{ hashFiles('webui/package-lock.json') }}61 key: ${{ runner.os }}-npm-${{ hashFiles('webui/package-lock.json') }}
@@ -82,21 +82,21 @@ jobs:
82 pacman -S --noconfirm git go nodejs npm webkitgtk-6.0 gtk4 libadwaita gobject-introspection base-devel82 pacman -S --noconfirm git go nodejs npm webkitgtk-6.0 gtk4 libadwaita gobject-introspection base-devel
83 - uses: actions/checkout@v683 - uses: actions/checkout@v6
84 - name: Cache Go modules84 - name: Cache Go modules
85- uses: actions/cache@v485+ uses: actions/cache@v5
86 with:86 with:
87 path: ~/go/pkg/mod87 path: ~/go/pkg/mod
88 key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}88 key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}
89 restore-keys: |89 restore-keys: |
90 ${{ runner.os }}-go-mod-90 ${{ runner.os }}-go-mod-
91 - name: Cache Go build cache91 - name: Cache Go build cache
92- uses: actions/cache@v492+ uses: actions/cache@v5
93 with:93 with:
94 path: ~/.cache/go-build94 path: ~/.cache/go-build
95 key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}95 key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}
96 restore-keys: |96 restore-keys: |
97 ${{ runner.os }}-go-build-97 ${{ runner.os }}-go-build-
98 - name: Cache npm98 - name: Cache npm
99- uses: actions/cache@v499+ uses: actions/cache@v5
100 with:100 with:
101 path: ~/.npm101 path: ~/.npm
102 key: ${{ runner.os }}-npm-${{ hashFiles('webui/package-lock.json') }}102 key: ${{ runner.os }}-npm-${{ hashFiles('webui/package-lock.json') }}
A .github/workflows/dependabot-auto-merge.yml +25 -0
@@ -0,0 +1,25 @@
1+name: Dependabot auto-merge
2+
3+on: pull_request
4+
5+permissions:
6+ contents: write
7+ pull-requests: write
8+
9+jobs:
10+ dependabot:
11+ runs-on: ubuntu-latest
12+ if: github.actor == 'dependabot[bot]'
13+ steps:
14+ - name: Dependabot metadata
15+ id: metadata
16+ uses: dependabot/fetch-metadata@v2
17+ with:
18+ github-token: "${{ secrets.GITHUB_TOKEN }}"
19+
20+ - name: Auto-merge patch and minor updates
21+ if: steps.metadata.outputs.update-type == 'version-update:semver-patch' || steps.metadata.outputs.update-type == 'version-update:semver-minor'
22+ run: gh pr merge --auto --squash "$PR_URL"
23+ env:
24+ PR_URL: ${{ github.event.pull_request.html_url }}
25+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,25 @@
1+name: Dependabot auto-merge
2+
3+on: pull_request
4+
5+permissions:
6+ contents: write
7+ pull-requests: write
8+
9+jobs:
10+ dependabot:
11+ runs-on: ubuntu-latest
12+ if: github.actor == 'dependabot[bot]'
13+ steps:
14+ - name: Dependabot metadata
15+ id: metadata
16+ uses: dependabot/fetch-metadata@v2
17+ with:
18+ github-token: "${{ secrets.GITHUB_TOKEN }}"
19+
20+ - name: Auto-merge patch and minor updates
21+ if: steps.metadata.outputs.update-type == 'version-update:semver-patch' || steps.metadata.outputs.update-type == 'version-update:semver-minor'
22+ run: gh pr merge --auto --squash "$PR_URL"
23+ env:
24+ PR_URL: ${{ github.event.pull_request.html_url }}
25+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
M .github/workflows/flatpak.yml +1 -1
@@ -21,7 +21,7 @@ jobs:
2121 image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-48
2222 options: --privileged
2323 steps:
24- - uses: actions/checkout@v4
24+ - uses: actions/checkout@v6
2525
2626 - name: Get version
2727 id: version
@@ -21,7 +21,7 @@ jobs:
21 image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-4821 image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-48
22 options: --privileged22 options: --privileged
23 steps:23 steps:
24- - uses: actions/checkout@v424+ - uses: actions/checkout@v6
25 25
26 - name: Get version26 - name: Get version
27 id: version27 id: version
M go.mod +23 -20
@@ -8,17 +8,17 @@ require (
88 github.com/charmbracelet/bubbletea v1.3.10
99 github.com/charmbracelet/lipgloss v1.1.0
1010 github.com/fsnotify/fsnotify v1.9.0
11- github.com/godbus/dbus/v5 v5.2.1
12- github.com/jwijenbergh/puregotk v0.0.0-20251222174431-73930527faa3
13- github.com/ncruces/go-sqlite3 v0.30.3
11+ github.com/godbus/dbus/v5 v5.2.2
12+ github.com/jwijenbergh/puregotk v0.0.0-20260115100645-c78e1521129b
13+ github.com/ncruces/go-sqlite3 v0.30.4
1414 github.com/pelletier/go-toml/v2 v2.2.4
1515 github.com/pressly/goose/v3 v3.26.0
16- github.com/rs/zerolog v1.33.0
16+ github.com/rs/zerolog v1.34.0
1717 github.com/spf13/cobra v1.10.2
1818 github.com/spf13/viper v1.21.0
1919 github.com/stretchr/testify v1.11.1
20- golang.org/x/image v0.34.0
21- golang.org/x/sys v0.38.0
20+ golang.org/x/image v0.35.0
21+ golang.org/x/sys v0.40.0
2222 gopkg.in/yaml.v3 v3.0.1
2323 )
2424
@@ -27,21 +27,24 @@ replace github.com/jwijenbergh/puregotk => github.com/bnema/puregotk v0.0.0-2025
2727 require (
2828 github.com/atotto/clipboard v0.1.4 // indirect
2929 github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
30- github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
31- github.com/charmbracelet/x/ansi v0.10.1 // indirect
32- github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
33- github.com/charmbracelet/x/term v0.2.1 // indirect
34- github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
30+ github.com/charmbracelet/colorprofile v0.4.1 // indirect
31+ github.com/charmbracelet/x/ansi v0.11.4 // indirect
32+ github.com/charmbracelet/x/cellbuf v0.0.14 // indirect
33+ github.com/charmbracelet/x/term v0.2.2 // indirect
34+ github.com/clipperhouse/displaywidth v0.7.0 // indirect
35+ github.com/clipperhouse/stringish v0.1.1 // indirect
36+ github.com/clipperhouse/uax29/v2 v2.3.1 // indirect
37+ github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
3538 github.com/davecgh/go-spew v1.1.1 // indirect
3639 github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
37- github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
40+ github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
3841 github.com/inconshreveable/mousetrap v1.1.0 // indirect
3942 github.com/jwijenbergh/purego v0.0.0-20251017112123-b71757b9ba42 // indirect
40- github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
41- github.com/mattn/go-colorable v0.1.13 // indirect
43+ github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
44+ github.com/mattn/go-colorable v0.1.14 // indirect
4245 github.com/mattn/go-isatty v0.0.20 // indirect
4346 github.com/mattn/go-localereader v0.0.1 // indirect
44- github.com/mattn/go-runewidth v0.0.16 // indirect
47+ github.com/mattn/go-runewidth v0.0.19 // indirect
4548 github.com/mfridman/interpolate v0.0.2 // indirect
4649 github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
4750 github.com/muesli/cancelreader v0.2.2 // indirect
@@ -50,19 +53,19 @@ require (
5053 github.com/pmezard/go-difflib v1.0.0 // indirect
5154 github.com/rivo/uniseg v0.4.7 // indirect
5255 github.com/russross/blackfriday/v2 v2.1.0 // indirect
53- github.com/sagikazarmark/locafero v0.11.0 // indirect
56+ github.com/sagikazarmark/locafero v0.12.0 // indirect
5457 github.com/sahilm/fuzzy v0.1.1 // indirect
5558 github.com/sethvargo/go-retry v0.3.0 // indirect
56- github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
5759 github.com/spf13/afero v1.15.0 // indirect
5860 github.com/spf13/cast v1.10.0 // indirect
5961 github.com/spf13/pflag v1.0.10 // indirect
60- github.com/stretchr/objx v0.5.2 // indirect
62+ github.com/stretchr/objx v0.5.3 // indirect
6163 github.com/subosito/gotenv v1.6.0 // indirect
62- github.com/tetratelabs/wazero v1.10.1 // indirect
64+ github.com/tetratelabs/wazero v1.11.0 // indirect
6365 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
6466 go.uber.org/multierr v1.11.0 // indirect
6567 go.yaml.in/yaml/v3 v3.0.4 // indirect
6668 golang.org/x/sync v0.19.0 // indirect
67- golang.org/x/text v0.32.0 // indirect
69+ golang.org/x/text v0.33.0 // indirect
70+ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
6871 )
@@ -8,17 +8,17 @@ require (
8 github.com/charmbracelet/bubbletea v1.3.108 github.com/charmbracelet/bubbletea v1.3.10
9 github.com/charmbracelet/lipgloss v1.1.09 github.com/charmbracelet/lipgloss v1.1.0
10 github.com/fsnotify/fsnotify v1.9.010 github.com/fsnotify/fsnotify v1.9.0
11- github.com/godbus/dbus/v5 v5.2.111+ github.com/godbus/dbus/v5 v5.2.2
12- github.com/jwijenbergh/puregotk v0.0.0-20251222174431-73930527faa312+ github.com/jwijenbergh/puregotk v0.0.0-20260115100645-c78e1521129b
13- github.com/ncruces/go-sqlite3 v0.30.313+ github.com/ncruces/go-sqlite3 v0.30.4
14 github.com/pelletier/go-toml/v2 v2.2.414 github.com/pelletier/go-toml/v2 v2.2.4
15 github.com/pressly/goose/v3 v3.26.015 github.com/pressly/goose/v3 v3.26.0
16- github.com/rs/zerolog v1.33.016+ github.com/rs/zerolog v1.34.0
17 github.com/spf13/cobra v1.10.217 github.com/spf13/cobra v1.10.2
18 github.com/spf13/viper v1.21.018 github.com/spf13/viper v1.21.0
19 github.com/stretchr/testify v1.11.119 github.com/stretchr/testify v1.11.1
20- golang.org/x/image v0.34.020+ golang.org/x/image v0.35.0
21- golang.org/x/sys v0.38.021+ golang.org/x/sys v0.40.0
22 gopkg.in/yaml.v3 v3.0.122 gopkg.in/yaml.v3 v3.0.1
23 )23 )
24 24
@@ -27,21 +27,24 @@ replace github.com/jwijenbergh/puregotk => github.com/bnema/puregotk v0.0.0-2025
27 require (27 require (
28 github.com/atotto/clipboard v0.1.4 // indirect28 github.com/atotto/clipboard v0.1.4 // indirect
29 github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect29 github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
30- github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect30+ github.com/charmbracelet/colorprofile v0.4.1 // indirect
31- github.com/charmbracelet/x/ansi v0.10.1 // indirect31+ github.com/charmbracelet/x/ansi v0.11.4 // indirect
32- github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect32+ github.com/charmbracelet/x/cellbuf v0.0.14 // indirect
33- github.com/charmbracelet/x/term v0.2.1 // indirect33+ github.com/charmbracelet/x/term v0.2.2 // indirect
34- github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect34+ github.com/clipperhouse/displaywidth v0.7.0 // indirect
35+ github.com/clipperhouse/stringish v0.1.1 // indirect
36+ github.com/clipperhouse/uax29/v2 v2.3.1 // indirect
37+ github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
35 github.com/davecgh/go-spew v1.1.1 // indirect38 github.com/davecgh/go-spew v1.1.1 // indirect
36 github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect39 github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
37- github.com/go-viper/mapstructure/v2 v2.4.0 // indirect40+ github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
38 github.com/inconshreveable/mousetrap v1.1.0 // indirect41 github.com/inconshreveable/mousetrap v1.1.0 // indirect
39 github.com/jwijenbergh/purego v0.0.0-20251017112123-b71757b9ba42 // indirect42 github.com/jwijenbergh/purego v0.0.0-20251017112123-b71757b9ba42 // indirect
40- github.com/lucasb-eyer/go-colorful v1.2.0 // indirect43+ github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
41- github.com/mattn/go-colorable v0.1.13 // indirect44+ github.com/mattn/go-colorable v0.1.14 // indirect
42 github.com/mattn/go-isatty v0.0.20 // indirect45 github.com/mattn/go-isatty v0.0.20 // indirect
43 github.com/mattn/go-localereader v0.0.1 // indirect46 github.com/mattn/go-localereader v0.0.1 // indirect
44- github.com/mattn/go-runewidth v0.0.16 // indirect47+ github.com/mattn/go-runewidth v0.0.19 // indirect
45 github.com/mfridman/interpolate v0.0.2 // indirect48 github.com/mfridman/interpolate v0.0.2 // indirect
46 github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect49 github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
47 github.com/muesli/cancelreader v0.2.2 // indirect50 github.com/muesli/cancelreader v0.2.2 // indirect
@@ -50,19 +53,19 @@ require (
50 github.com/pmezard/go-difflib v1.0.0 // indirect53 github.com/pmezard/go-difflib v1.0.0 // indirect
51 github.com/rivo/uniseg v0.4.7 // indirect54 github.com/rivo/uniseg v0.4.7 // indirect
52 github.com/russross/blackfriday/v2 v2.1.0 // indirect55 github.com/russross/blackfriday/v2 v2.1.0 // indirect
53- github.com/sagikazarmark/locafero v0.11.0 // indirect56+ github.com/sagikazarmark/locafero v0.12.0 // indirect
54 github.com/sahilm/fuzzy v0.1.1 // indirect57 github.com/sahilm/fuzzy v0.1.1 // indirect
55 github.com/sethvargo/go-retry v0.3.0 // indirect58 github.com/sethvargo/go-retry v0.3.0 // indirect
56- github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
57 github.com/spf13/afero v1.15.0 // indirect59 github.com/spf13/afero v1.15.0 // indirect
58 github.com/spf13/cast v1.10.0 // indirect60 github.com/spf13/cast v1.10.0 // indirect
59 github.com/spf13/pflag v1.0.10 // indirect61 github.com/spf13/pflag v1.0.10 // indirect
60- github.com/stretchr/objx v0.5.2 // indirect62+ github.com/stretchr/objx v0.5.3 // indirect
61 github.com/subosito/gotenv v1.6.0 // indirect63 github.com/subosito/gotenv v1.6.0 // indirect
62- github.com/tetratelabs/wazero v1.10.1 // indirect64+ github.com/tetratelabs/wazero v1.11.0 // indirect
63 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect65 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
64 go.uber.org/multierr v1.11.0 // indirect66 go.uber.org/multierr v1.11.0 // indirect
65 go.yaml.in/yaml/v3 v3.0.4 // indirect67 go.yaml.in/yaml/v3 v3.0.4 // indirect
66 golang.org/x/sync v0.19.0 // indirect68 golang.org/x/sync v0.19.0 // indirect
67- golang.org/x/text v0.32.0 // indirect69+ golang.org/x/text v0.33.0 // indirect
70+ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
68 )71 )
M go.sum +43 -38 generated file: collapsed
M internal/infrastructure/persistence/sqlite/history_repo.go +25 -4
@@ -50,9 +50,14 @@ func (r *historyRepo) FindByURL(ctx context.Context, url string) (*entity.Histor
5050 func (r *historyRepo) Search(ctx context.Context, query string, limit int) ([]entity.HistoryMatch, error) {
5151 log := logging.FromContext(ctx)
5252
53+ // Replace periods with spaces before splitting - FTS5 tokenizer treats periods as separators
54+ // so "gordon.bne" in URLs is indexed as "gordon" + "bne" tokens
55+ // This allows searching "gordon.bne" to match URLs containing both terms
56+ normalizedQuery := strings.ReplaceAll(query, ".", " ")
57+
5358 // Split query into words and add prefix matching to each
5459 // This enables multi-word searches like "github issues" -> "github* issues*"
55- words := strings.Fields(query)
60+ words := strings.Fields(normalizedQuery)
5661 if len(words) == 0 {
5762 return []entity.HistoryMatch{}, nil
5863 }
@@ -66,13 +71,16 @@ func (r *historyRepo) Search(ctx context.Context, query string, limit int) ([]en
6671 ftsQuery := strings.Join(parts, " ")
6772
6873 // Search both URL and title columns, then merge results
69- urlRows, urlErr := r.queries.SearchHistoryFTSUrl(ctx, sqlc.SearchHistoryFTSUrlParams{
74+ // Use domain-boosted query for URL search to prioritize domain matches
75+ // Use sanitized first word for domain boost (intentional: domain boost targets first search term)
76+ urlRows, urlErr := r.queries.SearchHistoryFTSUrlWithDomainBoost(ctx, sqlc.SearchHistoryFTSUrlWithDomainBoostParams{
77+ Term: sql.NullString{String: sanitizeFTS5Word(words[0]), Valid: true},
7078 Query: ftsQuery,
7179 Limit: int64(limit),
7280 })
7381 if urlErr != nil {
7482 log.Debug().Err(urlErr).Str("query", query).Msg("FTS URL search failed")
75- urlRows = []sqlc.History{}
83+ urlRows = []sqlc.SearchHistoryFTSUrlWithDomainBoostRow{}
7684 }
7785
7886 titleRows, titleErr := r.queries.SearchHistoryFTSTitle(ctx, sqlc.SearchHistoryFTSTitleParams{
@@ -97,7 +105,7 @@ func (r *historyRepo) Search(ctx context.Context, query string, limit int) ([]en
97105 if !seen[row.ID] {
98106 seen[row.ID] = true
99107 matches = append(matches, entity.HistoryMatch{
100- Entry: historyFromRow(row),
108+ Entry: historyFromDomainBoostRow(row),
101109 Score: 1.0,
102110 })
103111 }
@@ -120,8 +128,21 @@ func (r *historyRepo) Search(ctx context.Context, query string, limit int) ([]en
120128 return matches, nil
121129 }
122130
131+func historyFromDomainBoostRow(row sqlc.SearchHistoryFTSUrlWithDomainBoostRow) *entity.HistoryEntry {
132+ return &entity.HistoryEntry{
133+ ID: row.ID,
134+ URL: row.Url,
135+ Title: row.Title.String,
136+ FaviconURL: row.FaviconUrl.String,
137+ VisitCount: row.VisitCount.Int64,
138+ LastVisited: row.LastVisited.Time,
139+ CreatedAt: row.CreatedAt.Time,
140+ }
141+}
142+
123143 // sanitizeFTS5Word removes FTS5 special characters from a search word.
124144 // FTS5 special chars: " * ( ) : ^ - AND OR NOT NEAR
145+// Note: Periods are handled earlier by replacing with spaces (tokenizer separator)
125146 func sanitizeFTS5Word(word string) string {
126147 // Remove characters that have special meaning in FTS5
127148 var result strings.Builder
@@ -50,9 +50,14 @@ func (r *historyRepo) FindByURL(ctx context.Context, url string) (*entity.Histor
50 func (r *historyRepo) Search(ctx context.Context, query string, limit int) ([]entity.HistoryMatch, error) {50 func (r *historyRepo) Search(ctx context.Context, query string, limit int) ([]entity.HistoryMatch, error) {
51 log := logging.FromContext(ctx)51 log := logging.FromContext(ctx)
52 52
53+ // Replace periods with spaces before splitting - FTS5 tokenizer treats periods as separators
54+ // so "gordon.bne" in URLs is indexed as "gordon" + "bne" tokens
55+ // This allows searching "gordon.bne" to match URLs containing both terms
56+ normalizedQuery := strings.ReplaceAll(query, ".", " ")
57+
53 // Split query into words and add prefix matching to each58 // Split query into words and add prefix matching to each
54 // This enables multi-word searches like "github issues" -> "github* issues*"59 // This enables multi-word searches like "github issues" -> "github* issues*"
55- words := strings.Fields(query)60+ words := strings.Fields(normalizedQuery)
56 if len(words) == 0 {61 if len(words) == 0 {
57 return []entity.HistoryMatch{}, nil62 return []entity.HistoryMatch{}, nil
58 }63 }
@@ -66,13 +71,16 @@ func (r *historyRepo) Search(ctx context.Context, query string, limit int) ([]en
66 ftsQuery := strings.Join(parts, " ")71 ftsQuery := strings.Join(parts, " ")
67 72
68 // Search both URL and title columns, then merge results73 // Search both URL and title columns, then merge results
69- urlRows, urlErr := r.queries.SearchHistoryFTSUrl(ctx, sqlc.SearchHistoryFTSUrlParams{74+ // Use domain-boosted query for URL search to prioritize domain matches
75+ // Use sanitized first word for domain boost (intentional: domain boost targets first search term)
76+ urlRows, urlErr := r.queries.SearchHistoryFTSUrlWithDomainBoost(ctx, sqlc.SearchHistoryFTSUrlWithDomainBoostParams{
77+ Term: sql.NullString{String: sanitizeFTS5Word(words[0]), Valid: true},
70 Query: ftsQuery,78 Query: ftsQuery,
71 Limit: int64(limit),79 Limit: int64(limit),
72 })80 })
73 if urlErr != nil {81 if urlErr != nil {
74 log.Debug().Err(urlErr).Str("query", query).Msg("FTS URL search failed")82 log.Debug().Err(urlErr).Str("query", query).Msg("FTS URL search failed")
75- urlRows = []sqlc.History{}83+ urlRows = []sqlc.SearchHistoryFTSUrlWithDomainBoostRow{}
76 }84 }
77 85
78 titleRows, titleErr := r.queries.SearchHistoryFTSTitle(ctx, sqlc.SearchHistoryFTSTitleParams{86 titleRows, titleErr := r.queries.SearchHistoryFTSTitle(ctx, sqlc.SearchHistoryFTSTitleParams{
@@ -97,7 +105,7 @@ func (r *historyRepo) Search(ctx context.Context, query string, limit int) ([]en
97 if !seen[row.ID] {105 if !seen[row.ID] {
98 seen[row.ID] = true106 seen[row.ID] = true
99 matches = append(matches, entity.HistoryMatch{107 matches = append(matches, entity.HistoryMatch{
100- Entry: historyFromRow(row),108+ Entry: historyFromDomainBoostRow(row),
101 Score: 1.0,109 Score: 1.0,
102 })110 })
103 }111 }
@@ -120,8 +128,21 @@ func (r *historyRepo) Search(ctx context.Context, query string, limit int) ([]en
120 return matches, nil128 return matches, nil
121 }129 }
122 130
131+func historyFromDomainBoostRow(row sqlc.SearchHistoryFTSUrlWithDomainBoostRow) *entity.HistoryEntry {
132+ return &entity.HistoryEntry{
133+ ID: row.ID,
134+ URL: row.Url,
135+ Title: row.Title.String,
136+ FaviconURL: row.FaviconUrl.String,
137+ VisitCount: row.VisitCount.Int64,
138+ LastVisited: row.LastVisited.Time,
139+ CreatedAt: row.CreatedAt.Time,
140+ }
141+}
142+
123 // sanitizeFTS5Word removes FTS5 special characters from a search word.143 // sanitizeFTS5Word removes FTS5 special characters from a search word.
124 // FTS5 special chars: " * ( ) : ^ - AND OR NOT NEAR144 // FTS5 special chars: " * ( ) : ^ - AND OR NOT NEAR
145+// Note: Periods are handled earlier by replacing with spaces (tokenizer separator)
125 func sanitizeFTS5Word(word string) string {146 func sanitizeFTS5Word(word string) string {
126 // Remove characters that have special meaning in FTS5147 // Remove characters that have special meaning in FTS5
127 var result strings.Builder148 var result strings.Builder
M internal/infrastructure/persistence/sqlite/history_repo_test.go +71 -0
@@ -484,3 +484,74 @@ func TestHistoryRepository_Search_PrefixMatching(t *testing.T) {
484484 assert.True(t, urls["https://github.com"])
485485 assert.True(t, urls["https://gitlab.com"])
486486 }
487+
488+func TestHistoryRepository_Search_DomainLikeQuery(t *testing.T) {
489+ ctx := historyTestCtx()
490+ dbPath := filepath.Join(t.TempDir(), "dumber.db")
491+
492+ db, err := sqlite.NewConnection(ctx, dbPath)
493+ require.NoError(t, err)
494+ t.Cleanup(func() { _ = db.Close() })
495+
496+ repo := sqlite.NewHistoryRepository(db)
497+
498+ // Save entries with domain-like URLs
499+ entries := []*entity.HistoryEntry{
500+ {URL: "https://gordon.bnema.dev/", Title: "Gordon - Self-hosted Deployment Platform"},
501+ {URL: "https://example.com", Title: "Example Site"},
502+ {URL: "https://other.gordon.io", Title: "Another Gordon Site"},
503+ }
504+ for _, e := range entries {
505+ require.NoError(t, repo.Save(ctx, e))
506+ }
507+
508+ // Search with period in query (domain-like) - periods should be treated as separators
509+ // "gordon.bnem" should match "gordon.bnema.dev" (tokens: gordon, bnem -> gordon, bnema)
510+ results, err := repo.Search(ctx, "gordon.bnem", 10)
511+ require.NoError(t, err)
512+ require.NotEmpty(t, results, "domain-like query 'gordon.bnem' should match gordon.bnema.dev")
513+
514+ // Verify the gordon.bnema.dev entry is found
515+ found := false
516+ for _, r := range results {
517+ if r.Entry.URL == "https://gordon.bnema.dev/" {
518+ found = true
519+ break
520+ }
521+ }
522+ assert.True(t, found, "gordon.bnema.dev should be in results")
523+}
524+
525+func TestHistoryRepository_Search_SpecialCharacters(t *testing.T) {
526+ ctx := historyTestCtx()
527+ dbPath := filepath.Join(t.TempDir(), "dumber.db")
528+
529+ db, err := sqlite.NewConnection(ctx, dbPath)
530+ require.NoError(t, err)
531+ t.Cleanup(func() { _ = db.Close() })
532+
533+ repo := sqlite.NewHistoryRepository(db)
534+
535+ // Save entry
536+ require.NoError(t, repo.Save(ctx, &entity.HistoryEntry{
537+ URL: "https://example.com/path",
538+ Title: "Example Page",
539+ }))
540+
541+ // Search with FTS5 special characters - should not cause errors
542+ specialQueries := []string{
543+ "example:", // colon
544+ "example*", // asterisk
545+ "example()", // parentheses
546+ `example"test`, // quotes
547+ "example^", // caret
548+ "example-test", // hyphen
549+ }
550+
551+ for _, q := range specialQueries {
552+ results, err := repo.Search(ctx, q, 10)
553+ require.NoError(t, err, "query %q should not cause error", q)
554+ // May or may not find results, but should not error
555+ _ = results
556+ }
557+}
@@ -484,3 +484,74 @@ func TestHistoryRepository_Search_PrefixMatching(t *testing.T) {
484 assert.True(t, urls["https://github.com"])484 assert.True(t, urls["https://github.com"])
485 assert.True(t, urls["https://gitlab.com"])485 assert.True(t, urls["https://gitlab.com"])
486 }486 }
487+
488+func TestHistoryRepository_Search_DomainLikeQuery(t *testing.T) {
489+ ctx := historyTestCtx()
490+ dbPath := filepath.Join(t.TempDir(), "dumber.db")
491+
492+ db, err := sqlite.NewConnection(ctx, dbPath)
493+ require.NoError(t, err)
494+ t.Cleanup(func() { _ = db.Close() })
495+
496+ repo := sqlite.NewHistoryRepository(db)
497+
498+ // Save entries with domain-like URLs
499+ entries := []*entity.HistoryEntry{
500+ {URL: "https://gordon.bnema.dev/", Title: "Gordon - Self-hosted Deployment Platform"},
501+ {URL: "https://example.com", Title: "Example Site"},
502+ {URL: "https://other.gordon.io", Title: "Another Gordon Site"},
503+ }
504+ for _, e := range entries {
505+ require.NoError(t, repo.Save(ctx, e))
506+ }
507+
508+ // Search with period in query (domain-like) - periods should be treated as separators
509+ // "gordon.bnem" should match "gordon.bnema.dev" (tokens: gordon, bnem -> gordon, bnema)
510+ results, err := repo.Search(ctx, "gordon.bnem", 10)
511+ require.NoError(t, err)
512+ require.NotEmpty(t, results, "domain-like query 'gordon.bnem' should match gordon.bnema.dev")
513+
514+ // Verify the gordon.bnema.dev entry is found
515+ found := false
516+ for _, r := range results {
517+ if r.Entry.URL == "https://gordon.bnema.dev/" {
518+ found = true
519+ break
520+ }
521+ }
522+ assert.True(t, found, "gordon.bnema.dev should be in results")
523+}
524+
525+func TestHistoryRepository_Search_SpecialCharacters(t *testing.T) {
526+ ctx := historyTestCtx()
527+ dbPath := filepath.Join(t.TempDir(), "dumber.db")
528+
529+ db, err := sqlite.NewConnection(ctx, dbPath)
530+ require.NoError(t, err)
531+ t.Cleanup(func() { _ = db.Close() })
532+
533+ repo := sqlite.NewHistoryRepository(db)
534+
535+ // Save entry
536+ require.NoError(t, repo.Save(ctx, &entity.HistoryEntry{
537+ URL: "https://example.com/path",
538+ Title: "Example Page",
539+ }))
540+
541+ // Search with FTS5 special characters - should not cause errors
542+ specialQueries := []string{
543+ "example:", // colon
544+ "example*", // asterisk
545+ "example()", // parentheses
546+ `example"test`, // quotes
547+ "example^", // caret
548+ "example-test", // hyphen
549+ }
550+
551+ for _, q := range specialQueries {
552+ results, err := repo.Search(ctx, q, 10)
553+ require.NoError(t, err, "query %q should not cause error", q)
554+ // May or may not find results, but should not error
555+ _ = results
556+ }
557+}
M internal/infrastructure/persistence/sqlite/queries/history.sql +15 -0
@@ -57,6 +57,21 @@ WHERE fts.url MATCH @query
5757 ORDER BY h.visit_count DESC, h.last_visited DESC
5858 LIMIT @limit;
5959
60+-- name: SearchHistoryFTSUrlWithDomainBoost :many
61+SELECT h.id, h.url, h.title, h.favicon_url, h.visit_count, h.last_visited, h.created_at,
62+ CASE
63+ WHEN h.url LIKE '%://' || @term || '.%' THEN 2
64+ WHEN h.url LIKE '%://%.' || @term || '.%' THEN 2
65+ WHEN h.url LIKE '%://' || @term || '/%' THEN 2
66+ WHEN h.url LIKE '%://%.' || @term || '/%' THEN 1
67+ ELSE 0
68+ END as domain_boost
69+FROM history_fts fts
70+JOIN history h ON fts.rowid = h.id
71+WHERE fts.url MATCH @query
72+ORDER BY domain_boost DESC, h.visit_count DESC, h.last_visited DESC
73+LIMIT @limit;
74+
6075 -- name: SearchHistoryFTSTitle :many
6176 SELECT h.id, h.url, h.title, h.favicon_url, h.visit_count, h.last_visited, h.created_at
6277 FROM history_fts fts
@@ -57,6 +57,21 @@ WHERE fts.url MATCH @query
57 ORDER BY h.visit_count DESC, h.last_visited DESC57 ORDER BY h.visit_count DESC, h.last_visited DESC
58 LIMIT @limit;58 LIMIT @limit;
59 59
60+-- name: SearchHistoryFTSUrlWithDomainBoost :many
61+SELECT h.id, h.url, h.title, h.favicon_url, h.visit_count, h.last_visited, h.created_at,
62+ CASE
63+ WHEN h.url LIKE '%://' || @term || '.%' THEN 2
64+ WHEN h.url LIKE '%://%.' || @term || '.%' THEN 2
65+ WHEN h.url LIKE '%://' || @term || '/%' THEN 2
66+ WHEN h.url LIKE '%://%.' || @term || '/%' THEN 1
67+ ELSE 0
68+ END as domain_boost
69+FROM history_fts fts
70+JOIN history h ON fts.rowid = h.id
71+WHERE fts.url MATCH @query
72+ORDER BY domain_boost DESC, h.visit_count DESC, h.last_visited DESC
73+LIMIT @limit;
74+
60 -- name: SearchHistoryFTSTitle :many75 -- name: SearchHistoryFTSTitle :many
61 SELECT h.id, h.url, h.title, h.favicon_url, h.visit_count, h.last_visited, h.created_at76 SELECT h.id, h.url, h.title, h.favicon_url, h.visit_count, h.last_visited, h.created_at
62 FROM history_fts fts77 FROM history_fts fts
M internal/infrastructure/persistence/sqlite/sqlc/history.sql.go +65 -0
@@ -529,6 +529,71 @@ func (q *Queries) SearchHistoryFTSUrl(ctx context.Context, arg SearchHistoryFTSU
529529 return items, nil
530530 }
531531
532+const SearchHistoryFTSUrlWithDomainBoost = `-- name: SearchHistoryFTSUrlWithDomainBoost :many
533+SELECT h.id, h.url, h.title, h.favicon_url, h.visit_count, h.last_visited, h.created_at,
534+ CASE
535+ WHEN h.url LIKE '%://' || ?1 || '.%' THEN 2
536+ WHEN h.url LIKE '%://%.' || ?1 || '.%' THEN 2
537+ WHEN h.url LIKE '%://' || ?1 || '/%' THEN 2
538+ WHEN h.url LIKE '%://%.' || ?1 || '/%' THEN 1
539+ ELSE 0
540+ END as domain_boost
541+FROM history_fts fts
542+JOIN history h ON fts.rowid = h.id
543+WHERE fts.url MATCH ?2
544+ORDER BY domain_boost DESC, h.visit_count DESC, h.last_visited DESC
545+LIMIT ?3
546+`
547+
548+type SearchHistoryFTSUrlWithDomainBoostParams struct {
549+ Term sql.NullString `json:"term"`
550+ Query string `json:"query"`
551+ Limit int64 `json:"limit"`
552+}
553+
554+type SearchHistoryFTSUrlWithDomainBoostRow struct {
555+ ID int64 `json:"id"`
556+ Url string `json:"url"`
557+ Title sql.NullString `json:"title"`
558+ FaviconUrl sql.NullString `json:"favicon_url"`
559+ VisitCount sql.NullInt64 `json:"visit_count"`
560+ LastVisited sql.NullTime `json:"last_visited"`
561+ CreatedAt sql.NullTime `json:"created_at"`
562+ DomainBoost int64 `json:"domain_boost"`
563+}
564+
565+func (q *Queries) SearchHistoryFTSUrlWithDomainBoost(ctx context.Context, arg SearchHistoryFTSUrlWithDomainBoostParams) ([]SearchHistoryFTSUrlWithDomainBoostRow, error) {
566+ rows, err := q.db.QueryContext(ctx, SearchHistoryFTSUrlWithDomainBoost, arg.Term, arg.Query, arg.Limit)
567+ if err != nil {
568+ return nil, err
569+ }
570+ defer rows.Close()
571+ items := []SearchHistoryFTSUrlWithDomainBoostRow{}
572+ for rows.Next() {
573+ var i SearchHistoryFTSUrlWithDomainBoostRow
574+ if err := rows.Scan(
575+ &i.ID,
576+ &i.Url,
577+ &i.Title,
578+ &i.FaviconUrl,
579+ &i.VisitCount,
580+ &i.LastVisited,
581+ &i.CreatedAt,
582+ &i.DomainBoost,
583+ ); err != nil {
584+ return nil, err
585+ }
586+ items = append(items, i)
587+ }
588+ if err := rows.Close(); err != nil {
589+ return nil, err
590+ }
591+ if err := rows.Err(); err != nil {
592+ return nil, err
593+ }
594+ return items, nil
595+}
596+
532597 const UpsertHistory = `-- name: UpsertHistory :exec
533598 INSERT INTO history (url, title, favicon_url)
534599 VALUES (?, ?, ?)
@@ -529,6 +529,71 @@ func (q *Queries) SearchHistoryFTSUrl(ctx context.Context, arg SearchHistoryFTSU
529 return items, nil529 return items, nil
530 }530 }
531 531
532+const SearchHistoryFTSUrlWithDomainBoost = `-- name: SearchHistoryFTSUrlWithDomainBoost :many
533+SELECT h.id, h.url, h.title, h.favicon_url, h.visit_count, h.last_visited, h.created_at,
534+ CASE
535+ WHEN h.url LIKE '%://' || ?1 || '.%' THEN 2
536+ WHEN h.url LIKE '%://%.' || ?1 || '.%' THEN 2
537+ WHEN h.url LIKE '%://' || ?1 || '/%' THEN 2
538+ WHEN h.url LIKE '%://%.' || ?1 || '/%' THEN 1
539+ ELSE 0
540+ END as domain_boost
541+FROM history_fts fts
542+JOIN history h ON fts.rowid = h.id
543+WHERE fts.url MATCH ?2
544+ORDER BY domain_boost DESC, h.visit_count DESC, h.last_visited DESC
545+LIMIT ?3
546+`
547+
548+type SearchHistoryFTSUrlWithDomainBoostParams struct {
549+ Term sql.NullString `json:"term"`
550+ Query string `json:"query"`
551+ Limit int64 `json:"limit"`
552+}
553+
554+type SearchHistoryFTSUrlWithDomainBoostRow struct {
555+ ID int64 `json:"id"`
556+ Url string `json:"url"`
557+ Title sql.NullString `json:"title"`
558+ FaviconUrl sql.NullString `json:"favicon_url"`
559+ VisitCount sql.NullInt64 `json:"visit_count"`
560+ LastVisited sql.NullTime `json:"last_visited"`
561+ CreatedAt sql.NullTime `json:"created_at"`
562+ DomainBoost int64 `json:"domain_boost"`
563+}
564+
565+func (q *Queries) SearchHistoryFTSUrlWithDomainBoost(ctx context.Context, arg SearchHistoryFTSUrlWithDomainBoostParams) ([]SearchHistoryFTSUrlWithDomainBoostRow, error) {
566+ rows, err := q.db.QueryContext(ctx, SearchHistoryFTSUrlWithDomainBoost, arg.Term, arg.Query, arg.Limit)
567+ if err != nil {
568+ return nil, err
569+ }
570+ defer rows.Close()
571+ items := []SearchHistoryFTSUrlWithDomainBoostRow{}
572+ for rows.Next() {
573+ var i SearchHistoryFTSUrlWithDomainBoostRow
574+ if err := rows.Scan(
575+ &i.ID,
576+ &i.Url,
577+ &i.Title,
578+ &i.FaviconUrl,
579+ &i.VisitCount,
580+ &i.LastVisited,
581+ &i.CreatedAt,
582+ &i.DomainBoost,
583+ ); err != nil {
584+ return nil, err
585+ }
586+ items = append(items, i)
587+ }
588+ if err := rows.Close(); err != nil {
589+ return nil, err
590+ }
591+ if err := rows.Err(); err != nil {
592+ return nil, err
593+ }
594+ return items, nil
595+}
596+
532 const UpsertHistory = `-- name: UpsertHistory :exec597 const UpsertHistory = `-- name: UpsertHistory :exec
533 INSERT INTO history (url, title, favicon_url)598 INSERT INTO history (url, title, favicon_url)
534 VALUES (?, ?, ?)599 VALUES (?, ?, ?)
M internal/infrastructure/persistence/sqlite/sqlc/querier.go +1 -0
@@ -72,6 +72,7 @@ type Querier interface {
7272 SearchHistory(ctx context.Context, arg SearchHistoryParams) ([]History, error)
7373 SearchHistoryFTSTitle(ctx context.Context, arg SearchHistoryFTSTitleParams) ([]History, error)
7474 SearchHistoryFTSUrl(ctx context.Context, arg SearchHistoryFTSUrlParams) ([]History, error)
75+ SearchHistoryFTSUrlWithDomainBoost(ctx context.Context, arg SearchHistoryFTSUrlWithDomainBoostParams) ([]SearchHistoryFTSUrlWithDomainBoostRow, error)
7576 SetFavoriteFolder(ctx context.Context, arg SetFavoriteFolderParams) error
7677 SetFavoriteShortcut(ctx context.Context, arg SetFavoriteShortcutParams) error
7778 SetZoomLevel(ctx context.Context, arg SetZoomLevelParams) error
@@ -72,6 +72,7 @@ type Querier interface {
72 SearchHistory(ctx context.Context, arg SearchHistoryParams) ([]History, error)72 SearchHistory(ctx context.Context, arg SearchHistoryParams) ([]History, error)
73 SearchHistoryFTSTitle(ctx context.Context, arg SearchHistoryFTSTitleParams) ([]History, error)73 SearchHistoryFTSTitle(ctx context.Context, arg SearchHistoryFTSTitleParams) ([]History, error)
74 SearchHistoryFTSUrl(ctx context.Context, arg SearchHistoryFTSUrlParams) ([]History, error)74 SearchHistoryFTSUrl(ctx context.Context, arg SearchHistoryFTSUrlParams) ([]History, error)
75+ SearchHistoryFTSUrlWithDomainBoost(ctx context.Context, arg SearchHistoryFTSUrlWithDomainBoostParams) ([]SearchHistoryFTSUrlWithDomainBoostRow, error)
75 SetFavoriteFolder(ctx context.Context, arg SetFavoriteFolderParams) error76 SetFavoriteFolder(ctx context.Context, arg SetFavoriteFolderParams) error
76 SetFavoriteShortcut(ctx context.Context, arg SetFavoriteShortcutParams) error77 SetFavoriteShortcut(ctx context.Context, arg SetFavoriteShortcutParams) error
77 SetZoomLevel(ctx context.Context, arg SetZoomLevelParams) error78 SetZoomLevel(ctx context.Context, arg SetZoomLevelParams) error
M internal/ui/component/omnibox.go +13 -19
@@ -1272,21 +1272,21 @@ func (o *Omnibox) navigateToSelected() {
12721272
12731273 var targetURL string
12741274
1275- // Check if user typed a URL-like string (contains . and no spaces)
1276- // If so, navigate to that URL directly instead of the selection
1277- if o.looksLikeURL(entryText) {
1278- targetURL = o.buildURL(entryText)
1279- } else if idx < 0 {
1280- // No selection - use entry text as URL/search
1281- targetURL = o.buildURL(entryText)
1282- } else if mode == ViewModeHistory {
1283- if idx < len(suggestions) {
1284- targetURL = suggestions[idx].URL
1275+ // If user has selected a result, navigate to that result
1276+ // Otherwise, treat entry text as URL/search
1277+ if idx >= 0 {
1278+ if mode == ViewModeHistory {
1279+ if idx < len(suggestions) {
1280+ targetURL = suggestions[idx].URL
1281+ }
1282+ } else {
1283+ if idx < len(favorites) {
1284+ targetURL = favorites[idx].URL
1285+ }
12851286 }
12861287 } else {
1287- if idx < len(favorites) {
1288- targetURL = favorites[idx].URL
1289- }
1288+ // No selection - use entry text as URL/search
1289+ targetURL = o.buildURL(entryText)
12901290 }
12911291
12921292 if targetURL == "" {
@@ -1300,12 +1300,6 @@ func (o *Omnibox) navigateToSelected() {
13001300 }
13011301 }
13021302
1303-// looksLikeURL checks if the text appears to be a URL (not a search query).
1304-// Returns true for strings like "github.com", "google.com/search", etc.
1305-func (o *Omnibox) looksLikeURL(text string) bool {
1306- return url.LooksLikeURL(text)
1307-}
1308-
13091303 // toggleFavorite adds or removes the selected item from favorites.
13101304 // In History mode: adds the selected item to favorites
13111305 // In Favorites mode: removes the selected item from favorites
@@ -1272,21 +1272,21 @@ func (o *Omnibox) navigateToSelected() {
1272 1272
1273 var targetURL string1273 var targetURL string
1274 1274
1275- // Check if user typed a URL-like string (contains . and no spaces)1275+ // If user has selected a result, navigate to that result
1276- // If so, navigate to that URL directly instead of the selection1276+ // Otherwise, treat entry text as URL/search
1277- if o.looksLikeURL(entryText) {1277+ if idx >= 0 {
1278- targetURL = o.buildURL(entryText)1278+ if mode == ViewModeHistory {
1279- } else if idx < 0 {1279+ if idx < len(suggestions) {
1280- // No selection - use entry text as URL/search1280+ targetURL = suggestions[idx].URL
1281- targetURL = o.buildURL(entryText)1281+ }
1282- } else if mode == ViewModeHistory {1282+ } else {
1283- if idx < len(suggestions) {1283+ if idx < len(favorites) {
1284- targetURL = suggestions[idx].URL1284+ targetURL = favorites[idx].URL
1285+ }
1285 }1286 }
1286 } else {1287 } else {
1287- if idx < len(favorites) {1288+ // No selection - use entry text as URL/search
1288- targetURL = favorites[idx].URL1289+ targetURL = o.buildURL(entryText)
1289- }
1290 }1290 }
1291 1291
1292 if targetURL == "" {1292 if targetURL == "" {
@@ -1300,12 +1300,6 @@ func (o *Omnibox) navigateToSelected() {
1300 }1300 }
1301 }1301 }
1302 1302
1303-// looksLikeURL checks if the text appears to be a URL (not a search query).
1304-// Returns true for strings like "github.com", "google.com/search", etc.
1305-func (o *Omnibox) looksLikeURL(text string) bool {
1306- return url.LooksLikeURL(text)
1307-}
1308-
1309 // toggleFavorite adds or removes the selected item from favorites.1303 // toggleFavorite adds or removes the selected item from favorites.
1310 // In History mode: adds the selected item to favorites1304 // In History mode: adds the selected item to favorites
1311 // In Favorites mode: removes the selected item from favorites1305 // In Favorites mode: removes the selected item from favorites
M webui/package-lock.json +445 -441 generated file: collapsed

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close