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('info', 'frontend', 'init', 'BrowserControlsService initializing...');
12 this.debugConsole.addLog('info', 'frontend', 'init', 'Running without Wails bindings');
13
14 this.initializeEventListeners();
15 this.debugConsole.addLog('info', 'frontend', 'init', 'BrowserControlsService initialized ✓');
16 }
17
18
19 private initializeEventListeners(): void {
20 // Keyboard event handler for browser controls
21 document.addEventListener('keydown', this.handleKeyboardEvent.bind(this));
22
23 // Mouse button handler for navigation
24 document.addEventListener('mousedown', this.handleMouseEvent.bind(this));
25
26 // URL change listener (for SPA navigation)
27 window.addEventListener('popstate', this.handleURLChange.bind(this));
28 }
29
30 private async handleKeyboardEvent(event: KeyboardEvent): Promise<void> {
31 // Skip F12 (debug console toggle)
32 if (event.key === 'F12') {
33 return;
34 }
35
36 // Log all keyboard events to debug console
37 this.debugConsole.logKeyboardEvent(event);
38
39 const { ctrlKey, metaKey, shiftKey, altKey, key } = event;
40 const isCmd = ctrlKey || metaKey;
41
42 // Zoom controls handled natively by GTK; don't intercept
43 if (isCmd && (key === '+' || key === '=' || key === '-' || key === '0')) {
44 return; // allow native handler to process
45 }
46
47 // Copy URL
48 if (isCmd && shiftKey && key === 'C') {
49 this.debugConsole.addLog('info', 'frontend', 'action', 'Copy URL triggered');
50 event.preventDefault();
51 await this.copyCurrentURL();
52 return;
53 }
54
55 // Navigation
56 if (altKey && key === 'ArrowLeft') {
57 this.debugConsole.addLog('info', 'frontend', 'action', 'Go Back triggered');
58 event.preventDefault();
59 history.back();
60 return;
61 }
62
63 if (altKey && key === 'ArrowRight') {
64 this.debugConsole.addLog('info', 'frontend', 'action', 'Go Forward triggered');
65 event.preventDefault();
66 history.forward();
67 return;
68 }
69 }
70
71 private async handleMouseEvent(event: MouseEvent): Promise<void> {
72 // Log all mouse events to debug console
73 this.debugConsole.logMouseEvent(event);
74
75 // Mouse button 3 (back button)
76 if (event.button === 3) {
77 this.debugConsole.addLog('info', 'frontend', 'action', 'Mouse Back button triggered');
78 event.preventDefault();
79 await this.goBack();
80 return;
81 }
82
83 // Mouse button 4 (forward button)
84 if (event.button === 4) {
85 this.debugConsole.addLog('info', 'frontend', 'action', 'Mouse Forward button triggered');
86 event.preventDefault();
87 await this.goForward();
88 return;
89 }
90 }
91
92 private handleURLChange(): void {
93 this.setCurrentURL(window.location.href);
94 this.loadSavedZoom();
95 }
96
97 // Zoom is handled natively; no JS-based zoom adjustments needed.
98
99 async copyCurrentURL(): Promise<void> {
100 try {
101 const url = this.getCurrentURL();
102 await navigator.clipboard.writeText(url);
103
104 // URL copied successfully - no backend notification needed
105
106 this.notifications.show('URL copied to clipboard!', 'success');
107 } catch (error) {
108 console.error('Failed to copy URL:', error);
109 this.notifications.show('Failed to copy URL', 'error');
110 }
111 }
112
113 async goBack(): Promise<void> {
114 this.debugConsole.addLog('info', 'frontend', 'service', 'Calling GoBack');
115
116 try {
117 await BrowserService.GoBack();
118 this.debugConsole.logServiceCall('BrowserService', 'GoBack', []);
119 history.back();
120 this.debugConsole.addLog('info', 'frontend', 'navigation', 'Browser navigated back');
121 } catch (error) {
122 this.debugConsole.logServiceCall('BrowserService', 'GoBack', [], null, error as Error);
123 console.error('Failed to go back:', error);
124 }
125 }
126
127 async goForward(): Promise<void> {
128 this.debugConsole.addLog('info', 'frontend', 'service', 'Calling GoForward');
129
130 try {
131 await BrowserService.GoForward();
132 this.debugConsole.logServiceCall('BrowserService', 'GoForward', []);
133 history.forward();
134 this.debugConsole.addLog('info', 'frontend', 'navigation', 'Browser navigated forward');
135 } catch (error) {
136 this.debugConsole.logServiceCall('BrowserService', 'GoForward', [], null, error as Error);
137 console.error('Failed to go forward:', error);
138 }
139 }
140
141 getCurrentURL(): string {
142 return this.currentURL || window.location.href;
143 }
144
145 setCurrentURL(url: string): void {
146 this.currentURL = url;
147 this.updateWindowTitle(url);
148 }
149
150 private async updateWindowTitle(url: string): Promise<void> {
151 try {
152 // Extract domain or path for title
153 let title = 'Dumber Browser';
154 if (url && url !== '/') {
155 const urlObj = new URL(url);
156 title = `Dumber - ${urlObj.hostname}`;
157 }
158
159 document.title = title;
160
161 // Backend title updates handled natively; no frontend call
162 } catch (error) {
163 console.error('Failed to update title:', error);
164 }
165 }
166
167 async loadSavedZoom(): Promise<void> {
168 // Zoom persistence applied natively on page load
169 }
170
171 async initialize(): Promise<void> {
172 // Check if there's an initial URL from backend (direct navigation)
173 // Set initial URL from browser location (homepage)
174 this.setCurrentURL(window.location.href);
175
176 // Load saved zoom level (only if we're staying on homepage)
177 // Zoom handled natively
178 }
179}