···11+# Command Suggestions: Integrated Search-and-Act Flow for Cmd
22+33+**Status:** Research complete, ready for implementation
44+**Last Updated:** 2026-02-10
55+**Purpose:** Design a generic command + search matcher pattern for the cmd bar, enabling flows like `edit vampire` -> pick item from search results -> act on it
66+77+---
88+99+## 1. Problem Statement
1010+1111+Users want to type a command with a search term (e.g., `edit vampire`) and see matching items appear in the dropdown as they type. Currently, the `edit` command runs its search only on Enter, then dumps all results into output selection mode. The flow is:
1212+1313+1. Type `edit vampire`, press Enter
1414+2. Command executes, fetches all matching notes
1515+3. Panel enters output selection mode with results
1616+4. User picks one, panel routes to editor
1717+1818+The desired flow is:
1919+2020+1. Type `edit vampire`
2121+2. **As the user types**, matching items stream into the dropdown below the input
2222+3. Arrow down to browse results
2323+4. Press Enter on a result to open it
2424+2525+This should not be specific to `edit` or notes. It should be a generic pattern that any command can opt into: `tag <search>`, `open <search>`, `delete <search>`, `group add <search>`.
2626+2727+---
2828+2929+## 2. Current Cmd Architecture Analysis
3030+3131+### 2.1 File Layout
3232+3333+| File | Role |
3434+|------|------|
3535+| `extensions/cmd/background.js` | Command registry (provider pattern), shortcut handling, caching |
3636+| `extensions/cmd/panel.js` | Panel UI, input handling, matching, execution, chain mode |
3737+| `extensions/cmd/panel.html` | Panel layout and CSS |
3838+| `extensions/cmd/commands.js` | Command loader, proxy command creation for cross-extension dispatch |
3939+| `extensions/cmd/commands/index.js` | Aggregates built-in commands (edit, note, url, history, lists) |
4040+| `extensions/cmd/commands/edit.js` | Edit command -- search notes, return items |
4141+| `extensions/cmd/commands/note.js` | Note command -- save items |
4242+| `extensions/cmd/config.js` | Extension ID, defaults, schemas |
4343+4444+### 2.2 Command Registration Flow
4545+4646+Two paths for registering commands:
4747+4848+**Path A -- Built-in commands** (same process as panel):
4949+- `commands/index.js` exports an array of command objects
5050+- `commands.js` loads them and dispatches `cmd-update-commands` event
5151+- `panel.js` receives event, stores commands in `state.commands`
5252+- Execute is called directly (same JS context)
5353+5454+**Path B -- Extension commands** (cross-process via pubsub):
5555+- Extension calls `api.commands.register({ name, description, execute })`
5656+- Preload batches and publishes `cmd:register-batch` via IPC
5757+- `background.js` stores metadata in `commandRegistry` Map
5858+- When panel opens, it publishes `cmd:query-commands`
5959+- Background responds with `cmd:query-commands-response`
6060+- `commands.js` creates proxy commands that dispatch via `cmd:execute:{name}` pubsub
6161+- Results come back via `cmd:execute:{name}:result`
6262+6363+### 2.3 Current Matching Flow
6464+6565+In `panel.js`, when the user types:
6666+6767+1. `input` event fires, `state.typed` is updated
6868+2. `findMatchingCommands(typed)` is called -- this matches **command names only**
6969+3. Matches are sorted by exact match priority, then adaptive score, then frecency
7070+4. `updateCommandUI()` renders inline completion hint
7171+5. `updateResultsUI()` renders dropdown (only if `state.showResults` is true, toggled by ArrowDown)
7272+6. On Enter, the selected command is executed with a `context` object containing `{ typed, name, params, search }`
7373+7474+### 2.4 Current Output Selection Mode
7575+7676+After a command returns `{ output: { data: [...], mimeType: 'item' } }`:
7777+7878+1. Panel enters `outputSelectionMode`
7979+2. Items are rendered in the results dropdown
8080+3. Arrow keys navigate, Enter/ArrowRight selects
8181+4. For `mimeType: 'item'`, selection publishes `editor:open` with `itemId`
8282+5. For other mimeTypes, selection enters chain mode
8383+8484+### 2.5 Execution Context
8585+8686+Commands receive:
8787+```javascript
8888+{
8989+ typed: 'edit vampire', // Full typed string
9090+ name: 'edit', // Command name
9191+ params: ['vampire'], // Array of params after command name
9292+ search: 'vampire', // Text after command name (joined)
9393+ // Chain mode fields omitted for brevity
9494+}
9595+```
9696+9797+### 2.6 Key Observation: The Gap
9898+9999+The current architecture has a hard boundary between **command matching** (which operates on command names) and **item searching** (which happens inside command execution). There is no mechanism for a command to provide search suggestions back to the panel in real-time as the user types. The edit command's `searchNotes()` only runs on Enter.
100100+101101+---
102102+103103+## 3. Existing Search Infrastructure
104104+105105+### 3.1 Datastore Query API
106106+107107+The backend provides SQL-based search:
108108+109109+```javascript
110110+// Basic query with type filter
111111+api.datastore.queryItems({ type: 'text' })
112112+113113+// Query with search (LIKE %term% on content, title, domain)
114114+api.datastore.queryItems({ type: 'url', search: 'github' })
115115+116116+// Frecency-ranked URL search
117117+api.datastore.queryItemsByFrecency({ search: 'github', limit: 10 })
118118+```
119119+120120+The `search` parameter in `queryItems` does a SQL `LIKE %term%` match against `content`, `title`, and `domain` columns. This is a basic substring search -- no fuzzy matching, no ranking beyond frecency.
121121+122122+### 3.2 Current Search Usage in Commands
123123+124124+- **`edit.js`**: Calls `queryItems({ type: 'text' })` then filters client-side with `content.toLowerCase().includes(query)`
125125+- **`history.js`**: Calls `queryItems({ type: 'url' })` then filters client-side on URL and metadata title
126126+- **`note.js`**: Calls `queryItems({ type: 'text' })` with `getItemTags()` per item (N+1 query pattern)
127127+- **`tags/background.js`**: Uses `getTagsByFrecency()` for tag search
128128+129129+None use the built-in `search` parameter on `queryItems` -- they all fetch everything then filter client-side. This is an opportunity for optimization.
130130+131131+### 3.3 History Command: Dynamic Commands Pattern
132132+133133+The history module has a notable pattern: it registers **each URL as a separate command**. This makes URLs searchable by the command matcher, but does not scale and conflates items with commands.
134134+135135+---
136136+137137+## 4. Design Decision: Streaming Suggestions vs Top-Level Filter
138138+139139+### Option A: Extensions Stream Suggestions
140140+141141+Each extension publishes matching items for a query via pubsub. Cmd stitches results together from all responders.
142142+143143+**Flow:**
144144+1. User types `edit vam`
145145+2. Panel detects `edit` is a known command, extracts query `vam`
146146+3. Panel publishes `cmd:suggest:edit` with `{ query: 'vam' }`
147147+4. Edit command's extension responds with `cmd:suggest:edit:response` containing matching items
148148+5. Panel merges responses from all extensions and renders dropdown
149149+150150+**Pros:**
151151+- Fully decoupled -- any extension can provide suggestions for any command
152152+- Multiple extensions can respond to the same query
153153+- Works across process boundaries (pubsub is already cross-process)
154154+155155+**Cons:**
156156+- Complex coordination (multiple async responders, merging, deduplication)
157157+- Race conditions between slow and fast responders
158158+- UI jank from incremental rendering as responses arrive
159159+- Pubsub round-trips add latency (~5-20ms each in Electron IPC)
160160+- Overkill for the common case (one command, one data source)
161161+162162+### Option B: Command-Level Suggestion Provider
163163+164164+Commands opt into providing suggestions via a `suggest(query)` function declared alongside `execute()`. The panel calls it directly (for built-in commands) or via a targeted pubsub call (for extension commands).
165165+166166+**Flow:**
167167+1. User types `edit vam`
168168+2. Panel detects `edit` is a known command with `suggest` capability
169169+3. Panel calls `command.suggest('vam')` (or publishes targeted request)
170170+4. Command returns an array of suggestion items
171171+5. Panel renders them in the dropdown
172172+173173+**Pros:**
174174+- Simple protocol: one command, one response
175175+- No coordination complexity
176176+- Direct function call for built-in commands (zero latency)
177177+- Easy to reason about per-command behavior
178178+- Natural extension of existing command object shape
179179+180180+**Cons:**
181181+- Only the owning command provides suggestions (by design -- this is actually a feature)
182182+- Extension commands need a pubsub round-trip (but only one, with a single responder)
183183+184184+### Recommendation: Option B (Command-Level Suggestion Provider)
185185+186186+Option B is the clear winner. It adds a single optional method to the command protocol, keeps the panel logic simple, and maps directly to the user's mental model: "I'm running the `edit` command, show me what `edit` can find."
187187+188188+The multi-extension streaming model (Option A) would only matter if we wanted `edit vampire` to simultaneously search notes, URLs, AND tags. That is a different feature (unified search / omnibox), and should be built as its own command (e.g., `find`) rather than complicating every command's suggestion path.
189189+190190+---
191191+192192+## 5. Suggestion Protocol Design
193193+194194+### 5.1 Command Object Extension
195195+196196+Add an optional `suggest` method to the command interface:
197197+198198+```javascript
199199+{
200200+ name: 'edit',
201201+ description: 'Edit a note',
202202+ produces: ['item'],
203203+204204+ // NEW: Optional suggestion provider
205205+ // Called as user types after the command name
206206+ // Returns array of suggestion items for the dropdown
207207+ suggest: async (query, options) => {
208208+ // query: string -- the text after the command name
209209+ // options: { limit: 10 } -- hints from the panel
210210+ // Returns: Array of { id, title, subtitle, icon?, data? }
211211+ return [
212212+ { id: 'abc-123', title: 'Vampire Notes', subtitle: 'text -- 2 hours ago', data: { itemId: 'abc-123' } },
213213+ { id: 'def-456', title: 'Vampire Weekend Review', subtitle: 'url -- yesterday' }
214214+ ];
215215+ },
216216+217217+ // Existing execute method
218218+ execute: async (ctx) => { ... }
219219+}
220220+```
221221+222222+### 5.2 Suggestion Item Shape
223223+224224+```javascript
225225+{
226226+ id: string, // Unique identifier (for dedup and selection tracking)
227227+ title: string, // Primary display text (required)
228228+ subtitle: string, // Secondary text (type, date, tags, etc.)
229229+ icon: string, // Optional icon identifier or emoji
230230+ data: object, // Opaque payload passed to execute() on selection
231231+ // The panel does NOT interpret `data` -- it passes it through
232232+}
233233+```
234234+235235+### 5.3 Panel Behavior When Suggestions Are Available
236236+237237+The panel needs to change its behavior when a typed command has a `suggest` method:
238238+239239+**Current flow:** Type command -> ArrowDown shows command list -> Enter executes
240240+241241+**New flow:** Type command + space + query -> suggestions appear automatically -> ArrowDown/Enter to pick
242242+243243+Specifically:
244244+245245+1. User types `edit ` (command name + space)
246246+2. Panel recognizes `edit` as matched command
247247+3. Panel detects `edit` has `suggest` method
248248+4. Panel enters **suggestion mode** for this command
249249+5. As user continues typing (`edit vam`), panel calls `suggest('vam')` with debounce
250250+6. Results render in the dropdown (replacing command matches)
251251+7. ArrowDown/Up navigate suggestions
252252+8. Enter on a suggestion calls `execute()` with the suggestion's `data` in context
253253+9. Escape exits suggestion mode (back to command matching)
254254+10. Clearing back past the space exits suggestion mode
255255+256256+### 5.4 Execution Context with Suggestion Selection
257257+258258+When a user selects a suggestion, the execute context gets an extra field:
259259+260260+```javascript
261261+{
262262+ typed: 'edit vampire',
263263+ name: 'edit',
264264+ params: ['vampire'],
265265+ search: 'vampire',
266266+267267+ // NEW: populated when user selected a suggestion
268268+ selectedSuggestion: {
269269+ id: 'abc-123',
270270+ title: 'Vampire Notes',
271271+ data: { itemId: 'abc-123' }
272272+ }
273273+}
274274+```
275275+276276+The command's `execute()` can check for `ctx.selectedSuggestion` and skip its own search, directly acting on the selected item:
277277+278278+```javascript
279279+execute: async (ctx) => {
280280+ // Fast path: user already picked a specific item
281281+ if (ctx.selectedSuggestion?.data?.itemId) {
282282+ return {
283283+ output: {
284284+ data: { id: ctx.selectedSuggestion.data.itemId },
285285+ mimeType: 'item'
286286+ }
287287+ };
288288+ }
289289+290290+ // Slow path: execute search (for Enter without selecting)
291291+ const notes = await searchNotes(ctx.search);
292292+ // ... existing logic
293293+}
294294+```
295295+296296+### 5.5 Cross-Process Suggestion Protocol (Extension Commands)
297297+298298+For commands registered by other extensions (which execute via pubsub proxy):
299299+300300+1. Panel publishes `cmd:suggest:{commandName}` with `{ query, limit }`
301301+2. Extension's preload handler calls the local `suggest()` function
302302+3. Result published back via `cmd:suggest:{commandName}:result`
303303+4. Panel renders suggestions
304304+305305+This requires extending `preload.js` to store `suggest` handlers alongside `execute` handlers, and creating the pubsub wiring similar to how `cmd:execute:{name}` works today.
306306+307307+### 5.6 Debouncing and Cancellation
308308+309309+- **Debounce:** 150ms after typing stops (fast enough to feel instant, slow enough to avoid thrashing)
310310+- **Cancellation:** If user types more before suggestions return, discard stale results (use a generation counter or AbortController)
311311+- **Minimum query length:** 1 character (commands can enforce their own minimum internally)
312312+- **Maximum results:** Panel requests `limit: 10` by default, commands can return fewer
313313+314314+---
315315+316316+## 6. UI Flow Mockup
317317+318318+### 6.1 Initial State -- Command Matching (unchanged)
319319+320320+```
321321+┌──────────────────────────────────────┐
322322+│ edi │ <- User types "edi"
323323+│ edit (grey completion) │ <- Inline suggestion
324324+└──────────────────────────────────────┘
325325+```
326326+327327+User presses ArrowDown to see commands:
328328+329329+```
330330+┌──────────────────────────────────────┐
331331+│ edi │
332332+├──────────────────────────────────────┤
333333+│ ▸ edit Edit a note → item│
334334+│ open editor Open markdown editor │
335335+└──────────────────────────────────────┘
336336+```
337337+338338+### 6.2 Entering Suggestion Mode
339339+340340+User presses Tab (autocompletes to `edit `) or types `edit `:
341341+342342+```
343343+┌──────────────────────────────────────┐
344344+│ edit │ <- Command matched, space typed
345345+├──────────────────────────────────────┤
346346+│ (type to search...) │ <- Placeholder prompt
347347+└──────────────────────────────────────┘
348348+```
349349+350350+User types `vam`:
351351+352352+```
353353+┌──────────────────────────────────────┐
354354+│ edit vam │
355355+├──────────────────────────────────────┤
356356+│ ▸ Vampire Notes text 2h ago│
357357+│ Vampire Weekend Review url 1d ago│
358358+│ Interview with a Vampire text 3d ago│
359359+└──────────────────────────────────────┘
360360+```
361361+362362+### 6.3 Selection and Action
363363+364364+User arrows down to "Vampire Weekend Review" and presses Enter:
365365+366366+```
367367+┌──────────────────────────────────────┐
368368+│ edit vam │
369369+├──────────────────────────────────────┤
370370+│ Vampire Notes text 2h ago│
371371+│ ▸ Vampire Weekend Review url 1d ago│ <- Selected
372372+│ Interview with a Vampire text 3d ago│
373373+└──────────────────────────────────────┘
374374+```
375375+376376+Panel calls `edit.execute()` with `selectedSuggestion` -> routes to editor -> panel closes.
377377+378378+### 6.4 Enter Without Selection
379379+380380+If user presses Enter without arrowing down (no suggestion selected), the command executes with `ctx.search = 'vam'` and no `selectedSuggestion`. The command does its own search and returns results via the existing output selection mode.
381381+382382+This preserves backward compatibility -- suggestions are an acceleration, not a requirement.
383383+384384+### 6.5 Empty Query (No Search Term)
385385+386386+Some commands could provide "recent items" when the query is empty:
387387+388388+```
389389+┌──────────────────────────────────────┐
390390+│ edit │
391391+├──────────────────────────────────────┤
392392+│ ▸ Meeting Notes text 1h ago│ <- Recent items
393393+│ Shopping List text 3h ago│
394394+│ Project Ideas text 1d ago│
395395+└──────────────────────────────────────┘
396396+```
397397+398398+This is opt-in per command. `suggest('')` with empty string means "show recents."
399399+400400+---
401401+402402+## 7. Commands That Would Benefit
403403+404404+| Command | Suggest Source | Action on Selection |
405405+|---------|---------------|-------------------|
406406+| `edit <query>` | `queryItems({ type: 'text', search })` | Open item in editor |
407407+| `open <query>` | `queryItemsByFrecency({ search })` | Open URL in window |
408408+| `tag <query>` | `getTagsByFrecency()` + filter | Apply tag to active window |
409409+| `delete <query>` | `queryItems({ search })` | Soft-delete item |
410410+| `open group <query>` | Groups list | Open group's URLs |
411411+| `history <query>` | `queryItemsByFrecency({ search })` | Open URL from history |
412412+| `note <query>` | `queryItems({ type: 'text', search })` | Open existing note in editor |
413413+414414+The history command currently registers every URL as a separate command. With the suggestion protocol, it could be a single `history` command with `suggest()` that searches URLs -- much cleaner and more scalable.
415415+416416+---
417417+418418+## 8. Implementation Phases
419419+420420+### Phase 1: Core Suggestion Infrastructure (panel.js)
421421+422422+**Goal:** Panel can call `suggest()` on built-in commands and render results in the dropdown.
423423+424424+**Changes to `extensions/cmd/panel.js`:**
425425+- Add `suggestionMode` state: `{ active, commandName, query, results, selectedIndex, generation }`
426426+- In `input` event handler: detect when typed text matches a command + has a space + command has `suggest`
427427+- Call `command.suggest(query)` with debounce (150ms)
428428+- Render suggestion items in `#results` instead of command matches
429429+- Handle ArrowUp/Down for suggestion navigation
430430+- On Enter with selection: build context with `selectedSuggestion`, call execute
431431+- On Enter without selection: existing behavior (execute with `search`)
432432+- On Escape: exit suggestion mode
433433+- On Backspace past space: exit suggestion mode
434434+435435+**Changes to `extensions/cmd/panel.html`:**
436436+- Add CSS for suggestion items (title + subtitle layout, type badge, timestamp)
437437+438438+**Estimated effort:** 1-2 days
439439+440440+### Phase 2: Migrate `edit` Command
441441+442442+**Goal:** `edit` command has a working `suggest()` method.
443443+444444+**Changes to `extensions/cmd/commands/edit.js`:**
445445+- Add `suggest(query)` method that calls `queryItems({ type: 'text', search: query })` (use server-side search instead of client-side filter)
446446+- Return formatted suggestion items: `{ id, title: firstLine, subtitle: 'text -- relative time' }`
447447+- Modify `execute()` to check `ctx.selectedSuggestion` for fast-path routing
448448+449449+**Estimated effort:** Half a day
450450+451451+### Phase 3: Cross-Process Suggestion Protocol (preload + background)
452452+453453+**Goal:** Extension commands (registered via `api.commands.register`) can provide suggestions.
454454+455455+**Changes to `preload.js`:**
456456+- Accept optional `suggest` in command registration
457457+- Store suggest handler alongside execute handler in `window._cmdHandlers`
458458+- Subscribe to `cmd:suggest:{name}` topic
459459+- On receive: call local suggest handler, publish result to `cmd:suggest:{name}:result`
460460+461461+**Changes to `extensions/cmd/commands.js`:**
462462+- When creating proxy commands, check if source extension declared `suggest` capability
463463+- Add `suggest()` method on proxy that publishes `cmd:suggest:{name}` and awaits response
464464+465465+**Changes to `extensions/cmd/background.js`:**
466466+- Store `hasSuggest` flag in command registry metadata
467467+- Include in `cmd:query-commands-response`
468468+469469+**Estimated effort:** 1 day
470470+471471+### Phase 4: Migrate More Commands
472472+473473+**Goal:** `history`, `open group`, `tag` commands gain `suggest()` methods.
474474+475475+- **`history`**: Replace dynamic command registration with a single `suggest()` that calls `queryItemsByFrecency({ search })`
476476+- **`open group`**: `suggest()` searches group names via `getTagsByFrecency()`
477477+- **`tag`**: `suggest()` searches tag names for autocomplete
478478+479479+**Estimated effort:** 1 day
480480+481481+### Phase 5: Polish and UX
482482+483483+- Loading indicator while suggestions are fetching (subtle spinner after 200ms)
484484+- "No results" message when search returns empty
485485+- Keyboard hint in placeholder: "Type to search, Arrow to browse"
486486+- Suggestion result count indicator
487487+- Adaptive ranking: boost suggestions the user previously selected for a given query (reuse existing adaptive feedback mechanism)
488488+489489+**Estimated effort:** 1 day
490490+491491+---
492492+493493+## 9. Key Files That Need Changes
494494+495495+| File | Change Type | Description |
496496+|------|------------|-------------|
497497+| `extensions/cmd/panel.js` | Major | Add suggestion mode state machine, debounced suggest calls, suggestion rendering, keyboard navigation within suggestions |
498498+| `extensions/cmd/panel.html` | Minor | CSS for suggestion item layout (title/subtitle/badge/time) |
499499+| `extensions/cmd/commands.js` | Moderate | Proxy command `suggest()` via pubsub for extension commands |
500500+| `extensions/cmd/commands/edit.js` | Moderate | Add `suggest()` method, update `execute()` with selectedSuggestion fast-path |
501501+| `extensions/cmd/commands/history.js` | Moderate | Add `suggest()` method, potentially remove dynamic command registration |
502502+| `extensions/cmd/background.js` | Minor | Track `hasSuggest` in registry metadata |
503503+| `preload.js` | Moderate | Accept and wire `suggest` handler in command registration, pubsub plumbing |
504504+| `extensions/tags/background.js` | Minor | Add `suggest()` to tag command |
505505+| `extensions/groups/background.js` | Minor | Add `suggest()` to open group command |
506506+507507+---
508508+509509+## 10. State Machine: Suggestion Mode in panel.js
510510+511511+The panel currently has these modes:
512512+- **Normal mode:** typing matches commands
513513+- **Output selection mode:** navigating array results from command execution
514514+- **Chain mode:** selecting next command after receiving typed output
515515+516516+The new **suggestion mode** fits between normal mode and output selection mode:
517517+518518+```
519519+Normal Mode
520520+ │
521521+ ├─ (type command + space, command has suggest)
522522+ ▼
523523+Suggestion Mode ──── (Escape / Backspace past space) ──── Normal Mode
524524+ │
525525+ ├─ (Enter with selection) → execute(ctx + selectedSuggestion) → close/chain
526526+ ├─ (Enter without selection) → execute(ctx) → may enter Output Selection Mode
527527+ │
528528+ ▼
529529+Output Selection Mode (if command returns array)
530530+ │
531531+ ├─ (select item)
532532+ ▼
533533+Chain Mode (if item has chainable mimeType)
534534+ or
535535+Close Panel (if mimeType is 'item' → editor routing)
536536+```
537537+538538+### Suggestion Mode State
539539+540540+```javascript
541541+// Add to state object in panel.js
542542+suggestionMode: false, // Whether we're in suggestion mode
543543+suggestionCommand: null, // The command providing suggestions
544544+suggestionQuery: '', // Current query text
545545+suggestionResults: [], // Array of suggestion items
546546+suggestionIndex: -1, // Selected index (-1 = none)
547547+suggestionGeneration: 0, // Counter to discard stale results
548548+suggestionTimer: null, // Debounce timer
549549+```
550550+551551+### Entry/Exit Conditions
552552+553553+**Enter suggestion mode when:**
554554+1. Typed text matches exactly one command (or the top match)
555555+2. There is a space after the command name
556556+3. The matched command has a `suggest` method
557557+558558+**Exit suggestion mode when:**
559559+1. User presses Escape
560560+2. User deletes back past the space (no longer has command + space pattern)
561561+3. User clears input entirely
562562+4. A suggestion is selected and executed (mode exits naturally)
563563+564564+---
565565+566566+## 11. Alternative Considered: Unified Omnibox
567567+568568+An alternative to per-command suggestions is a unified "search everything" omnibox where typing any text simultaneously searches commands, items, URLs, tags, and groups:
569569+570570+```
571571+┌──────────────────────────────────────┐
572572+│ vampire │
573573+├──────────────────────────────────────┤
574574+│ Commands │
575575+│ edit Edit a note │
576576+│ Notes │
577577+│ ▸ Vampire Notes 2h ago │
578578+│ Interview with a Vampire 3d ago │
579579+│ URLs │
580580+│ vampirefreaks.com 1w ago │
581581+│ Tags │
582582+│ #vampire │
583583+└──────────────────────────────────────┘
584584+```
585585+586586+This is a compelling UX but is architecturally different from the command suggestion pattern. It would require:
587587+- A central search coordinator that queries all data sources
588588+- Category grouping in the dropdown
589589+- Disambiguation of what "Enter" does on different result types
590590+- Priority ranking across heterogeneous result types
591591+592592+**Recommendation:** Build command suggestions (this doc) first. The omnibox can be built later as a special `find` command that uses the same `suggest()` protocol, aggregating suggestions from multiple sources.
593593+594594+---
595595+596596+## 12. Performance Considerations
597597+598598+### Query Optimization
599599+600600+The current `edit.js` fetches ALL text items then filters client-side. With the `suggest()` protocol, we should:
601601+602602+1. **Use server-side search:** `queryItems({ type: 'text', search: query })` instead of fetching all + filtering
603603+2. **Limit results:** Always pass `limit: 10` to avoid transferring large result sets
604604+3. **Index content column:** Consider adding SQLite FTS5 index for full-text search (future enhancement)
605605+606606+### Latency Budget
607607+608608+Target: suggestions visible within 100ms of typing pause.
609609+610610+- Debounce wait: 150ms
611611+- IPC round-trip (built-in command): ~0ms (same process)
612612+- IPC round-trip (extension command): ~10-20ms (pubsub via main process)
613613+- SQLite query: ~1-5ms for LIKE search on <10K items
614614+- **Total (built-in):** ~155ms -- meets target
615615+- **Total (extension):** ~175ms -- meets target
616616+617617+For larger datasets (>10K items), FTS5 indexing would keep query time <5ms.
618618+619619+### Caching
620620+621621+Suggestions are ephemeral and short-lived. No persistent caching needed. In-memory cache with 5s TTL could help if user is rapidly editing the same query (backspace + retype), but this is premature optimization.
622622+623623+---
624624+625625+## 13. Relationship to Existing Research
626626+627627+### Search Extension (notes/research-search-extension.md)
628628+629629+The search extension research designs web search suggestions (Google, DuckDuckGo, etc.) in the cmd bar. The suggestion protocol designed here is complementary:
630630+631631+- Search extension commands (`google`, `ddg`) would use `suggest()` to fetch web search suggestions from engine APIs
632632+- The same dropdown rendering, debounce, and selection logic applies
633633+- The search extension is a **consumer** of the suggestion protocol, not the foundation of it
634634+635635+### Entity Recognition (notes/research-entity-recognition.md)
636636+637637+Entity recognition could enrich suggestion results with extracted metadata (people, places, dates). But it is orthogonal to the suggestion protocol itself.
638638+639639+---
640640+641641+## 14. Summary
642642+643643+### What to build
644644+645645+A `suggest(query)` optional method on commands, called by the panel as the user types after a command name, with results rendered in the dropdown for immediate selection.
646646+647647+### Why this approach
648648+649649+- Minimal protocol addition (one optional method)
650650+- Zero breaking changes (commands without `suggest` work exactly as before)
651651+- Natural extension of existing command shape
652652+- Works for both built-in and extension commands
653653+- Provides the desired `edit vampire` flow as the first use case
654654+- Generalizes cleanly to `open`, `tag`, `delete`, `history`, `group` commands
655655+- Leaves room for a unified omnibox as a future `find` command
656656+657657+### Key architectural decisions
658658+659659+1. **Command-level, not global:** Each command owns its suggestions. No central search coordinator.
660660+2. **Optional opt-in:** Commands without `suggest()` are unaffected.
661661+3. **Selection passthrough:** Selected suggestion's `data` is passed to `execute()` via `ctx.selectedSuggestion`, enabling fast-path execution.
662662+4. **Enter without selection still works:** Falls back to existing execute-then-select flow.
663663+5. **Debounced, not streamed:** Panel calls `suggest()` after 150ms typing pause, not on every keystroke.