refactor(services): extract search shortcuts to shared constants
- Create gui/src/constants/shortcuts.ts with DEFAULT_SHORTCUTS - Define SearchShortcutDefinition interface - Add helper functions: isValidShortcut, getShortcutDescription, SHORTCUT_KEYS - Update AppService to use DEFAULT_SHORTCUTS for fallback - Simplify loadShortcuts() error handling with shared constants - Add deprecation notice: backend Go config is source of truth - Keep constants only for development fallback when backend unavailable
- commit
- cdb4cef561106f0524b1d3f9dcfa6a484d382a0e
- author
- bamen · · 2025-10-06 18:25 UTC
- committer
- bamen · · 2025-10-06 17:40 UTC
- parent
- 926c7d2
- signature
- unsigned
2 files · +105 -32
A gui/src/constants/shortcuts.ts +91 -0
| @@ -0,0 +1,91 @@ | |||
| 1 | + | /** | |
| 2 | + | * Search shortcuts configuration | |
| 3 | + | * | |
| 4 | + | * @deprecated This file is only used as a fallback. The real source of truth | |
| 5 | + | * is the backend Go config (internal/config/defaults.go). Shortcuts should be | |
| 6 | + | * fetched from the backend via /api/config or the messaging bridge. | |
| 7 | + | * | |
| 8 | + | * Only kept for development fallback when backend is unavailable. | |
| 9 | + | */ | |
| 10 | + | ||
| 11 | + | export interface SearchShortcutDefinition { | |
| 12 | + | shortcut: string; | |
| 13 | + | url_template: string; | |
| 14 | + | description: string; | |
| 15 | + | } | |
| 16 | + | ||
| 17 | + | /** | |
| 18 | + | * Default search shortcuts | |
| 19 | + | * Alphabetically ordered by shortcut key | |
| 20 | + | */ | |
| 21 | + | export const DEFAULT_SHORTCUTS: Record<string, SearchShortcutDefinition> = { | |
| 22 | + | ddg: { | |
| 23 | + | shortcut: "ddg", | |
| 24 | + | url_template: "https://duckduckgo.com/?q={query}", | |
| 25 | + | description: "DuckDuckGo search", | |
| 26 | + | }, | |
| 27 | + | g: { | |
| 28 | + | shortcut: "g", | |
| 29 | + | url_template: "https://www.google.com/search?q={query}", | |
| 30 | + | description: "Google search", | |
| 31 | + | }, | |
| 32 | + | gh: { | |
| 33 | + | shortcut: "gh", | |
| 34 | + | url_template: "https://github.com/search?q={query}", | |
| 35 | + | description: "GitHub search", | |
| 36 | + | }, | |
| 37 | + | go: { | |
| 38 | + | shortcut: "go", | |
| 39 | + | url_template: "https://pkg.go.dev/search?q={query}", | |
| 40 | + | description: "Go package search", | |
| 41 | + | }, | |
| 42 | + | mdn: { | |
| 43 | + | shortcut: "mdn", | |
| 44 | + | url_template: "https://developer.mozilla.org/en-US/search?q={query}", | |
| 45 | + | description: "MDN Web Docs search", | |
| 46 | + | }, | |
| 47 | + | npm: { | |
| 48 | + | shortcut: "npm", | |
| 49 | + | url_template: "https://www.npmjs.com/search?q={query}", | |
| 50 | + | description: "npm package search", | |
| 51 | + | }, | |
| 52 | + | r: { | |
| 53 | + | shortcut: "r", | |
| 54 | + | url_template: "https://www.reddit.com/search?q={query}", | |
| 55 | + | description: "Reddit search", | |
| 56 | + | }, | |
| 57 | + | so: { | |
| 58 | + | shortcut: "so", | |
| 59 | + | url_template: "https://stackoverflow.com/search?q={query}", | |
| 60 | + | description: "Stack Overflow search", | |
| 61 | + | }, | |
| 62 | + | w: { | |
| 63 | + | shortcut: "w", | |
| 64 | + | url_template: "https://en.wikipedia.org/wiki/{query}", | |
| 65 | + | description: "Wikipedia search", | |
| 66 | + | }, | |
| 67 | + | yt: { | |
| 68 | + | shortcut: "yt", | |
| 69 | + | url_template: "https://www.youtube.com/results?search_query={query}", | |
| 70 | + | description: "YouTube search", | |
| 71 | + | }, | |
| 72 | + | }; | |
| 73 | + | ||
| 74 | + | /** | |
| 75 | + | * Get array of shortcut keys for validation | |
| 76 | + | */ | |
| 77 | + | export const SHORTCUT_KEYS = Object.keys(DEFAULT_SHORTCUTS); | |
| 78 | + | ||
| 79 | + | /** | |
| 80 | + | * Check if a string is a valid shortcut | |
| 81 | + | */ | |
| 82 | + | export function isValidShortcut(key: string): boolean { | |
| 83 | + | return key in DEFAULT_SHORTCUTS; | |
| 84 | + | } | |
| 85 | + | ||
| 86 | + | /** | |
| 87 | + | * Get shortcut description by key | |
| 88 | + | */ | |
| 89 | + | export function getShortcutDescription(key: string): string | null { | |
| 90 | + | return DEFAULT_SHORTCUTS[key]?.description ?? null; | |
| 91 | + | } | |
| @@ -0,0 +1,91 @@ | |||||
| 1 | + | /** | |||
| 2 | + | * Search shortcuts configuration | |||
| 3 | + | * | |||
| 4 | + | * @deprecated This file is only used as a fallback. The real source of truth | |||
| 5 | + | * is the backend Go config (internal/config/defaults.go). Shortcuts should be | |||
| 6 | + | * fetched from the backend via /api/config or the messaging bridge. | |||
| 7 | + | * | |||
| 8 | + | * Only kept for development fallback when backend is unavailable. | |||
| 9 | + | */ | |||
| 10 | + | ||||
| 11 | + | export interface SearchShortcutDefinition { | |||
| 12 | + | shortcut: string; | |||
| 13 | + | url_template: string; | |||
| 14 | + | description: string; | |||
| 15 | + | } | |||
| 16 | + | ||||
| 17 | + | /** | |||
| 18 | + | * Default search shortcuts | |||
| 19 | + | * Alphabetically ordered by shortcut key | |||
| 20 | + | */ | |||
| 21 | + | export const DEFAULT_SHORTCUTS: Record<string, SearchShortcutDefinition> = { | |||
| 22 | + | ddg: { | |||
| 23 | + | shortcut: "ddg", | |||
| 24 | + | url_template: "https://duckduckgo.com/?q={query}", | |||
| 25 | + | description: "DuckDuckGo search", | |||
| 26 | + | }, | |||
| 27 | + | g: { | |||
| 28 | + | shortcut: "g", | |||
| 29 | + | url_template: "https://www.google.com/search?q={query}", | |||
| 30 | + | description: "Google search", | |||
| 31 | + | }, | |||
| 32 | + | gh: { | |||
| 33 | + | shortcut: "gh", | |||
| 34 | + | url_template: "https://github.com/search?q={query}", | |||
| 35 | + | description: "GitHub search", | |||
| 36 | + | }, | |||
| 37 | + | go: { | |||
| 38 | + | shortcut: "go", | |||
| 39 | + | url_template: "https://pkg.go.dev/search?q={query}", | |||
| 40 | + | description: "Go package search", | |||
| 41 | + | }, | |||
| 42 | + | mdn: { | |||
| 43 | + | shortcut: "mdn", | |||
| 44 | + | url_template: "https://developer.mozilla.org/en-US/search?q={query}", | |||
| 45 | + | description: "MDN Web Docs search", | |||
| 46 | + | }, | |||
| 47 | + | npm: { | |||
| 48 | + | shortcut: "npm", | |||
| 49 | + | url_template: "https://www.npmjs.com/search?q={query}", | |||
| 50 | + | description: "npm package search", | |||
| 51 | + | }, | |||
| 52 | + | r: { | |||
| 53 | + | shortcut: "r", | |||
| 54 | + | url_template: "https://www.reddit.com/search?q={query}", | |||
| 55 | + | description: "Reddit search", | |||
| 56 | + | }, | |||
| 57 | + | so: { | |||
| 58 | + | shortcut: "so", | |||
| 59 | + | url_template: "https://stackoverflow.com/search?q={query}", | |||
| 60 | + | description: "Stack Overflow search", | |||
| 61 | + | }, | |||
| 62 | + | w: { | |||
| 63 | + | shortcut: "w", | |||
| 64 | + | url_template: "https://en.wikipedia.org/wiki/{query}", | |||
| 65 | + | description: "Wikipedia search", | |||
| 66 | + | }, | |||
| 67 | + | yt: { | |||
| 68 | + | shortcut: "yt", | |||
| 69 | + | url_template: "https://www.youtube.com/results?search_query={query}", | |||
| 70 | + | description: "YouTube search", | |||
| 71 | + | }, | |||
| 72 | + | }; | |||
| 73 | + | ||||
| 74 | + | /** | |||
| 75 | + | * Get array of shortcut keys for validation | |||
| 76 | + | */ | |||
| 77 | + | export const SHORTCUT_KEYS = Object.keys(DEFAULT_SHORTCUTS); | |||
| 78 | + | ||||
| 79 | + | /** | |||
| 80 | + | * Check if a string is a valid shortcut | |||
| 81 | + | */ | |||
| 82 | + | export function isValidShortcut(key: string): boolean { | |||
| 83 | + | return key in DEFAULT_SHORTCUTS; | |||
| 84 | + | } | |||
| 85 | + | ||||
| 86 | + | /** | |||
| 87 | + | * Get shortcut description by key | |||
| 88 | + | */ | |||
| 89 | + | export function getShortcutDescription(key: string): string | null { | |||
| 90 | + | return DEFAULT_SHORTCUTS[key]?.description ?? null; | |||
| 91 | + | } | |||
M gui/src/services/app.ts +14 -32
| @@ -1,4 +1,5 @@ | |||
| 1 | 1 | import type { HistoryEntry, SearchShortcut } from "../types/generated.js"; | |
| 2 | + | import { DEFAULT_SHORTCUTS } from "../constants/shortcuts.js"; | |
| 2 | 3 | ||
| 3 | 4 | export class AppService { | |
| 4 | 5 | private history: HistoryEntry[] = []; | |
| @@ -97,38 +98,19 @@ export class AppService { | |||
| 97 | 98 | this.shortcuts = normalized; | |
| 98 | 99 | } catch (error) { | |
| 99 | 100 | console.error("Failed to load shortcuts:", error); | |
| 100 | - | // Mock shortcuts for development/fallback | |
| 101 | - | this.shortcuts = { | |
| 102 | - | g: { | |
| 103 | - | id: 0, | |
| 104 | - | shortcut: "g", | |
| 105 | - | url_template: "https://google.com/search?q={query}", | |
| 106 | - | description: "Google Search", | |
| 107 | - | created_at: null, | |
| 108 | - | }, | |
| 109 | - | gh: { | |
| 110 | - | id: 0, | |
| 111 | - | shortcut: "gh", | |
| 112 | - | url_template: "https://github.com/search?q={query}", | |
| 113 | - | description: "GitHub Search", | |
| 114 | - | created_at: null, | |
| 115 | - | }, | |
| 116 | - | so: { | |
| 117 | - | id: 0, | |
| 118 | - | shortcut: "so", | |
| 119 | - | url_template: "https://stackoverflow.com/search?q={query}", | |
| 120 | - | description: "Stack Overflow", | |
| 121 | - | created_at: null, | |
| 122 | - | }, | |
| 123 | - | w: { | |
| 124 | - | id: 0, | |
| 125 | - | shortcut: "w", | |
| 126 | - | url_template: | |
| 127 | - | "https://en.wikipedia.org/wiki/Special:Search?search={query}", | |
| 128 | - | description: "Wikipedia", | |
| 129 | - | created_at: null, | |
| 130 | - | }, | |
| 131 | - | }; | |
| 101 | + | // Mock shortcuts for development/fallback using shared constants | |
| 102 | + | this.shortcuts = Object.fromEntries( | |
| 103 | + | Object.entries(DEFAULT_SHORTCUTS).map(([key, def]) => [ | |
| 104 | + | key, | |
| 105 | + | { | |
| 106 | + | id: 0, | |
| 107 | + | shortcut: def.shortcut, | |
| 108 | + | url_template: def.url_template, | |
| 109 | + | description: def.description, | |
| 110 | + | created_at: null, | |
| 111 | + | }, | |
| 112 | + | ]) | |
| 113 | + | ); | |
| 132 | 114 | } | |
| 133 | 115 | } | |
| 134 | 116 | ||
| @@ -1,4 +1,5 @@ | |||||
| 1 | import type { HistoryEntry, SearchShortcut } from "../types/generated.js"; | 1 | import type { HistoryEntry, SearchShortcut } from "../types/generated.js"; | ||
| 2 | + | import { DEFAULT_SHORTCUTS } from "../constants/shortcuts.js"; | |||
| 2 | 3 | ||||
| 3 | export class AppService { | 4 | export class AppService { | ||
| 4 | private history: HistoryEntry[] = []; | 5 | private history: HistoryEntry[] = []; | ||
| @@ -97,38 +98,19 @@ export class AppService { | |||||
| 97 | this.shortcuts = normalized; | 98 | this.shortcuts = normalized; | ||
| 98 | } catch (error) { | 99 | } catch (error) { | ||
| 99 | console.error("Failed to load shortcuts:", error); | 100 | console.error("Failed to load shortcuts:", error); | ||
| 100 | - | // Mock shortcuts for development/fallback | 101 | + | // Mock shortcuts for development/fallback using shared constants |
| 101 | - | this.shortcuts = { | 102 | + | this.shortcuts = Object.fromEntries( |
| 102 | - | g: { | 103 | + | Object.entries(DEFAULT_SHORTCUTS).map(([key, def]) => [ |
| 103 | - | id: 0, | 104 | + | key, |
| 104 | - | shortcut: "g", | 105 | + | { |
| 105 | - | url_template: "https://google.com/search?q={query}", | 106 | + | id: 0, |
| 106 | - | description: "Google Search", | 107 | + | shortcut: def.shortcut, |
| 107 | - | created_at: null, | 108 | + | url_template: def.url_template, |
| 108 | - | }, | 109 | + | description: def.description, |
| 109 | - | gh: { | 110 | + | created_at: null, |
| 110 | - | id: 0, | 111 | + | }, |
| 111 | - | shortcut: "gh", | 112 | + | ]) |
| 112 | - | url_template: "https://github.com/search?q={query}", | 113 | + | ); |
| 113 | - | description: "GitHub Search", | |||
| 114 | - | created_at: null, | |||
| 115 | - | }, | |||
| 116 | - | so: { | |||
| 117 | - | id: 0, | |||
| 118 | - | shortcut: "so", | |||
| 119 | - | url_template: "https://stackoverflow.com/search?q={query}", | |||
| 120 | - | description: "Stack Overflow", | |||
| 121 | - | created_at: null, | |||
| 122 | - | }, | |||
| 123 | - | w: { | |||
| 124 | - | id: 0, | |||
| 125 | - | shortcut: "w", | |||
| 126 | - | url_template: | |||
| 127 | - | "https://en.wikipedia.org/wiki/Special:Search?search={query}", | |||
| 128 | - | description: "Wikipedia", | |||
| 129 | - | created_at: null, | |||
| 130 | - | }, | |||
| 131 | - | }; | |||
| 132 | } | 114 | } | ||
| 133 | } | 115 | } | ||
| 134 | 116 | ||||