v0.3.0 d226234

browser-controls.ts RawHistoryFolder

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}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close