Two exports for two different consumers were both just "Export". Name them:
- Classification (header, AI round-trip): "Export for editing" / "Import edits"
— a filename-per-file JSON with no scanned tree, meant to be hand/AI-edited
and re-imported. Download suffix is now .zddc-classification.json.
- Workspace (welcome screen, transfer/backup): "Import workspace" + a row
"Export" tooltip spelling out it carries the snapshot + classifications for
moving between browsers. Download suffix stays .zddc-workspace.json.
Each tooltip points at the other so they're not confused.
Also wire workspace.activeName (referenced by the dataset export but never
exported), so a classification file is named after its workspace.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The scan is the expensive part (minutes on cloud mounts), so a workspace is now
portable between browsers/machines without re-scanning:
- Per-workspace "Export" downloads the snapshot + classify map as one
<name>.zddc-workspace.json (the source-directory handle is omitted — it can't
be serialized across browsers).
- Landing-page "Import" recreates the workspace from that JSON; the user clicks
"Connect directory" once on the new browser to re-attach the folder (no
re-scan — the snapshot carries the 2-hour walk). A classification-dataset JSON
is rejected with a pointer to the in-app Import.
Also fix the welcome screen clipping its top on short viewports: the base
.empty-state centers with align-items:center, which overflows symmetrically and
puts the card's top out of scroll reach. Center the inner card with auto margins
instead — they collapse when it's taller than the viewport, keeping the top
reachable.
Test: workspace import recreates a transferable record (snapshot + map, no handle); 46 green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Welcome: drop the 'absorbed into Browse' notice; bigger, inviting intro with
a two-method tutorial (Classify & copy — recommended/non-destructive; Rename
in place — edits files) and a OneDrive 'keep on device' tip.
- Resumable scan: the snapshot now records per-folder scan state, the workspace
record is created up front, and the partial snapshot is persisted every 5s
during the (slow) scan. scanner.resumeScan() resolves handles for only the
still-pending folders and drains them — so an interrupted scan picks up where
it left off instead of starting over.
- Reconnect on restore: opening a workspace no longer assumes the source is
connected; a header 'Connect directory' button (and a prompt) re-grants the
persisted handle in one click or lets you re-pick it. Until connected you can
still edit the data model; connecting also resumes any pending scan.
- Tests: resume-scan via mock root handle (31 classify/classifier green).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The classifier re-scanned the source on every session; on cloud-backed mounts
(OneDrive/Samba) that's minutes of per-op latency. Workspaces fix it: scan a
folder ONCE, snapshot the completed tree, and resume instantly — all
classification runs on the data model; the filesystem is only touched at copy.
- persist.js v2: multi-workspace IndexedDB (tiny 'index' store for the welcome
list + 'data' store holding the source handle, tree snapshot, and map). DB v2.
- scanner.js: snapshotTree()/loadSnapshot() (compact, handle-less, marked done,
totals recomputed) + lazy resolveFileHandle/resolveDirHandle from the root.
- workspace.js: welcome manager (new/open/rename/delete), debounced autosave of
the active workspace, 'Refresh from disk' (re-scan → re-snapshot, path-keyed
map carries over). New workspace = the one slow full scan; reopen = instant.
- copy.js: resolves snapshot files' handles from the workspace root with a
one-click read permission re-grant; missing-on-disk files surface as errors.
- app.js: enterAppShell() shared by rename/workspace flows; exposes setMode;
classify.js decoupled from persistence.
- template/css: welcome workspace list + header 'Workspaces' button.
- tests: snapshot round-trip, persist CRUD + classify-only-preserves-tree,
copy-from-snapshot via mock root handle (28 classify/classifier tests green).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>