@flanksource/clicky-ui 0.3.48 → 0.3.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/data/DataTable.cjs +18 -8
- package/dist/data/DataTable.cjs.map +1 -1
- package/dist/data/DataTable.d.ts +4 -0
- package/dist/data/DataTable.d.ts.map +1 -1
- package/dist/data/DataTable.js +18 -8
- package/dist/data/DataTable.js.map +1 -1
- package/dist/data/TaskManager.cjs +183 -159
- package/dist/data/TaskManager.cjs.map +1 -1
- package/dist/data/TaskManager.d.ts +3 -1
- package/dist/data/TaskManager.d.ts.map +1 -1
- package/dist/data/TaskManager.js +185 -161
- package/dist/data/TaskManager.js.map +1 -1
- package/dist/data/TaskManagerButton.cjs +1 -0
- package/dist/data/TaskManagerButton.cjs.map +1 -1
- package/dist/data/TaskManagerButton.js +1 -0
- package/dist/data/TaskManagerButton.js.map +1 -1
- package/dist/data/TaskProgress.cjs +13 -7
- package/dist/data/TaskProgress.cjs.map +1 -1
- package/dist/data/TaskProgress.d.ts +3 -1
- package/dist/data/TaskProgress.d.ts.map +1 -1
- package/dist/data/TaskProgress.js +13 -7
- package/dist/data/TaskProgress.js.map +1 -1
- package/dist/data/traces/ProcessTraceTree.cjs +93 -0
- package/dist/data/traces/ProcessTraceTree.cjs.map +1 -0
- package/dist/data/traces/ProcessTraceTree.d.ts +24 -0
- package/dist/data/traces/ProcessTraceTree.d.ts.map +1 -0
- package/dist/data/traces/ProcessTraceTree.js +93 -0
- package/dist/data/traces/ProcessTraceTree.js.map +1 -0
- package/dist/data/traces/Sparkline.cjs +23 -0
- package/dist/data/traces/Sparkline.cjs.map +1 -0
- package/dist/data/traces/Sparkline.d.ts +13 -0
- package/dist/data/traces/Sparkline.d.ts.map +1 -0
- package/dist/data/traces/Sparkline.js +23 -0
- package/dist/data/traces/Sparkline.js.map +1 -0
- package/dist/data/traces/SqlTraceTable.cjs +104 -0
- package/dist/data/traces/SqlTraceTable.cjs.map +1 -0
- package/dist/data/traces/SqlTraceTable.d.ts +19 -0
- package/dist/data/traces/SqlTraceTable.d.ts.map +1 -0
- package/dist/data/traces/SqlTraceTable.js +104 -0
- package/dist/data/traces/SqlTraceTable.js.map +1 -0
- package/dist/data/traces/TerminalCastPlayer.cjs +162 -0
- package/dist/data/traces/TerminalCastPlayer.cjs.map +1 -0
- package/dist/data/traces/TerminalCastPlayer.js +162 -0
- package/dist/data/traces/TerminalCastPlayer.js.map +1 -0
- package/dist/data/traces/TraceHarView.cjs +15 -0
- package/dist/data/traces/TraceHarView.cjs.map +1 -0
- package/dist/data/traces/TraceHarView.js +15 -0
- package/dist/data/traces/TraceHarView.js.map +1 -0
- package/dist/data/traces/TraceView.cjs +54 -0
- package/dist/data/traces/TraceView.cjs.map +1 -0
- package/dist/data/traces/TraceView.d.ts +18 -0
- package/dist/data/traces/TraceView.d.ts.map +1 -0
- package/dist/data/traces/TraceView.js +54 -0
- package/dist/data/traces/TraceView.js.map +1 -0
- package/dist/data/traces/cast.cjs +81 -0
- package/dist/data/traces/cast.cjs.map +1 -0
- package/dist/data/traces/cast.js +81 -0
- package/dist/data/traces/cast.js.map +1 -0
- package/dist/data/traces/har-adapter.cjs +83 -0
- package/dist/data/traces/har-adapter.cjs.map +1 -0
- package/dist/data/traces/har-adapter.js +83 -0
- package/dist/data/traces/har-adapter.js.map +1 -0
- package/dist/data/traces/index.d.ts +12 -0
- package/dist/data/traces/index.d.ts.map +1 -0
- package/dist/data/traces/process-tree.cjs +53 -0
- package/dist/data/traces/process-tree.cjs.map +1 -0
- package/dist/data/traces/process-tree.d.ts +21 -0
- package/dist/data/traces/process-tree.d.ts.map +1 -0
- package/dist/data/traces/process-tree.js +53 -0
- package/dist/data/traces/process-tree.js.map +1 -0
- package/dist/data/traces/sql-trace.cjs +39 -0
- package/dist/data/traces/sql-trace.cjs.map +1 -0
- package/dist/data/traces/sql-trace.d.ts +31 -0
- package/dist/data/traces/sql-trace.d.ts.map +1 -0
- package/dist/data/traces/sql-trace.js +39 -0
- package/dist/data/traces/sql-trace.js.map +1 -0
- package/dist/data/traces/terminal-screen.cjs +270 -0
- package/dist/data/traces/terminal-screen.cjs.map +1 -0
- package/dist/data/traces/terminal-screen.js +270 -0
- package/dist/data/traces/terminal-screen.js.map +1 -0
- package/dist/data/traces/terminal-timeline.cjs +48 -0
- package/dist/data/traces/terminal-timeline.cjs.map +1 -0
- package/dist/data/traces/terminal-timeline.js +48 -0
- package/dist/data/traces/terminal-timeline.js.map +1 -0
- package/dist/data/traces/types.d.ts +1 -3
- package/dist/data/traces/types.d.ts.map +1 -1
- package/dist/data/version-info.cjs +3 -3
- package/dist/data/version-info.cjs.map +1 -1
- package/dist/data/version-info.js +3 -3
- package/dist/data/version-info.js.map +1 -1
- package/dist/data.cjs +22 -0
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.ts +1 -0
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +22 -0
- package/dist/data.js.map +1 -1
- package/dist/index.cjs +22 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { SqlStatementRow } from './types.js';
|
|
2
|
+
/** One table row: a statement, or every execution of statements sharing a `repeatKey`. */
|
|
3
|
+
export type SqlDisplayRow = {
|
|
4
|
+
id: string;
|
|
5
|
+
startedAt: string;
|
|
6
|
+
op: string;
|
|
7
|
+
tables: string[];
|
|
8
|
+
sql: string;
|
|
9
|
+
/** Total of the executions. */
|
|
10
|
+
durationMs: number;
|
|
11
|
+
/** Rows the executions returned, or -1 when no driver could tell. */
|
|
12
|
+
rows: number;
|
|
13
|
+
/** The first error among the executions, or "". */
|
|
14
|
+
error: string;
|
|
15
|
+
/** How many executions returned an error. */
|
|
16
|
+
failed: number;
|
|
17
|
+
/** How many executions the row stands for. */
|
|
18
|
+
count: number;
|
|
19
|
+
occurrences: SqlStatementRow[];
|
|
20
|
+
};
|
|
21
|
+
/** A statement's duration: sub-100ms values keep a decimal (a fast query is 0.9 ms, not 1 ms), longer ones read as elsewhere. */
|
|
22
|
+
export declare function formatStatementDuration(ms: number): string;
|
|
23
|
+
/**
|
|
24
|
+
* Turns statements into table rows. With `group`, statements sharing a
|
|
25
|
+
* non-empty `repeatKey` (the same statement run with different values) become
|
|
26
|
+
* one row, placed where the first of them ran.
|
|
27
|
+
*/
|
|
28
|
+
export declare function groupStatements(statements: SqlStatementRow[], options: {
|
|
29
|
+
group: boolean;
|
|
30
|
+
}): SqlDisplayRow[];
|
|
31
|
+
//# sourceMappingURL=sql-trace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-trace.d.ts","sourceRoot":"","sources":["../../../src/data/traces/sql-trace.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,0FAA0F;AAC1F,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,+BAA+B;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,eAAe,EAAE,CAAC;CAChC,CAAC;AAEF,iIAAiI;AACjI,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAG1D;AAwBD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,eAAe,EAAE,EAAE,OAAO,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,aAAa,EAAE,CAO3G"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { formatDuration } from "../../lib/format.js";
|
|
2
|
+
function formatStatementDuration(ms) {
|
|
3
|
+
if (ms >= 100) return formatDuration(ms);
|
|
4
|
+
return `${Number(ms.toFixed(1))} ms`;
|
|
5
|
+
}
|
|
6
|
+
function sumKnownRows(statements) {
|
|
7
|
+
const known = statements.filter((statement) => statement.rows >= 0);
|
|
8
|
+
return known.length === 0 ? -1 : known.reduce((total, statement) => total + statement.rows, 0);
|
|
9
|
+
}
|
|
10
|
+
function toDisplayRow(id, statements) {
|
|
11
|
+
var _a;
|
|
12
|
+
const [first] = statements;
|
|
13
|
+
return {
|
|
14
|
+
id,
|
|
15
|
+
startedAt: first.startedAt,
|
|
16
|
+
op: first.op,
|
|
17
|
+
tables: first.tables,
|
|
18
|
+
sql: first.sql,
|
|
19
|
+
durationMs: statements.reduce((total, statement) => total + statement.durationMs, 0),
|
|
20
|
+
rows: sumKnownRows(statements),
|
|
21
|
+
error: ((_a = statements.find((statement) => statement.error)) == null ? void 0 : _a.error) ?? "",
|
|
22
|
+
failed: statements.filter((statement) => statement.error).length,
|
|
23
|
+
count: statements.length,
|
|
24
|
+
occurrences: statements
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function groupStatements(statements, options) {
|
|
28
|
+
const groups = /* @__PURE__ */ new Map();
|
|
29
|
+
statements.forEach((statement, index) => {
|
|
30
|
+
const key = options.group && statement.repeatKey ? `repeat:${statement.repeatKey}` : `row:${index}`;
|
|
31
|
+
groups.set(key, [...groups.get(key) ?? [], statement]);
|
|
32
|
+
});
|
|
33
|
+
return [...groups].map(([key, members]) => toDisplayRow(key, members));
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
formatStatementDuration,
|
|
37
|
+
groupStatements
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=sql-trace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-trace.js","sources":["../../../src/data/traces/sql-trace.ts"],"sourcesContent":["import { formatDuration } from \"../../lib/format\";\nimport type { SqlStatementRow } from \"./types\";\n\n/** One table row: a statement, or every execution of statements sharing a `repeatKey`. */\nexport type SqlDisplayRow = {\n id: string;\n startedAt: string;\n op: string;\n tables: string[];\n sql: string;\n /** Total of the executions. */\n durationMs: number;\n /** Rows the executions returned, or -1 when no driver could tell. */\n rows: number;\n /** The first error among the executions, or \"\". */\n error: string;\n /** How many executions returned an error. */\n failed: number;\n /** How many executions the row stands for. */\n count: number;\n occurrences: SqlStatementRow[];\n};\n\n/** A statement's duration: sub-100ms values keep a decimal (a fast query is 0.9 ms, not 1 ms), longer ones read as elsewhere. */\nexport function formatStatementDuration(ms: number): string {\n if (ms >= 100) return formatDuration(ms);\n return `${Number(ms.toFixed(1))} ms`;\n}\n\nfunction sumKnownRows(statements: SqlStatementRow[]): number {\n const known = statements.filter((statement) => statement.rows >= 0);\n return known.length === 0 ? -1 : known.reduce((total, statement) => total + statement.rows, 0);\n}\n\nfunction toDisplayRow(id: string, statements: SqlStatementRow[]): SqlDisplayRow {\n const [first] = statements as [SqlStatementRow, ...SqlStatementRow[]];\n return {\n id,\n startedAt: first.startedAt,\n op: first.op,\n tables: first.tables,\n sql: first.sql,\n durationMs: statements.reduce((total, statement) => total + statement.durationMs, 0),\n rows: sumKnownRows(statements),\n error: statements.find((statement) => statement.error)?.error ?? \"\",\n failed: statements.filter((statement) => statement.error).length,\n count: statements.length,\n occurrences: statements,\n };\n}\n\n/**\n * Turns statements into table rows. With `group`, statements sharing a\n * non-empty `repeatKey` (the same statement run with different values) become\n * one row, placed where the first of them ran.\n */\nexport function groupStatements(statements: SqlStatementRow[], options: { group: boolean }): SqlDisplayRow[] {\n const groups = new Map<string, SqlStatementRow[]>();\n statements.forEach((statement, index) => {\n const key = options.group && statement.repeatKey ? `repeat:${statement.repeatKey}` : `row:${index}`;\n groups.set(key, [...(groups.get(key) ?? []), statement]);\n });\n return [...groups].map(([key, members]) => toDisplayRow(key, members));\n}\n"],"names":[],"mappings":";AAwBO,SAAS,wBAAwB,IAAoB;AAC1D,MAAI,MAAM,IAAK,QAAO,eAAe,EAAE;AACvC,SAAO,GAAG,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC;AACjC;AAEA,SAAS,aAAa,YAAuC;AAC3D,QAAM,QAAQ,WAAW,OAAO,CAAC,cAAc,UAAU,QAAQ,CAAC;AAClE,SAAO,MAAM,WAAW,IAAI,KAAK,MAAM,OAAO,CAAC,OAAO,cAAc,QAAQ,UAAU,MAAM,CAAC;AAC/F;AAEA,SAAS,aAAa,IAAY,YAA8C;;AAC9E,QAAM,CAAC,KAAK,IAAI;AAChB,SAAO;AAAA,IACL;AAAA,IACA,WAAW,MAAM;AAAA,IACjB,IAAI,MAAM;AAAA,IACV,QAAQ,MAAM;AAAA,IACd,KAAK,MAAM;AAAA,IACX,YAAY,WAAW,OAAO,CAAC,OAAO,cAAc,QAAQ,UAAU,YAAY,CAAC;AAAA,IACnF,MAAM,aAAa,UAAU;AAAA,IAC7B,SAAO,gBAAW,KAAK,CAAC,cAAc,UAAU,KAAK,MAA9C,mBAAiD,UAAS;AAAA,IACjE,QAAQ,WAAW,OAAO,CAAC,cAAc,UAAU,KAAK,EAAE;AAAA,IAC1D,OAAO,WAAW;AAAA,IAClB,aAAa;AAAA,EAAA;AAEjB;AAOO,SAAS,gBAAgB,YAA+B,SAA8C;AAC3G,QAAM,6BAAa,IAAA;AACnB,aAAW,QAAQ,CAAC,WAAW,UAAU;AACvC,UAAM,MAAM,QAAQ,SAAS,UAAU,YAAY,UAAU,UAAU,SAAS,KAAK,OAAO,KAAK;AACjG,WAAO,IAAI,KAAK,CAAC,GAAI,OAAO,IAAI,GAAG,KAAK,IAAK,SAAS,CAAC;AAAA,EACzD,CAAC;AACD,SAAO,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,OAAO,MAAM,aAAa,KAAK,OAAO,CAAC;AACvE;"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const BLANK = { ch: " ", sgr: "" };
|
|
4
|
+
const TAB_STOP = 8;
|
|
5
|
+
const MAX_PENDING = 4096;
|
|
6
|
+
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
7
|
+
function createScreen(width, height) {
|
|
8
|
+
if (!(width > 0) || !(height > 0)) throw new Error(`terminal screen needs a positive size, got ${width}x${height}`);
|
|
9
|
+
return {
|
|
10
|
+
width,
|
|
11
|
+
height,
|
|
12
|
+
rows: Array.from({ length: height }, () => []),
|
|
13
|
+
row: 0,
|
|
14
|
+
col: 0,
|
|
15
|
+
style: { bold: false, dim: false, italic: false, underline: false, fg: null },
|
|
16
|
+
sgr: "",
|
|
17
|
+
saved: null,
|
|
18
|
+
pending: ""
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function resizeScreen(screen, width, height) {
|
|
22
|
+
if (!(width > 0) || !(height > 0)) throw new Error(`terminal screen needs a positive size, got ${width}x${height}`);
|
|
23
|
+
if (height < screen.rows.length) {
|
|
24
|
+
const dropFromTop = Math.max(0, screen.row - height + 1);
|
|
25
|
+
screen.rows.splice(0, dropFromTop);
|
|
26
|
+
screen.row -= dropFromTop;
|
|
27
|
+
screen.rows.length = height;
|
|
28
|
+
}
|
|
29
|
+
while (screen.rows.length < height) screen.rows.push([]);
|
|
30
|
+
for (const row of screen.rows) if (row.length > width) row.length = width;
|
|
31
|
+
screen.width = width;
|
|
32
|
+
screen.height = height;
|
|
33
|
+
screen.row = clamp(screen.row, 0, height - 1);
|
|
34
|
+
screen.col = clamp(screen.col, 0, width - 1);
|
|
35
|
+
}
|
|
36
|
+
function lineFeed(screen) {
|
|
37
|
+
if (screen.row < screen.height - 1) {
|
|
38
|
+
screen.row++;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
screen.rows.shift();
|
|
42
|
+
screen.rows.push([]);
|
|
43
|
+
}
|
|
44
|
+
function putChar(screen, ch) {
|
|
45
|
+
if (screen.col >= screen.width) {
|
|
46
|
+
screen.col = 0;
|
|
47
|
+
lineFeed(screen);
|
|
48
|
+
}
|
|
49
|
+
const line = screen.rows[screen.row];
|
|
50
|
+
while (line.length < screen.col) line.push(BLANK);
|
|
51
|
+
line[screen.col] = { ch, sgr: screen.sgr };
|
|
52
|
+
screen.col++;
|
|
53
|
+
}
|
|
54
|
+
function eraseLine(screen, mode) {
|
|
55
|
+
const line = screen.rows[screen.row];
|
|
56
|
+
if (mode === 0) {
|
|
57
|
+
line.length = Math.min(line.length, screen.col);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (mode === 2) {
|
|
61
|
+
line.length = 0;
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const end = Math.min(screen.col + 1, screen.width);
|
|
65
|
+
while (line.length < end) line.push(BLANK);
|
|
66
|
+
for (let i = 0; i < end; i++) line[i] = BLANK;
|
|
67
|
+
}
|
|
68
|
+
function eraseDisplay(screen, mode) {
|
|
69
|
+
if (mode === 0) {
|
|
70
|
+
eraseLine(screen, 0);
|
|
71
|
+
for (let r = screen.row + 1; r < screen.height; r++) screen.rows[r] = [];
|
|
72
|
+
} else if (mode === 1) {
|
|
73
|
+
for (let r = 0; r < screen.row; r++) screen.rows[r] = [];
|
|
74
|
+
eraseLine(screen, 1);
|
|
75
|
+
} else {
|
|
76
|
+
for (let r = 0; r < screen.height; r++) screen.rows[r] = [];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function applySgr(screen, tokens) {
|
|
80
|
+
const style = screen.style;
|
|
81
|
+
const codes = tokens.length === 0 ? [""] : tokens;
|
|
82
|
+
for (let i = 0; i < codes.length; i++) {
|
|
83
|
+
const token = codes[i];
|
|
84
|
+
if (token.includes(":")) continue;
|
|
85
|
+
const code = token === "" ? 0 : Number.parseInt(token, 10);
|
|
86
|
+
if (code === 0) {
|
|
87
|
+
style.bold = style.dim = style.italic = style.underline = false;
|
|
88
|
+
style.fg = null;
|
|
89
|
+
} else if (code === 1) style.bold = true;
|
|
90
|
+
else if (code === 2) style.dim = true;
|
|
91
|
+
else if (code === 3) style.italic = true;
|
|
92
|
+
else if (code === 4) style.underline = true;
|
|
93
|
+
else if (code === 22) style.bold = style.dim = false;
|
|
94
|
+
else if (code === 23) style.italic = false;
|
|
95
|
+
else if (code === 24) style.underline = false;
|
|
96
|
+
else if (code >= 30 && code <= 37 || code >= 90 && code <= 97) style.fg = code;
|
|
97
|
+
else if (code === 39) style.fg = null;
|
|
98
|
+
else if (code === 38 || code === 48 || code === 58) {
|
|
99
|
+
const mode = codes[i + 1];
|
|
100
|
+
i += mode === "5" ? 2 : mode === "2" ? 4 : 0;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
screen.sgr = [
|
|
104
|
+
style.bold ? "1" : "",
|
|
105
|
+
style.dim ? "2" : "",
|
|
106
|
+
style.italic ? "3" : "",
|
|
107
|
+
style.underline ? "4" : "",
|
|
108
|
+
style.fg === null ? "" : String(style.fg)
|
|
109
|
+
].filter(Boolean).join(";");
|
|
110
|
+
}
|
|
111
|
+
function csi(screen, params, final) {
|
|
112
|
+
if (/^[?>=<!]/.test(params)) return;
|
|
113
|
+
const tokens = params === "" ? [] : params.split(";");
|
|
114
|
+
const arg = (index, fallback) => {
|
|
115
|
+
const value = Number.parseInt(tokens[index] ?? "", 10);
|
|
116
|
+
return Number.isNaN(value) || value === 0 ? fallback : value;
|
|
117
|
+
};
|
|
118
|
+
const mode = Number.parseInt(tokens[0] ?? "0", 10) || 0;
|
|
119
|
+
const line = screen.rows[screen.row];
|
|
120
|
+
switch (final) {
|
|
121
|
+
case "m":
|
|
122
|
+
applySgr(screen, tokens);
|
|
123
|
+
break;
|
|
124
|
+
case "A":
|
|
125
|
+
screen.row = Math.max(0, screen.row - arg(0, 1));
|
|
126
|
+
break;
|
|
127
|
+
case "B":
|
|
128
|
+
screen.row = Math.min(screen.height - 1, screen.row + arg(0, 1));
|
|
129
|
+
break;
|
|
130
|
+
case "C":
|
|
131
|
+
screen.col = Math.min(screen.width - 1, screen.col + arg(0, 1));
|
|
132
|
+
break;
|
|
133
|
+
case "D":
|
|
134
|
+
screen.col = Math.max(0, screen.col - arg(0, 1));
|
|
135
|
+
break;
|
|
136
|
+
case "E":
|
|
137
|
+
screen.row = Math.min(screen.height - 1, screen.row + arg(0, 1));
|
|
138
|
+
screen.col = 0;
|
|
139
|
+
break;
|
|
140
|
+
case "F":
|
|
141
|
+
screen.row = Math.max(0, screen.row - arg(0, 1));
|
|
142
|
+
screen.col = 0;
|
|
143
|
+
break;
|
|
144
|
+
case "G":
|
|
145
|
+
screen.col = clamp(arg(0, 1) - 1, 0, screen.width - 1);
|
|
146
|
+
break;
|
|
147
|
+
case "d":
|
|
148
|
+
screen.row = clamp(arg(0, 1) - 1, 0, screen.height - 1);
|
|
149
|
+
break;
|
|
150
|
+
case "H":
|
|
151
|
+
case "f":
|
|
152
|
+
screen.row = clamp(arg(0, 1) - 1, 0, screen.height - 1);
|
|
153
|
+
screen.col = clamp(arg(1, 1) - 1, 0, screen.width - 1);
|
|
154
|
+
break;
|
|
155
|
+
case "J":
|
|
156
|
+
eraseDisplay(screen, mode);
|
|
157
|
+
break;
|
|
158
|
+
case "K":
|
|
159
|
+
eraseLine(screen, mode);
|
|
160
|
+
break;
|
|
161
|
+
case "P":
|
|
162
|
+
line.splice(screen.col, arg(0, 1));
|
|
163
|
+
break;
|
|
164
|
+
case "X":
|
|
165
|
+
for (let i = 0; i < arg(0, 1) && screen.col + i < line.length; i++) line[screen.col + i] = BLANK;
|
|
166
|
+
break;
|
|
167
|
+
case "@":
|
|
168
|
+
if (line.length > screen.col) line.splice(screen.col, 0, ...Array.from({ length: arg(0, 1) }, () => BLANK));
|
|
169
|
+
line.length = Math.min(line.length, screen.width);
|
|
170
|
+
break;
|
|
171
|
+
case "L":
|
|
172
|
+
screen.rows.splice(screen.row, 0, ...Array.from({ length: arg(0, 1) }, () => []));
|
|
173
|
+
screen.rows.length = screen.height;
|
|
174
|
+
break;
|
|
175
|
+
case "M":
|
|
176
|
+
screen.rows.splice(screen.row, arg(0, 1));
|
|
177
|
+
while (screen.rows.length < screen.height) screen.rows.push([]);
|
|
178
|
+
break;
|
|
179
|
+
case "s":
|
|
180
|
+
screen.saved = { row: screen.row, col: screen.col };
|
|
181
|
+
break;
|
|
182
|
+
case "u":
|
|
183
|
+
if (screen.saved) ({ row: screen.row, col: screen.col } = screen.saved);
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function consumeEscape(screen, input, start) {
|
|
188
|
+
const kind = input[start + 1];
|
|
189
|
+
if (kind === void 0) return -1;
|
|
190
|
+
if (kind === "[") {
|
|
191
|
+
let end = start + 2;
|
|
192
|
+
while (end < input.length && /[\x20-\x3f]/.test(input[end])) end++;
|
|
193
|
+
if (end >= input.length) return -1;
|
|
194
|
+
csi(screen, input.slice(start + 2, end).replace(/[\x20-\x2f]/g, ""), input[end]);
|
|
195
|
+
return end + 1;
|
|
196
|
+
}
|
|
197
|
+
if (kind === "]") {
|
|
198
|
+
for (let end = start + 2; end < input.length; end++) {
|
|
199
|
+
if (input[end] === "\x07") return end + 1;
|
|
200
|
+
if (input[end] === "\x1B" && input[end + 1] === "\\") return end + 2;
|
|
201
|
+
}
|
|
202
|
+
return -1;
|
|
203
|
+
}
|
|
204
|
+
if ("()*+".includes(kind)) return start + 3 > input.length ? -1 : start + 3;
|
|
205
|
+
if (kind === "7") screen.saved = { row: screen.row, col: screen.col };
|
|
206
|
+
else if (kind === "8" && screen.saved) ({ row: screen.row, col: screen.col } = screen.saved);
|
|
207
|
+
else if (kind === "M") screen.row = Math.max(0, screen.row - 1);
|
|
208
|
+
return start + 2;
|
|
209
|
+
}
|
|
210
|
+
function writeScreen(screen, data) {
|
|
211
|
+
const input = screen.pending + data;
|
|
212
|
+
screen.pending = "";
|
|
213
|
+
let i = 0;
|
|
214
|
+
while (i < input.length) {
|
|
215
|
+
const ch = input[i];
|
|
216
|
+
if (ch === "\x1B") {
|
|
217
|
+
const next = consumeEscape(screen, input, i);
|
|
218
|
+
if (next === -1) {
|
|
219
|
+
if (input.length - i <= MAX_PENDING) {
|
|
220
|
+
screen.pending = input.slice(i);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
i++;
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
i = next;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
i++;
|
|
230
|
+
if (ch === "\r") screen.col = 0;
|
|
231
|
+
else if (ch === "\n") lineFeed(screen);
|
|
232
|
+
else if (ch === "\b") screen.col = Math.max(0, screen.col - 1);
|
|
233
|
+
else if (ch === " ") screen.col = Math.min(screen.width - 1, (Math.floor(screen.col / TAB_STOP) + 1) * TAB_STOP);
|
|
234
|
+
else if (ch < " " || ch === "") continue;
|
|
235
|
+
else {
|
|
236
|
+
const codePoint = input.codePointAt(i - 1);
|
|
237
|
+
const full = String.fromCodePoint(codePoint);
|
|
238
|
+
i += full.length - 1;
|
|
239
|
+
putChar(screen, full);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function trimmed(row) {
|
|
244
|
+
let end = row.length;
|
|
245
|
+
while (end > 0 && row[end - 1].ch === " ") end--;
|
|
246
|
+
return row.slice(0, end);
|
|
247
|
+
}
|
|
248
|
+
function transition(from, to) {
|
|
249
|
+
if (to === "") return "\x1B[0m";
|
|
250
|
+
return from === "" ? `\x1B[${to}m` : `\x1B[0;${to}m`;
|
|
251
|
+
}
|
|
252
|
+
function screenToAnsi(screen) {
|
|
253
|
+
let current = "";
|
|
254
|
+
return screen.rows.map((row) => {
|
|
255
|
+
let line = "";
|
|
256
|
+
for (const cell of trimmed(row)) {
|
|
257
|
+
if (cell.sgr !== current) {
|
|
258
|
+
line += transition(current, cell.sgr);
|
|
259
|
+
current = cell.sgr;
|
|
260
|
+
}
|
|
261
|
+
line += cell.ch;
|
|
262
|
+
}
|
|
263
|
+
return line;
|
|
264
|
+
}).join("\n");
|
|
265
|
+
}
|
|
266
|
+
exports.createScreen = createScreen;
|
|
267
|
+
exports.resizeScreen = resizeScreen;
|
|
268
|
+
exports.screenToAnsi = screenToAnsi;
|
|
269
|
+
exports.writeScreen = writeScreen;
|
|
270
|
+
//# sourceMappingURL=terminal-screen.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-screen.cjs","sources":["../../../src/data/traces/terminal-screen.ts"],"sourcesContent":["/**\n * A deliberately minimal terminal screen for replaying a `terminal` trace: it\n * settles the sequences a build or test run emits (CR/LF/BS/TAB, cursor moves,\n * erase, SGR colour and weight) into a grid, and serialises the grid back to\n * ANSI text for AnsiHtml. Everything else (OSC, private modes, character sets,\n * backgrounds, 256/truecolour) is consumed and ignored, as AnsiHtml cannot draw\n * it.\n */\n\ntype Cell = { ch: string; sgr: string };\n\ntype SgrState = { bold: boolean; dim: boolean; italic: boolean; underline: boolean; fg: number | null };\n\nexport type TerminalScreen = {\n width: number;\n height: number;\n rows: Cell[][];\n row: number;\n col: number;\n style: SgrState;\n /** `style` serialised as SGR parameters, stored on each cell written. */\n sgr: string;\n saved: { row: number; col: number } | null;\n /** An escape sequence cut off at the end of the last write. */\n pending: string;\n};\n\nconst BLANK: Cell = { ch: \" \", sgr: \"\" };\nconst TAB_STOP = 8;\n/** A sequence this long without a terminator is garbage; its ESC is dropped. */\nconst MAX_PENDING = 4096;\n\nconst clamp = (value: number, min: number, max: number) => Math.min(Math.max(value, min), max);\n\nexport function createScreen(width: number, height: number): TerminalScreen {\n if (!(width > 0) || !(height > 0)) throw new Error(`terminal screen needs a positive size, got ${width}x${height}`);\n return {\n width,\n height,\n rows: Array.from({ length: height }, () => []),\n row: 0,\n col: 0,\n style: { bold: false, dim: false, italic: false, underline: false, fg: null },\n sgr: \"\",\n saved: null,\n pending: \"\",\n };\n}\n\nexport function resizeScreen(screen: TerminalScreen, width: number, height: number): void {\n if (!(width > 0) || !(height > 0)) throw new Error(`terminal screen needs a positive size, got ${width}x${height}`);\n if (height < screen.rows.length) {\n const dropFromTop = Math.max(0, screen.row - height + 1);\n screen.rows.splice(0, dropFromTop);\n screen.row -= dropFromTop;\n screen.rows.length = height;\n }\n while (screen.rows.length < height) screen.rows.push([]);\n for (const row of screen.rows) if (row.length > width) row.length = width;\n screen.width = width;\n screen.height = height;\n screen.row = clamp(screen.row, 0, height - 1);\n screen.col = clamp(screen.col, 0, width - 1);\n}\n\nfunction lineFeed(screen: TerminalScreen): void {\n if (screen.row < screen.height - 1) {\n screen.row++;\n return;\n }\n screen.rows.shift();\n screen.rows.push([]);\n}\n\nfunction putChar(screen: TerminalScreen, ch: string): void {\n if (screen.col >= screen.width) {\n screen.col = 0;\n lineFeed(screen);\n }\n const line = screen.rows[screen.row]!;\n while (line.length < screen.col) line.push(BLANK);\n line[screen.col] = { ch, sgr: screen.sgr };\n screen.col++;\n}\n\nfunction eraseLine(screen: TerminalScreen, mode: number): void {\n const line = screen.rows[screen.row]!;\n if (mode === 0) {\n line.length = Math.min(line.length, screen.col);\n return;\n }\n if (mode === 2) {\n line.length = 0;\n return;\n }\n const end = Math.min(screen.col + 1, screen.width);\n while (line.length < end) line.push(BLANK);\n for (let i = 0; i < end; i++) line[i] = BLANK;\n}\n\nfunction eraseDisplay(screen: TerminalScreen, mode: number): void {\n if (mode === 0) {\n eraseLine(screen, 0);\n for (let r = screen.row + 1; r < screen.height; r++) screen.rows[r] = [];\n } else if (mode === 1) {\n for (let r = 0; r < screen.row; r++) screen.rows[r] = [];\n eraseLine(screen, 1);\n } else {\n for (let r = 0; r < screen.height; r++) screen.rows[r] = [];\n }\n}\n\nfunction applySgr(screen: TerminalScreen, tokens: string[]): void {\n const style = screen.style;\n const codes = tokens.length === 0 ? [\"\"] : tokens;\n for (let i = 0; i < codes.length; i++) {\n const token = codes[i]!;\n if (token.includes(\":\")) continue;\n const code = token === \"\" ? 0 : Number.parseInt(token, 10);\n if (code === 0) {\n style.bold = style.dim = style.italic = style.underline = false;\n style.fg = null;\n } else if (code === 1) style.bold = true;\n else if (code === 2) style.dim = true;\n else if (code === 3) style.italic = true;\n else if (code === 4) style.underline = true;\n else if (code === 22) style.bold = style.dim = false;\n else if (code === 23) style.italic = false;\n else if (code === 24) style.underline = false;\n else if ((code >= 30 && code <= 37) || (code >= 90 && code <= 97)) style.fg = code;\n else if (code === 39) style.fg = null;\n else if (code === 38 || code === 48 || code === 58) {\n const mode = codes[i + 1];\n i += mode === \"5\" ? 2 : mode === \"2\" ? 4 : 0;\n }\n }\n screen.sgr = [\n style.bold ? \"1\" : \"\",\n style.dim ? \"2\" : \"\",\n style.italic ? \"3\" : \"\",\n style.underline ? \"4\" : \"\",\n style.fg === null ? \"\" : String(style.fg),\n ]\n .filter(Boolean)\n .join(\";\");\n}\n\nfunction csi(screen: TerminalScreen, params: string, final: string): void {\n if (/^[?>=<!]/.test(params)) return;\n const tokens = params === \"\" ? [] : params.split(\";\");\n const arg = (index: number, fallback: number): number => {\n const value = Number.parseInt(tokens[index] ?? \"\", 10);\n return Number.isNaN(value) || value === 0 ? fallback : value;\n };\n const mode = Number.parseInt(tokens[0] ?? \"0\", 10) || 0;\n const line = screen.rows[screen.row]!;\n switch (final) {\n case \"m\":\n applySgr(screen, tokens);\n break;\n case \"A\":\n screen.row = Math.max(0, screen.row - arg(0, 1));\n break;\n case \"B\":\n screen.row = Math.min(screen.height - 1, screen.row + arg(0, 1));\n break;\n case \"C\":\n screen.col = Math.min(screen.width - 1, screen.col + arg(0, 1));\n break;\n case \"D\":\n screen.col = Math.max(0, screen.col - arg(0, 1));\n break;\n case \"E\":\n screen.row = Math.min(screen.height - 1, screen.row + arg(0, 1));\n screen.col = 0;\n break;\n case \"F\":\n screen.row = Math.max(0, screen.row - arg(0, 1));\n screen.col = 0;\n break;\n case \"G\":\n screen.col = clamp(arg(0, 1) - 1, 0, screen.width - 1);\n break;\n case \"d\":\n screen.row = clamp(arg(0, 1) - 1, 0, screen.height - 1);\n break;\n case \"H\":\n case \"f\":\n screen.row = clamp(arg(0, 1) - 1, 0, screen.height - 1);\n screen.col = clamp(arg(1, 1) - 1, 0, screen.width - 1);\n break;\n case \"J\":\n eraseDisplay(screen, mode);\n break;\n case \"K\":\n eraseLine(screen, mode);\n break;\n case \"P\":\n line.splice(screen.col, arg(0, 1));\n break;\n case \"X\":\n for (let i = 0; i < arg(0, 1) && screen.col + i < line.length; i++) line[screen.col + i] = BLANK;\n break;\n case \"@\":\n if (line.length > screen.col) line.splice(screen.col, 0, ...Array.from({ length: arg(0, 1) }, () => BLANK));\n line.length = Math.min(line.length, screen.width);\n break;\n case \"L\":\n screen.rows.splice(screen.row, 0, ...Array.from({ length: arg(0, 1) }, (): Cell[] => []));\n screen.rows.length = screen.height;\n break;\n case \"M\":\n screen.rows.splice(screen.row, arg(0, 1));\n while (screen.rows.length < screen.height) screen.rows.push([]);\n break;\n case \"s\":\n screen.saved = { row: screen.row, col: screen.col };\n break;\n case \"u\":\n if (screen.saved) ({ row: screen.row, col: screen.col } = screen.saved);\n break;\n default:\n break;\n }\n}\n\n/** Consumes the escape sequence at `start`; returns the index after it, or -1 when it is cut off. */\nfunction consumeEscape(screen: TerminalScreen, input: string, start: number): number {\n const kind = input[start + 1];\n if (kind === undefined) return -1;\n if (kind === \"[\") {\n let end = start + 2;\n while (end < input.length && /[\\x20-\\x3f]/.test(input[end]!)) end++;\n if (end >= input.length) return -1;\n csi(screen, input.slice(start + 2, end).replace(/[\\x20-\\x2f]/g, \"\"), input[end]!);\n return end + 1;\n }\n if (kind === \"]\") {\n for (let end = start + 2; end < input.length; end++) {\n if (input[end] === \"\\u0007\") return end + 1;\n if (input[end] === \"\\u001b\" && input[end + 1] === \"\\\\\") return end + 2;\n }\n return -1;\n }\n if (\"()*+\".includes(kind)) return start + 3 > input.length ? -1 : start + 3;\n if (kind === \"7\") screen.saved = { row: screen.row, col: screen.col };\n else if (kind === \"8\" && screen.saved) ({ row: screen.row, col: screen.col } = screen.saved);\n else if (kind === \"M\") screen.row = Math.max(0, screen.row - 1);\n return start + 2;\n}\n\n/** Feeds output to the screen; an escape sequence cut off at the end waits for the next write. */\nexport function writeScreen(screen: TerminalScreen, data: string): void {\n const input = screen.pending + data;\n screen.pending = \"\";\n let i = 0;\n while (i < input.length) {\n const ch = input[i]!;\n if (ch === \"\\u001b\") {\n const next = consumeEscape(screen, input, i);\n if (next === -1) {\n if (input.length - i <= MAX_PENDING) {\n screen.pending = input.slice(i);\n return;\n }\n i++;\n continue;\n }\n i = next;\n continue;\n }\n i++;\n if (ch === \"\\r\") screen.col = 0;\n else if (ch === \"\\n\") lineFeed(screen);\n else if (ch === \"\\b\") screen.col = Math.max(0, screen.col - 1);\n else if (ch === \"\\t\") screen.col = Math.min(screen.width - 1, (Math.floor(screen.col / TAB_STOP) + 1) * TAB_STOP);\n else if (ch < \" \" || ch === \"\\u007f\") continue;\n else {\n const codePoint = input.codePointAt(i - 1)!;\n const full = String.fromCodePoint(codePoint);\n i += full.length - 1;\n putChar(screen, full);\n }\n }\n}\n\nfunction trimmed(row: Cell[]): Cell[] {\n let end = row.length;\n while (end > 0 && row[end - 1]!.ch === \" \") end--;\n return row.slice(0, end);\n}\n\n/** The screen as plain text, one line per row, trailing blanks trimmed. */\nexport function screenText(screen: TerminalScreen): string {\n return screen.rows.map((row) => trimmed(row).map((cell) => cell.ch).join(\"\")).join(\"\\n\");\n}\n\nfunction transition(from: string, to: string): string {\n if (to === \"\") return \"\\u001b[0m\";\n return from === \"\" ? `\\u001b[${to}m` : `\\u001b[0;${to}m`;\n}\n\n/** The screen as ANSI text (weight, italic, underline, foreground) for AnsiHtml. */\nexport function screenToAnsi(screen: TerminalScreen): string {\n let current = \"\";\n return screen.rows\n .map((row) => {\n let line = \"\";\n for (const cell of trimmed(row)) {\n if (cell.sgr !== current) {\n line += transition(current, cell.sgr);\n current = cell.sgr;\n }\n line += cell.ch;\n }\n return line;\n })\n .join(\"\\n\");\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
const BLANK = { ch: " ", sgr: "" };
|
|
2
|
+
const TAB_STOP = 8;
|
|
3
|
+
const MAX_PENDING = 4096;
|
|
4
|
+
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
5
|
+
function createScreen(width, height) {
|
|
6
|
+
if (!(width > 0) || !(height > 0)) throw new Error(`terminal screen needs a positive size, got ${width}x${height}`);
|
|
7
|
+
return {
|
|
8
|
+
width,
|
|
9
|
+
height,
|
|
10
|
+
rows: Array.from({ length: height }, () => []),
|
|
11
|
+
row: 0,
|
|
12
|
+
col: 0,
|
|
13
|
+
style: { bold: false, dim: false, italic: false, underline: false, fg: null },
|
|
14
|
+
sgr: "",
|
|
15
|
+
saved: null,
|
|
16
|
+
pending: ""
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function resizeScreen(screen, width, height) {
|
|
20
|
+
if (!(width > 0) || !(height > 0)) throw new Error(`terminal screen needs a positive size, got ${width}x${height}`);
|
|
21
|
+
if (height < screen.rows.length) {
|
|
22
|
+
const dropFromTop = Math.max(0, screen.row - height + 1);
|
|
23
|
+
screen.rows.splice(0, dropFromTop);
|
|
24
|
+
screen.row -= dropFromTop;
|
|
25
|
+
screen.rows.length = height;
|
|
26
|
+
}
|
|
27
|
+
while (screen.rows.length < height) screen.rows.push([]);
|
|
28
|
+
for (const row of screen.rows) if (row.length > width) row.length = width;
|
|
29
|
+
screen.width = width;
|
|
30
|
+
screen.height = height;
|
|
31
|
+
screen.row = clamp(screen.row, 0, height - 1);
|
|
32
|
+
screen.col = clamp(screen.col, 0, width - 1);
|
|
33
|
+
}
|
|
34
|
+
function lineFeed(screen) {
|
|
35
|
+
if (screen.row < screen.height - 1) {
|
|
36
|
+
screen.row++;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
screen.rows.shift();
|
|
40
|
+
screen.rows.push([]);
|
|
41
|
+
}
|
|
42
|
+
function putChar(screen, ch) {
|
|
43
|
+
if (screen.col >= screen.width) {
|
|
44
|
+
screen.col = 0;
|
|
45
|
+
lineFeed(screen);
|
|
46
|
+
}
|
|
47
|
+
const line = screen.rows[screen.row];
|
|
48
|
+
while (line.length < screen.col) line.push(BLANK);
|
|
49
|
+
line[screen.col] = { ch, sgr: screen.sgr };
|
|
50
|
+
screen.col++;
|
|
51
|
+
}
|
|
52
|
+
function eraseLine(screen, mode) {
|
|
53
|
+
const line = screen.rows[screen.row];
|
|
54
|
+
if (mode === 0) {
|
|
55
|
+
line.length = Math.min(line.length, screen.col);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (mode === 2) {
|
|
59
|
+
line.length = 0;
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const end = Math.min(screen.col + 1, screen.width);
|
|
63
|
+
while (line.length < end) line.push(BLANK);
|
|
64
|
+
for (let i = 0; i < end; i++) line[i] = BLANK;
|
|
65
|
+
}
|
|
66
|
+
function eraseDisplay(screen, mode) {
|
|
67
|
+
if (mode === 0) {
|
|
68
|
+
eraseLine(screen, 0);
|
|
69
|
+
for (let r = screen.row + 1; r < screen.height; r++) screen.rows[r] = [];
|
|
70
|
+
} else if (mode === 1) {
|
|
71
|
+
for (let r = 0; r < screen.row; r++) screen.rows[r] = [];
|
|
72
|
+
eraseLine(screen, 1);
|
|
73
|
+
} else {
|
|
74
|
+
for (let r = 0; r < screen.height; r++) screen.rows[r] = [];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function applySgr(screen, tokens) {
|
|
78
|
+
const style = screen.style;
|
|
79
|
+
const codes = tokens.length === 0 ? [""] : tokens;
|
|
80
|
+
for (let i = 0; i < codes.length; i++) {
|
|
81
|
+
const token = codes[i];
|
|
82
|
+
if (token.includes(":")) continue;
|
|
83
|
+
const code = token === "" ? 0 : Number.parseInt(token, 10);
|
|
84
|
+
if (code === 0) {
|
|
85
|
+
style.bold = style.dim = style.italic = style.underline = false;
|
|
86
|
+
style.fg = null;
|
|
87
|
+
} else if (code === 1) style.bold = true;
|
|
88
|
+
else if (code === 2) style.dim = true;
|
|
89
|
+
else if (code === 3) style.italic = true;
|
|
90
|
+
else if (code === 4) style.underline = true;
|
|
91
|
+
else if (code === 22) style.bold = style.dim = false;
|
|
92
|
+
else if (code === 23) style.italic = false;
|
|
93
|
+
else if (code === 24) style.underline = false;
|
|
94
|
+
else if (code >= 30 && code <= 37 || code >= 90 && code <= 97) style.fg = code;
|
|
95
|
+
else if (code === 39) style.fg = null;
|
|
96
|
+
else if (code === 38 || code === 48 || code === 58) {
|
|
97
|
+
const mode = codes[i + 1];
|
|
98
|
+
i += mode === "5" ? 2 : mode === "2" ? 4 : 0;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
screen.sgr = [
|
|
102
|
+
style.bold ? "1" : "",
|
|
103
|
+
style.dim ? "2" : "",
|
|
104
|
+
style.italic ? "3" : "",
|
|
105
|
+
style.underline ? "4" : "",
|
|
106
|
+
style.fg === null ? "" : String(style.fg)
|
|
107
|
+
].filter(Boolean).join(";");
|
|
108
|
+
}
|
|
109
|
+
function csi(screen, params, final) {
|
|
110
|
+
if (/^[?>=<!]/.test(params)) return;
|
|
111
|
+
const tokens = params === "" ? [] : params.split(";");
|
|
112
|
+
const arg = (index, fallback) => {
|
|
113
|
+
const value = Number.parseInt(tokens[index] ?? "", 10);
|
|
114
|
+
return Number.isNaN(value) || value === 0 ? fallback : value;
|
|
115
|
+
};
|
|
116
|
+
const mode = Number.parseInt(tokens[0] ?? "0", 10) || 0;
|
|
117
|
+
const line = screen.rows[screen.row];
|
|
118
|
+
switch (final) {
|
|
119
|
+
case "m":
|
|
120
|
+
applySgr(screen, tokens);
|
|
121
|
+
break;
|
|
122
|
+
case "A":
|
|
123
|
+
screen.row = Math.max(0, screen.row - arg(0, 1));
|
|
124
|
+
break;
|
|
125
|
+
case "B":
|
|
126
|
+
screen.row = Math.min(screen.height - 1, screen.row + arg(0, 1));
|
|
127
|
+
break;
|
|
128
|
+
case "C":
|
|
129
|
+
screen.col = Math.min(screen.width - 1, screen.col + arg(0, 1));
|
|
130
|
+
break;
|
|
131
|
+
case "D":
|
|
132
|
+
screen.col = Math.max(0, screen.col - arg(0, 1));
|
|
133
|
+
break;
|
|
134
|
+
case "E":
|
|
135
|
+
screen.row = Math.min(screen.height - 1, screen.row + arg(0, 1));
|
|
136
|
+
screen.col = 0;
|
|
137
|
+
break;
|
|
138
|
+
case "F":
|
|
139
|
+
screen.row = Math.max(0, screen.row - arg(0, 1));
|
|
140
|
+
screen.col = 0;
|
|
141
|
+
break;
|
|
142
|
+
case "G":
|
|
143
|
+
screen.col = clamp(arg(0, 1) - 1, 0, screen.width - 1);
|
|
144
|
+
break;
|
|
145
|
+
case "d":
|
|
146
|
+
screen.row = clamp(arg(0, 1) - 1, 0, screen.height - 1);
|
|
147
|
+
break;
|
|
148
|
+
case "H":
|
|
149
|
+
case "f":
|
|
150
|
+
screen.row = clamp(arg(0, 1) - 1, 0, screen.height - 1);
|
|
151
|
+
screen.col = clamp(arg(1, 1) - 1, 0, screen.width - 1);
|
|
152
|
+
break;
|
|
153
|
+
case "J":
|
|
154
|
+
eraseDisplay(screen, mode);
|
|
155
|
+
break;
|
|
156
|
+
case "K":
|
|
157
|
+
eraseLine(screen, mode);
|
|
158
|
+
break;
|
|
159
|
+
case "P":
|
|
160
|
+
line.splice(screen.col, arg(0, 1));
|
|
161
|
+
break;
|
|
162
|
+
case "X":
|
|
163
|
+
for (let i = 0; i < arg(0, 1) && screen.col + i < line.length; i++) line[screen.col + i] = BLANK;
|
|
164
|
+
break;
|
|
165
|
+
case "@":
|
|
166
|
+
if (line.length > screen.col) line.splice(screen.col, 0, ...Array.from({ length: arg(0, 1) }, () => BLANK));
|
|
167
|
+
line.length = Math.min(line.length, screen.width);
|
|
168
|
+
break;
|
|
169
|
+
case "L":
|
|
170
|
+
screen.rows.splice(screen.row, 0, ...Array.from({ length: arg(0, 1) }, () => []));
|
|
171
|
+
screen.rows.length = screen.height;
|
|
172
|
+
break;
|
|
173
|
+
case "M":
|
|
174
|
+
screen.rows.splice(screen.row, arg(0, 1));
|
|
175
|
+
while (screen.rows.length < screen.height) screen.rows.push([]);
|
|
176
|
+
break;
|
|
177
|
+
case "s":
|
|
178
|
+
screen.saved = { row: screen.row, col: screen.col };
|
|
179
|
+
break;
|
|
180
|
+
case "u":
|
|
181
|
+
if (screen.saved) ({ row: screen.row, col: screen.col } = screen.saved);
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function consumeEscape(screen, input, start) {
|
|
186
|
+
const kind = input[start + 1];
|
|
187
|
+
if (kind === void 0) return -1;
|
|
188
|
+
if (kind === "[") {
|
|
189
|
+
let end = start + 2;
|
|
190
|
+
while (end < input.length && /[\x20-\x3f]/.test(input[end])) end++;
|
|
191
|
+
if (end >= input.length) return -1;
|
|
192
|
+
csi(screen, input.slice(start + 2, end).replace(/[\x20-\x2f]/g, ""), input[end]);
|
|
193
|
+
return end + 1;
|
|
194
|
+
}
|
|
195
|
+
if (kind === "]") {
|
|
196
|
+
for (let end = start + 2; end < input.length; end++) {
|
|
197
|
+
if (input[end] === "\x07") return end + 1;
|
|
198
|
+
if (input[end] === "\x1B" && input[end + 1] === "\\") return end + 2;
|
|
199
|
+
}
|
|
200
|
+
return -1;
|
|
201
|
+
}
|
|
202
|
+
if ("()*+".includes(kind)) return start + 3 > input.length ? -1 : start + 3;
|
|
203
|
+
if (kind === "7") screen.saved = { row: screen.row, col: screen.col };
|
|
204
|
+
else if (kind === "8" && screen.saved) ({ row: screen.row, col: screen.col } = screen.saved);
|
|
205
|
+
else if (kind === "M") screen.row = Math.max(0, screen.row - 1);
|
|
206
|
+
return start + 2;
|
|
207
|
+
}
|
|
208
|
+
function writeScreen(screen, data) {
|
|
209
|
+
const input = screen.pending + data;
|
|
210
|
+
screen.pending = "";
|
|
211
|
+
let i = 0;
|
|
212
|
+
while (i < input.length) {
|
|
213
|
+
const ch = input[i];
|
|
214
|
+
if (ch === "\x1B") {
|
|
215
|
+
const next = consumeEscape(screen, input, i);
|
|
216
|
+
if (next === -1) {
|
|
217
|
+
if (input.length - i <= MAX_PENDING) {
|
|
218
|
+
screen.pending = input.slice(i);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
i++;
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
i = next;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
i++;
|
|
228
|
+
if (ch === "\r") screen.col = 0;
|
|
229
|
+
else if (ch === "\n") lineFeed(screen);
|
|
230
|
+
else if (ch === "\b") screen.col = Math.max(0, screen.col - 1);
|
|
231
|
+
else if (ch === " ") screen.col = Math.min(screen.width - 1, (Math.floor(screen.col / TAB_STOP) + 1) * TAB_STOP);
|
|
232
|
+
else if (ch < " " || ch === "") continue;
|
|
233
|
+
else {
|
|
234
|
+
const codePoint = input.codePointAt(i - 1);
|
|
235
|
+
const full = String.fromCodePoint(codePoint);
|
|
236
|
+
i += full.length - 1;
|
|
237
|
+
putChar(screen, full);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function trimmed(row) {
|
|
242
|
+
let end = row.length;
|
|
243
|
+
while (end > 0 && row[end - 1].ch === " ") end--;
|
|
244
|
+
return row.slice(0, end);
|
|
245
|
+
}
|
|
246
|
+
function transition(from, to) {
|
|
247
|
+
if (to === "") return "\x1B[0m";
|
|
248
|
+
return from === "" ? `\x1B[${to}m` : `\x1B[0;${to}m`;
|
|
249
|
+
}
|
|
250
|
+
function screenToAnsi(screen) {
|
|
251
|
+
let current = "";
|
|
252
|
+
return screen.rows.map((row) => {
|
|
253
|
+
let line = "";
|
|
254
|
+
for (const cell of trimmed(row)) {
|
|
255
|
+
if (cell.sgr !== current) {
|
|
256
|
+
line += transition(current, cell.sgr);
|
|
257
|
+
current = cell.sgr;
|
|
258
|
+
}
|
|
259
|
+
line += cell.ch;
|
|
260
|
+
}
|
|
261
|
+
return line;
|
|
262
|
+
}).join("\n");
|
|
263
|
+
}
|
|
264
|
+
export {
|
|
265
|
+
createScreen,
|
|
266
|
+
resizeScreen,
|
|
267
|
+
screenToAnsi,
|
|
268
|
+
writeScreen
|
|
269
|
+
};
|
|
270
|
+
//# sourceMappingURL=terminal-screen.js.map
|