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>
66 lines
2.3 KiB
TypeScript
66 lines
2.3 KiB
TypeScript
/**
|
|
* Joins an xterm buffer row with its wrapped continuations.
|
|
*
|
|
* `WebLinksAddon` does the same in its private `LinkComputer`, which the
|
|
* built package does not export — so the walk is repeated here, with the same
|
|
* 2048-character budget. Rows are read with `translateToString(true)`, which
|
|
* trims the right edge; a wrap never ends in trailing spaces xterm would keep,
|
|
* so the join is exact for the text a path can occur in.
|
|
*
|
|
* Wide characters (CJK, emoji) occupy two cells but one string index, so a
|
|
* column computed from a string offset drifts right of the glyph on such rows.
|
|
* The addon corrects this with `getCell`; v1 accepts the drift (underline
|
|
* lands a cell early; the click still resolves the same link).
|
|
*/
|
|
|
|
export const MAX_JOINED_LENGTH = 2048;
|
|
|
|
/** Minimal slice of xterm's IBuffer this needs. */
|
|
export interface RowSource {
|
|
getLine(y: number): { isWrapped: boolean; translateToString(trimRight?: boolean): string } | undefined;
|
|
}
|
|
|
|
export interface JoinedLine {
|
|
text: string;
|
|
/** 0-based index of the first buffer row that contributed. */
|
|
firstRow: number;
|
|
/** For each contributed row (in order), the string offset at which it starts. */
|
|
rowStarts: number[];
|
|
}
|
|
|
|
export function joinWrappedRows(buffer: RowSource, row: number): JoinedLine {
|
|
let top = row;
|
|
while (top > 0 && buffer.getLine(top)?.isWrapped) top--;
|
|
|
|
const parts: string[] = [];
|
|
let length = 0;
|
|
let y = top;
|
|
for (;;) {
|
|
const line = buffer.getLine(y);
|
|
if (!line) break;
|
|
if (y !== top && !line.isWrapped) break;
|
|
const text = line.translateToString(true);
|
|
if (length + text.length > MAX_JOINED_LENGTH && parts.length > 0) break;
|
|
parts.push(text);
|
|
length += text.length;
|
|
y++;
|
|
}
|
|
|
|
const rowStarts: number[] = [];
|
|
let offset = 0;
|
|
for (const p of parts) {
|
|
rowStarts.push(offset);
|
|
offset += p.length;
|
|
}
|
|
return { text: parts.join(""), firstRow: top, rowStarts };
|
|
}
|
|
|
|
/** String offset → 1-based {x, y} cell (y is the buffer row + 1). */
|
|
export function offsetToCell(joined: JoinedLine, offset: number): { x: number; y: number } {
|
|
let rowIdx = 0;
|
|
for (let i = 0; i < joined.rowStarts.length; i++) {
|
|
if (joined.rowStarts[i] <= offset) rowIdx = i;
|
|
}
|
|
return { x: offset - joined.rowStarts[rowIdx] + 1, y: joined.firstRow + rowIdx + 1 };
|
|
}
|