Terminal file viewer/editor + per-window app-command lockdown (#60)
Build App / compute-version (push) Successful in 7s
Secret Scan / scan (push) Successful in 8s
Build App / build-macos (push) Successful in 2m53s
Build App / build-linux (push) Successful in 5m12s
Build App / build-windows (push) Successful in 5m15s
Build App / create-tag (push) Successful in 3s
Build App / sync-to-github (push) Successful in 1m5s
Build App / compute-version (push) Successful in 7s
Secret Scan / scan (push) Successful in 8s
Build App / build-macos (push) Successful in 2m53s
Build App / build-linux (push) Successful in 5m12s
Build App / build-windows (push) Successful in 5m15s
Build App / create-tag (push) Successful in 3s
Build App / sync-to-github (push) Successful in 1m5s
Clicking a file path in Claude's terminal output now opens the file in its own window with a CodeMirror 6 editor. The editor highlights the target line, live-reloads while the file changes, and saves explicitly with hash-based conflict detection. The viewer commands are gated by window label. Every app command is now ACL-gated per window through a Tauri AppManifest. build.rs checks the handler list against the capability files and fails the build on any mismatch. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit was merged in pull request #60.
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
import { beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import { act, render } from "@testing-library/react";
|
||||
import { createRef } from "react";
|
||||
import { EditorView } from "@codemirror/view";
|
||||
import { CodeEditor, type CodeEditorHandle } from "./CodeEditor";
|
||||
|
||||
beforeAll(() => {
|
||||
// P17: CodeMirror's measure pass calls Range geometry, which jsdom lacks.
|
||||
Range.prototype.getClientRects = () => ({ length: 0, item: () => null, [Symbol.iterator]: [][Symbol.iterator] }) as unknown as DOMRectList;
|
||||
Range.prototype.getBoundingClientRect = () => ({ x: 0, y: 0, top: 0, left: 0, right: 0, bottom: 0, width: 0, height: 0, toJSON() {} }) as DOMRect;
|
||||
});
|
||||
|
||||
const mount = (initialDoc: string) => {
|
||||
const ref = createRef<CodeEditorHandle>();
|
||||
const onDocChanged = vi.fn();
|
||||
const utils = render(
|
||||
<CodeEditor
|
||||
ref={ref}
|
||||
initialDoc={initialDoc}
|
||||
readOnly={false}
|
||||
language={null}
|
||||
lineWrapping={false}
|
||||
initialLocation={{ line: null, col: null, end_line: null }}
|
||||
onDocChanged={onDocChanged}
|
||||
onSave={() => {}}
|
||||
/>,
|
||||
);
|
||||
const view = EditorView.findFromDOM(utils.container.querySelector(".cm-editor") as HTMLElement)!;
|
||||
return { ref, view, onDocChanged };
|
||||
};
|
||||
|
||||
describe("CodeEditor.setDoc (a reload)", () => {
|
||||
it("keeps the cursor and the scroll position, and is not an edit", () => {
|
||||
const { ref, view, onDocChanged } = mount("one\ntwo\nthree\nfour\n");
|
||||
act(() => { view.dispatch({ selection: { anchor: 9 } }); }); // inside "three"
|
||||
// jsdom has no layout, so give the scroller a real, settable scrollTop.
|
||||
let top = 0;
|
||||
Object.defineProperty(view.scrollDOM, "scrollTop", { configurable: true, get: () => top, set: (v: number) => { top = v; } });
|
||||
view.scrollDOM.scrollTop = 120;
|
||||
|
||||
act(() => { ref.current!.setDoc("one\ntwo\nTHREE\nfour\nfive\n"); });
|
||||
|
||||
expect(view.state.doc.toString()).toBe("one\ntwo\nTHREE\nfour\nfive\n");
|
||||
expect(view.state.selection.main.head).toBe(9);
|
||||
expect(view.scrollDOM.scrollTop).toBe(120);
|
||||
expect(onDocChanged).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("clamps the cursor when the new text is shorter", () => {
|
||||
const { ref, view } = mount("a long first line\n");
|
||||
act(() => { view.dispatch({ selection: { anchor: 15 } }); });
|
||||
act(() => { ref.current!.setDoc("short"); });
|
||||
expect(view.state.selection.main.head).toBe(5);
|
||||
});
|
||||
|
||||
it("a user edit is reported as a change", () => {
|
||||
const { view, onDocChanged } = mount("x");
|
||||
act(() => { view.dispatch({ changes: { from: 1, insert: "y" } }); });
|
||||
expect(onDocChanged).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user