Skip to content

feat(library): document-style File menu for the saved-query Library (#29)#31

Merged
BorisTyshkevich merged 3 commits into
feat/saved-query-descriptionsfrom
feat/library-file-menu
Jun 24, 2026
Merged

feat(library): document-style File menu for the saved-query Library (#29)#31
BorisTyshkevich merged 3 commits into
feat/saved-query-descriptionsfrom
feat/library-file-menu

Conversation

@BorisTyshkevich

Copy link
Copy Markdown
Collaborator

Closes #29. Stacked on #30 (the descriptions pre-phase) — review/merge that first; this PR's base is feat/saved-query-descriptions, so its own diff is just the File-menu work.

What & why

Reframes the saved-query collection as a named, savable document — "the Library" — with a header File ▾ menu, an editable name, and an unsaved-changes dot, replacing the Export/Import buttons hidden at the bottom of the Saved panel.

Changes

State (src/state.js)

  • libraryName (persisted, asb:libraryName) + libraryDirty (session-only).
  • Ops: renameLibrary / newLibrary / replaceLibrary / appendLibrary / markLibrarySaved. The existing saved-query mutators now mark the Library dirty. New/Replace prune now-dangling tab.savedId links; Replace adopts the loaded file's base name and keeps original ids (lossless).

Serializers (src/core/saved-io.js) — export-only

  • buildMarkdownDoc (### name + optional description paragraph + fenced ```sql; 4-backtick fence when the body contains a triple backtick).
  • buildSqlDoc (/* name + description */ comment + statement, ;-delimited; */ defanged).

UI

  • New src/ui/file-menu.js: the header File ▾ dropdown (New Library · Save JSON .json · Replace… / Append… · Download Markdown .md / Download SQL .sql · N in Library footer), the inline-editable library title + dirty dot, and Replace/New confirm dialogs.
  • app.js mounts the controls in the header and exposes the downloadFile / saveStr / FileReader / library-title seams; the obsolete exportSaved/importSavedFile actions are removed.
  • saved-history.js drops savedActions() and relabels the sidebar tab Library · N.

Decisions (per issue #29 + the design owner)

  • Menu label is File ▾ (the design's ISSUE-library-redesign.md saying Library ▾ is an obsolete doc).
  • JSON is the only importable format; Markdown/SQL are one-way share downloads (lossy by design — JSON stays canonical). Imported SQL is never auto-run.
  • Replace/New confirm before destructive changes; open editor tabs are unaffected.

Tests

Per-file 100% statements/lines held (branches ≥90 — file-menu.js at 95.5%; functions ≥95). New tests/unit/file-menu.test.js covers menu structure, Save JSON filename + dirty-clear, Replace confirm/empty paths, Append merge counts, invalid/read-error toasts, New confirm, dialog dismissal, and inline rename. npm test → 715 passing; npm run build clean. README documents the Library menu.

🤖 Generated with Claude Code

BorisTyshkevich and others added 3 commits June 24, 2026 11:15
)

Reframes the saved-query collection as a named, savable document — "the
Library" — with a header File ▾ menu, an editable name, and an unsaved-changes
dot, replacing the Export/Import row hidden at the bottom of the Saved panel.

State (src/state.js):
- libraryName (persisted, key asb:libraryName) + libraryDirty (session-only).
- ops renameLibrary / newLibrary / replaceLibrary / appendLibrary /
  markLibrarySaved; the existing saved-query mutators now mark the Library
  dirty. New/Replace prune now-dangling tab→saved links; Replace adopts the
  loaded file's base name and keeps original ids (lossless).

Serializers (src/core/saved-io.js):
- buildMarkdownDoc / buildSqlDoc — one-way "share" exports (### + fenced sql;
  /* name + description */ comment + ;-delimited sql). JSON stays canonical.

UI:
- New src/ui/file-menu.js: the header File ▾ menu (New / Save JSON / Replace… /
  Append… / Download Markdown / Download SQL + "N in Library" footer), the
  inline-editable library title with a dirty dot, and Replace/New confirm
  dialogs. JSON is the only importable format; imported SQL is never run.
- app.js mounts the controls in the header and exposes the download / saveStr /
  FileReader / library-title seams; the obsolete exportSaved/importSavedFile
  actions are removed. saved-history.js drops savedActions() and relabels the
  tab "Library".

Tests stay at the per-file 100% statements/lines gate (branches ≥90, functions
≥95); adds tests/unit/file-menu.test.js. README documents the Library menu.

Builds on the descriptions pre-phase (#29 phase 1, PR #30).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AX4YR7RHKj7JfmS2AqnRrm
…file-menu + review fixes

Resolves the binary-merge conflict on src/core/saved-io.js and folds in the
PR #30 review fixes plus PR #31 (File menu) review fixes found this pass.

saved-io.js (conflict resolution + fixes):
- Kept PR #31's feature-complete version (buildMarkdownDoc/buildSqlDoc, append).
- Converted the raw NUL byte in contentKey to the '\0' escape, so the file is
  text again — that NUL is what made it binary-to-git and un-3-way-mergeable.
- Re-applied the PR #30 fix: parseImportDoc trims `description` (drops whitespace-
  only), matching saveQuery/renameSaved.
- buildMarkdownDoc collapses whitespace in the `### name` heading so a query name
  with a newline can't break the cookbook structure.

file-menu.js (PR #31 review fixes):
- CRITICAL: the File menu read bare `app.document` (undefined — createApp only
  exposes a local doc), so it threw on first click in production. Use
  `app.document || document`, matching shortcuts.js/results.js. Tests passed only
  because the fake-app supplies document.
- Anchor the dropdown via zoomScale() (divide getBoundingClientRect by the
  html{zoom} scale), like the editor popovers — it mis-anchored ~20% off otherwise.

state.js auto-merged: renameSaved keeps both PR #30's null-safe String(... || '')
and PR #31's libraryDirty flag.

All 715 tests pass; per-file coverage gate holds.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QGBS74oUsXarGkCRQKEFLu
…ds (PR #31 review P2)

A Replace-loaded JSON containing repeated `id` values previously preserved every
duplicate id in savedQueries. The sidebar addresses rows by id (find/filter), so
deleting one row removed all duplicates and rename/favorite could affect the wrong
row. Now mint a fresh id for any missing OR already-seen id (unique ids preserved),
matching the mergeSaved-based Append path which never left duplicate ids behind.

Test covers duplicate + missing ids and the mint-collision retry loop.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QGBS74oUsXarGkCRQKEFLu
@BorisTyshkevich BorisTyshkevich merged commit 6518b75 into feat/saved-query-descriptions Jun 24, 2026
1 check passed
BorisTyshkevich added a commit that referenced this pull request Jun 24, 2026
chore(library): land File menu (#31) onto main — stacked-merge catch-up
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant