Skip to content

Edit overrides in another editor, and open them in VS Code - #29

Merged
olehwebdev merged 1 commit into
mainfrom
feature/edit-overrides-externally
Sep 26, 2026
Merged

olehwebdev merged 1 commit into
mainfrom
feature/edit-overrides-externally

Conversation

@olehwebdev

@olehwebdev olehwebdev commented Sep 26, 2026 •

Copy link
Copy Markdown
Owner

What and why

Implements U11 (roadmap M2): edit an override's file in your own editor and the app picks it up.

  • Picks up edits made elsewhere. Save an override's file in VS Code, or any other editor, and it's served at once. The page reloads as it does after a save in the app (when that setting is on and the override is on), and a toast names the file.
  • An open tab follows.
    • A tab without unsaved edits shows the new text as saved; undo brings back the old text.
    • A tab with unsaved edits keeps them, and a banner says the file changed. Use that version swaps the edits for the file, and undo brings them back. Saving replaces the file with the tab's text.
    • Until one of those happens, no undo makes that tab saved, since none of its texts is the file's.
  • Open in VS Code. It's in an override tab's header, its Explorer row's context menu and the palette. When the system can't open the URL, the error toast offers Show in folder, which the row's menu also has.

How it works:

  • Main (src/main/overrideFiles/):
    • OverrideFileWatcher watches workspace/files with a non-persistent fs.watch and counts only content files (<id>.<ext>). Bases, writeAtomic's temp files and editors' swap files are ignored.
    • Once the folder has been quiet for 200 ms, the changed ids are handed to OverrideStore.takeFileEdit in one batch.
  • takeFileEdit reads the file inside the store's write queue, so the app's own writes read back unchanged.
    • A file that differs becomes the override's content, in memory and in the index. The engine reads it from memory, so the next request is served from it.
    • A file that is missing for a moment (an editor renaming its save over it) is taken once it is back.
  • Events: main sends overrides-changed, then a new overrides-edited event with the active workspace's changed ids. The renderer (features/override/external-editor) updates the tabs, shows the toast and reloads.
  • Open in VS Code uses VS Code's own vscode://file/<path> URL, built in main from the override id. It never goes through shell.openPath, since Windows runs .js files with Windows Script Host.
  • Code-structure splits:
    • The override IPC handlers moved to registerOverrideIpc.ts.
    • removeWorkspace and takeFileEdit got their own files, to keep OverrideStore under 150 lines.
    • The palette's file-tab items moved to fileTabItems.ts.
    • The new slice sits in a new features/override/ group: an ungrouped one would have gone over steiger's limit of 20. The older override slices stay where they are, to keep this PR to one change.

Only VS Code has a direct action; other editors get the file through Show in folder. A setting for another editor's command could come later.

How it was tested

  • Unit (test/unit/overrideFiles.test.ts):
    • which file names count;
    • taking an edit, and keeping it after a restart;
    • the app's own saves read back unchanged, even while they are still being written;
    • a missing file, a deleted override, another workspace's override;
    • batching, and a save renamed over the file as vim does;
    • stopping;
    • the VS Code URL.
  • Renderer (test/renderer/external-edits.test.ts):
    • a clean tab follows the file, and a tab with edits keeps them;
    • Use that version, and saving over the file;
    • one notice and one reload for several files, and no reload when it's off;
    • the Show in folder fallback.
  • E2E (test/e2e/app.e2e.test.ts), in the real app:
    • Open in VS Code hands main the file's vscode:// URL (shell.openExternal is stubbed);
    • writing the file on disk reloads the page with the new version and updates the tab;
    • with unsaved edits, the banner appears and Use that version takes the file.

Checklist

  • Comes from a git flow branch (feature/…, bugfix/…) into main, and does one thing
  • npm run typecheck, npm run lint:fsd, npm run lint:structure, npm run lint, npm run lint:unused, npm run lint:duplicates, npm run lint:secrets, npm test and npm run test:e2e pass (1193 unit, 110 e2e)
  • docs/SPEC.md describes any behaviour this changes (§2, §5 Edits made elsewhere, §7 Another editor, §8, §11)
  • User-visible changes are noted under [Unreleased] in CHANGELOG.md

Main watches workspace/files: once the folder has been quiet for 200 ms,
each changed override's file is read in the store's write queue (so the
app's own writes read back unchanged) and, when it differs, becomes the
served content. The renderer then updates the open tab (as saved, or
keeping unsaved edits behind a banner), says so and reloads the page as
a save does.

Open in VS Code (tab header, Explorer row menu, palette) opens the file
through VS Code's vscode://file URL, never the system's handler for its
type; Show in folder finds it for any other editor.
@olehwebdev
olehwebdev merged commit 204bec3 into main Sep 26, 2026
1 check passed
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