Download src/modules/editor/lib/useDocument.ts from lekmikdok/termix: direct link, hf CLI and curl.
- Browser
- Download file 3.04 kB
-
https://huggingface.co/spaces/lekmikdok/termix/resolve/main/src/modules/editor/lib/useDocument.ts
- Command line
-
hf download hf://spaces/lekmikdok/termix/src/modules/editor/lib/useDocument.ts
-
curl -L -o useDocument.ts https://huggingface.co/spaces/lekmikdok/termix/resolve/main/src/modules/editor/lib/useDocument.ts
3.04 kB
| import { invoke } from "@tauri-apps/api/core"; | |
| import { useCallback, useEffect, useRef, useState } from "react"; | |
| type ReadResult = | |
| | { kind: "text"; content: string; size: number } | |
| | { kind: "binary"; size: number } | |
| | { kind: "toolarge"; size: number; limit: number }; | |
| export type DocumentState = | |
| | { status: "loading" } | |
| | { status: "ready"; content: string; size: number } | |
| | { status: "binary"; size: number } | |
| | { status: "toolarge"; size: number; limit: number } | |
| | { status: "error"; message: string }; | |
| type Options = { | |
| path: string; | |
| onDirtyChange?: (dirty: boolean) => void; | |
| }; | |
| export function useDocument({ path, onDirtyChange }: Options) { | |
| const [doc, setDoc] = useState<DocumentState>({ status: "loading" }); | |
| const [dirty, setDirty] = useState(false); | |
| const [reloadCounter, setReloadCounter] = useState(0); | |
| // Track the saved buffer so we can detect changes cheaply. | |
| const savedRef = useRef<string>(""); | |
| const bufferRef = useRef<string>(""); | |
| const dirtyRef = useRef(false); | |
| useEffect(() => { | |
| dirtyRef.current = dirty; | |
| }, [dirty]); | |
| // Notify parent of dirty transitions. | |
| const onDirtyChangeRef = useRef(onDirtyChange); | |
| useEffect(() => { | |
| onDirtyChangeRef.current = onDirtyChange; | |
| }, [onDirtyChange]); | |
| useEffect(() => { | |
| onDirtyChangeRef.current?.(dirty); | |
| }, [dirty]); | |
| // Load on path change or explicit reload. | |
| useEffect(() => { | |
| let cancelled = false; | |
| setDoc({ status: "loading" }); | |
| setDirty(false); | |
| invoke<ReadResult>("fs_read_file", { path }) | |
| .then((res) => { | |
| if (cancelled) return; | |
| if (res.kind === "text") { | |
| savedRef.current = res.content; | |
| bufferRef.current = res.content; | |
| setDoc({ | |
| status: "ready", | |
| content: res.content, | |
| size: res.size, | |
| }); | |
| } else if (res.kind === "binary") { | |
| setDoc({ status: "binary", size: res.size }); | |
| } else if (res.kind === "toolarge") { | |
| setDoc({ | |
| status: "toolarge", | |
| size: res.size, | |
| limit: res.limit, | |
| }); | |
| } | |
| }) | |
| .catch((e) => { | |
| if (!cancelled) setDoc({ status: "error", message: String(e) }); | |
| }); | |
| return () => { | |
| cancelled = true; | |
| }; | |
| }, [path, reloadCounter]); | |
| /** Re-read the file from disk. No-op (silent) if the buffer is dirty — | |
| * callers shouldn't clobber unsaved user edits. Returns whether reload ran. */ | |
| const reload = useCallback((): boolean => { | |
| if (dirtyRef.current) return false; | |
| setReloadCounter((n) => n + 1); | |
| return true; | |
| }, []); | |
| const onChange = useCallback((next: string) => { | |
| bufferRef.current = next; | |
| setDirty(next !== savedRef.current); | |
| }, []); | |
| const save = useCallback(async () => { | |
| if (!dirty) return; | |
| const content = bufferRef.current; | |
| await invoke("fs_write_file", { path, content }); | |
| savedRef.current = content; | |
| setDirty(false); | |
| }, [path, dirty]); | |
| return { doc, dirty, onChange, save, reload }; | |
| } | |