1import { NotificationService } from "./notifications.js";
2import { DebugConsoleService } from "./debug-console.js";
3
4export class BrowserControlsService {
5 private currentURL = "";
6 private notifications = new NotificationService();
7 private debugConsole = new DebugConsoleService();
8
9 constructor() {
10 // Debug logging with enhanced details
11 this.debugConsole.addLog(
12 "info",
13 "frontend",
14 "init",
15 "BrowserControlsService initializing...",
16 );
17 this.debugConsole.addLog(
18 "info",
19 "frontend",
20 "init",
21 "Running without Wails bindings",
22 );
23
24 this.initializeEventListeners();
25 this.debugConsole.addLog(
26 "info",
27 "frontend",
28 "init",
29 "BrowserControlsService initialized",
30 );
31 }
32
33 private initializeEventListeners(): void {
34 // Keyboard event handler for browser controls
35 document.addEventListener("keydown", this.handleKeyboardEvent.bind(this));
36
37 // Mouse button handler for navigation
38 document.addEventListener("mousedown", this.handleMouseEvent.bind(this));
39
40 // URL change listener (for SPA navigation)
41 window.addEventListener("popstate", this.handleURLChange.bind(this));
42 }
43
44 private async handleKeyboardEvent(event: KeyboardEvent): Promise<void> {
45 // Skip F12 (debug console toggle)
46 if (event.key === "F12") {
47 return;
48 }
49
50 // Log all keyboard events to debug console
51 this.debugConsole.logKeyboardEvent(event);
52
53 const { ctrlKey, metaKey, shiftKey, altKey, key } = event;
54 const isCmd = ctrlKey || metaKey;
55
56 // Zoom controls handled natively by GTK; don't intercept
57 if (isCmd && (key === "+" || key === "=" || key === "-" || key === "0")) {
58 return; // allow native handler to process
59 }
60
61 // Copy URL
62 if (isCmd && shiftKey && key === "C") {
63 this.debugConsole.addLog(
64 "info",
65 "frontend",
66 "action",
67 "Copy URL triggered",
68 );
69 event.preventDefault();
70 await this.copyCurrentURL();
71 return;
72 }
73
74 // Navigation
75 if (altKey && key === "ArrowLeft") {
76 this.debugConsole.addLog(
77 "info",
78 "frontend",
79 "action",
80 "Go Back triggered",
81 );
82 event.preventDefault();
83 history.back();
84 return;
85 }
86
87 if (altKey && key === "ArrowRight") {
88 this.debugConsole.addLog(
89 "info",
90 "frontend",
91 "action",
92 "Go Forward triggered",
93 );
94 event.preventDefault();
95 history.forward();
96 return;
97 }
98 }
99
100 private async handleMouseEvent(event: MouseEvent): Promise<void> {
101 // Log all mouse events to debug console
102 this.debugConsole.logMouseEvent(event);
103
104 // Mouse button 3 (back button)
105 if (event.button === 3) {
106 this.debugConsole.addLog(
107 "info",
108 "frontend",
109 "action",
110 "Mouse Back button triggered",
111 );
112 event.preventDefault();
113 await this.goBack();
114 return;
115 }
116
117 // Mouse button 4 (forward button)
118 if (event.button === 4) {
119 this.debugConsole.addLog(
120 "info",
121 "frontend",
122 "action",
123 "Mouse Forward button triggered",
124 );
125 event.preventDefault();
126 await this.goForward();
127 return;
128 }
129 }
130
131 private handleURLChange(): void {
132 this.setCurrentURL(window.location.href);
133 this.loadSavedZoom();
134 }
135
136 // Zoom is handled natively; no JS-based zoom adjustments needed.
137
138 async copyCurrentURL(): Promise<void> {
139 try {
140 const url = this.getCurrentURL();
141 await navigator.clipboard.writeText(url);
142
143 // URL copied successfully - no backend notification needed
144
145 this.notifications.show("URL copied to clipboard!", "success");
146 } catch (error) {
147 console.error("Failed to copy URL:", error);
148 this.notifications.show("Failed to copy URL", "error");
149 }
150 }
151
152 async goBack(): Promise<void> {
153 this.debugConsole.addLog("info", "frontend", "service", "Calling GoBack");
154
155 try {
156 // await BrowserService.GoBack(); // TODO: Implement BrowserService
157 this.debugConsole.logServiceCall("BrowserService", "GoBack", []);
158 history.back();
159 this.debugConsole.addLog(
160 "info",
161 "frontend",
162 "navigation",
163 "Browser navigated back",
164 );
165 } catch (error) {
166 this.debugConsole.logServiceCall(
167 "BrowserService",
168 "GoBack",
169 [],
170 null,
171 error as Error,
172 );
173 console.error("Failed to go back:", error);
174 }
175 }
176
177 async goForward(): Promise<void> {
178 this.debugConsole.addLog(
179 "info",
180 "frontend",
181 "service",
182 "Calling GoForward",
183 );
184
185 try {
186 // await BrowserService.GoForward(); // TODO: Implement BrowserService
187 this.debugConsole.logServiceCall("BrowserService", "GoForward", []);
188 history.forward();
189 this.debugConsole.addLog(
190 "info",
191 "frontend",
192 "navigation",
193 "Browser navigated forward",
194 );
195 } catch (error) {
196 this.debugConsole.logServiceCall(
197 "BrowserService",
198 "GoForward",
199 [],
200 null,
201 error as Error,
202 );
203 console.error("Failed to go forward:", error);
204 }
205 }
206
207 getCurrentURL(): string {
208 return this.currentURL || window.location.href;
209 }
210
211 setCurrentURL(url: string): void {
212 this.currentURL = url;
213 this.updateWindowTitle(url);
214 }
215
216 private async updateWindowTitle(url: string): Promise<void> {
217 try {
218 // Extract domain or path for title
219 let title = "Dumber Browser";
220 if (url && url !== "/") {
221 const urlObj = new URL(url);
222 title = `Dumber - ${urlObj.hostname}`;
223 }
224
225 document.title = title;
226
227 // Backend title updates handled natively; no frontend call
228 } catch (error) {
229 console.error("Failed to update title:", error);
230 }
231 }
232
233 async loadSavedZoom(): Promise<void> {
234 // Zoom persistence applied natively on page load
235 }
236
237 async initialize(): Promise<void> {
238 // Check if there's an initial URL from backend (direct navigation)
239 // Set initial URL from browser location (homepage)
240 this.setCurrentURL(window.location.href);
241
242 // Load saved zoom level (only if we're staying on homepage)
243 // Zoom handled natively
244 }
245}