detached babf2f5

feat(omnibox): add search shortcuts data structures and bridge

- Add SearchShortcut interface (url, description)
- Add searchShortcuts state to omnibox store with getter and updater
- Add setSearchShortcuts() method to OmniboxBridge
- Add fetchSearchShortcuts() to fetch from /api/config endpoint
- Update OmniboxMessage type to include 'get_search_shortcuts'
- Normalize backend response to match SearchShortcut interface
commit
babf2f5c0a1593f6ea0303aabf5516be6a35273f
author
bamen · · 2025-10-06 20:29 UTC
committer
bamen · · 2025-10-06 19:27 UTC
parent
5b002da
signature
unsigned
3 files · +47 -2
M gui/src/components/omnibox/messaging.ts +31 -1
@@ -4,7 +4,7 @@
44 * TypeScript interface for Go-JavaScript communication
55 */
66
7-import type { OmniboxMessage, OmniboxMessageBridge, Suggestion } from "./types";
7+import type { OmniboxMessage, OmniboxMessageBridge, Suggestion, SearchShortcut } from "./types";
88 import { omniboxStore } from "./stores.svelte.ts";
99
1010 export class OmniboxBridge implements OmniboxMessageBridge {
@@ -46,6 +46,14 @@ export class OmniboxBridge implements OmniboxMessageBridge {
4646 omniboxStore.updateSuggestions(suggestions);
4747 }
4848
49+ /**
50+ * Update search shortcuts from Go backend
51+ */
52+ setSearchShortcuts(shortcuts: Record<string, SearchShortcut>): void {
53+ console.log("📝 [DEBUG] Received search shortcuts from backend:", shortcuts);
54+ omniboxStore.updateSearchShortcuts(shortcuts);
55+ }
56+
4957 /**
5058 * Handle navigation request
5159 */
@@ -65,6 +73,28 @@ export class OmniboxBridge implements OmniboxMessageBridge {
6573 this.postMessage({ type: "query", q, limit: lim });
6674 }
6775
76+ /**
77+ * Fetch search shortcuts from backend config
78+ */
79+ async fetchSearchShortcuts(): Promise<void> {
80+ try {
81+ const cfg = await fetch("/api/config").then((r) => r.json());
82+ const raw = cfg?.search_shortcuts || {};
83+ // Normalize to match our SearchShortcut type
84+ const normalized: Record<string, SearchShortcut> = {};
85+ for (const [key, value] of Object.entries(raw)) {
86+ const v = value as Record<string, unknown>;
87+ normalized[key] = {
88+ url: (v.url ?? v.URL ?? "") as string,
89+ description: (v.description ?? v.Description ?? "") as string,
90+ };
91+ }
92+ this.setSearchShortcuts(normalized);
93+ } catch (error) {
94+ console.error("Failed to fetch search shortcuts:", error);
95+ }
96+ }
97+
6898 // Suggestions are returned via setSuggestions() when native handler responds
6999 }
70100
@@ -4,7 +4,7 @@
4 * TypeScript interface for Go-JavaScript communication4 * TypeScript interface for Go-JavaScript communication
5 */5 */
6 6
7-import type { OmniboxMessage, OmniboxMessageBridge, Suggestion } from "./types";7+import type { OmniboxMessage, OmniboxMessageBridge, Suggestion, SearchShortcut } from "./types";
8 import { omniboxStore } from "./stores.svelte.ts";8 import { omniboxStore } from "./stores.svelte.ts";
9 9
10 export class OmniboxBridge implements OmniboxMessageBridge {10 export class OmniboxBridge implements OmniboxMessageBridge {
@@ -46,6 +46,14 @@ export class OmniboxBridge implements OmniboxMessageBridge {
46 omniboxStore.updateSuggestions(suggestions);46 omniboxStore.updateSuggestions(suggestions);
47 }47 }
48 48
49+ /**
50+ * Update search shortcuts from Go backend
51+ */
52+ setSearchShortcuts(shortcuts: Record<string, SearchShortcut>): void {
53+ console.log("📝 [DEBUG] Received search shortcuts from backend:", shortcuts);
54+ omniboxStore.updateSearchShortcuts(shortcuts);
55+ }
56+
49 /**57 /**
50 * Handle navigation request58 * Handle navigation request
51 */59 */
@@ -65,6 +73,28 @@ export class OmniboxBridge implements OmniboxMessageBridge {
65 this.postMessage({ type: "query", q, limit: lim });73 this.postMessage({ type: "query", q, limit: lim });
66 }74 }
67 75
76+ /**
77+ * Fetch search shortcuts from backend config
78+ */
79+ async fetchSearchShortcuts(): Promise<void> {
80+ try {
81+ const cfg = await fetch("/api/config").then((r) => r.json());
82+ const raw = cfg?.search_shortcuts || {};
83+ // Normalize to match our SearchShortcut type
84+ const normalized: Record<string, SearchShortcut> = {};
85+ for (const [key, value] of Object.entries(raw)) {
86+ const v = value as Record<string, unknown>;
87+ normalized[key] = {
88+ url: (v.url ?? v.URL ?? "") as string,
89+ description: (v.description ?? v.Description ?? "") as string,
90+ };
91+ }
92+ this.setSearchShortcuts(normalized);
93+ } catch (error) {
94+ console.error("Failed to fetch search shortcuts:", error);
95+ }
96+ }
97+
68 // Suggestions are returned via setSuggestions() when native handler responds98 // Suggestions are returned via setSuggestions() when native handler responds
69 }99 }
70 100
M gui/src/components/omnibox/stores.svelte.ts +9 -0
@@ -10,6 +10,7 @@ import type {
1010 FindMatch,
1111 HighlightNode,
1212 OmniboxConfig,
13+ SearchShortcut,
1314 } from "./types";
1415
1516 // Default configuration
@@ -31,6 +32,7 @@ let inputValue = $state("");
3132 let highlightNodes = $state<HighlightNode[]>([]);
3233 let prevOverflow = $state("");
3334 let debounceTimer = $state(0);
35+let searchShortcuts = $state<Record<string, SearchShortcut>>({});
3436
3537 // Configuration
3638 let config = $state<Required<OmniboxConfig>>(DEFAULT_CONFIG);
@@ -70,6 +72,9 @@ export const omniboxStore = {
7072 get config() {
7173 return config;
7274 },
75+ get searchShortcuts() {
76+ return searchShortcuts;
77+ },
7378
7479 // Computed getters
7580 get hasContent() {
@@ -233,6 +238,10 @@ export const omniboxStore = {
233238 config = { ...config, ...newConfig };
234239 },
235240
241+ updateSearchShortcuts(shortcuts: Record<string, SearchShortcut>) {
242+ searchShortcuts = shortcuts;
243+ },
244+
236245 reset() {
237246 visible = false;
238247 mode = "omnibox";
@@ -10,6 +10,7 @@ import type {
10 FindMatch,10 FindMatch,
11 HighlightNode,11 HighlightNode,
12 OmniboxConfig,12 OmniboxConfig,
13+ SearchShortcut,
13 } from "./types";14 } from "./types";
14 15
15 // Default configuration16 // Default configuration
@@ -31,6 +32,7 @@ let inputValue = $state("");
31 let highlightNodes = $state<HighlightNode[]>([]);32 let highlightNodes = $state<HighlightNode[]>([]);
32 let prevOverflow = $state("");33 let prevOverflow = $state("");
33 let debounceTimer = $state(0);34 let debounceTimer = $state(0);
35+let searchShortcuts = $state<Record<string, SearchShortcut>>({});
34 36
35 // Configuration37 // Configuration
36 let config = $state<Required<OmniboxConfig>>(DEFAULT_CONFIG);38 let config = $state<Required<OmniboxConfig>>(DEFAULT_CONFIG);
@@ -70,6 +72,9 @@ export const omniboxStore = {
70 get config() {72 get config() {
71 return config;73 return config;
72 },74 },
75+ get searchShortcuts() {
76+ return searchShortcuts;
77+ },
73 78
74 // Computed getters79 // Computed getters
75 get hasContent() {80 get hasContent() {
@@ -233,6 +238,10 @@ export const omniboxStore = {
233 config = { ...config, ...newConfig };238 config = { ...config, ...newConfig };
234 },239 },
235 240
241+ updateSearchShortcuts(shortcuts: Record<string, SearchShortcut>) {
242+ searchShortcuts = shortcuts;
243+ },
244+
236 reset() {245 reset() {
237 visible = false;246 visible = false;
238 mode = "omnibox";247 mode = "omnibox";
M gui/src/components/omnibox/types.ts +7 -1
@@ -9,8 +9,13 @@ export interface Suggestion {
99 favicon?: string;
1010 }
1111
12+export interface SearchShortcut {
13+ url: string;
14+ description: string;
15+}
16+
1217 export interface OmniboxMessage {
13- type: "navigate" | "query";
18+ type: "navigate" | "query" | "get_search_shortcuts";
1419 url?: string;
1520 q?: string;
1621 limit?: number;
@@ -45,6 +50,7 @@ export interface OmniboxState {
4550 inputValue: string;
4651 highlightNodes: HighlightNode[];
4752 prevOverflow: string;
53+ searchShortcuts: Record<string, SearchShortcut>;
4854 }
4955
5056 // Message bridge interface for Go communication
@@ -9,8 +9,13 @@ export interface Suggestion {
9 favicon?: string;9 favicon?: string;
10 }10 }
11 11
12+export interface SearchShortcut {
13+ url: string;
14+ description: string;
15+}
16+
12 export interface OmniboxMessage {17 export interface OmniboxMessage {
13- type: "navigate" | "query";18+ type: "navigate" | "query" | "get_search_shortcuts";
14 url?: string;19 url?: string;
15 q?: string;20 q?: string;
16 limit?: number;21 limit?: number;
@@ -45,6 +50,7 @@ export interface OmniboxState {
45 inputValue: string;50 inputValue: string;
46 highlightNodes: HighlightNode[];51 highlightNodes: HighlightNode[];
47 prevOverflow: string;52 prevOverflow: string;
53+ searchShortcuts: Record<string, SearchShortcut>;
48 }54 }
49 55
50 // Message bridge interface for Go communication56 // Message bridge interface for Go communication

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