Something went wrong. Try again.
experiments in a post-browser web
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193/** * Chain Editor - popup editor for chain mode text editing * * Opened by the cmd panel's openChainPopup() as a modal child window. * Receives content via pubsub, returns edited content via pubsub. * * Protocol: * 1. Reads sessionId, mimeType, title from URL params * 2. Subscribes to 'chain-popup:content' to receive actual content (matched by sessionId) * 3. Creates a CodeMirror editor filling the window * 4. On close (Escape in vim normal mode, or :q), publishes 'chain-popup:result' */
import * as CodeMirror from 'peek://extensions/editor/codemirror.js';import { StatusLine } from 'peek://extensions/editor/status-line.js';const { getCM, Vim } = CodeMirror;
const api = window.app;
// Read URL paramsconst params = new URLSearchParams(window.location.search);const sessionId = params.get('sessionId');const mimeType = params.get('mimeType') || 'text/plain';const title = params.get('title') || 'Edit';
// Editor statelet editor = null;let statusLine = null;let vimMode = false;let resultPublished = false;
/** * Load vim mode preference from editor extension settings */async function loadVimPreference() { try { if (api?.settings?.getExtKey) { const result = await api.settings.getExtKey('editor', 'prefs'); if (result.success && result.data && typeof result.data === 'object') { vimMode = !!result.data.vimMode; console.log('[chain-editor] vimMode:', vimMode); } } } catch (err) { console.log('[chain-editor] Failed to load vim pref:', err); }}
/** * Publish the result back to the cmd panel and close */function publishResult() { if (resultPublished) return; resultPublished = true;
const data = editor ? CodeMirror.getContent(editor) : ''; console.log('[chain-editor] Publishing result, sessionId:', sessionId, 'length:', data.length);
api.publish('chain-popup:result', { sessionId, data, mimeType });}
/** * Create the CodeMirror editor with the given content */async function createEditor(content) { const container = document.getElementById('editor-container'); const statusLineEl = document.getElementById('status-line');
// Load vim preference await loadVimPreference();
// Create status line if (statusLineEl) { statusLine = new StatusLine({ container: statusLineEl }); if (vimMode) { statusLine.show(); statusLine.updateMode('normal'); } else { statusLine.hide(); statusLineEl.style.display = 'none'; } }
editor = CodeMirror.createEditor({ parent: container, content: content || '', vimMode: vimMode, showLineNumbers: true, onSelectionChange: (line, col) => { if (statusLine && vimMode) { statusLine.updatePosition(line, col); } }, onVimModeChange: (mode) => { if (statusLine && vimMode) { statusLine.updateMode(mode); } } });
// Register vim :q ex command to publish result and close if (vimMode) { try { Vim.defineEx('q', 'q', () => { publishResult(); // Let the window close via escape handler returning { handled: false } // or close directly window.close(); }); Vim.defineEx('wq', 'wq', () => { publishResult(); window.close(); }); } catch (e) { console.log('[chain-editor] Failed to define vim ex commands:', e); } }
// Focus the editor setTimeout(() => { if (editor) CodeMirror.focus(editor); }, 100);}
/** * Register escape handler for the IZUI escape flow */api.escape.onEscape(() => { if (editor && vimMode) { try { const cm = getCM(editor); if (cm && cm.state && cm.state.vim) { const vimState = cm.state.vim; if (vimState.insertMode || vimState.visualMode) { // In insert/visual mode: send Esc to vim, stay in editor console.log('[chain-editor] vim insert/visual -> normal'); Vim.handleKey(cm, '<Esc>'); return { handled: true }; } // In normal mode: publish result and let window close console.log('[chain-editor] vim normal -> publish and close'); publishResult(); return { handled: false }; } } catch (e) { console.log('[chain-editor] vim escape error:', e); } }
// Non-vim mode: publish result and let window close publishResult(); return { handled: false };});
/** * Safety net: publish result if window is closing and we haven't published yet */window.addEventListener('beforeunload', () => { publishResult();});
/** * Initialize: subscribe for content, signal readiness, then wait for content. * Uses a retry loop because pubsub subscription registration is async — * the ready signal might arrive at the panel before our subscribe is processed. */function init() { if (!sessionId) { console.error('[chain-editor] No sessionId in URL params'); return; }
console.log('[chain-editor] Waiting for content, sessionId:', sessionId);
let received = false;
// Subscribe for content from the cmd panel api.subscribe('chain-popup:content', (msg) => { if (msg.sessionId !== sessionId) return; if (received) return; received = true;
console.log('[chain-editor] Received content, length:', (msg.data || '').length); createEditor(msg.data || ''); });}
init();