fix(viewer): final-review fixes — save base from written bytes, honest poll errors, retryable first read
- 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>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import type { Project, ProjectPath, ProjectRemovalReport, ProjectResetOutcome, ContainerInfo, AppSettings, SettingsImportPreview, SettingsImportOutcome, UpdateInfo, ImageUpdateInfo, FileEntry, FileContents, WebTerminalInfo, SttStatus, GatewayStatus, InstallOptions, ClaudeSession, ContainerCapabilities, ScheduledTask, ScheduledTaskInput, SchedulerNotification, AuthBridgeStatus, BrowserViewStatus, BrowserViewPopoutState, BrowserPageState, PlaywrightDetection, BrowserSetupOutcome, BrowserInstallTarget, ContainerStaleness, MigrationOptions, MigrationReport, MigrationState, ClearTokenOutcome, CaCertInfo, UploadOutcome, Note, ViewerFile, ViewerPoll, ViewerState } from "./types";
|
||||
import type { Project, ProjectPath, ProjectRemovalReport, ProjectResetOutcome, ContainerInfo, AppSettings, SettingsImportPreview, SettingsImportOutcome, UpdateInfo, ImageUpdateInfo, FileEntry, FileContents, WebTerminalInfo, SttStatus, GatewayStatus, InstallOptions, ClaudeSession, ContainerCapabilities, ScheduledTask, ScheduledTaskInput, SchedulerNotification, AuthBridgeStatus, BrowserViewStatus, BrowserViewPopoutState, BrowserPageState, PlaywrightDetection, BrowserSetupOutcome, BrowserInstallTarget, ContainerStaleness, MigrationOptions, MigrationReport, MigrationState, ClearTokenOutcome, CaCertInfo, UploadOutcome, Note, ViewerFile, ViewerPoll, ViewerSaved, ViewerState } from "./types";
|
||||
|
||||
// Docker
|
||||
export const checkDocker = () => invoke<boolean>("check_docker");
|
||||
@@ -429,6 +429,6 @@ export const viewerReadFile = (maxBytes: number) =>
|
||||
invoke<ViewerFile>("viewer_read_file", { maxBytes });
|
||||
export const viewerPollFile = () => invoke<ViewerPoll>("viewer_poll_file");
|
||||
export const viewerWriteFile = (contentsBase64: string, baseHash: string) =>
|
||||
invoke<string>("viewer_write_file", { contentsBase64, baseHash });
|
||||
invoke<ViewerSaved>("viewer_write_file", { contentsBase64, baseHash });
|
||||
export const viewerChooseFile = (index: number) =>
|
||||
invoke<ViewerState>("viewer_choose_file", { index });
|
||||
|
||||
@@ -987,6 +987,14 @@ export interface ViewerFile {
|
||||
readonly_reason: string | null;
|
||||
}
|
||||
|
||||
/** A successful save (`write.rs`'s `SavedFile`). */
|
||||
export interface ViewerSaved {
|
||||
/** SHA-256 of the bytes written: the editor's new base hash. */
|
||||
hash: string;
|
||||
/** What the container hashed right after the swap; differs from `hash` only if another writer landed first. */
|
||||
disk_hash: string;
|
||||
}
|
||||
|
||||
export interface ViewerPoll {
|
||||
exists: boolean;
|
||||
hash: string | null;
|
||||
|
||||
Reference in New Issue
Block a user