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([this.loadHistory(), this.loadShortcuts()]);
12 } catch (error) {
13 console.error("Failed to initialize app:", error);
14 }
15 }
16
17 async loadHistory(): Promise<void> {
18 try {
19 // Use message bridge instead of fetch
20 return new Promise((resolve, reject) => {
21 // Set up response handler
22 window.__dumber_history_recent = (data: HistoryEntry[]) => {
23 this.history = Array.isArray(data) ? data : [];
24 resolve();
25 };
26
27 window.__dumber_history_error = (error: string) => {
28 console.error("Failed to load history:", error);
29 reject(new Error(error));
30 };
31
32 // Send message to Go backend
33 const bridge = window.webkit?.messageHandlers?.dumber;
34 if (bridge && typeof bridge.postMessage === "function") {
35 bridge.postMessage(
36 JSON.stringify({
37 type: "history_recent",
38 limit: 50,
39 offset: 0,
40 }),
41 );
42 } else {
43 reject(new Error("WebKit message handler not available"));
44 }
45 });
46 } catch (error) {
47 console.error("Failed to load history:", error);
48 // Mock history for development/fallback
49 this.history = [
50 {
51 id: 1,
52 url: "https://github.com/wailsapp/wails",
53 title: "Wails Framework",
54 visit_count: 3,
55 last_visited: new Date().toISOString(),
56 created_at: new Date().toISOString(),
57 favicon_url: null,
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 favicon_url: null,
67 },
68 {
69 id: 3,
70 url: "https://developer.mozilla.org",
71 title: "MDN Web Docs",
72 visit_count: 1,
73 last_visited: new Date().toISOString(),
74 created_at: new Date().toISOString(),
75 favicon_url: null,
76 },
77 ];
78 }
79 }
80
81 async loadShortcuts(): Promise<void> {
82 return new Promise((resolve, reject) => {
83 // Set up response handler
84 window.__dumber_search_shortcuts = (data: unknown) => {
85 // Type guard
86 if (typeof data !== "object" || data === null) {
87 reject(new Error("Invalid shortcuts data"));
88 return;
89 }
90 const dataObj = data as Record<string, unknown>;
91 // Normalize field casing from backend (supports URL/Description and url/description)
92 const normalized: Record<string, SearchShortcut> = {};
93 for (const [key, value] of Object.entries(dataObj)) {
94 const v = value as Record<string, unknown>;
95 normalized[key] = {
96 id: 0,
97 shortcut: key,
98 url_template: (v.url ?? v.URL ?? "") as string,
99 description: (v.description ?? v.Description ?? "") as string,
100 created_at: null,
101 };
102 }
103 this.shortcuts = normalized;
104 resolve();
105 };
106
107 window.__dumber_search_shortcuts_error = (error: string) => {
108 console.error("Failed to load shortcuts:", error);
109 reject(new Error(error));
110 };
111
112 // Send message to Go backend
113 const bridge = window.webkit?.messageHandlers?.dumber;
114 if (bridge && typeof bridge.postMessage === "function") {
115 bridge.postMessage(
116 JSON.stringify({
117 type: "get_search_shortcuts",
118 }),
119 );
120 } else {
121 reject(new Error("WebKit message handler not available"));
122 }
123 });
124 }
125
126 getHistory(): HistoryEntry[] {
127 return this.history;
128 }
129
130 getShortcuts(): Record<string, SearchShortcut> {
131 return this.shortcuts;
132 }
133
134 async copyToClipboard(text: string): Promise<void> {
135 try {
136 await navigator.clipboard.writeText(text);
137 console.log("Copied to clipboard:", text);
138 } catch (err) {
139 console.error("Failed to copy:", err);
140 }
141 }
142}