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,40 @@
|
||||
import { StateEffect, StateField, type Extension, type Text } from "@codemirror/state";
|
||||
import { Decoration, EditorView, type DecorationSet } from "@codemirror/view";
|
||||
|
||||
export const setHighlight = StateEffect.define<{ from: number; to: number } | null>();
|
||||
|
||||
const lineMark = Decoration.line({ class: "cm-triple-c-target" });
|
||||
|
||||
export function lineRangeToPositions(doc: Text, from: number, to: number): { from: number; to: number } | null {
|
||||
const clamp = (n: number) => Math.min(Math.max(1, Math.floor(n)), doc.lines);
|
||||
const a = clamp(from);
|
||||
const b = Math.max(a, clamp(to));
|
||||
return { from: doc.line(a).from, to: doc.line(b).from };
|
||||
}
|
||||
|
||||
export const highlightLineField = StateField.define<DecorationSet>({
|
||||
create: () => Decoration.none,
|
||||
update(value, tr) {
|
||||
let next = value.map(tr.changes);
|
||||
for (const e of tr.effects) {
|
||||
if (!e.is(setHighlight)) continue;
|
||||
if (e.value === null) { next = Decoration.none; continue; }
|
||||
const range = lineRangeToPositions(tr.state.doc, e.value.from, e.value.to);
|
||||
if (!range) { next = Decoration.none; continue; }
|
||||
const marks = [];
|
||||
for (let pos = range.from; pos <= range.to; ) {
|
||||
const line = tr.state.doc.lineAt(pos);
|
||||
marks.push(lineMark.range(line.from));
|
||||
if (line.to + 1 > tr.state.doc.length) break;
|
||||
pos = line.to + 1;
|
||||
}
|
||||
next = Decoration.set(marks, true);
|
||||
}
|
||||
return next;
|
||||
},
|
||||
provide: (f) => EditorView.decorations.from(f),
|
||||
});
|
||||
|
||||
export function highlightExtension(): Extension {
|
||||
return [highlightLineField];
|
||||
}
|
||||
Reference in New Issue
Block a user