Search: saved searches as friendly smart folders #64

Open
opened 2026-09-24 15:53:06 +00:00 by kayg · 3 comments
Owner

Owner decision S10 (DESIGN §32): saved searches work like smart folders with a user-friendly UI: save any query (with its pills) from the search window, rename it, pin it to the sidebar, and it updates live; in Files mode they appear as smart folders. Stored as plain JSON in <home>/.calternal/saved-searches/<id>.json (schema calternal.saved-search/1, file over app). Counts shown on pinned items, refreshed via the change stream.

Context for the owning job

  • Repo: kayg/calternal (~/Developer/calternal). Read CLAUDE.md, CONTEXT.md and docs/DESIGN.md (§15, §18 budgets, §31, §32) first. Prior art: calternal.js docs/search.md (read-only at /home/kayg/Developer/calternal.js) — its palette UX, > command mode, ranking weights, a11y combobox pattern and recents carry over.
  • Existing code: crates/calternal-search (Tantivy index, watcher + reconcile, providers via calternal-plugin fan-out), the ⌘K registry in apps/web.
  • Owner rules: search must be ultra fast (⌘K results < 50 ms p95 at 100k items; first keystroke to first results < 16 ms for client providers); file over app (indexes are derived and rebuildable); performance first but never at the cost of finesse; calternal.js design system (Claude reviews screenshots; floating window over a dimmed + blurred background, spring motion, reduced-motion respected); never ship sample data; atomic commits (commit every 30–45 min); adversarial testing after API work.
  • Comment on this issue when you start, on findings, when blocked, and when finished. Never close it.
Owner decision S10 (DESIGN §32): saved searches work like smart folders with a user-friendly UI: save any query (with its pills) from the search window, **rename** it, **pin** it to the sidebar, and it updates live; in Files mode they appear as smart folders. Stored as plain JSON in `<home>/.calternal/saved-searches/<id>.json` (schema `calternal.saved-search/1`, file over app). Counts shown on pinned items, refreshed via the change stream. ## Context for the owning job - Repo: kayg/calternal (~/Developer/calternal). Read CLAUDE.md, CONTEXT.md and docs/DESIGN.md (§15, §18 budgets, §31, §32) first. Prior art: calternal.js `docs/search.md` (read-only at /home/kayg/Developer/calternal.js) — its palette UX, `>` command mode, ranking weights, a11y combobox pattern and recents carry over. - Existing code: `crates/calternal-search` (Tantivy index, watcher + reconcile, providers via calternal-plugin fan-out), the ⌘K registry in apps/web. - Owner rules: search must be **ultra fast** (⌘K results < 50 ms p95 at 100k items; first keystroke to first results < 16 ms for client providers); file over app (indexes are derived and rebuildable); performance first but never at the cost of finesse; calternal.js design system (Claude reviews screenshots; floating window over a dimmed + blurred background, spring motion, reduced-motion respected); never ship sample data; atomic commits (commit every 30–45 min); adversarial testing after API work. - Comment on this issue when you start, on findings, when blocked, and when finished. Never close it.
Author
Owner

Starting the search window UI job for #57, #61, #63 and #64 (one job, one worktree). Branch: job/search-ui; base SHA: 8c216759f01d704e69ecf37ef8293f422abf5552.

Plan: extend the existing ⌘K dialog and registry in apps/web (no second palette) into one floating search window with pills, previews and an in-place expanded view; add Files ⌘F and Calendar contextual search; add a saved-search CRUD API (JSON files through calternal-fs) and smart folders. I will post findings and a finish comment here.

Starting the search window UI job for #57, #61, #63 and #64 (one job, one worktree). Branch: `job/search-ui`; base SHA: `8c216759f01d704e69ecf37ef8293f422abf5552`. Plan: extend the existing ⌘K dialog and registry in apps/web (no second palette) into one floating search window with pills, previews and an in-place expanded view; add Files ⌘F and Calendar contextual search; add a saved-search CRUD API (JSON files through `calternal-fs`) and smart folders. I will post findings and a finish comment here.
Author
Owner

Finished #64 on branch job/search-ui at a8d2f644b41c0ebdd2eeff2bf4cfca6c6a3cfa5c (not pushed, not merged).

API (search crate, the search Plugin's own routes behind the data-scope guard):

  • GET /api/v1/search/saved, POST /api/v1/search/saved, GET/PATCH/DELETE /api/v1/search/saved/{id} (operation ids search_saved_*).
  • Storage is one JSON file per search: <home>/.calternal/saved-searches/<id>.json, schema calternal.saved-search/1, sorted keys (created, id, name, pinned, query, schema, updated). The Index holds nothing; a list reads the directory and skips a hand-edited file that does not parse.
  • Paths come only from the authenticated user's Home and a canonical lowercase UUID. Another user's id is "not found". Names are 1 to 120 characters without control, bidi or zero-width characters; queries must parse with the shared grammar. Writes go through calternal-fs under a per-user lock; delete moves the file to the Trash.
  • Adversarial probe section: hostile ids, names, queries, unknown fields, malformed and 2 MB bodies, cross-user GET/PATCH/DELETE, a rename storm, a create storm, a broken file on disk.

UI:

  • Save any query (pills included) from the window; pin to the sidebar on save or later; rename and delete from the window's saved-search bar or the sidebar ⋯ menu (also right-click and Shift+F10); "Update saved search" when the query changed.
  • Pinned searches show in the sidebar with live counts: "Smart folders" in Files, "Saved searches" in Calendar. Counts refresh on the Files change stream and when the tab becomes visible.
  • Link: /search/saved/<id>; Copy link is in the sidebar menu and in the window. DESIGN §33 lists /s/<saved-search-id>, but /s/<slug> is the public-link route and public-link slugs default to UUIDs, so the two cannot share the prefix. Please confirm or change the grammar.
Finished #64 on branch `job/search-ui` at `a8d2f644b41c0ebdd2eeff2bf4cfca6c6a3cfa5c` (not pushed, not merged). API (search crate, the search Plugin's own routes behind the data-scope guard): - `GET /api/v1/search/saved`, `POST /api/v1/search/saved`, `GET/PATCH/DELETE /api/v1/search/saved/{id}` (operation ids `search_saved_*`). - Storage is one JSON file per search: `<home>/.calternal/saved-searches/<id>.json`, schema `calternal.saved-search/1`, sorted keys (`created`, `id`, `name`, `pinned`, `query`, `schema`, `updated`). The Index holds nothing; a list reads the directory and skips a hand-edited file that does not parse. - Paths come only from the authenticated user's Home and a canonical lowercase UUID. Another user's id is "not found". Names are 1 to 120 characters without control, bidi or zero-width characters; queries must parse with the shared grammar. Writes go through calternal-fs under a per-user lock; delete moves the file to the Trash. - Adversarial probe section: hostile ids, names, queries, unknown fields, malformed and 2 MB bodies, cross-user GET/PATCH/DELETE, a rename storm, a create storm, a broken file on disk. UI: - Save any query (pills included) from the window; pin to the sidebar on save or later; rename and delete from the window's saved-search bar or the sidebar ⋯ menu (also right-click and Shift+F10); "Update saved search" when the query changed. - Pinned searches show in the sidebar with live counts: "Smart folders" in Files, "Saved searches" in Calendar. Counts refresh on the Files change stream and when the tab becomes visible. - Link: `/search/saved/<id>`; Copy link is in the sidebar menu and in the window. DESIGN §33 lists `/s/<saved-search-id>`, but `/s/<slug>` is the public-link route and public-link slugs default to UUIDs, so the two cannot share the prefix. Please confirm or change the grammar.
Author
Owner

Hygiene review: the merged saved-search URL is /search/saved/<id>, while DESIGN §33 specifies /s/<saved-search-id>. The report asks the owner to confirm the grammar; keeping #64 open.

Hygiene review: the merged saved-search URL is `/search/saved/<id>`, while DESIGN §33 specifies `/s/<saved-search-id>`. The report asks the owner to confirm the grammar; keeping #64 open.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#64
No description provided.