|
2 | 2 |
|
3 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. |
4 | 4 |
|
| 5 | +## 1.5.6 (2026-01-07) - DEFINITIVE SIDEBAR TOGGLE FIX |
| 6 | + |
| 7 | +### Overview |
| 8 | + |
| 9 | +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). This release implements a reliable solution using `executeJavaScript` + `CustomEvent` that bypasses all contextBridge/IPC callback issues. |
| 10 | + |
| 11 | +### Bug Fixes |
| 12 | + |
| 13 | +- **Sidebar Toggle (Ctrl+I)**: Definitively fixed using executeJavaScript + CustomEvent architecture |
| 14 | + - Identified root cause by comparing v1.4.0 (working) with v1.5.x (broken) |
| 15 | + - v1.4.0 worked because `globalShortcut` was COMMENTED OUT - sidebar worked via keyboard handler |
| 16 | + - v1.5.1+ broke because `globalShortcut` intercepted Ctrl+I at OS level before renderer |
| 17 | + - IPC callback pattern through contextBridge wasn't reliably invoking React state setters |
| 18 | + |
| 19 | +### Root Cause Analysis |
| 20 | + |
| 21 | +**Why v1.4.0 Worked:** |
| 22 | +- `globalShortcut.register("Control+I")` was COMMENTED OUT in main.ts |
| 23 | +- Sidebar toggle was handled entirely by keyboard event listener in the renderer |
| 24 | +- Keyboard events propagate normally when window has focus |
| 25 | + |
| 26 | +**Why v1.5.1-v1.5.5 Failed:** |
| 27 | +- v1.5.1: Added `onSidebarToggle(callback)` - contextBridge callback proxy unreliable |
| 28 | +- v1.5.2: Technical debt release - sidebar still broken |
| 29 | +- v1.5.3: Tried CustomEvent in preload - context isolation blocked it (preload's window !== page's window) |
| 30 | +- v1.5.4: Stored callback pattern - same contextBridge proxy issue |
| 31 | +- v1.5.5: Simplified callback - still same underlying contextBridge problem |
| 32 | + |
| 33 | +**The Pattern That Failed:** |
| 34 | +``` |
| 35 | +Main Process → IPC → Preload → contextBridge callback → Page |
| 36 | + ↑ |
| 37 | + Proxy fails to reliably invoke React state setter |
| 38 | +``` |
| 39 | + |
| 40 | +### Technical Solution: executeJavaScript + CustomEvent |
| 41 | + |
| 42 | +**The Pattern That Works:** |
| 43 | +``` |
| 44 | +Main Process (globalShortcut detects Ctrl+I) |
| 45 | + ↓ |
| 46 | +webContents.executeJavaScript() dispatches CustomEvent directly into renderer |
| 47 | + ↓ |
| 48 | +document.addEventListener('geforce-sidebar-toggle') in overlay |
| 49 | + ↓ |
| 50 | +React state setter called in the same JavaScript context |
| 51 | + ↓ |
| 52 | +Sidebar toggles successfully |
| 53 | +``` |
| 54 | + |
| 55 | +**Why This Works:** |
| 56 | +1. `executeJavaScript()` runs code directly in the renderer's main world (same context as overlay) |
| 57 | +2. `CustomEvent` is a native DOM mechanism that doesn't require IPC callback proxying |
| 58 | +3. The event listener receives events reliably because it's in the same JavaScript context |
| 59 | +4. No contextBridge proxy issues - the event dispatch and listener are both in page context |
| 60 | + |
| 61 | +### Implementation Details |
| 62 | + |
| 63 | +**src/electron/main.ts - registerShortcuts():** |
| 64 | +```typescript |
| 65 | +const success = globalShortcut.register("Control+I", () => { |
| 66 | + // Dispatch CustomEvent directly into renderer - bypasses IPC callback issues |
| 67 | + mainWindow.webContents.executeJavaScript(` |
| 68 | + (function() { |
| 69 | + document.dispatchEvent(new CustomEvent('geforce-sidebar-toggle')); |
| 70 | + })(); |
| 71 | + `); |
| 72 | +}); |
| 73 | +``` |
| 74 | + |
| 75 | +**src/overlay/index.tsx - useEffect():** |
| 76 | +```typescript |
| 77 | +// PRIMARY: Listen for CustomEvent dispatched by main process via executeJavaScript |
| 78 | +const customEventHandler = () => { |
| 79 | + setVisible((v) => !v); |
| 80 | +}; |
| 81 | +document.addEventListener("geforce-sidebar-toggle", customEventHandler); |
| 82 | + |
| 83 | +// FALLBACK: Keyboard handler for when globalShortcut fails to register |
| 84 | +const keyboardHandler = (e: KeyboardEvent) => { |
| 85 | + if (e.ctrlKey && e.key === "i") { |
| 86 | + e.preventDefault(); |
| 87 | + setVisible((v) => !v); |
| 88 | + } |
| 89 | +}; |
| 90 | +window.addEventListener("keydown", keyboardHandler); |
| 91 | + |
| 92 | +// Proper cleanup |
| 93 | +return () => { |
| 94 | + document.removeEventListener("geforce-sidebar-toggle", customEventHandler); |
| 95 | + window.removeEventListener("keydown", keyboardHandler); |
| 96 | +}; |
| 97 | +``` |
| 98 | + |
| 99 | +### Architecture Improvements |
| 100 | + |
| 101 | +- **Dual Handler System**: Primary (globalShortcut + executeJavaScript) + Fallback (keyboard handler) |
| 102 | +- **Proper Cleanup**: useEffect return function removes both event listeners |
| 103 | +- **Comprehensive Logging**: Debug logging throughout the shortcut registration and event handling |
| 104 | +- **Graceful Degradation**: Falls back to keyboard handler if globalShortcut registration fails |
| 105 | + |
| 106 | +### Files Modified |
| 107 | + |
| 108 | +- `src/electron/main.ts` - executeJavaScript + CustomEvent dispatch in registerShortcuts() |
| 109 | +- `src/overlay/index.tsx` - CustomEvent listener + fallback keyboard handler with cleanup |
| 110 | +- `CHANGELOG.md` - This release documentation |
| 111 | +- `README.md` - Updated latest release section |
| 112 | +- `package.json` - Version bump to 1.5.6 |
| 113 | +- `VERSION` - Version bump to 1.5.6 |
| 114 | + |
| 115 | +### Lessons Learned |
| 116 | + |
| 117 | +1. **contextBridge callback proxying is unreliable** for IPC-triggered state changes |
| 118 | +2. **executeJavaScript bypasses context isolation** for simple event dispatch |
| 119 | +3. **CustomEvent is reliable** when dispatched and listened in the same context |
| 120 | +4. **Always compare with working version** when debugging regressions |
| 121 | +5. **Document previous failed attempts** to avoid repeating them |
| 122 | + |
| 123 | +--- |
| 124 | + |
5 | 125 | ## 1.5.5 (2026-01-07) - DEVELOPMENT EXPERIENCE IMPROVEMENTS |
6 | 126 |
|
7 | 127 | ### Overview |
|
0 commit comments