feat(editor): first save opens the file explorer - #224
Conversation
The Save command (Ctrl+S / the toolbar Save button / File ▸ Save) now opens the
native file picker on the FIRST save of a session — the same showSaveFilePicker
flow as Save As — so the user chooses where the .feedpak lands. Once a location
is chosen (externalSaveHandle set), later saves write straight to it and mirror
there, with no re-prompt.
New editorSave() in file-ops.js routes on _saveShouldPickPure(hasHandle,
hasPickerApi): pick only when nothing is chosen yet AND the File System Access
API exists — without it, fall back to the plain library save rather than a
download loop. window.editorSave is rebound from saveCDLC to editorSave; all
three user Save entry points already go through window.editorSave, and the
programmatic saveCDLC() callers (Loop-in-3D handoff, host saveSession hook,
build) are untouched.
tests/first_save_picker.test.mjs pins the routing pure. Verified live: the 1st
Save opens the picker and writes the chosen file ("Saved to the selected file");
the 2nd Save does NOT re-open it and saves straight through ("Saved
successfully"). npm test 115 green, lint 0 errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EBQCHCNA81E9tHmSDHSe2Q
|
Warning Review limit reached
Next review available in: 28 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe editor’s first eligible user-initiated Save now opens the native file picker when supported. Later saves reuse the selected destination, while create-mode and directory-form sessions retain library saving. Programmatic save paths remain unchanged. ChangesSave routing
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Editor
participant editorSave
participant NativePicker
participant LibrarySave
Editor->>editorSave: invoke Save
editorSave->>NativePicker: choose destination for eligible first save
editorSave->>LibrarySave: save directly for later or non-exportable saves
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
tests/first_save_picker.test.mjs (1)
18-42: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd coverage for the actual Save entry point.
These tests validate
_saveShouldPickPure()only; they would still pass ifeditorSave()called the wrong branch orsrc/main.jsretained the oldwindow.editorSavebinding. Add routing coverage for no session, first save with picker support, existing handle, and API fallback.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/first_save_picker.test.mjs` around lines 18 - 42, Add tests that invoke the actual editorSave entry point rather than only _saveShouldPickPure(), covering no session, first save with picker support, an existing handle, and missing picker API fallback. Verify each case routes to the expected save behavior, and update the src/main.js window.editorSave binding if needed so the tests exercise the current implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/file-ops.js`:
- Around line 774-778: Update editorSave to serialize concurrent save requests
with an in-flight promise: return the existing promise when a save is already
running, and assign/clear the guard around the complete editorSaveAs or saveCDLC
operation so it remains active until completion. Preserve the current session
and routing checks for the first request.
---
Nitpick comments:
In `@tests/first_save_picker.test.mjs`:
- Around line 18-42: Add tests that invoke the actual editorSave entry point
rather than only _saveShouldPickPure(), covering no session, first save with
picker support, an existing handle, and missing picker API fallback. Verify each
case routes to the expected save behavior, and update the src/main.js
window.editorSave binding if needed so the tests exercise the current
implementation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 6802a353-e71a-4d00-a407-981ff9768336
📒 Files selected for processing (4)
CHANGELOG.mdsrc/file-ops.jssrc/main.jstests/first_save_picker.test.mjs
…ve picker
The first-save file explorer routed EVERY session with no chosen location
through editorSaveAs, but two session kinds can't complete that flow:
- create-mode sessions: /save rejects them outright ("Only sloppak-format
sessions can be saved") — Ctrl+S popped the native picker, the user chose
a destination, and then nothing was written to it (pre-PR Ctrl+S surfaced
the save error immediately, with no dialog).
- authoring-directory sloppaks: the library save succeeds but /session/export
409s ("Saved feedpak is not a packed file"), so editorSaveAs marked the
session dirty again after a successful library save and every subsequent
Ctrl+S re-opened the picker into the same failure.
Gate the picker on sessionCanExport: not create mode, sloppak format, and
packed (zip) form. The load route now surfaces sloppak_form so the frontend
can tell zip from directory form; absent field (older server) defaults to
zip, preserving the picker flow for normal library feedpaks.
Regression test extended (verified failing against the pre-fix predicate).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The first Save of a session now opens the native file explorer — the same picker Save As already uses — so you choose where the
.feedpaklands instead of it going somewhere implicit. Once a location is chosen, later saves write straight to it.Behaviour
showSaveFilePicker, saves to the chosen file, and remembers it (externalSaveHandle).saveSessionhook, and build all still callsaveCDLC()directly; only the user's Save routes through the picker.Per the product call, this applies to every song's first save in a session (a song opened from the library resets the chosen-location handle on load, so its first Save this session prompts too), not just brand-new songs.
Implementation
editorSave()infile-ops.jsroutes on a tiny pure,_saveShouldPickPure(hasHandle, hasPickerApi)— pick only when nothing's chosen yet and the API exists.window.editorSaveis rebound fromsaveCDLCtoeditorSave; all three Save entry points already dispatch throughwindow.editorSave.Tests & verification
tests/first_save_picker.test.mjspins the routing pure.npm test115 green,npm run lint0 errors (3 pre-existing warnings = main's ratchet).Note: the
/api/plugins/editor/session/exportroute the external copy uses exists inroutes.py(added in #210); a long-running testbed server needs a restart to pick it up, but that's orthogonal to this change (it already governs Save As).🤖 Generated with Claude Code
Summary by CodeRabbit
.feedpakdestination.