experiments in a post-browser web
10
fork

Configure Feed

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

fix(tags): apply vim mode pref to embedded editor, fix ESC cascade

+88 -3
+1 -1
extensions/editor/codemirror.js
··· 720 720 view.destroy(); 721 721 } 722 722 723 - export { EditorView, EditorState }; 723 + export { EditorView, EditorState, getCM, Vim };
+73 -2
extensions/tags/home.js
··· 12 12 */ 13 13 14 14 import * as CodeMirror from 'peek://extensions/editor/codemirror.js'; 15 + const { getCM, Vim } = CodeMirror; 15 16 16 17 const api = window.app; 17 18 const debug = api?.debug; ··· 94 95 let editorSaveTimer = null; 95 96 let editorSaveStatus = 'saved'; // 'saved' | 'saving' | 'unsaved' 96 97 const EDITOR_AUTOSAVE_DELAY_MS = 1500; 98 + 99 + // Vim mode preference (read from editor extension's settings) 100 + let editorVimMode = false; 97 101 98 102 // DOM elements 99 103 let searchInput; ··· 186 190 * Internal ESC handler for tags navigation (following groups pattern). 187 191 * Returns { handled: true } if we navigated internally. 188 192 * Returns { handled: false } if at root (list view) and window should close. 193 + * 194 + * Vim mode ESC cascade: 195 + * 1. ESC in vim insert mode -> exits to vim normal mode (handled here, not by CodeMirror 196 + * because the preload intercepts ESC before the DOM keydown event fires) 197 + * 2. ESC in vim normal mode -> exits detail view back to tags list 198 + * 3. ESC in tags list -> closes window 199 + * 200 + * Without vim mode: 201 + * 1. ESC in editor -> exits detail view 202 + * 2. ESC in tags list -> closes window 189 203 */ 190 204 const handleEscape = () => { 191 205 console.log('[tags:esc] handleEscape called, view:', state.currentView, 'searchQuery:', state.searchQuery); 192 206 207 + // If in detail view with embedded editor and vim mode is enabled, 208 + // check if vim is in insert mode. If so, exit insert mode instead 209 + // of navigating away. The preload intercepts ESC via before-input-event 210 + // so CodeMirror's vim extension never sees it — we must handle it here. 211 + if (state.currentView === VIEW_DETAIL && embeddedEditor && editorVimMode) { 212 + try { 213 + const cm = getCM(embeddedEditor); 214 + if (cm && cm.state && cm.state.vim) { 215 + const vimState = cm.state.vim; 216 + if (vimState.insertMode || vimState.visualMode) { 217 + console.log('[tags:esc] Vim in insert/visual mode, sending <Esc> to vim'); 218 + Vim.handleKey(cm, '<Esc>'); 219 + return { handled: true }; 220 + } 221 + // In normal mode, fall through to exit detail view 222 + console.log('[tags:esc] Vim in normal mode, will exit detail view'); 223 + } 224 + } catch (e) { 225 + console.log('[tags:esc] Could not check vim state:', e); 226 + } 227 + } 228 + 193 229 // If in detail view, navigate back to list 194 230 if (state.currentView === VIEW_DETAIL) { 195 231 // If editor has unsaved changes, flush them before leaving ··· 348 384 // Register escape handler (following groups pattern - extracted function, not inline) 349 385 if (api.escape) { 350 386 api.escape.onEscape(handleEscape); 387 + } 388 + 389 + // Listen for editor settings changes to live-reload vim mode on embedded editor 390 + if (api?.subscribe) { 391 + api.subscribe('editor:settings-changed', async () => { 392 + debug && console.log('[tags] Editor settings changed, reloading vim mode'); 393 + await loadEditorVimPreference(); 394 + if (embeddedEditor) { 395 + CodeMirror.setVimMode(embeddedEditor, editorVimMode); 396 + debug && console.log('[tags] Updated embedded editor vimMode:', editorVimMode); 397 + } 398 + }, api.scopes.GLOBAL); 351 399 } 352 400 }; 353 401 ··· 950 998 }; 951 999 952 1000 /** 1001 + * Load vim mode preference from the editor extension's settings. 1002 + * Uses the cross-extension settings read API (api.settings.getExtKey). 1003 + */ 1004 + const loadEditorVimPreference = async () => { 1005 + try { 1006 + if (api?.settings?.getExtKey) { 1007 + const result = await api.settings.getExtKey('editor', 'prefs'); 1008 + if (result.success && result.data && typeof result.data === 'object') { 1009 + editorVimMode = !!result.data.vimMode; 1010 + debug && console.log('[tags] Loaded editor vimMode preference:', editorVimMode); 1011 + } else { 1012 + debug && console.log('[tags] No editor prefs found, using default vimMode:', editorVimMode); 1013 + } 1014 + } 1015 + } catch (err) { 1016 + debug && console.log('[tags] Failed to load editor vim preference:', err); 1017 + } 1018 + }; 1019 + 1020 + /** 953 1021 * Create the embedded CodeMirror editor for a text item 954 1022 */ 955 - const createEmbeddedEditor = (item) => { 1023 + const createEmbeddedEditor = async (item) => { 956 1024 destroyEmbeddedEditor(); 957 1025 958 1026 const container = document.querySelector('.detail-editor-container'); ··· 961 1029 // Clear the container 962 1030 container.innerHTML = ''; 963 1031 1032 + // Load vim mode preference from editor extension before creating editor 1033 + await loadEditorVimPreference(); 1034 + 964 1035 embeddedEditor = CodeMirror.createEditor({ 965 1036 parent: container, 966 1037 content: item.content || '', 967 - vimMode: false, 1038 + vimMode: editorVimMode, 968 1039 showLineNumbers: true, 969 1040 onChange: (content) => { 970 1041 scheduleEditorAutosave(content);
+14
preload.js
··· 1607 1607 return Promise.resolve({ success: false, error: 'Not an extension context' }); 1608 1608 } 1609 1609 return ipcRenderer.invoke('extension-settings-set-key', { extId, key, value }); 1610 + }, 1611 + 1612 + /** 1613 + * Read a single setting key from another extension (read-only) 1614 + * Allows extensions to share preferences (e.g., tags reading editor's vim mode) 1615 + * @param {string} extId - Target extension ID 1616 + * @param {string} key - Setting key (e.g., 'prefs') 1617 + * @returns {Promise<{success: boolean, data?: any, error?: string}>} 1618 + */ 1619 + getExtKey: (extId, key) => { 1620 + if (!extId || !key) { 1621 + return Promise.resolve({ success: false, error: 'extId and key are required' }); 1622 + } 1623 + return ipcRenderer.invoke('extension-settings-get-key', { extId, key }); 1610 1624 } 1611 1625 }; 1612 1626