fix(gui): track workspace focus events for pane shortcuts
- commit
- 0e1c397d0f3ac5ae104a4cdf66e7b29197b5c93a
- author
- bamen · · 2025-10-12 08:30 UTC
- parent
- 05647bb
- signature
- unsigned
1 file · +35 -1
M gui/src/injected/modules/workspace.ts +35 -1
| @@ -143,6 +143,8 @@ class WorkspaceController implements WorkspaceRuntime { | |||
| 143 | 143 | private paneModeTimer: ReturnType<typeof setTimeout> | null = null; | |
| 144 | 144 | private instanceId: string; | |
| 145 | 145 | private lastActiveCheckTime = 0; | |
| 146 | + | private workspaceHasFocus = true; | |
| 147 | + | private focusEventReceived = false; | |
| 146 | 148 | ||
| 147 | 149 | constructor() { | |
| 148 | 150 | // Generate unique instance ID for this webview | |
| @@ -150,6 +152,27 @@ class WorkspaceController implements WorkspaceRuntime { | |||
| 150 | 152 | console.log( | |
| 151 | 153 | `[workspace] Created WorkspaceController instance: ${this.instanceId}`, | |
| 152 | 154 | ); | |
| 155 | + | ||
| 156 | + | document.addEventListener( | |
| 157 | + | "dumber:workspace-focus", | |
| 158 | + | (event: Event) => { | |
| 159 | + | const detail = (event as CustomEvent).detail ?? {}; | |
| 160 | + | this.focusEventReceived = true; | |
| 161 | + | ||
| 162 | + | if (typeof detail?.active === "boolean") { | |
| 163 | + | this.workspaceHasFocus = detail.active; | |
| 164 | + | } else { | |
| 165 | + | this.workspaceHasFocus = detail !== false; | |
| 166 | + | } | |
| 167 | + | ||
| 168 | + | if (!this.workspaceHasFocus) { | |
| 169 | + | console.log( | |
| 170 | + | `[workspace] Instance ${this.instanceId} marked inactive via focus event`, | |
| 171 | + | ); | |
| 172 | + | } | |
| 173 | + | }, | |
| 174 | + | { passive: true }, | |
| 175 | + | ); | |
| 153 | 176 | } | |
| 154 | 177 | ||
| 155 | 178 | getInstanceId(): string { | |
| @@ -160,10 +183,19 @@ class WorkspaceController implements WorkspaceRuntime { | |||
| 160 | 183 | // Cache active check for 50ms to avoid excessive checks | |
| 161 | 184 | const now = Date.now(); | |
| 162 | 185 | if (now - this.lastActiveCheckTime < 50) { | |
| 163 | - | return true; // Assume active within debounce window | |
| 186 | + | return this.workspaceHasFocus; | |
| 164 | 187 | } | |
| 165 | 188 | this.lastActiveCheckTime = now; | |
| 166 | 189 | ||
| 190 | + | if (this.focusEventReceived) { | |
| 191 | + | if (!this.workspaceHasFocus) { | |
| 192 | + | console.log( | |
| 193 | + | `[workspace] Instance ${this.instanceId} not active (focus event state)`, | |
| 194 | + | ); | |
| 195 | + | } | |
| 196 | + | return this.workspaceHasFocus; | |
| 197 | + | } | |
| 198 | + | ||
| 167 | 199 | // Check if this webview has focus by attempting to query document.hasFocus() | |
| 168 | 200 | // In a multi-pane environment, only the focused webview should handle pane mode | |
| 169 | 201 | try { | |
| @@ -171,6 +203,8 @@ class WorkspaceController implements WorkspaceRuntime { | |||
| 171 | 203 | const isVisible = document.visibilityState === "visible"; | |
| 172 | 204 | const isActive = hasFocus && isVisible; | |
| 173 | 205 | ||
| 206 | + | this.workspaceHasFocus = isActive; | |
| 207 | + | ||
| 174 | 208 | if (!isActive) { | |
| 175 | 209 | console.log( | |
| 176 | 210 | `[workspace] Instance ${this.instanceId} not active: focus=${hasFocus} visible=${isVisible}`, | |
| @@ -143,6 +143,8 @@ class WorkspaceController implements WorkspaceRuntime { | |||||
| 143 | private paneModeTimer: ReturnType<typeof setTimeout> | null = null; | 143 | private paneModeTimer: ReturnType<typeof setTimeout> | null = null; | ||
| 144 | private instanceId: string; | 144 | private instanceId: string; | ||
| 145 | private lastActiveCheckTime = 0; | 145 | private lastActiveCheckTime = 0; | ||
| 146 | + | private workspaceHasFocus = true; | |||
| 147 | + | private focusEventReceived = false; | |||
| 146 | 148 | ||||
| 147 | constructor() { | 149 | constructor() { | ||
| 148 | // Generate unique instance ID for this webview | 150 | // Generate unique instance ID for this webview | ||
| @@ -150,6 +152,27 @@ class WorkspaceController implements WorkspaceRuntime { | |||||
| 150 | console.log( | 152 | console.log( | ||
| 151 | `[workspace] Created WorkspaceController instance: ${this.instanceId}`, | 153 | `[workspace] Created WorkspaceController instance: ${this.instanceId}`, | ||
| 152 | ); | 154 | ); | ||
| 155 | + | ||||
| 156 | + | document.addEventListener( | |||
| 157 | + | "dumber:workspace-focus", | |||
| 158 | + | (event: Event) => { | |||
| 159 | + | const detail = (event as CustomEvent).detail ?? {}; | |||
| 160 | + | this.focusEventReceived = true; | |||
| 161 | + | ||||
| 162 | + | if (typeof detail?.active === "boolean") { | |||
| 163 | + | this.workspaceHasFocus = detail.active; | |||
| 164 | + | } else { | |||
| 165 | + | this.workspaceHasFocus = detail !== false; | |||
| 166 | + | } | |||
| 167 | + | ||||
| 168 | + | if (!this.workspaceHasFocus) { | |||
| 169 | + | console.log( | |||
| 170 | + | `[workspace] Instance ${this.instanceId} marked inactive via focus event`, | |||
| 171 | + | ); | |||
| 172 | + | } | |||
| 173 | + | }, | |||
| 174 | + | { passive: true }, | |||
| 175 | + | ); | |||
| 153 | } | 176 | } | ||
| 154 | 177 | ||||
| 155 | getInstanceId(): string { | 178 | getInstanceId(): string { | ||
| @@ -160,10 +183,19 @@ class WorkspaceController implements WorkspaceRuntime { | |||||
| 160 | // Cache active check for 50ms to avoid excessive checks | 183 | // Cache active check for 50ms to avoid excessive checks | ||
| 161 | const now = Date.now(); | 184 | const now = Date.now(); | ||
| 162 | if (now - this.lastActiveCheckTime < 50) { | 185 | if (now - this.lastActiveCheckTime < 50) { | ||
| 163 | - | return true; // Assume active within debounce window | 186 | + | return this.workspaceHasFocus; |
| 164 | } | 187 | } | ||
| 165 | this.lastActiveCheckTime = now; | 188 | this.lastActiveCheckTime = now; | ||
| 166 | 189 | ||||
| 190 | + | if (this.focusEventReceived) { | |||
| 191 | + | if (!this.workspaceHasFocus) { | |||
| 192 | + | console.log( | |||
| 193 | + | `[workspace] Instance ${this.instanceId} not active (focus event state)`, | |||
| 194 | + | ); | |||
| 195 | + | } | |||
| 196 | + | return this.workspaceHasFocus; | |||
| 197 | + | } | |||
| 198 | + | ||||
| 167 | // Check if this webview has focus by attempting to query document.hasFocus() | 199 | // Check if this webview has focus by attempting to query document.hasFocus() | ||
| 168 | // In a multi-pane environment, only the focused webview should handle pane mode | 200 | // In a multi-pane environment, only the focused webview should handle pane mode | ||
| 169 | try { | 201 | try { | ||
| @@ -171,6 +203,8 @@ class WorkspaceController implements WorkspaceRuntime { | |||||
| 171 | const isVisible = document.visibilityState === "visible"; | 203 | const isVisible = document.visibilityState === "visible"; | ||
| 172 | const isActive = hasFocus && isVisible; | 204 | const isActive = hasFocus && isVisible; | ||
| 173 | 205 | ||||
| 206 | + | this.workspaceHasFocus = isActive; | |||
| 207 | + | ||||
| 174 | if (!isActive) { | 208 | if (!isActive) { | ||
| 175 | console.log( | 209 | console.log( | ||
| 176 | `[workspace] Instance ${this.instanceId} not active: focus=${hasFocus} visible=${isVisible}`, | 210 | `[workspace] Instance ${this.instanceId} not active: focus=${hasFocus} visible=${isVisible}`, | ||