toolcraft 0.0.231 → 0.0.233
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/composition.json +3 -3
- package/dist/applied-default.d.ts +3 -0
- package/dist/applied-default.js +16 -0
- package/dist/cli.js +508 -204
- package/dist/composition.json +3 -3
- package/dist/discriminator.d.ts +9 -0
- package/dist/discriminator.js +32 -0
- package/dist/error-report.d.ts +2 -18
- package/dist/error-report.js +12 -14
- package/dist/http.js +8 -1
- package/dist/human-in-loop/gate.js +6 -0
- package/dist/index.compile-check.js +22 -5
- package/dist/index.d.ts +12 -5
- package/dist/index.js +50 -0
- package/dist/json-schema-converter.d.ts +14 -6
- package/dist/json-schema-converter.js +150 -20
- package/dist/mcp-proxy-metadata.d.ts +2 -0
- package/dist/mcp-proxy-metadata.js +13 -0
- package/dist/mcp-proxy.d.ts +1 -0
- package/dist/mcp-proxy.js +65 -47
- package/dist/mcp-result.d.ts +3 -0
- package/dist/mcp-result.js +10 -0
- package/dist/mcp.js +211 -153
- package/dist/redaction.js +26 -17
- package/dist/renderer.js +40 -29
- package/dist/schema-member-names.d.ts +2 -0
- package/dist/schema-member-names.js +39 -0
- package/dist/schema-scope.d.ts +1 -0
- package/dist/schema-scope.js +29 -16
- package/dist/schema.d.ts +1 -1
- package/dist/sdk.d.ts +2 -1
- package/dist/sdk.js +164 -50
- package/dist/stream.js +24 -23
- package/dist/testing/diagnostic-value.d.ts +1 -0
- package/dist/testing/diagnostic-value.js +57 -0
- package/dist/testing/fakes.js +2 -1
- package/dist/testing/harness.js +1 -0
- package/dist/testing/memory-fs.js +42 -1
- package/dist/testing/parity.js +50 -6
- package/dist/union-validation.d.ts +5 -0
- package/dist/union-validation.js +36 -0
- package/node_modules/@poe-code/process-runner/dist/types.d.ts +2 -0
- package/node_modules/mcp-oauth-server/dist/index.js +47 -6
- package/node_modules/tiny-http-mcp-server/dist/cli.js +14 -2
- package/node_modules/tiny-http-mcp-server/dist/composition.json +1 -1
- package/node_modules/tiny-http-mcp-server/dist/http-server.d.ts +7 -5
- package/node_modules/tiny-http-mcp-server/dist/http-server.js +3 -11
- package/node_modules/tiny-http-mcp-server/dist/http-transport.d.ts +6 -1
- package/node_modules/tiny-http-mcp-server/dist/http-transport.js +196 -15
- package/node_modules/tiny-http-mcp-server/dist/index.d.ts +2 -2
- package/node_modules/tiny-http-mcp-server/dist/modern-headers.d.ts +3 -0
- package/node_modules/tiny-http-mcp-server/dist/modern-headers.js +35 -0
- package/node_modules/tiny-http-mcp-server/dist/parse-body.d.ts +1 -0
- package/node_modules/tiny-http-mcp-server/dist/parse-body.js +9 -5
- package/node_modules/tiny-http-mcp-server/dist/test-support.js +17 -0
- package/node_modules/tiny-http-mcp-server/dist/testing.d.ts +1 -1
- package/node_modules/tiny-http-mcp-server/dist/testing.js +3 -2
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +21 -4
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/token-endpoint.d.ts +1 -1
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/token-endpoint.js +14 -6
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/http-fetch.d.ts +1 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/http-fetch.js +8 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/http-response.d.ts +1 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/http-response.js +50 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/index.d.ts +2 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/index.js +2 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/server/jwks-token-verifier.js +21 -3
- package/node_modules/tiny-http-mcp-server/package.json +1 -1
- package/node_modules/tiny-mcp-client/README.md +1 -0
- package/node_modules/tiny-mcp-client/dist/index.d.ts +178 -101
- package/node_modules/tiny-mcp-client/dist/index.js +4696 -291
- package/node_modules/tiny-mcp-client/package.json +2 -1
- package/node_modules/tiny-stdio-mcp-server/dist/base64.d.ts +1 -0
- package/node_modules/tiny-stdio-mcp-server/dist/base64.js +5 -0
- package/node_modules/tiny-stdio-mcp-server/dist/composition.json +1 -1
- package/node_modules/tiny-stdio-mcp-server/dist/content/audio.d.ts +3 -0
- package/node_modules/tiny-stdio-mcp-server/dist/content/convert.d.ts +5 -1
- package/node_modules/tiny-stdio-mcp-server/dist/content/convert.js +41 -6
- package/node_modules/tiny-stdio-mcp-server/dist/content/file.d.ts +5 -0
- package/node_modules/tiny-stdio-mcp-server/dist/content/image.d.ts +3 -0
- package/node_modules/tiny-stdio-mcp-server/dist/headers.d.ts +10 -0
- package/node_modules/tiny-stdio-mcp-server/dist/headers.js +180 -0
- package/node_modules/tiny-stdio-mcp-server/dist/index.d.ts +5 -5
- package/node_modules/tiny-stdio-mcp-server/dist/index.js +1 -1
- package/node_modules/tiny-stdio-mcp-server/dist/input-request-schema.d.ts +220 -0
- package/node_modules/tiny-stdio-mcp-server/dist/input-request-schema.js +1995 -0
- package/node_modules/tiny-stdio-mcp-server/dist/jsonrpc.d.ts +1 -0
- package/node_modules/tiny-stdio-mcp-server/dist/jsonrpc.js +38 -25
- package/node_modules/tiny-stdio-mcp-server/dist/metadata.d.ts +2 -0
- package/node_modules/tiny-stdio-mcp-server/dist/metadata.js +37 -0
- package/node_modules/tiny-stdio-mcp-server/dist/protocol-validation.d.ts +4 -0
- package/node_modules/tiny-stdio-mcp-server/dist/protocol-validation.js +53 -0
- package/node_modules/tiny-stdio-mcp-server/dist/protocol.d.ts +17 -0
- package/node_modules/tiny-stdio-mcp-server/dist/protocol.js +228 -0
- package/node_modules/tiny-stdio-mcp-server/dist/request-cancellation.d.ts +1 -0
- package/node_modules/tiny-stdio-mcp-server/dist/request-cancellation.js +18 -0
- package/node_modules/tiny-stdio-mcp-server/dist/sampling-messages.d.ts +18 -0
- package/node_modules/tiny-stdio-mcp-server/dist/sampling-messages.js +34 -0
- package/node_modules/tiny-stdio-mcp-server/dist/schema.d.ts +4 -1
- package/node_modules/tiny-stdio-mcp-server/dist/server.d.ts +9 -4
- package/node_modules/tiny-stdio-mcp-server/dist/server.js +334 -135
- package/node_modules/tiny-stdio-mcp-server/dist/stdio-input.d.ts +3 -0
- package/node_modules/tiny-stdio-mcp-server/dist/stdio-input.js +50 -28
- package/node_modules/tiny-stdio-mcp-server/dist/subscriptions.d.ts +11 -0
- package/node_modules/tiny-stdio-mcp-server/dist/subscriptions.js +128 -0
- package/node_modules/tiny-stdio-mcp-server/dist/types.d.ts +58 -18
- package/node_modules/tiny-stdio-mcp-server/dist/types.js +2 -1
- package/node_modules/tiny-stdio-mcp-server/dist/uri.d.ts +1 -0
- package/node_modules/tiny-stdio-mcp-server/dist/uri.js +41 -0
- package/node_modules/tiny-stdio-mcp-server/package.json +8 -0
- package/node_modules/toolcraft-design/dist/command-registry.d.ts +11 -0
- package/node_modules/toolcraft-design/dist/command-registry.js +24 -0
- package/node_modules/toolcraft-design/dist/dashboard/ansi.d.ts +2 -0
- package/node_modules/toolcraft-design/dist/dashboard/ansi.js +81 -27
- package/node_modules/toolcraft-design/dist/dashboard/buffer.js +39 -25
- package/node_modules/toolcraft-design/dist/dashboard/components/border.js +6 -5
- package/node_modules/toolcraft-design/dist/dashboard/components/footer.js +26 -21
- package/node_modules/toolcraft-design/dist/dashboard/components/output-pane.d.ts +1 -1
- package/node_modules/toolcraft-design/dist/dashboard/components/output-pane.js +12 -6
- package/node_modules/toolcraft-design/dist/dashboard/components/stats-pane.d.ts +2 -1
- package/node_modules/toolcraft-design/dist/dashboard/components/stats-pane.js +51 -10
- package/node_modules/toolcraft-design/dist/dashboard/dashboard.d.ts +4 -0
- package/node_modules/toolcraft-design/dist/dashboard/dashboard.js +77 -4
- package/node_modules/toolcraft-design/dist/dashboard/index.d.ts +2 -0
- package/node_modules/toolcraft-design/dist/dashboard/index.js +2 -0
- package/node_modules/toolcraft-design/dist/dashboard/keymap.js +26 -6
- package/node_modules/toolcraft-design/dist/dashboard/layout.d.ts +1 -0
- package/node_modules/toolcraft-design/dist/dashboard/layout.js +19 -3
- package/node_modules/toolcraft-design/dist/dashboard/line-buffer.d.ts +9 -0
- package/node_modules/toolcraft-design/dist/dashboard/line-buffer.js +63 -0
- package/node_modules/toolcraft-design/dist/dashboard/output-preview.d.ts +9 -0
- package/node_modules/toolcraft-design/dist/dashboard/output-preview.js +70 -0
- package/node_modules/toolcraft-design/dist/dashboard/snapshot.js +3 -1
- package/node_modules/toolcraft-design/dist/dashboard/store.js +9 -3
- package/node_modules/toolcraft-design/dist/dashboard/terminal-strings.d.ts +6 -0
- package/node_modules/toolcraft-design/dist/dashboard/terminal-strings.js +108 -0
- package/node_modules/toolcraft-design/dist/dashboard/terminal-width.js +3 -1
- package/node_modules/toolcraft-design/dist/dashboard/terminal.js +11 -1
- package/node_modules/toolcraft-design/dist/dashboard/testing/pipeline-scenario.d.ts +1 -0
- package/node_modules/toolcraft-design/dist/dashboard/testing/pipeline-scenario.js +126 -0
- package/node_modules/toolcraft-design/dist/dashboard/types.d.ts +3 -1
- package/node_modules/toolcraft-design/dist/event-groups.d.ts +19 -0
- package/node_modules/toolcraft-design/dist/event-groups.js +49 -0
- package/node_modules/toolcraft-design/dist/index.d.ts +6 -1
- package/node_modules/toolcraft-design/dist/index.js +5 -1
- package/node_modules/toolcraft-design/dist/inline-notice.d.ts +14 -0
- package/node_modules/toolcraft-design/dist/inline-notice.js +28 -0
- package/node_modules/toolcraft-design/dist/metric.d.ts +8 -0
- package/node_modules/toolcraft-design/dist/metric.js +21 -0
- package/node_modules/toolcraft-design/dist/overlay-manager.d.ts +6 -0
- package/node_modules/toolcraft-design/dist/overlay-manager.js +10 -0
- package/node_modules/toolcraft-design/dist/primitives.d.ts +12 -0
- package/node_modules/toolcraft-design/dist/primitives.js +7 -0
- package/node_modules/toolcraft-design/dist/progress-group.d.ts +7 -0
- package/node_modules/toolcraft-design/dist/progress-group.js +10 -0
- package/node_modules/toolcraft-design/dist/render-performance.d.ts +33 -0
- package/node_modules/toolcraft-design/dist/render-performance.js +71 -0
- package/node_modules/toolcraft-design/dist/task-tree.d.ts +22 -0
- package/node_modules/toolcraft-design/dist/task-tree.js +79 -0
- package/node_modules/toolcraft-design/dist/viewport.d.ts +18 -0
- package/node_modules/toolcraft-design/dist/viewport.js +56 -0
- package/node_modules/toolcraft-schema/dist/clone-default.d.ts +1 -0
- package/node_modules/toolcraft-schema/dist/clone-default.js +26 -0
- package/node_modules/toolcraft-schema/dist/index.compile-check.js +1 -0
- package/node_modules/toolcraft-schema/dist/index.d.ts +18 -9
- package/node_modules/toolcraft-schema/dist/index.js +46 -8
- package/node_modules/toolcraft-schema/dist/json-schema/compiler.d.ts +1 -0
- package/node_modules/toolcraft-schema/dist/json-schema/compiler.js +6 -0
- package/node_modules/toolcraft-schema/dist/json-schema/evaluate.js +6 -3
- package/node_modules/toolcraft-schema/dist/json-schema/normalize-nullability.d.ts +1 -0
- package/node_modules/toolcraft-schema/dist/json-schema/normalize-nullability.js +69 -0
- package/node_modules/toolcraft-schema/dist/json-schema/types.d.ts +1 -0
- package/node_modules/toolcraft-schema/dist/json.d.ts +4 -1
- package/node_modules/toolcraft-schema/dist/json.js +57 -1
- package/node_modules/toolcraft-schema/dist/native-json-schema.d.ts +10 -0
- package/node_modules/toolcraft-schema/dist/native-json-schema.js +11 -0
- package/node_modules/toolcraft-schema/dist/validate.d.ts +5 -1
- package/node_modules/toolcraft-schema/dist/validate.js +61 -47
- package/node_modules/toolcraft-schema/package.json +1 -1
- package/package.json +6 -3
|
@@ -1,4 +1,16 @@
|
|
|
1
|
+
import { graphemes, graphemeWidth } from "./terminal-width.js";
|
|
2
|
+
import { createTerminalStringFilter } from "./terminal-strings.js";
|
|
1
3
|
const ESC = "\u001b";
|
|
4
|
+
/** Visible text for a plain screen row; styles are supplied by the caller. */
|
|
5
|
+
export function plainTerminalText(text) {
|
|
6
|
+
for (let index = 0; index < text.length; index++) {
|
|
7
|
+
const code = text.charCodeAt(index);
|
|
8
|
+
if ((code < 0x20 && code !== 0x09) || (code >= 0x7f && code <= 0x9f)) {
|
|
9
|
+
return parseAnsi(text).map(line => line.segments.map(segment => segment.text).join("")).join(" ");
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return text;
|
|
13
|
+
}
|
|
2
14
|
export function hasAnsi(text) {
|
|
3
15
|
return text.includes(ESC);
|
|
4
16
|
}
|
|
@@ -11,12 +23,38 @@ export function hasAnsi(text) {
|
|
|
11
23
|
* `baseStyle` is used as the initial style and as the restore target for SGR reset / default color.
|
|
12
24
|
*/
|
|
13
25
|
export function parseAnsi(text, baseStyle) {
|
|
26
|
+
text = createTerminalStringFilter().push(text);
|
|
14
27
|
const base = normalizeStyle(baseStyle);
|
|
15
28
|
let style = { ...base };
|
|
16
29
|
let concealed = false;
|
|
17
30
|
const lines = [];
|
|
18
31
|
let cells = [];
|
|
19
32
|
let column = 0;
|
|
33
|
+
const clearCell = (position) => {
|
|
34
|
+
if (cells[position]?.ch === "" && position > 0) {
|
|
35
|
+
cells[position - 1] = { ch: " ", style: { ...style } };
|
|
36
|
+
}
|
|
37
|
+
if (cells[position + 1]?.ch === "") {
|
|
38
|
+
cells[position + 1] = { ch: " ", style: { ...style } };
|
|
39
|
+
}
|
|
40
|
+
cells[position] = undefined;
|
|
41
|
+
};
|
|
42
|
+
const writeGrapheme = (ch) => {
|
|
43
|
+
const width = graphemeWidth(ch);
|
|
44
|
+
if (width === 0) {
|
|
45
|
+
const previous = cells[column - 1]?.ch === "" ? column - 2 : column - 1;
|
|
46
|
+
if (!concealed && cells[previous])
|
|
47
|
+
cells[previous].ch += ch;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
clearCell(column);
|
|
51
|
+
if (width === 2)
|
|
52
|
+
clearCell(column + 1);
|
|
53
|
+
cells[column] = { ch: concealed ? " " : ch, style: { ...style } };
|
|
54
|
+
if (width === 2)
|
|
55
|
+
cells[column + 1] = { ch: concealed ? " " : "", style: { ...style } };
|
|
56
|
+
column += width;
|
|
57
|
+
};
|
|
20
58
|
const finishLine = () => {
|
|
21
59
|
lines.push({ segments: cellsToSegments(cells) });
|
|
22
60
|
cells = [];
|
|
@@ -25,8 +63,8 @@ export function parseAnsi(text, baseStyle) {
|
|
|
25
63
|
let index = 0;
|
|
26
64
|
while (index < text.length) {
|
|
27
65
|
const ch = text[index];
|
|
28
|
-
if (ch === ESC && text[index + 1] === "[") {
|
|
29
|
-
const paramsStart = index + 2;
|
|
66
|
+
if ((ch === ESC && text[index + 1] === "[") || ch === "\u009b") {
|
|
67
|
+
const paramsStart = index + (ch === ESC ? 2 : 1);
|
|
30
68
|
let cursor = paramsStart;
|
|
31
69
|
while (cursor < text.length && !isCsiFinalByte(text[cursor])) {
|
|
32
70
|
cursor += 1;
|
|
@@ -42,18 +80,30 @@ export function parseAnsi(text, baseStyle) {
|
|
|
42
80
|
style = sgr.style;
|
|
43
81
|
concealed = sgr.concealed;
|
|
44
82
|
}
|
|
45
|
-
else if (finalByte === "K"
|
|
46
|
-
|
|
83
|
+
else if (finalByte === "K") {
|
|
84
|
+
const mode = parseParams(params)[0];
|
|
85
|
+
if (mode === 0) {
|
|
86
|
+
clearCell(column);
|
|
87
|
+
cells.length = Math.min(cells.length, column);
|
|
88
|
+
}
|
|
89
|
+
else if (mode === 1) {
|
|
90
|
+
const end = Math.min(column, cells.length - 1);
|
|
91
|
+
for (let position = 0; position <= end; position += 1)
|
|
92
|
+
clearCell(position);
|
|
93
|
+
}
|
|
94
|
+
else if (mode === 2) {
|
|
95
|
+
cells = [];
|
|
96
|
+
}
|
|
47
97
|
}
|
|
48
98
|
index = cursor + 1;
|
|
49
99
|
continue;
|
|
50
100
|
}
|
|
101
|
+
if (ch === "\u0085") {
|
|
102
|
+
finishLine();
|
|
103
|
+
index += 1;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
51
106
|
if (ch === ESC) {
|
|
52
|
-
const next = text[index + 1];
|
|
53
|
-
if (next === "]" || next === "P" || next === "X" || next === "^" || next === "_") {
|
|
54
|
-
index = skipStringTerminated(text, index + 2);
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
107
|
index += 2;
|
|
58
108
|
continue;
|
|
59
109
|
}
|
|
@@ -72,14 +122,32 @@ export function parseAnsi(text, baseStyle) {
|
|
|
72
122
|
index += 1;
|
|
73
123
|
continue;
|
|
74
124
|
}
|
|
125
|
+
if (ch === "\t") {
|
|
126
|
+
const spaces = 8 - (column % 8);
|
|
127
|
+
for (let offset = 0; offset < spaces; offset++) {
|
|
128
|
+
if (cells[column] === undefined)
|
|
129
|
+
writeGrapheme(" ");
|
|
130
|
+
else
|
|
131
|
+
column += 1;
|
|
132
|
+
}
|
|
133
|
+
index += 1;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
75
136
|
const code = ch.charCodeAt(0);
|
|
76
|
-
if (code < 0x20 &&
|
|
137
|
+
if (code < 0x20 || (code >= 0x7f && code <= 0x9f)) {
|
|
77
138
|
index += 1;
|
|
78
139
|
continue;
|
|
79
140
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
141
|
+
let end = index + 1;
|
|
142
|
+
while (end < text.length) {
|
|
143
|
+
const next = text.charCodeAt(end);
|
|
144
|
+
if (next < 0x20 || (next >= 0x7f && next <= 0x9f))
|
|
145
|
+
break;
|
|
146
|
+
end += 1;
|
|
147
|
+
}
|
|
148
|
+
for (const grapheme of graphemes(text.slice(index, end)))
|
|
149
|
+
writeGrapheme(grapheme);
|
|
150
|
+
index = end;
|
|
83
151
|
}
|
|
84
152
|
finishLine();
|
|
85
153
|
return lines;
|
|
@@ -102,20 +170,6 @@ function isCsiFinalByte(ch) {
|
|
|
102
170
|
const code = ch.charCodeAt(0);
|
|
103
171
|
return code >= 0x40 && code <= 0x7e;
|
|
104
172
|
}
|
|
105
|
-
function skipStringTerminated(text, start) {
|
|
106
|
-
let index = start;
|
|
107
|
-
while (index < text.length) {
|
|
108
|
-
const ch = text[index];
|
|
109
|
-
if (ch === "\u0007") {
|
|
110
|
-
return index + 1;
|
|
111
|
-
}
|
|
112
|
-
if (ch === ESC && text[index + 1] === "\\") {
|
|
113
|
-
return index + 2;
|
|
114
|
-
}
|
|
115
|
-
index += 1;
|
|
116
|
-
}
|
|
117
|
-
return index;
|
|
118
|
-
}
|
|
119
173
|
function parseParams(params) {
|
|
120
174
|
if (params.length === 0) {
|
|
121
175
|
return [0];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { color } from "../components/color.js";
|
|
2
2
|
import { expandTabs, graphemes, graphemeWidth } from "./terminal-width.js";
|
|
3
|
+
import { parseAnsi } from "./ansi.js";
|
|
3
4
|
const EMPTY_CELL = { ch: " ", style: {} };
|
|
4
5
|
export class ScreenBuffer {
|
|
5
6
|
_width;
|
|
@@ -22,17 +23,19 @@ export class ScreenBuffer {
|
|
|
22
23
|
}
|
|
23
24
|
const normalizedStyle = normalizeStyle(style);
|
|
24
25
|
let offset = 0;
|
|
25
|
-
for (const
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
this.
|
|
26
|
+
for (const segment of screenSegments(text, normalizedStyle)) {
|
|
27
|
+
for (const ch of graphemes(expandTabs(segment.text, Math.max(0, x + offset)))) {
|
|
28
|
+
const targetX = x + offset;
|
|
29
|
+
const width = graphemeWidth(ch);
|
|
30
|
+
offset += width;
|
|
31
|
+
if (!this.isInBoundsX(targetX)) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
this._cells[this.index(targetX, y)] = { ch, style: segment.style };
|
|
35
|
+
for (let continuation = 1; continuation < width; continuation += 1) {
|
|
36
|
+
if (this.isInBoundsX(targetX + continuation)) {
|
|
37
|
+
this._cells[this.index(targetX + continuation, y)] = { ch: "", style: segment.style };
|
|
38
|
+
}
|
|
36
39
|
}
|
|
37
40
|
}
|
|
38
41
|
}
|
|
@@ -84,20 +87,22 @@ export class ScreenBuffer {
|
|
|
84
87
|
const normalizedStyle = normalizeStyle(style);
|
|
85
88
|
const rectEndX = rect.x + rect.width;
|
|
86
89
|
let offset = 0;
|
|
87
|
-
for (const
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
this.
|
|
90
|
+
for (const segment of screenSegments(text, normalizedStyle)) {
|
|
91
|
+
for (const ch of graphemes(expandTabs(segment.text, offset))) {
|
|
92
|
+
const targetX = rect.x + offset;
|
|
93
|
+
const width = graphemeWidth(ch);
|
|
94
|
+
offset += width;
|
|
95
|
+
if (targetX + width > rectEndX) {
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
if (!this.isInBoundsX(targetX)) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
this._cells[this.index(targetX, y)] = { ch, style: segment.style };
|
|
102
|
+
for (let continuation = 1; continuation < width; continuation += 1) {
|
|
103
|
+
if (this.isInBoundsX(targetX + continuation)) {
|
|
104
|
+
this._cells[this.index(targetX + continuation, y)] = { ch: "", style: segment.style };
|
|
105
|
+
}
|
|
101
106
|
}
|
|
102
107
|
}
|
|
103
108
|
}
|
|
@@ -115,6 +120,15 @@ export class ScreenBuffer {
|
|
|
115
120
|
return y >= 0 && y < this._height;
|
|
116
121
|
}
|
|
117
122
|
}
|
|
123
|
+
function screenSegments(text, style) {
|
|
124
|
+
for (let index = 0; index < text.length; index++) {
|
|
125
|
+
const code = text.charCodeAt(index);
|
|
126
|
+
if ((code < 0x20 && code !== 0x09) || (code >= 0x7f && code <= 0x9f)) {
|
|
127
|
+
return parseAnsi(text, style).flatMap((line, row) => row === 0 ? line.segments : [{ text: " ", style }, ...line.segments]);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return [{ text, style }];
|
|
131
|
+
}
|
|
118
132
|
export function diff(prev, next) {
|
|
119
133
|
const changes = [];
|
|
120
134
|
const width = Math.max(prev.width, next.width);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { plainTerminalText } from "../ansi.js";
|
|
2
|
+
import { displayWidth, truncateToWidth } from "../terminal-width.js";
|
|
1
3
|
export function renderBorder(buffer, layout, opts) {
|
|
2
4
|
const outer = layout.outerBorder;
|
|
3
5
|
if (outer.width <= 0 || outer.height <= 0) {
|
|
@@ -100,17 +102,16 @@ function renderFooterDivider(buffer, left, right, bottom, layout, style) {
|
|
|
100
102
|
}
|
|
101
103
|
}
|
|
102
104
|
function renderTopSegment(width, title) {
|
|
105
|
+
if (title !== undefined)
|
|
106
|
+
title = plainTerminalText(title);
|
|
103
107
|
if (width <= 0) {
|
|
104
108
|
return "";
|
|
105
109
|
}
|
|
106
110
|
if (!title) {
|
|
107
111
|
return "─".repeat(width);
|
|
108
112
|
}
|
|
109
|
-
const content = `─ ${title}
|
|
110
|
-
|
|
111
|
-
return content.slice(0, width);
|
|
112
|
-
}
|
|
113
|
-
return `${content}${"─".repeat(width - content.length)}`;
|
|
113
|
+
const content = truncateToWidth(`─ ${title} `, width);
|
|
114
|
+
return `${content}${"─".repeat(width - displayWidth(content))}`;
|
|
114
115
|
}
|
|
115
116
|
function hasInteriorRows(top, bottom) {
|
|
116
117
|
return (bottom - top) > 1;
|
|
@@ -1,14 +1,32 @@
|
|
|
1
1
|
import { getTheme } from "../../internal/theme-detect.js";
|
|
2
|
+
import { displayWidth, graphemes, graphemeWidth, truncateToWidth } from "../terminal-width.js";
|
|
3
|
+
import { plainTerminalText } from "../ansi.js";
|
|
2
4
|
export function renderFooter(buffer, rect, hints) {
|
|
3
5
|
buffer.clearRect(rect);
|
|
4
6
|
if (rect.width <= 0 || rect.height <= 0 || hints.length === 0) {
|
|
5
7
|
return;
|
|
6
8
|
}
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
+
const fitted = [];
|
|
10
|
+
hints = hints.map(hint => ({ key: plainTerminalText(hint.key), label: plainTerminalText(hint.label) }));
|
|
11
|
+
let width = 0;
|
|
12
|
+
for (const hint of hints) {
|
|
13
|
+
const nextWidth = displayWidth(`${hint.key} ${hint.label}`) + (fitted.length > 0 ? 2 : 0);
|
|
14
|
+
if (width + nextWidth > rect.width)
|
|
15
|
+
break;
|
|
16
|
+
fitted.push(hint);
|
|
17
|
+
width += nextWidth;
|
|
18
|
+
}
|
|
19
|
+
if (fitted.length === 0) {
|
|
20
|
+
const key = truncateToWidth(hints[0].key, rect.width);
|
|
21
|
+
fitted.push({ key, label: "" });
|
|
22
|
+
width = displayWidth(key);
|
|
23
|
+
}
|
|
24
|
+
const cells = hintsToCells(fitted);
|
|
25
|
+
let x = rect.x + Math.floor((rect.width - width) / 2);
|
|
9
26
|
const y = rect.y + Math.floor(rect.height / 2);
|
|
10
|
-
cells.forEach((cell
|
|
11
|
-
buffer.put(
|
|
27
|
+
cells.forEach((cell) => {
|
|
28
|
+
buffer.put(x, y, cell.ch, cell.style);
|
|
29
|
+
x += graphemeWidth(cell.ch);
|
|
12
30
|
});
|
|
13
31
|
}
|
|
14
32
|
export function defaultHints() {
|
|
@@ -27,30 +45,17 @@ function hintsToCells(hints) {
|
|
|
27
45
|
if (hintIndex > 0) {
|
|
28
46
|
cells.push({ ch: " ", style: {} }, { ch: " ", style: {} });
|
|
29
47
|
}
|
|
30
|
-
for (const ch of hint.key) {
|
|
48
|
+
for (const ch of graphemes(hint.key)) {
|
|
31
49
|
cells.push({ ch, style: accentStyle });
|
|
32
50
|
}
|
|
33
|
-
|
|
34
|
-
|
|
51
|
+
if (hint.label.length > 0)
|
|
52
|
+
cells.push({ ch: " ", style: {} });
|
|
53
|
+
for (const ch of graphemes(hint.label)) {
|
|
35
54
|
cells.push({ ch, style: {} });
|
|
36
55
|
}
|
|
37
56
|
});
|
|
38
57
|
return cells;
|
|
39
58
|
}
|
|
40
|
-
function truncateCells(cells, width) {
|
|
41
|
-
if (cells.length <= width) {
|
|
42
|
-
return cells;
|
|
43
|
-
}
|
|
44
|
-
if (width <= 3) {
|
|
45
|
-
return Array.from({ length: Math.max(0, width) }, () => ({ ch: ".", style: {} }));
|
|
46
|
-
}
|
|
47
|
-
return [
|
|
48
|
-
...cells.slice(0, width - 3),
|
|
49
|
-
{ ch: ".", style: {} },
|
|
50
|
-
{ ch: ".", style: {} },
|
|
51
|
-
{ ch: ".", style: {} }
|
|
52
|
-
];
|
|
53
|
-
}
|
|
54
59
|
function getAccentStyle() {
|
|
55
60
|
return getTheme().styles.accent;
|
|
56
61
|
}
|
|
@@ -8,5 +8,5 @@ export type VisualLine = {
|
|
|
8
8
|
prefixStyle: CellStyle;
|
|
9
9
|
segments?: StyledSegment[];
|
|
10
10
|
};
|
|
11
|
-
export declare function renderOutputPane(buffer: ScreenBuffer, rect: Rect, items: OutputItem[]):
|
|
11
|
+
export declare function renderOutputPane(buffer: ScreenBuffer, rect: Rect, items: OutputItem[], scrollOffset?: number): number;
|
|
12
12
|
export declare function computeVisualLines(items: OutputItem[], width: number): VisualLine[];
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
+
import { selectViewportTail } from "../../viewport.js";
|
|
1
2
|
import { getTheme } from "../../internal/theme-detect.js";
|
|
2
3
|
import { hasAnsi, parseAnsi } from "../ansi.js";
|
|
3
4
|
import { displayWidth, expandTabs, graphemes, graphemeWidth } from "../terminal-width.js";
|
|
4
5
|
const TEXT_OFFSET = 3;
|
|
5
6
|
const CONTINUATION_PREFIX = "│";
|
|
6
|
-
export function renderOutputPane(buffer, rect, items) {
|
|
7
|
+
export function renderOutputPane(buffer, rect, items, scrollOffset = 0) {
|
|
7
8
|
buffer.clearRect(rect);
|
|
8
9
|
if (rect.width <= 0 || rect.height <= 0) {
|
|
9
|
-
return;
|
|
10
|
+
return 0;
|
|
10
11
|
}
|
|
11
|
-
const visualLines =
|
|
12
|
-
const startLine = Math.max(visualLines.length - rect.height, 0);
|
|
12
|
+
const { rows: visualLines, offset: actualOffset } = selectViewportTail(items, rect.height, scrollOffset, item => computeVisualLines([item], rect.width));
|
|
13
13
|
const textRect = {
|
|
14
14
|
x: rect.x + TEXT_OFFSET,
|
|
15
15
|
y: rect.y,
|
|
@@ -17,7 +17,7 @@ export function renderOutputPane(buffer, rect, items) {
|
|
|
17
17
|
height: rect.height
|
|
18
18
|
};
|
|
19
19
|
for (let row = 0; row < rect.height; row += 1) {
|
|
20
|
-
const line = visualLines[
|
|
20
|
+
const line = visualLines[row];
|
|
21
21
|
if (line === undefined) {
|
|
22
22
|
continue;
|
|
23
23
|
}
|
|
@@ -44,6 +44,7 @@ export function renderOutputPane(buffer, rect, items) {
|
|
|
44
44
|
}
|
|
45
45
|
buffer.putInRect(textRect, row, line.text, line.style);
|
|
46
46
|
}
|
|
47
|
+
return actualOffset;
|
|
47
48
|
}
|
|
48
49
|
export function computeVisualLines(items, width) {
|
|
49
50
|
if (width <= 0) {
|
|
@@ -85,7 +86,12 @@ export function computeVisualLines(items, width) {
|
|
|
85
86
|
return visualLines;
|
|
86
87
|
}
|
|
87
88
|
function hasCursorControls(text) {
|
|
88
|
-
|
|
89
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
90
|
+
const code = text.charCodeAt(index);
|
|
91
|
+
if ((code < 0x20 && code !== 0x0a && code !== 0x09) || (code >= 0x7f && code <= 0x9f))
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
89
95
|
}
|
|
90
96
|
function hardWrapSegments(segments, width) {
|
|
91
97
|
if (width <= 0) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ScreenBuffer } from "../buffer.js";
|
|
2
2
|
import type { DashboardStats, Rect } from "../types.js";
|
|
3
|
-
import type
|
|
3
|
+
import { type VisualLine } from "./output-pane.js";
|
|
4
|
+
export declare function renderCompactStatsPane(buffer: ScreenBuffer, rect: Rect, stats: DashboardStats): void;
|
|
4
5
|
export declare function renderStatsPane(buffer: ScreenBuffer, rect: Rect, stats: DashboardStats): void;
|
|
5
6
|
export declare function formatElapsed(ms: number): string;
|
|
6
7
|
export declare function formatNumber(n: number): string;
|
|
@@ -1,10 +1,42 @@
|
|
|
1
1
|
import { getTheme } from "../../internal/theme-detect.js";
|
|
2
|
+
import { plainTerminalText } from "../ansi.js";
|
|
3
|
+
import { displayWidth, graphemes, graphemeWidth, truncateToWidth } from "../terminal-width.js";
|
|
4
|
+
import { computeVisualLines } from "./output-pane.js";
|
|
5
|
+
export function renderCompactStatsPane(buffer, rect, stats) {
|
|
6
|
+
buffer.clearRect(rect);
|
|
7
|
+
if (rect.width <= 0 || rect.height <= 0)
|
|
8
|
+
return;
|
|
9
|
+
const status = formatStatus(stats.status);
|
|
10
|
+
const metrics = `${status} · ${stats.iterationsLabel ?? "Iteration"} ${formatNumber(stats.iterations)} · ${formatElapsed(stats.elapsedMs)} · ${formatNumber(stats.tokensIn + stats.tokensOut)} tokens`;
|
|
11
|
+
const firstLine = rect.height === 1 && stats.currentAction ? `${status} · ${stats.currentAction}` : metrics;
|
|
12
|
+
const messages = [firstLine, stats.currentAction ?? ""].map(plainTerminalText);
|
|
13
|
+
buffer.putInRect(rect, 0, truncateToWidth(messages[0], rect.width), getStatusStyle(stats.status));
|
|
14
|
+
if (rect.height > 1 && stats.currentAction !== undefined) {
|
|
15
|
+
buffer.putInRect(rect, 1, truncateToWidth(messages[1], rect.width), getToneStyle("muted"));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
2
18
|
export function renderStatsPane(buffer, rect, stats) {
|
|
3
19
|
buffer.clearRect(rect);
|
|
4
20
|
if (rect.width <= 0 || rect.height <= 0) {
|
|
5
21
|
return;
|
|
6
22
|
}
|
|
7
|
-
|
|
23
|
+
let lines = statsToLines(stats, rect.width);
|
|
24
|
+
if (lines.length > rect.height) {
|
|
25
|
+
const actions = stats.currentAction === undefined ? [] : lines.slice(9);
|
|
26
|
+
const visibleActions = actions.slice(0, Math.max(0, rect.height - 1));
|
|
27
|
+
if (visibleActions.length > 0 && visibleActions.length < actions.length) {
|
|
28
|
+
const last = visibleActions[visibleActions.length - 1];
|
|
29
|
+
visibleActions[visibleActions.length - 1] = {
|
|
30
|
+
...last,
|
|
31
|
+
text: truncateToWidth(`${last.text}…`, Math.max(0, rect.width - displayWidth(last.prefix)))
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
lines = [
|
|
35
|
+
lines[0],
|
|
36
|
+
...visibleActions,
|
|
37
|
+
...lines.slice(1, 7).filter((line) => line.prefix.length > 0 || line.text.length > 0)
|
|
38
|
+
];
|
|
39
|
+
}
|
|
8
40
|
for (let row = 0; row < rect.height; row += 1) {
|
|
9
41
|
const line = lines[row];
|
|
10
42
|
if (line === undefined) {
|
|
@@ -16,7 +48,7 @@ export function renderStatsPane(buffer, rect, stats) {
|
|
|
16
48
|
if (line.text.length === 0) {
|
|
17
49
|
continue;
|
|
18
50
|
}
|
|
19
|
-
const textStart = Math.min(line.prefix
|
|
51
|
+
const textStart = Math.min(displayWidth(line.prefix), rect.width);
|
|
20
52
|
buffer.putInRect({ x: rect.x + textStart, y: rect.y + row, width: rect.width - textStart, height: 1 }, 0, line.text, line.style);
|
|
21
53
|
}
|
|
22
54
|
}
|
|
@@ -37,7 +69,7 @@ export function statsToLines(stats, width) {
|
|
|
37
69
|
}
|
|
38
70
|
const mutedStyle = getToneStyle("muted");
|
|
39
71
|
const totalTokens = stats.tokensIn + stats.tokensOut;
|
|
40
|
-
const iterationsLabel = stats.iterationsLabel ?? "Iteration";
|
|
72
|
+
const iterationsLabel = plainTerminalText(stats.iterationsLabel ?? "Iteration");
|
|
41
73
|
const lines = [
|
|
42
74
|
createKeyValueLine("Status", formatStatus(stats.status), width, getStatusStyle(stats.status)),
|
|
43
75
|
createKeyValueLine(iterationsLabel, formatNumber(stats.iterations), width),
|
|
@@ -53,12 +85,12 @@ export function statsToLines(stats, width) {
|
|
|
53
85
|
prefixStyle: {},
|
|
54
86
|
style: {},
|
|
55
87
|
text: ""
|
|
56
|
-
}, {
|
|
57
|
-
prefix:
|
|
88
|
+
}, ...computeVisualLines([{ kind: "status", text: stats.currentAction, ts: 0 }], width + 1).map((line) => ({
|
|
89
|
+
prefix: clipText(" ", width),
|
|
58
90
|
prefixStyle: mutedStyle,
|
|
59
91
|
style: mutedStyle,
|
|
60
|
-
text:
|
|
61
|
-
});
|
|
92
|
+
text: line.text
|
|
93
|
+
})));
|
|
62
94
|
}
|
|
63
95
|
return lines;
|
|
64
96
|
}
|
|
@@ -72,17 +104,26 @@ function createBlankLine() {
|
|
|
72
104
|
}
|
|
73
105
|
function createKeyValueLine(label, value, width, valueStyle = {}) {
|
|
74
106
|
const clippedValue = clipText(value, width);
|
|
75
|
-
const availableBeforeValue = Math.max(width - clippedValue
|
|
107
|
+
const availableBeforeValue = Math.max(width - displayWidth(clippedValue), 0);
|
|
76
108
|
const clippedLabel = clipText(label, Math.max(availableBeforeValue - 1, 0));
|
|
77
109
|
return {
|
|
78
|
-
prefix: clippedLabel + " ".repeat(Math.max(availableBeforeValue - clippedLabel
|
|
110
|
+
prefix: clippedLabel + " ".repeat(Math.max(availableBeforeValue - displayWidth(clippedLabel), 0)),
|
|
79
111
|
prefixStyle: {},
|
|
80
112
|
style: valueStyle,
|
|
81
113
|
text: clippedValue
|
|
82
114
|
};
|
|
83
115
|
}
|
|
84
116
|
function clipText(value, width) {
|
|
85
|
-
|
|
117
|
+
let result = "";
|
|
118
|
+
let cells = 0;
|
|
119
|
+
for (const grapheme of graphemes(value)) {
|
|
120
|
+
const size = graphemeWidth(grapheme);
|
|
121
|
+
if (cells + size > width)
|
|
122
|
+
break;
|
|
123
|
+
result += grapheme;
|
|
124
|
+
cells += size;
|
|
125
|
+
}
|
|
126
|
+
return result;
|
|
86
127
|
}
|
|
87
128
|
function formatStatus(status) {
|
|
88
129
|
return `${status.slice(0, 1).toUpperCase()}${status.slice(1)}`;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type RenderPerformanceSnapshot } from "../render-performance.js";
|
|
1
2
|
import type { FooterHint } from "./components/footer.js";
|
|
2
3
|
import type { Command, DashboardStats, OutputItem } from "./types.js";
|
|
3
4
|
export type DashboardOptions = {
|
|
@@ -8,6 +9,8 @@ export type DashboardOptions = {
|
|
|
8
9
|
hints?: FooterHint[];
|
|
9
10
|
stdin?: NodeJS.ReadStream;
|
|
10
11
|
stdout?: NodeJS.WriteStream;
|
|
12
|
+
/** Observe bounded performance snapshots without forcing idle repaints. */
|
|
13
|
+
onPerformance?: (stats: RenderPerformanceSnapshot) => void;
|
|
11
14
|
};
|
|
12
15
|
export type Dashboard = {
|
|
13
16
|
start(): void;
|
|
@@ -16,5 +19,6 @@ export type Dashboard = {
|
|
|
16
19
|
updateStats(stats: Partial<DashboardStats>): void;
|
|
17
20
|
onCommand(handler: (cmd: Command) => void): void;
|
|
18
21
|
destroy(): void;
|
|
22
|
+
getPerformance(): RenderPerformanceSnapshot;
|
|
19
23
|
};
|
|
20
24
|
export declare function createDashboard(opts?: DashboardOptions): Dashboard;
|