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 @@ | |||
| 4 | 4 | * 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 | 8 | import { omniboxStore } from "./stores.svelte.ts"; | |
| 9 | 9 | ||
| 10 | 10 | export class OmniboxBridge implements OmniboxMessageBridge { | |
| @@ -46,6 +46,14 @@ export class OmniboxBridge implements OmniboxMessageBridge { | |||
| 46 | 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 | 58 | * Handle navigation request | |
| 51 | 59 | */ | |
| @@ -65,6 +73,28 @@ export class OmniboxBridge implements OmniboxMessageBridge { | |||
| 65 | 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 | 98 | // Suggestions are returned via setSuggestions() when native handler responds | |
| 69 | 99 | } | |
| 70 | 100 | ||
| @@ -4,7 +4,7 @@ | |||||
| 4 | * TypeScript interface for Go-JavaScript communication | 4 | * 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 request | 58 | * 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 responds | 98 | // 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 { | |||
| 10 | 10 | FindMatch, | |
| 11 | 11 | HighlightNode, | |
| 12 | 12 | OmniboxConfig, | |
| 13 | + | SearchShortcut, | |
| 13 | 14 | } from "./types"; | |
| 14 | 15 | ||
| 15 | 16 | // Default configuration | |
| @@ -31,6 +32,7 @@ let inputValue = $state(""); | |||
| 31 | 32 | let highlightNodes = $state<HighlightNode[]>([]); | |
| 32 | 33 | let prevOverflow = $state(""); | |
| 33 | 34 | let debounceTimer = $state(0); | |
| 35 | + | let searchShortcuts = $state<Record<string, SearchShortcut>>({}); | |
| 34 | 36 | ||
| 35 | 37 | // Configuration | |
| 36 | 38 | let config = $state<Required<OmniboxConfig>>(DEFAULT_CONFIG); | |
| @@ -70,6 +72,9 @@ export const omniboxStore = { | |||
| 70 | 72 | get config() { | |
| 71 | 73 | return config; | |
| 72 | 74 | }, | |
| 75 | + | get searchShortcuts() { | |
| 76 | + | return searchShortcuts; | |
| 77 | + | }, | |
| 73 | 78 | ||
| 74 | 79 | // Computed getters | |
| 75 | 80 | get hasContent() { | |
| @@ -233,6 +238,10 @@ export const omniboxStore = { | |||
| 233 | 238 | config = { ...config, ...newConfig }; | |
| 234 | 239 | }, | |
| 235 | 240 | ||
| 241 | + | updateSearchShortcuts(shortcuts: Record<string, SearchShortcut>) { | |
| 242 | + | searchShortcuts = shortcuts; | |
| 243 | + | }, | |
| 244 | + | ||
| 236 | 245 | reset() { | |
| 237 | 246 | visible = false; | |
| 238 | 247 | 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 configuration | 16 | // 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 | // Configuration | 37 | // 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 getters | 79 | // 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 { | |||
| 9 | 9 | favicon?: string; | |
| 10 | 10 | } | |
| 11 | 11 | ||
| 12 | + | export interface SearchShortcut { | |
| 13 | + | url: string; | |
| 14 | + | description: string; | |
| 15 | + | } | |
| 16 | + | ||
| 12 | 17 | export interface OmniboxMessage { | |
| 13 | - | type: "navigate" | "query"; | |
| 18 | + | type: "navigate" | "query" | "get_search_shortcuts"; | |
| 14 | 19 | url?: string; | |
| 15 | 20 | q?: string; | |
| 16 | 21 | limit?: number; | |
| @@ -45,6 +50,7 @@ export interface OmniboxState { | |||
| 45 | 50 | inputValue: string; | |
| 46 | 51 | highlightNodes: HighlightNode[]; | |
| 47 | 52 | prevOverflow: string; | |
| 53 | + | searchShortcuts: Record<string, SearchShortcut>; | |
| 48 | 54 | } | |
| 49 | 55 | ||
| 50 | 56 | // 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 communication | 56 | // Message bridge interface for Go communication | ||