Skip to content

feat(editor): first save opens the file explorer - #224

Merged
byrongamatos merged 3 commits into
mainfrom
feat/editor-first-save-picker
Jul 12, 2026
Merged

feat(editor): first save opens the file explorer#224
byrongamatos merged 3 commits into
mainfrom
feat/editor-first-save-picker

Conversation

@ChrisBeWithYou

@ChrisBeWithYou ChrisBeWithYou commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

The first Save of a session now opens the native file explorer — the same picker Save As already uses — so you choose where the .feedpak lands instead of it going somewhere implicit. Once a location is chosen, later saves write straight to it.

Behaviour

  • First Save (Ctrl+S / toolbar Save / File ▸ Save) with no location chosen yet → opens showSaveFilePicker, saves to the chosen file, and remembers it (externalSaveHandle).
  • Later saves → write straight to that file + the library, no re-prompt.
  • No File System Access API (older/embedded webviews) → falls back to the plain library save, exactly as before — never a download-on-every-save loop.
  • Programmatic saves are untouched — the Loop-in-3D handoff, the host saveSession hook, and build all still call saveCDLC() 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

  • New editorSave() in file-ops.js routes on a tiny pure, _saveShouldPickPure(hasHandle, hasPickerApi) — pick only when nothing's chosen yet and the API exists. window.editorSave is rebound from saveCDLC to editorSave; all three Save entry points already dispatch through window.editorSave.

Tests & verification

  • tests/first_save_picker.test.mjs pins the routing pure.
  • Verified live on the testbed with a stubbed picker: the 1st Save opens the picker and writes the chosen file ("Saved to the selected file"); the 2nd Save does not re-open it (picker call count stays 1) and saves straight through ("Saved successfully").
  • npm test 115 green, npm run lint 0 errors (3 pre-existing warnings = main's ratchet).

Note: the /api/plugins/editor/session/export route the external copy uses exists in routes.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

  • New Features
    • The first user-initiated Save now opens the native file picker so you explicitly choose a .feedpak destination.
    • Subsequent saves write directly to the already chosen location without re-prompting.
  • Bug Fixes
    • If the native file picker isn’t available, saving reliably falls back to the prior library-based save behavior (avoiding repeated save prompting).
    • Sessions that can’t complete exporting (e.g., certain directory-form or incomplete cases) will keep the plain library save instead of attempting the picker flow.
  • No Change
    • Programmatic save actions are unaffected.

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
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@byrongamatos, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 28 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ac77482f-ac14-4eb0-9f67-da96cb19080b

📥 Commits

Reviewing files that changed from the base of the PR and between 86a6916 and 819f411.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • src/main.js
📝 Walkthrough

Walkthrough

The 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.

Changes

Save routing

Layer / File(s) Summary
Session export state
routes.py, src/file-ops.js
Propagates sloppak form metadata and records packed form after conversion so save routing can identify exportable sessions.
Save command routing
src/file-ops.js
Routes eligible first saves through the native picker and falls back to library saving for existing destinations, unavailable APIs, or non-exportable sessions.
Save entry point and validation
src/main.js, CHANGELOG.md, tests/first_save_picker.test.mjs
Wires the global Save handler to editorSave, documents the behavior, and tests picker decisions and edge cases.

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
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly reflects the main change: the editor’s first save now opens the native picker/file explorer.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/editor-first-save-picker

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/first_save_picker.test.mjs (1)

18-42: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the actual Save entry point.

These tests validate _saveShouldPickPure() only; they would still pass if editorSave() called the wrong branch or src/main.js retained the old window.editorSave binding. 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

📥 Commits

Reviewing files that changed from the base of the PR and between 2a764a1 and 8c94e3a.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • src/file-ops.js
  • src/main.js
  • tests/first_save_picker.test.mjs

Comment thread src/file-ops.js
byrongamatos and others added 2 commits July 12, 2026 19:15
…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>
@byrongamatos
byrongamatos merged commit a203df4 into main Jul 12, 2026
3 checks passed
@byrongamatos
byrongamatos deleted the feat/editor-first-save-picker branch July 12, 2026 19:23
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.

2 participants