experiments in a post-browser web
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix(izui): workspace windows never close on ESC, fix search ESC bypass

+25 -12
+5 -1
app/lib/izui-state.js
··· 378 378 // These roles always close regardless of session state 379 379 if (['quick-view', 'palette', 'utility'].includes(role)) return 'close'; 380 380 if (role === 'overlay') return 'close-and-restore'; 381 - // child-content, workspace, and content: only close in transient sessions. 381 + // Workspace windows are root/persistent extension windows (groups, search, etc.). 382 + // They must NEVER close on ESC regardless of session state — the renderer handles 383 + // internal navigation and the window persists. 384 + if (role === 'workspace') return 'nothing'; 385 + // child-content and content: only close in transient sessions. 382 386 // In active sessions, ESC navigates internally (renderer handles it) but 383 387 // never closes the window — even child-content windows are user-opened 384 388 // content that should persist.
+9 -4
backend/electron/izui-state.test.ts
··· 715 715 assert.strictEqual(escPolicy('active', 'content'), 'nothing'); 716 716 }); 717 717 718 - it('should close workspace in transient session', () => { 719 - assert.strictEqual(escPolicy('transient', 'workspace'), 'close'); 718 + // REGRESSION TEST: workspace must NEVER close on ESC regardless of session state. 719 + // Workspace windows are root/persistent extension windows (groups, search, etc.) 720 + // that should never be dismissed by the backend ESC policy. 721 + it('should NOT close workspace in transient session (regression test)', () => { 722 + assert.strictEqual(escPolicy('transient', 'workspace'), 'nothing', 723 + 'workspace windows must never close on ESC — they are root/persistent extension windows'); 720 724 }); 721 725 722 726 it('should NOT close workspace in active session', () => { ··· 786 790 assert.strictEqual(coordinator.getState(), 'transient'); 787 791 assert.strictEqual(coordinator.isTransient(), true); 788 792 789 - // In transient mode, ESC should close all content roles 793 + // In transient mode, ESC should close content roles but NOT workspace 790 794 assert.strictEqual(escPolicy(coordinator.getState(), 'child-content'), 'close'); 791 795 assert.strictEqual(escPolicy(coordinator.getState(), 'content'), 'close'); 792 - assert.strictEqual(escPolicy(coordinator.getState(), 'workspace'), 'close'); 796 + assert.strictEqual(escPolicy(coordinator.getState(), 'workspace'), 'nothing', 797 + 'workspace windows must never close on ESC, even in transient sessions'); 793 798 }); 794 799 }); 795 800 });
+5 -1
backend/electron/windows.ts
··· 107 107 // These roles always close regardless of session state 108 108 if (['quick-view', 'palette', 'utility'].includes(role)) return 'close'; 109 109 if (role === 'overlay') return 'close-and-restore'; 110 - // child-content, workspace, and content: only close in transient sessions. 110 + // Workspace windows are root/persistent extension windows (groups, search, etc.). 111 + // They must NEVER close on ESC regardless of session state — the renderer handles 112 + // internal navigation and the window persists. 113 + if (role === 'workspace') return 'nothing'; 114 + // child-content and content: only close in transient sessions. 111 115 // In active sessions, ESC navigates internally (renderer handles it) but 112 116 // never closes the window — even child-content windows are user-opened 113 117 // content that should persist. Root-level content windows must not close
+4 -3
extensions/groups/home.js
··· 149 149 150 150 /** 151 151 * Internal ESC handler for groups navigation 152 - * Returns { handled: true } if we navigated internally 153 - * Returns { handled: false } if at root (groups list) and window should close 152 + * Returns { handled: true } if we navigated internally (search clear, back to groups list) 153 + * Returns { handled: false } at root — backend escPolicy decides (workspace role = never close) 154 154 */ 155 155 const handleEscape = () => { 156 156 // If search has content, clear it first ··· 172 172 }, 0); 173 173 return { handled: true }; 174 174 } 175 - // At root (groups list) - let window close 175 + // At root (groups list) — defer to backend escPolicy. 176 + // Groups window has role:'workspace', so escPolicy returns 'nothing' (never close). 176 177 return { handled: false }; 177 178 }; 178 179
+2 -3
extensions/search/home.js
··· 407 407 // Keyboard navigation 408 408 document.addEventListener('keydown', handleKeydown); 409 409 410 - // Register escape handler 410 + // Register escape handler — defer to backend escPolicy (workspace role = never close) 411 411 if (api.escape) { 412 412 api.escape.onEscape(() => { 413 - window.close(); 414 - return { handled: true }; 413 + return { handled: false }; 415 414 }); 416 415 } 417 416