v1.5.6 - Definitive Sidebar Toggle Fix
Summary
Critical bug fix release that definitively resolves the Ctrl+I sidebar toggle functionality after 5 failed attempts (v1.5.1-v1.5.5). The root cause was finally identified by comparing v1.4.0 (working) with v1.5.x (broken).
Root Cause Analysis
Why v1.4.0 Worked
globalShortcut.register("Control+I")was COMMENTED OUT in main.ts- Sidebar toggle was handled entirely by keyboard event listener in the renderer
- Keyboard events propagate normally when window has focus
Why v1.5.1-v1.5.5 Failed
| Version | Approach | Why It Failed |
|---|---|---|
| v1.5.1 | onSidebarToggle(callback) |
contextBridge callback proxy unreliable |
| v1.5.2 | Technical debt release | Sidebar still broken |
| v1.5.3 | CustomEvent in preload | Context isolation blocked it (preload's window !== page's window) |
| v1.5.4 | Stored callback pattern | Same contextBridge proxy issue |
| v1.5.5 | Simplified callback | Same underlying contextBridge problem |
The Pattern That Failed:
Main Process → IPC → Preload → contextBridge callback → Page
↑
Proxy fails to reliably invoke React state setter
The Solution
Use webContents.executeJavaScript() to dispatch a CustomEvent directly into the renderer context.
The Pattern That Works:
Main Process (globalShortcut detects Ctrl+I)
↓
webContents.executeJavaScript() dispatches CustomEvent directly into renderer
↓
document.addEventListener('geforce-sidebar-toggle') in overlay
↓
React state setter called in the same JavaScript context
↓
Sidebar toggles successfully
Why This Works
executeJavaScript()runs code directly in the renderer's main world (same context as overlay)CustomEventis a native DOM mechanism that doesn't require IPC callback proxying- The event listener receives events reliably because it's in the same JavaScript context
- No contextBridge proxy issues - the event dispatch and listener are both in page context
Implementation Details
main.ts - registerShortcuts()
const success = globalShortcut.register("Control+I", () => {
// Dispatch CustomEvent directly in renderer - bypasses IPC callback issues
mainWindow.webContents.executeJavaScript(`
(function() {
document.dispatchEvent(new CustomEvent('geforce-sidebar-toggle'));
})();
`);
});index.tsx - useEffect()
// PRIMARY: Listen for CustomEvent dispatched by main process
const customEventHandler = () => {
setVisible((v) => !v);
};
document.addEventListener("geforce-sidebar-toggle", customEventHandler);
// FALLBACK: Keyboard handler for when globalShortcut fails
const keyboardHandler = (e: KeyboardEvent) => {
if (e.ctrlKey && e.key === "i") {
e.preventDefault();
setVisible((v) => !v);
}
};
window.addEventListener("keydown", keyboardHandler);Testing Instructions
- Launch GeForce Infinity
- Press
Ctrl+I- sidebar should toggle open/closed - Press
Ctrl+Iagain - sidebar should toggle again - Verify toggle works regardless of where focus is in the window
- Check DevTools console for debug logging confirming CustomEvent dispatch
Files Changed
src/electron/main.ts- executeJavaScript + CustomEvent dispatch in registerShortcuts()src/overlay/index.tsx- CustomEvent listener + fallback keyboard handler with cleanupCHANGELOG.md- v1.5.6 release documentationREADME.md- Updated latest release sectionpackage.json- Version bump to 1.5.6VERSION- Version bump to 1.5.6
Lessons Learned
- contextBridge callback proxying is unreliable for IPC-triggered state changes
- executeJavaScript bypasses context isolation for simple event dispatch
- CustomEvent is reliable when dispatched and listened in the same context
- Always compare with working version when debugging regressions
- Document previous failed attempts to avoid repeating them
Generated with Claude Code