- write.rs: a save's new base is sha256 of the bytes written; the script's
post-mv hash comes back as disk_hash, and a mismatch (another writer landed
after us) shows "Changed on disk" instead of being adopted (ledger M2).
- write.rs: conflict:/gone:/read-only strings are constants with a pure
saved_file() mapping and tests; app/src/viewer/ipcMessages.ts is the one TS
copy and a cargo test checks it against the Rust originals.
- write.rs: the comment now says the in-place `cat >` fallback follows a
planted symlink, and why that is accepted (runs as claude).
- poll.rs: a file deleted between `test -f` and `sha256sum` reads as gone.
- viewerState/EditorPane: poll_failed carries its message; only the
"Start the project before" refusal reads as Container not running, anything
else gets its own banner and leaves Save enabled.
- EditorPane: a failed first read shows Retry and is retried by the poll.
- spec §1: refused OSC 8 targets keep the refusal card (Task 9 ruling).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Keep CRLF (or CR) line endings and a UTF-8 BOM through the editor:
textFormat.ts records the dominant separator and the BOM on load and
restores both on save, so a save changes only the user's edits.
- A clean document whose reload failed retries on the next poll.
- A poll that overlaps a save, or was issued before one settled, is
ignored instead of reading the pre-save hash as a change.
- A conflict whose follow-up poll has no hash shows an error with a
Reload button rather than an Overwrite that could only conflict again.
- Match write.rs's exact read-only message; show the read-only reason as
visible text; error banners are role="alert".
- vite/client types move to src/vite-env.d.ts.
- Tests: CRLF and BOM saves, reload retry, poll/save race, null-hash
conflict, Save and close success and failure, and CodeEditor.setDoc
keeping cursor and scroll.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
EditorPane loads the resolved file, polls it every 2 s while visible,
reloads a clean buffer silently and shows the "Changed on disk" banner
for a dirty one, saves against the loaded hash, and intercepts closing
with unsaved edits. ViewerApp routes to the editor, the not-found list
or the choose list.
Preflight rulings carried: one reload helper that passes the truncated
flag and polled hash (P3/P14), a poll right after a save conflict so
Overwrite on save adopts the current hash (P4), a chunked base64
encoder (P5), StatusIndicator for the badge (P11), banner-only test
queries (P2), and a Range geometry stub for jsdom (P17). A save the
container user may not write is reported as read-only and keeps the
buffer.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Implements Task 10 of the terminal file viewer plan: highlightLine.ts
(line-range StateField + setHighlight effect), viewerTheme.ts (EditorView
theme + syntax HighlightStyle), and CodeEditor.tsx (the React wrapper with
getDoc/setDoc/goTo/focus).
Also implements languages.ts + languages.test.ts, moved here from Task 7
per preflight ruling P1 (they need @codemirror/* packages, which only
Task 6 installs).
Preflight rulings applied:
- P1: languages.ts/.test.ts live here, not in Task 7.
- P12: syntax colours are `--syntax-*` custom properties in index.css,
not hard-coded hex.
- P13: languageFor tests assert `.not.toBeNull()` for mapped extensions
and move README to the "returns null" case, instead of the vacuous
`.resolves.toBeDefined()`.
- P14(c): readOnly extension array factored into readOnlyExt(readOnly)
instead of being duplicated in CodeEditor.
- P16: no custom Mod-g binding; searchKeymap's Mod-Alt-g covers "go to
line" already.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Task 6 of the terminal file viewer plan: app/viewer.html plus
src/viewer/{main,ViewerApp}.tsx (placeholder ViewerApp for Task 11 to
replace), registers viewer.html as a second Rollup input in
vite.config.ts, adds the file-viewer capability restricted to
file-viewer-* windows (allow-listen/unlisten for the goto event,
allow-destroy for the close-button/prevent_close interaction,
allow-internal-toggle-devtools to match default.json's dev
convenience), installs the CodeMirror packages Task 10 builds
languages.ts on top of (P1), and pins the viewer entry/capability
with a Rust fallback-trap test.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- viewerState.ts: pure reducer for the clean/dirty, same/changed/gone,
container-down and overwrite-on-save states (spec §5), plus the
pollEffect/canSave helpers EditorPane will drive off.
- editability.ts: classifies a fetched file as text/image/binary and
decides whether it is editable, deferring to Rust's readonly_reason
when it refuses.
Per preflight P1, languages.ts/.test.ts move to Task 10 (needs the
CodeMirror packages Task 6 installs; out of scope for this task's
worktree). Per P3, the "reloaded" action now carries `truncated` and
`polledHash` so a poll-driven reload of a truncated (prefix-hash-only)
file adopts the polled full-file hash instead of re-triggering a
reload on every subsequent poll -- with a reducer test covering it.
Per P13, tightened the poll_failed/canSave test to start from a dirty
doc so it actually exercises containerDown rather than passing only
because the doc was clean.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>