experiments in a post-browser web
10
fork

Configure Feed

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

fix(cmd): restore param mode for connector commands, route item selection through execute

The previous fix disabled param mode entirely for connector commands (those
with produces), which broke autocomplete for the edit command. This restores
param mode so autocomplete works, and fixes acceptParamSuggestion() to route
item-type param selections through execute() instead of directly publishing
editor:open. This way the command's output handling flow works correctly
(enters output selection mode for multiple results, routes to editor for
single items, supports chaining).

Also adds a Playwright test for the edit+Tab flow verifying that param mode
activates, suggestions appear, and selecting one triggers editor:open.

+116 -3
+10 -3
extensions/cmd/panel.js
··· 420 420 421 421 if (paramCandidate) { 422 422 const cmd = state.commands[paramCandidate]; 423 + // Enter param mode for commands with params (provides autocomplete) 423 424 if (cmd && cmd.params && cmd.params.length > 0) { 424 425 enterParamMode(paramCandidate); 425 426 } else { ··· 861 862 commandInput.value = state.typed; 862 863 863 864 // Check if Tab-completed command has params → enter param mode 865 + // (provides autocomplete suggestions including for connector commands) 864 866 const cmd = state.commands[match]; 865 867 if (cmd && cmd.params && cmd.params.length > 0) { 866 868 enterParamMode(match); ··· 2199 2201 2200 2202 const suggestion = state.paramSuggestions[index]; 2201 2203 2202 - // Item-type param: open editor directly instead of inserting text 2204 + // Item-type param for connector commands (those with produces): 2205 + // Route through execute() so the command's output handling works properly 2206 + // (enters output selection mode, routes to editor, supports chaining, etc.) 2203 2207 const cmd = state.commands[state.paramCommand]; 2204 2208 const paramDef = cmd && cmd.params && cmd.params[0]; 2205 2209 if (paramDef && paramDef.type === 'item' && suggestion._item) { 2206 - api.publish('editor:open', { itemId: suggestion._item.id }, api.scopes.GLOBAL); 2207 - setTimeout(shutdown, 100); 2210 + const commandName = state.paramCommand; 2211 + // Build typed string with the suggestion title as search text 2212 + const typed = commandName + ' ' + (suggestion.title || suggestion.value || ''); 2213 + exitParamMode(); 2214 + execute(commandName, typed); 2208 2215 return; 2209 2216 } 2210 2217
+106
tests/desktop/smoke.spec.ts
··· 138 138 }, openResult.id); 139 139 } 140 140 }); 141 + 142 + test('edit command Tab-completion shows autocomplete and opens editor', async () => { 143 + await waitForExtensionsReady(sharedBgWindow, 15000); 144 + 145 + // Create a test note so the edit command has something to autocomplete 146 + const addResult = await sharedBgWindow.evaluate(async () => { 147 + return await (window as any).app.datastore.addItem('text', { 148 + content: '# Edit Tab Test Note\nThis is a note for testing edit tab-completion.' 149 + }); 150 + }); 151 + expect(addResult.success).toBe(true); 152 + const noteId = addResult.data?.id; 153 + 154 + // Open cmd panel 155 + const openResult = await sharedBgWindow.evaluate(async () => { 156 + return await (window as any).app.window.open('peek://ext/cmd/panel.html', { 157 + modal: true, 158 + width: 600, 159 + height: 50, 160 + frame: false, 161 + transparent: true, 162 + alwaysOnTop: true, 163 + center: true 164 + }); 165 + }); 166 + expect(openResult.success).toBe(true); 167 + 168 + const cmdWindow = await sharedApp.getWindow('cmd/panel.html', 5000); 169 + expect(cmdWindow).toBeTruthy(); 170 + 171 + await cmdWindow.waitForSelector('input', { timeout: 5000 }); 172 + await waitForPanelCommandsLoaded(cmdWindow, 10000); 173 + 174 + // Type "edit" and press Tab to trigger param mode 175 + await cmdWindow.fill('input', 'edit'); 176 + // Press ArrowDown first to show results, then Tab to complete 177 + await cmdWindow.keyboard.press('ArrowDown'); 178 + await waitForCommandResults(cmdWindow, 1, 10000); 179 + await cmdWindow.keyboard.press('Tab'); 180 + 181 + // Verify param mode is active - the panel should show autocomplete suggestions 182 + // The input should now be "edit " (with trailing space) 183 + const paramModeActive = await cmdWindow.waitForFunction( 184 + () => { 185 + const state = (window as any)._cmdState; 186 + return state && state.paramMode === true && state.paramCommand === 'edit'; 187 + }, 188 + undefined, 189 + { timeout: 5000 } 190 + ); 191 + expect(paramModeActive).toBeTruthy(); 192 + 193 + // Wait for param suggestions to populate (autocomplete list of notes) 194 + await cmdWindow.waitForFunction( 195 + () => { 196 + const state = (window as any)._cmdState; 197 + return state && state.paramSuggestions && state.paramSuggestions.length > 0; 198 + }, 199 + undefined, 200 + { timeout: 5000 } 201 + ); 202 + 203 + // Verify results are visible with suggestion items 204 + await waitForResultsWithContent(cmdWindow, 5000); 205 + 206 + // Listen for editor:open event before selecting the suggestion 207 + const editorOpenPromise = sharedBgWindow.evaluate(async () => { 208 + return new Promise<any>((resolve) => { 209 + const unsub = (window as any).app.subscribe('editor:open', (data: any) => { 210 + unsub(); 211 + resolve(data); 212 + }, (window as any).app.scopes.GLOBAL); 213 + // Timeout after 10s 214 + setTimeout(() => { 215 + unsub(); 216 + resolve(null); 217 + }, 10000); 218 + }); 219 + }); 220 + 221 + // Press Enter to accept the first suggestion 222 + // This should route through execute() and open the editor 223 + await cmdWindow.keyboard.press('Enter'); 224 + 225 + // Verify editor:open was published (the command executed properly) 226 + const editorOpenData = await editorOpenPromise; 227 + expect(editorOpenData).toBeTruthy(); 228 + 229 + // Close cmd window if still open 230 + try { 231 + if (openResult.id) { 232 + await sharedBgWindow.evaluate(async (id: number) => { 233 + return await (window as any).app.window.close(id); 234 + }, openResult.id); 235 + } 236 + } catch { 237 + // Panel may have already closed via shutdown() 238 + } 239 + 240 + // Clean up the test note 241 + if (noteId) { 242 + await sharedBgWindow.evaluate(async (id: string) => { 243 + return await (window as any).app.datastore.deleteItem(id); 244 + }, noteId); 245 + } 246 + }); 141 247 }); 142 248 143 249 // ============================================================================