1import type { HistoryEntry, SearchShortcut } from '../types/generated.js';
2
3export class AppService {
4 private history: HistoryEntry[] = [];
5 private shortcuts: Record<string, SearchShortcut> = {};
6
7 constructor() {}
8
9 async initialize(): Promise<void> {
10 try {
11 await Promise.all([
12 this.loadHistory(),
13 this.loadShortcuts()
14 ]);
15 } catch (error) {
16 console.error('Failed to initialize app:', error);
17 }
18 }
19
20 async loadHistory(): Promise<void> {
21 try {
22 // Use message bridge instead of fetch
23 return new Promise((resolve, reject) => {
24 // Set up response handler
25 window.__dumber_history_recent = (data: HistoryEntry[]) => {
26 this.history = Array.isArray(data) ? data : [];
27 resolve();
28 };
29
30 window.__dumber_history_error = (error: string) => {
31 console.error('Failed to load history:', error);
32 reject(new Error(error));
33 };
34
35 // Send message to Go backend
36 const bridge = window.webkit?.messageHandlers?.dumber;
37 if (bridge && typeof bridge.postMessage === 'function') {
38 bridge.postMessage(JSON.stringify({
39 type: 'history_recent',
40 limit: 50,
41 offset: 0
42 }));
43 } else {
44 reject(new Error('WebKit message handler not available'));
45 }
46 });
47 } catch (error) {
48 console.error('Failed to load history:', error);
49 // Mock history for development/fallback
50 this.history = [
51 {
52 id: 1,
53 url: 'https://github.com/wailsapp/wails',
54 title: 'Wails Framework',
55 visit_count: 3,
56 last_visited: new Date().toISOString(),
57 created_at: new Date().toISOString()
58 },
59 {
60 id: 2,
61 url: 'https://go.dev',
62 title: 'The Go Programming Language',
63 visit_count: 2,
64 last_visited: new Date().toISOString(),
65 created_at: new Date().toISOString()
66 },
67 {
68 id: 3,
69 url: 'https://developer.mozilla.org',
70 title: 'MDN Web Docs',
71 visit_count: 1,
72 last_visited: new Date().toISOString(),
73 created_at: new Date().toISOString()
74 }
75 ];
76 }
77 }
78
79 async loadShortcuts(): Promise<void> {
80 try {
81 const cfg = await fetch('/api/config').then(r => r.json());
82 const raw = cfg?.search_shortcuts || {};
83 // Normalize field casing from backend (supports URL/Description and url/description)
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 id: 0,
89 shortcut: key,
90 url_template: (v.url ?? v.URL ?? '') as string,
91 description: (v.description ?? v.Description ?? '') as string,
92 created_at: null
93 };
94 }
95 this.shortcuts = normalized;
96 } catch (error) {
97 console.error('Failed to load shortcuts:', error);
98 // Mock shortcuts for development/fallback
99 this.shortcuts = {
100 'g': { id: 0, shortcut: 'g', url_template: 'https://google.com/search?q={query}', description: 'Google Search', created_at: null },
101 'gh': { id: 0, shortcut: 'gh', url_template: 'https://github.com/search?q={query}', description: 'GitHub Search', created_at: null },
102 'so': { id: 0, shortcut: 'so', url_template: 'https://stackoverflow.com/search?q={query}', description: 'Stack Overflow', created_at: null },
103 'w': { id: 0, shortcut: 'w', url_template: 'https://en.wikipedia.org/wiki/Special:Search?search={query}', description: 'Wikipedia', created_at: null }
104 };
105 }
106 }
107
108 getHistory(): HistoryEntry[] {
109 return this.history;
110 }
111
112 getShortcuts(): Record<string, SearchShortcut> {
113 return this.shortcuts;
114 }
115
116 async copyToClipboard(text: string): Promise<void> {
117 try {
118 await navigator.clipboard.writeText(text);
119 console.log('Copied to clipboard:', text);
120 } catch (err) {
121 console.error('Failed to copy:', err);
122 }
123 }
124}