experiments in a post-browser web
10
fork

Configure Feed

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

fix(notes): fix notes command, tags detail view, and editor opening flow

- Fix `notes` command to return correct output format (mimeType: 'item')
so cmd panel can display the notes list and let users select items
- Show full note text in tags detail modal instead of truncating to 100 chars,
with scrollable container for long content
- Add "Edit in Editor" button for text items in tags detail modal that
publishes editor:open with the item's ID
- Make ESC in tags detail modal go back to tags list instead of closing
the entire window (IZUI navigation pattern from groups extension)
- Add editor:add subscriber in editor/background.js to open blank editor
when other extensions publish editor:add events

+75 -6
+9 -1
extensions/cmd/commands/note.js
··· 138 138 const preview = (note.content || '').substring(0, 50) + (note.content?.length > 50 ? '...' : ''); 139 139 console.log(`${i + 1}. ${preview}`); 140 140 }); 141 - return { success: true, data: notes }; 141 + return { 142 + success: true, 143 + message: `${notes.length} note${notes.length !== 1 ? 's' : ''} found`, 144 + output: { 145 + data: notes, 146 + mimeType: 'item', 147 + title: 'Recent notes' 148 + } 149 + }; 142 150 } 143 151 } 144 152 }
+6
extensions/editor/background.js
··· 88 88 openEditor(Object.keys(params).length > 0 ? params : undefined); 89 89 }, api.scopes.GLOBAL); 90 90 91 + // Subscribe to editor:add — open a new blank editor document 92 + api.subscribe('editor:add', (msg) => { 93 + console.log('[editor] editor:add received:', msg); 94 + openEditor(); // No params = blank document 95 + }, api.scopes.GLOBAL); 96 + 91 97 console.log('[editor] Extension loaded'); 92 98 }, 93 99
+60 -5
extensions/tags/home.js
··· 267 267 // Escape handling 268 268 if (api.escape) { 269 269 api.escape.onEscape(() => { 270 - // Dialog closing is handled automatically by the preload escape system. 271 - // When ESC is pressed, the preload detects open peek-dialog elements and 272 - // closes them before this callback is reached. No per-extension workaround needed. 270 + // If the detail modal is open (or was just closed by the preload escape system), 271 + // treat ESC as "go back to tags list" rather than closing the window. 272 + // The preload closes peek-dialog elements before this callback fires, 273 + // so we check state.editingItem to know a modal was just dismissed. 274 + if (state.editingItem) { 275 + state.editingItem = null; 276 + // Ensure the dialog is closed (preload may have already closed it) 277 + if (modalOverlay.open) { 278 + modalOverlay.close(); 279 + } 280 + return { handled: true }; 281 + } 273 282 274 283 // If search has content, clear it 275 284 if (state.searchQuery) { ··· 831 840 subtitle = item.content; 832 841 faviconUrl = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text y=".9em" font-size="90">🌐</text></svg>'; 833 842 } else if (itemType === 'text') { 834 - title = item.content.substring(0, 100) + (item.content.length > 100 ? '...' : ''); 843 + title = item.content || ''; 835 844 if (noteUrl) { 836 845 subtitle = noteUrl; 837 846 faviconUrl = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text y=".9em" font-size="90">🔗</text></svg>'; ··· 852 861 853 862 // Set item info 854 863 modal.querySelector('.modal-favicon').src = faviconUrl; 855 - modal.querySelector('.modal-item-title').textContent = title; 864 + const titleEl = modal.querySelector('.modal-item-title'); 865 + titleEl.textContent = title; 866 + // For text items, make the title scrollable to show full content 867 + if (itemType === 'text') { 868 + titleEl.style.maxHeight = '200px'; 869 + titleEl.style.overflowY = 'auto'; 870 + titleEl.style.whiteSpace = 'pre-wrap'; 871 + titleEl.style.wordBreak = 'break-word'; 872 + } else { 873 + titleEl.style.maxHeight = ''; 874 + titleEl.style.overflowY = ''; 875 + titleEl.style.whiteSpace = ''; 876 + titleEl.style.wordBreak = ''; 877 + } 856 878 modal.querySelector('.modal-item-url').textContent = subtitle; 857 879 858 880 // Show or hide the "Open Page" button based on whether item has a URL ··· 872 894 } else { 873 895 openPageBtn.style.display = 'none'; 874 896 } 897 + } 898 + 899 + // Show or hide "Edit in Editor" button for text items 900 + let editInEditorBtn = modal.querySelector('.modal-edit-editor-btn'); 901 + if (itemType === 'text') { 902 + if (!editInEditorBtn) { 903 + // Create the button on first use 904 + editInEditorBtn = document.createElement('peek-button'); 905 + editInEditorBtn.className = 'modal-edit-editor-btn'; 906 + editInEditorBtn.variant = 'ghost'; 907 + editInEditorBtn.size = 'sm'; 908 + editInEditorBtn.title = 'Edit in Editor'; 909 + editInEditorBtn.innerHTML = ` 910 + <svg slot="prefix" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 911 + <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path> 912 + <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path> 913 + </svg> 914 + Edit in Editor 915 + `; 916 + // Insert after the Open Page button's parent area 917 + const itemInfoEl = modal.querySelector('.modal-item-info'); 918 + itemInfoEl.appendChild(editInEditorBtn); 919 + } 920 + editInEditorBtn.style.display = 'flex'; 921 + // Clone to remove old listeners 922 + const newEditBtn = editInEditorBtn.cloneNode(true); 923 + editInEditorBtn.parentNode.replaceChild(newEditBtn, editInEditorBtn); 924 + newEditBtn.addEventListener('click', () => { 925 + api.publish('editor:open', { itemId: item.id }, api.scopes.GLOBAL); 926 + closeModal(); 927 + }); 928 + } else if (editInEditorBtn) { 929 + editInEditorBtn.style.display = 'none'; 875 930 } 876 931 877 932 // Render current tags