- 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>
16 lines
963 B
TypeScript
16 lines
963 B
TypeScript
import { imageMimeFor, looksBinary, TEXT_PREVIEW_LIMIT } from "../components/projects/home/filePreview";
|
|
import type { ViewerFile } from "../lib/types";
|
|
|
|
export type ViewerKind = "text" | "image" | "binary";
|
|
export interface Editability { kind: ViewerKind; editable: boolean; reason: string | null }
|
|
|
|
const MIB = TEXT_PREVIEW_LIMIT / (1024 * 1024);
|
|
|
|
export function classifyViewerFile(path: string, file: ViewerFile, bytes: Uint8Array): Editability {
|
|
if (imageMimeFor(path)) return { kind: "image", editable: false, reason: "Images are shown, not edited." };
|
|
if (looksBinary(bytes)) return { kind: "binary", editable: false, reason: "This file is not text." };
|
|
if (file.truncated) return { kind: "text", editable: false, reason: `Files over ${MIB} MiB are read-only.` };
|
|
if (!file.editable) return { kind: "text", editable: false, reason: file.readonly_reason ?? "This location is read-only." };
|
|
return { kind: "text", editable: true, reason: null };
|
|
}
|