toolcraft-openapi 0.0.1
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/README.md +64 -0
- package/dist/api-command.d.ts +7 -0
- package/dist/api-command.js +4 -0
- package/dist/auth/bearer-token-auth.d.ts +8 -0
- package/dist/auth/bearer-token-auth.js +216 -0
- package/dist/auth/types.d.ts +9 -0
- package/dist/auth/types.js +1 -0
- package/dist/bin/generate.d.ts +40 -0
- package/dist/bin/generate.js +248 -0
- package/dist/define-client.d.ts +20 -0
- package/dist/define-client.js +148 -0
- package/dist/generate.d.ts +210 -0
- package/dist/generate.js +1131 -0
- package/dist/group-by-noun.d.ts +6 -0
- package/dist/group-by-noun.js +17 -0
- package/dist/http.d.ts +26 -0
- package/dist/http.js +123 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +6 -0
- package/dist/interpreter.d.ts +6 -0
- package/dist/interpreter.js +289 -0
- package/dist/lock.d.ts +14 -0
- package/dist/lock.js +48 -0
- package/dist/naming.d.ts +24 -0
- package/dist/naming.js +218 -0
- package/dist/request-shape.d.ts +15 -0
- package/dist/request-shape.js +5 -0
- package/dist/runtime.d.ts +13 -0
- package/dist/runtime.js +94 -0
- package/dist/spec-source.d.ts +11 -0
- package/dist/spec-source.js +63 -0
- package/node_modules/@poe-code/design-system/dist/acp/components.d.ts +11 -0
- package/node_modules/@poe-code/design-system/dist/acp/components.js +121 -0
- package/node_modules/@poe-code/design-system/dist/acp/index.d.ts +3 -0
- package/node_modules/@poe-code/design-system/dist/acp/index.js +2 -0
- package/node_modules/@poe-code/design-system/dist/acp/writer.d.ts +13 -0
- package/node_modules/@poe-code/design-system/dist/acp/writer.js +21 -0
- package/node_modules/@poe-code/design-system/dist/components/command-errors.d.ts +16 -0
- package/node_modules/@poe-code/design-system/dist/components/command-errors.js +22 -0
- package/node_modules/@poe-code/design-system/dist/components/help-formatter.d.ts +20 -0
- package/node_modules/@poe-code/design-system/dist/components/help-formatter.js +27 -0
- package/node_modules/@poe-code/design-system/dist/components/index.d.ts +10 -0
- package/node_modules/@poe-code/design-system/dist/components/index.js +7 -0
- package/node_modules/@poe-code/design-system/dist/components/logger.d.ts +11 -0
- package/node_modules/@poe-code/design-system/dist/components/logger.js +60 -0
- package/node_modules/@poe-code/design-system/dist/components/symbols.d.ts +12 -0
- package/node_modules/@poe-code/design-system/dist/components/symbols.js +71 -0
- package/node_modules/@poe-code/design-system/dist/components/table.d.ts +13 -0
- package/node_modules/@poe-code/design-system/dist/components/table.js +74 -0
- package/node_modules/@poe-code/design-system/dist/components/text.d.ts +14 -0
- package/node_modules/@poe-code/design-system/dist/components/text.js +104 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/ansi.d.ts +18 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/ansi.js +298 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/buffer.d.ts +25 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/buffer.js +189 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/components/border.d.ts +9 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/components/border.js +123 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/components/footer.d.ts +8 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/components/footer.js +57 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/components/output-pane.d.ts +12 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/components/output-pane.js +254 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/components/stats-pane.d.ts +7 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/components/stats-pane.js +121 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/dashboard.d.ts +20 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/dashboard.js +167 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/demo.d.ts +13 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/demo.js +145 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/index.d.ts +8 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/index.js +4 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/keymap.d.ts +3 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/keymap.js +99 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/layout.d.ts +25 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/layout.js +79 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/should-use-dashboard.d.ts +10 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/should-use-dashboard.js +7 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/snapshot.d.ts +10 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/snapshot.js +68 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/store.d.ts +8 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/store.js +51 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/terminal.d.ts +37 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/terminal.js +233 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/types.d.ts +36 -0
- package/node_modules/@poe-code/design-system/dist/dashboard/types.js +1 -0
- package/node_modules/@poe-code/design-system/dist/index.d.ts +33 -0
- package/node_modules/@poe-code/design-system/dist/index.js +31 -0
- package/node_modules/@poe-code/design-system/dist/internal/output-format.d.ts +6 -0
- package/node_modules/@poe-code/design-system/dist/internal/output-format.js +22 -0
- package/node_modules/@poe-code/design-system/dist/internal/strip-ansi.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/internal/strip-ansi.js +3 -0
- package/node_modules/@poe-code/design-system/dist/internal/theme-detect.d.ts +11 -0
- package/node_modules/@poe-code/design-system/dist/internal/theme-detect.js +49 -0
- package/node_modules/@poe-code/design-system/dist/prompts/index.d.ts +66 -0
- package/node_modules/@poe-code/design-system/dist/prompts/index.js +132 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/cancel.d.ts +2 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/cancel.js +9 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/intro.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/intro.js +15 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/log.d.ts +18 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/log.js +101 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/note.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/note.js +39 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/outro.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/outro.js +16 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/spinner.d.ts +6 -0
- package/node_modules/@poe-code/design-system/dist/prompts/primitives/spinner.js +74 -0
- package/node_modules/@poe-code/design-system/dist/prompts/theme.d.ts +11 -0
- package/node_modules/@poe-code/design-system/dist/prompts/theme.js +12 -0
- package/node_modules/@poe-code/design-system/dist/static/index.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/static/index.js +2 -0
- package/node_modules/@poe-code/design-system/dist/static/menu.d.ts +11 -0
- package/node_modules/@poe-code/design-system/dist/static/menu.js +36 -0
- package/node_modules/@poe-code/design-system/dist/static/spinner.d.ts +14 -0
- package/node_modules/@poe-code/design-system/dist/static/spinner.js +46 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/ast.d.ts +92 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/ast.js +1 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/demo-content.d.ts +2 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/demo-content.js +139 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/index.d.ts +6 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/index.js +8 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/block.d.ts +7 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/block.js +1495 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/frontmatter.d.ts +8 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/frontmatter.js +412 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/inline.d.ts +10 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/inline.js +1166 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser.d.ts +5 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser.js +42 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/renderer.d.ts +6 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/renderer.js +572 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/testing/theme-render-fixture.d.ts +1 -0
- package/node_modules/@poe-code/design-system/dist/terminal-markdown/testing/theme-render-fixture.js +27 -0
- package/node_modules/@poe-code/design-system/dist/tokens/colors.d.ts +35 -0
- package/node_modules/@poe-code/design-system/dist/tokens/colors.js +34 -0
- package/node_modules/@poe-code/design-system/dist/tokens/index.d.ts +4 -0
- package/node_modules/@poe-code/design-system/dist/tokens/index.js +4 -0
- package/node_modules/@poe-code/design-system/dist/tokens/spacing.d.ts +6 -0
- package/node_modules/@poe-code/design-system/dist/tokens/spacing.js +6 -0
- package/node_modules/@poe-code/design-system/dist/tokens/typography.d.ts +7 -0
- package/node_modules/@poe-code/design-system/dist/tokens/typography.js +8 -0
- package/node_modules/@poe-code/design-system/dist/tokens/widths.d.ts +5 -0
- package/node_modules/@poe-code/design-system/dist/tokens/widths.js +5 -0
- package/node_modules/@poe-code/design-system/package.json +25 -0
- package/node_modules/auth-store/README.md +47 -0
- package/node_modules/auth-store/dist/create-secret-store.d.ts +2 -0
- package/node_modules/auth-store/dist/create-secret-store.js +35 -0
- package/node_modules/auth-store/dist/encrypted-file-store.d.ts +39 -0
- package/node_modules/auth-store/dist/encrypted-file-store.js +156 -0
- package/node_modules/auth-store/dist/index.d.ts +7 -0
- package/node_modules/auth-store/dist/index.js +4 -0
- package/node_modules/auth-store/dist/keychain-store.d.ts +22 -0
- package/node_modules/auth-store/dist/keychain-store.js +111 -0
- package/node_modules/auth-store/dist/provider-store.d.ts +10 -0
- package/node_modules/auth-store/dist/provider-store.js +28 -0
- package/node_modules/auth-store/dist/types.d.ts +20 -0
- package/node_modules/auth-store/dist/types.js +1 -0
- package/node_modules/auth-store/package.json +25 -0
- package/package.json +48 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { getTheme } from "../../internal/theme-detect.js";
|
|
2
|
+
import { light } from "../../tokens/colors.js";
|
|
3
|
+
export function renderFooter(buffer, rect, hints) {
|
|
4
|
+
buffer.clearRect(rect);
|
|
5
|
+
if (rect.width <= 0 || rect.height <= 0 || hints.length === 0) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const cells = truncateCells(hintsToCells(hints), rect.width);
|
|
9
|
+
const startX = rect.x + Math.floor((rect.width - cells.length) / 2);
|
|
10
|
+
const y = rect.y + Math.floor(rect.height / 2);
|
|
11
|
+
cells.forEach((cell, index) => {
|
|
12
|
+
buffer.put(startX + index, y, cell.ch, cell.style);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
export function defaultHints() {
|
|
16
|
+
return [
|
|
17
|
+
{ key: "q", label: "Quit" },
|
|
18
|
+
{ key: "e", label: "Edit" },
|
|
19
|
+
{ key: "l", label: "Log" },
|
|
20
|
+
{ key: "p", label: "Pause" },
|
|
21
|
+
{ key: "r", label: "Retry" }
|
|
22
|
+
];
|
|
23
|
+
}
|
|
24
|
+
function hintsToCells(hints) {
|
|
25
|
+
const accentStyle = getAccentStyle();
|
|
26
|
+
const cells = [];
|
|
27
|
+
hints.forEach((hint, hintIndex) => {
|
|
28
|
+
if (hintIndex > 0) {
|
|
29
|
+
cells.push({ ch: " ", style: {} }, { ch: " ", style: {} });
|
|
30
|
+
}
|
|
31
|
+
for (const ch of hint.key) {
|
|
32
|
+
cells.push({ ch, style: accentStyle });
|
|
33
|
+
}
|
|
34
|
+
cells.push({ ch: " ", style: {} });
|
|
35
|
+
for (const ch of hint.label) {
|
|
36
|
+
cells.push({ ch, style: {} });
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
return cells;
|
|
40
|
+
}
|
|
41
|
+
function truncateCells(cells, width) {
|
|
42
|
+
if (cells.length <= width) {
|
|
43
|
+
return cells;
|
|
44
|
+
}
|
|
45
|
+
if (width <= 3) {
|
|
46
|
+
return Array.from({ length: Math.max(0, width) }, () => ({ ch: ".", style: {} }));
|
|
47
|
+
}
|
|
48
|
+
return [
|
|
49
|
+
...cells.slice(0, width - 3),
|
|
50
|
+
{ ch: ".", style: {} },
|
|
51
|
+
{ ch: ".", style: {} },
|
|
52
|
+
{ ch: ".", style: {} }
|
|
53
|
+
];
|
|
54
|
+
}
|
|
55
|
+
function getAccentStyle() {
|
|
56
|
+
return getTheme() === light ? { fg: "#006699", bold: true } : { fg: "cyan", bold: true };
|
|
57
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type StyledSegment } from "../ansi.js";
|
|
2
|
+
import { ScreenBuffer } from "../buffer.js";
|
|
3
|
+
import type { CellStyle, OutputItem, Rect } from "../types.js";
|
|
4
|
+
export type VisualLine = {
|
|
5
|
+
text: string;
|
|
6
|
+
style: CellStyle;
|
|
7
|
+
prefix: string;
|
|
8
|
+
prefixStyle: CellStyle;
|
|
9
|
+
segments?: StyledSegment[];
|
|
10
|
+
};
|
|
11
|
+
export declare function renderOutputPane(buffer: ScreenBuffer, rect: Rect, items: OutputItem[]): void;
|
|
12
|
+
export declare function computeVisualLines(items: OutputItem[], width: number): VisualLine[];
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { resolveThemeName } from "../../internal/theme-detect.js";
|
|
2
|
+
import { hasAnsi, parseAnsi } from "../ansi.js";
|
|
3
|
+
const TEXT_OFFSET = 3;
|
|
4
|
+
const CONTINUATION_PREFIX = "│";
|
|
5
|
+
export function renderOutputPane(buffer, rect, items) {
|
|
6
|
+
buffer.clearRect(rect);
|
|
7
|
+
if (rect.width <= 0 || rect.height <= 0) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const visualLines = computeVisualLines(items, rect.width);
|
|
11
|
+
const startLine = Math.max(visualLines.length - rect.height, 0);
|
|
12
|
+
const textRect = {
|
|
13
|
+
x: rect.x + TEXT_OFFSET,
|
|
14
|
+
y: rect.y,
|
|
15
|
+
width: rect.width - TEXT_OFFSET,
|
|
16
|
+
height: rect.height
|
|
17
|
+
};
|
|
18
|
+
for (let row = 0; row < rect.height; row += 1) {
|
|
19
|
+
const line = visualLines[startLine + row];
|
|
20
|
+
if (line === undefined) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
buffer.putInRect(rect, row, line.prefix, line.prefixStyle);
|
|
24
|
+
if (line.segments && line.segments.length > 0) {
|
|
25
|
+
let offsetX = 0;
|
|
26
|
+
for (const segment of line.segments) {
|
|
27
|
+
if (segment.text.length === 0) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const remaining = textRect.width - offsetX;
|
|
31
|
+
if (remaining <= 0) {
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
buffer.putInRect({
|
|
35
|
+
x: textRect.x + offsetX,
|
|
36
|
+
y: textRect.y,
|
|
37
|
+
width: remaining,
|
|
38
|
+
height: textRect.height
|
|
39
|
+
}, row, segment.text, segment.style);
|
|
40
|
+
offsetX += countCells(segment.text);
|
|
41
|
+
}
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
buffer.putInRect(textRect, row, line.text, line.style);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export function computeVisualLines(items, width) {
|
|
48
|
+
if (width <= 0) {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
const themeName = resolveThemeName();
|
|
52
|
+
const mutedStyle = getMutedStyle(themeName);
|
|
53
|
+
const textWidth = Math.max(width - TEXT_OFFSET, 0);
|
|
54
|
+
const visualLines = [];
|
|
55
|
+
for (const item of items) {
|
|
56
|
+
const itemStyle = getItemStyle(item.kind, themeName);
|
|
57
|
+
if (hasAnsi(item.text)) {
|
|
58
|
+
const styledLines = parseAnsi(item.text, {});
|
|
59
|
+
let firstRow = true;
|
|
60
|
+
for (const styledLine of styledLines) {
|
|
61
|
+
const rows = hardWrapSegments(styledLine.segments, textWidth);
|
|
62
|
+
for (const rowSegments of rows) {
|
|
63
|
+
visualLines.push({
|
|
64
|
+
prefix: firstRow ? getPrefix(item.kind) : CONTINUATION_PREFIX,
|
|
65
|
+
prefixStyle: firstRow ? itemStyle : mutedStyle,
|
|
66
|
+
style: itemStyle,
|
|
67
|
+
text: rowSegments.map((segment) => segment.text).join(""),
|
|
68
|
+
segments: rowSegments
|
|
69
|
+
});
|
|
70
|
+
firstRow = false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const wrappedLines = wrapText(item.text, textWidth);
|
|
76
|
+
for (let index = 0; index < wrappedLines.length; index += 1) {
|
|
77
|
+
visualLines.push({
|
|
78
|
+
prefix: index === 0 ? getPrefix(item.kind) : CONTINUATION_PREFIX,
|
|
79
|
+
prefixStyle: index === 0 ? itemStyle : mutedStyle,
|
|
80
|
+
style: itemStyle,
|
|
81
|
+
text: wrappedLines[index] ?? ""
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return visualLines;
|
|
86
|
+
}
|
|
87
|
+
function hardWrapSegments(segments, width) {
|
|
88
|
+
if (width <= 0) {
|
|
89
|
+
return [[]];
|
|
90
|
+
}
|
|
91
|
+
const rows = [[]];
|
|
92
|
+
let rowWidth = 0;
|
|
93
|
+
for (const segment of segments) {
|
|
94
|
+
if (segment.text.length === 0) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const chars = [...segment.text];
|
|
98
|
+
let cursor = 0;
|
|
99
|
+
while (cursor < chars.length) {
|
|
100
|
+
const space = width - rowWidth;
|
|
101
|
+
if (space <= 0) {
|
|
102
|
+
rows.push([]);
|
|
103
|
+
rowWidth = 0;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const take = chars.slice(cursor, cursor + space).join("");
|
|
107
|
+
const currentRow = rows[rows.length - 1];
|
|
108
|
+
currentRow.push({ text: take, style: { ...segment.style } });
|
|
109
|
+
rowWidth += Math.min(space, chars.length - cursor);
|
|
110
|
+
cursor += space;
|
|
111
|
+
if (cursor < chars.length) {
|
|
112
|
+
rows.push([]);
|
|
113
|
+
rowWidth = 0;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return rows;
|
|
118
|
+
}
|
|
119
|
+
function countCells(text) {
|
|
120
|
+
return Array.from(text).length;
|
|
121
|
+
}
|
|
122
|
+
function getPrefix(kind) {
|
|
123
|
+
if (kind === "success") {
|
|
124
|
+
return "◆";
|
|
125
|
+
}
|
|
126
|
+
if (kind === "error") {
|
|
127
|
+
return "■";
|
|
128
|
+
}
|
|
129
|
+
if (kind === "tool") {
|
|
130
|
+
return CONTINUATION_PREFIX;
|
|
131
|
+
}
|
|
132
|
+
if (kind === "status") {
|
|
133
|
+
return "●";
|
|
134
|
+
}
|
|
135
|
+
return "◇";
|
|
136
|
+
}
|
|
137
|
+
function getItemStyle(kind, themeName) {
|
|
138
|
+
if (kind === "success") {
|
|
139
|
+
return themeName === "light" ? { fg: "#008800" } : { fg: "green" };
|
|
140
|
+
}
|
|
141
|
+
if (kind === "error") {
|
|
142
|
+
return themeName === "light" ? { fg: "#cc0000" } : { fg: "red" };
|
|
143
|
+
}
|
|
144
|
+
if (kind === "tool") {
|
|
145
|
+
return getMutedStyle(themeName);
|
|
146
|
+
}
|
|
147
|
+
if (kind === "status") {
|
|
148
|
+
return themeName === "light" ? { fg: "#a200ff" } : { fg: "magenta" };
|
|
149
|
+
}
|
|
150
|
+
return themeName === "light" ? { fg: "#a200ff" } : { fg: "magenta" };
|
|
151
|
+
}
|
|
152
|
+
function getMutedStyle(themeName) {
|
|
153
|
+
return themeName === "light" ? { fg: "#666666" } : { dim: true };
|
|
154
|
+
}
|
|
155
|
+
function wrapText(value, width) {
|
|
156
|
+
const logicalLines = splitLogicalLines(value);
|
|
157
|
+
if (logicalLines.length === 0) {
|
|
158
|
+
return [""];
|
|
159
|
+
}
|
|
160
|
+
if (width <= 0) {
|
|
161
|
+
return logicalLines.map(() => "");
|
|
162
|
+
}
|
|
163
|
+
return logicalLines.flatMap((line) => wrapParagraph(line, width));
|
|
164
|
+
}
|
|
165
|
+
function wrapParagraph(value, width) {
|
|
166
|
+
if (value.length === 0) {
|
|
167
|
+
return [""];
|
|
168
|
+
}
|
|
169
|
+
const tokens = tokenize(value);
|
|
170
|
+
const lines = [];
|
|
171
|
+
let currentLine = "";
|
|
172
|
+
let pendingSpace = "";
|
|
173
|
+
const flushLine = () => {
|
|
174
|
+
lines.push(currentLine);
|
|
175
|
+
currentLine = "";
|
|
176
|
+
pendingSpace = "";
|
|
177
|
+
};
|
|
178
|
+
for (const token of tokens) {
|
|
179
|
+
if (token.kind === "space") {
|
|
180
|
+
if (currentLine.length > 0) {
|
|
181
|
+
pendingSpace += token.value;
|
|
182
|
+
}
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
const chunks = splitWord(token.value, width);
|
|
186
|
+
for (let index = 0; index < chunks.length; index += 1) {
|
|
187
|
+
const chunk = chunks[index] ?? "";
|
|
188
|
+
const gap = index === 0 ? pendingSpace : "";
|
|
189
|
+
if (currentLine.length > 0 && currentLine.length + gap.length + chunk.length > width) {
|
|
190
|
+
flushLine();
|
|
191
|
+
}
|
|
192
|
+
if (currentLine.length > 0 && gap.length > 0) {
|
|
193
|
+
currentLine += gap;
|
|
194
|
+
}
|
|
195
|
+
currentLine += chunk;
|
|
196
|
+
pendingSpace = "";
|
|
197
|
+
if (index < chunks.length - 1) {
|
|
198
|
+
flushLine();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (currentLine.length > 0 || lines.length === 0) {
|
|
203
|
+
lines.push(currentLine);
|
|
204
|
+
}
|
|
205
|
+
return lines;
|
|
206
|
+
}
|
|
207
|
+
function splitWord(value, width) {
|
|
208
|
+
if (value.length <= width) {
|
|
209
|
+
return [value];
|
|
210
|
+
}
|
|
211
|
+
const chunks = [];
|
|
212
|
+
for (let index = 0; index < value.length; index += width) {
|
|
213
|
+
chunks.push(value.slice(index, index + width));
|
|
214
|
+
}
|
|
215
|
+
return chunks;
|
|
216
|
+
}
|
|
217
|
+
function tokenize(value) {
|
|
218
|
+
const tokens = [];
|
|
219
|
+
let current = "";
|
|
220
|
+
let currentKind;
|
|
221
|
+
for (const ch of value) {
|
|
222
|
+
const nextKind = isWrappingSpace(ch) ? "space" : "word";
|
|
223
|
+
if (currentKind !== undefined && currentKind !== nextKind) {
|
|
224
|
+
tokens.push({ kind: currentKind, value: current });
|
|
225
|
+
current = "";
|
|
226
|
+
}
|
|
227
|
+
currentKind = nextKind;
|
|
228
|
+
current += ch;
|
|
229
|
+
}
|
|
230
|
+
if (currentKind !== undefined) {
|
|
231
|
+
tokens.push({ kind: currentKind, value: current });
|
|
232
|
+
}
|
|
233
|
+
return tokens;
|
|
234
|
+
}
|
|
235
|
+
function splitLogicalLines(value) {
|
|
236
|
+
const lines = [];
|
|
237
|
+
let currentLine = "";
|
|
238
|
+
for (const ch of value) {
|
|
239
|
+
if (ch === "\r") {
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
if (ch === "\n") {
|
|
243
|
+
lines.push(currentLine);
|
|
244
|
+
currentLine = "";
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
currentLine += ch;
|
|
248
|
+
}
|
|
249
|
+
lines.push(currentLine);
|
|
250
|
+
return lines;
|
|
251
|
+
}
|
|
252
|
+
function isWrappingSpace(ch) {
|
|
253
|
+
return ch === " " || ch === "\t";
|
|
254
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ScreenBuffer } from "../buffer.js";
|
|
2
|
+
import type { DashboardStats, Rect } from "../types.js";
|
|
3
|
+
import type { VisualLine } from "./output-pane.js";
|
|
4
|
+
export declare function renderStatsPane(buffer: ScreenBuffer, rect: Rect, stats: DashboardStats): void;
|
|
5
|
+
export declare function formatElapsed(ms: number): string;
|
|
6
|
+
export declare function formatNumber(n: number): string;
|
|
7
|
+
export declare function statsToLines(stats: DashboardStats, width: number): VisualLine[];
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { getTheme } from "../../internal/theme-detect.js";
|
|
2
|
+
import { light } from "../../tokens/colors.js";
|
|
3
|
+
export function renderStatsPane(buffer, rect, stats) {
|
|
4
|
+
buffer.clearRect(rect);
|
|
5
|
+
if (rect.width <= 0 || rect.height <= 0) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const lines = statsToLines(stats, rect.width);
|
|
9
|
+
for (let row = 0; row < rect.height; row += 1) {
|
|
10
|
+
const line = lines[row];
|
|
11
|
+
if (line === undefined) {
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
if (line.prefix.length > 0) {
|
|
15
|
+
buffer.putInRect(rect, row, line.prefix, line.prefixStyle);
|
|
16
|
+
}
|
|
17
|
+
if (line.text.length === 0) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
const textStart = Math.min(line.prefix.length, rect.width);
|
|
21
|
+
buffer.putInRect({ x: rect.x + textStart, y: rect.y + row, width: rect.width - textStart, height: 1 }, 0, line.text, line.style);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function formatElapsed(ms) {
|
|
25
|
+
const safeMs = Number.isFinite(ms) ? ms : 0;
|
|
26
|
+
const totalSeconds = Math.max(0, Math.floor(safeMs / 1_000));
|
|
27
|
+
const hours = Math.floor(totalSeconds / 3_600);
|
|
28
|
+
const minutes = Math.floor((totalSeconds % 3_600) / 60);
|
|
29
|
+
const seconds = totalSeconds % 60;
|
|
30
|
+
return [hours, minutes, seconds].map((value) => value.toString().padStart(2, "0")).join(":");
|
|
31
|
+
}
|
|
32
|
+
export function formatNumber(n) {
|
|
33
|
+
return new Intl.NumberFormat("en-US").format(n);
|
|
34
|
+
}
|
|
35
|
+
export function statsToLines(stats, width) {
|
|
36
|
+
if (width <= 0) {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
const mutedStyle = getToneStyle("muted");
|
|
40
|
+
const totalTokens = stats.tokensIn + stats.tokensOut;
|
|
41
|
+
const iterationsLabel = stats.iterationsLabel ?? "Iteration";
|
|
42
|
+
const lines = [
|
|
43
|
+
createKeyValueLine("Status", formatStatus(stats.status), width, getStatusStyle(stats.status)),
|
|
44
|
+
createKeyValueLine(iterationsLabel, formatNumber(stats.iterations), width),
|
|
45
|
+
createKeyValueLine("Elapsed", formatElapsed(stats.elapsedMs), width),
|
|
46
|
+
createBlankLine(),
|
|
47
|
+
createKeyValueLine("Tokens In", formatNumber(stats.tokensIn), width),
|
|
48
|
+
createKeyValueLine("Tokens Out", formatNumber(stats.tokensOut), width),
|
|
49
|
+
createKeyValueLine("Total", formatNumber(totalTokens), width)
|
|
50
|
+
];
|
|
51
|
+
if (stats.currentAction !== undefined) {
|
|
52
|
+
lines.push(createBlankLine(), {
|
|
53
|
+
prefix: clipText("Current:", width),
|
|
54
|
+
prefixStyle: {},
|
|
55
|
+
style: {},
|
|
56
|
+
text: ""
|
|
57
|
+
}, {
|
|
58
|
+
prefix: width > 0 ? clipText(" ", width) : "",
|
|
59
|
+
prefixStyle: mutedStyle,
|
|
60
|
+
style: mutedStyle,
|
|
61
|
+
text: clipText(stats.currentAction, Math.max(width - 2, 0))
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return lines;
|
|
65
|
+
}
|
|
66
|
+
function createBlankLine() {
|
|
67
|
+
return {
|
|
68
|
+
prefix: "",
|
|
69
|
+
prefixStyle: {},
|
|
70
|
+
style: {},
|
|
71
|
+
text: ""
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function createKeyValueLine(label, value, width, valueStyle = {}) {
|
|
75
|
+
const clippedValue = clipText(value, width);
|
|
76
|
+
const availableBeforeValue = Math.max(width - clippedValue.length, 0);
|
|
77
|
+
const clippedLabel = clipText(label, Math.max(availableBeforeValue - 1, 0));
|
|
78
|
+
return {
|
|
79
|
+
prefix: clippedLabel + " ".repeat(Math.max(availableBeforeValue - clippedLabel.length, 0)),
|
|
80
|
+
prefixStyle: {},
|
|
81
|
+
style: valueStyle,
|
|
82
|
+
text: clippedValue
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function clipText(value, width) {
|
|
86
|
+
return width <= 0 ? "" : value.slice(0, width);
|
|
87
|
+
}
|
|
88
|
+
function formatStatus(status) {
|
|
89
|
+
return `${status.slice(0, 1).toUpperCase()}${status.slice(1)}`;
|
|
90
|
+
}
|
|
91
|
+
function getStatusStyle(status) {
|
|
92
|
+
if (status === "running") {
|
|
93
|
+
return getToneStyle("info");
|
|
94
|
+
}
|
|
95
|
+
if (status === "paused") {
|
|
96
|
+
return getToneStyle("warning");
|
|
97
|
+
}
|
|
98
|
+
if (status === "error") {
|
|
99
|
+
return getToneStyle("error");
|
|
100
|
+
}
|
|
101
|
+
if (status === "done") {
|
|
102
|
+
return getToneStyle("success");
|
|
103
|
+
}
|
|
104
|
+
return getToneStyle("muted");
|
|
105
|
+
}
|
|
106
|
+
function getToneStyle(tone) {
|
|
107
|
+
const isLightTheme = getTheme() === light;
|
|
108
|
+
if (tone === "muted") {
|
|
109
|
+
return isLightTheme ? { fg: "#666666" } : { dim: true };
|
|
110
|
+
}
|
|
111
|
+
if (tone === "info") {
|
|
112
|
+
return isLightTheme ? { fg: "#a200ff" } : { fg: "magenta" };
|
|
113
|
+
}
|
|
114
|
+
if (tone === "warning") {
|
|
115
|
+
return isLightTheme ? { fg: "#cc6600" } : { fg: "yellow" };
|
|
116
|
+
}
|
|
117
|
+
if (tone === "error") {
|
|
118
|
+
return isLightTheme ? { fg: "#cc0000" } : { fg: "red" };
|
|
119
|
+
}
|
|
120
|
+
return isLightTheme ? { fg: "#008800" } : { fg: "green" };
|
|
121
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { FooterHint } from "./components/footer.js";
|
|
2
|
+
import type { Command, DashboardStats, OutputItem } from "./types.js";
|
|
3
|
+
export type DashboardOptions = {
|
|
4
|
+
title?: string;
|
|
5
|
+
statsTitle?: string;
|
|
6
|
+
keymap?: Partial<Record<Command, string[]>>;
|
|
7
|
+
rightPaneWidth?: number;
|
|
8
|
+
hints?: FooterHint[];
|
|
9
|
+
stdin?: NodeJS.ReadStream;
|
|
10
|
+
stdout?: NodeJS.WriteStream;
|
|
11
|
+
};
|
|
12
|
+
export type Dashboard = {
|
|
13
|
+
start(): void;
|
|
14
|
+
stop(): void;
|
|
15
|
+
appendOutput(item: OutputItem): void;
|
|
16
|
+
updateStats(stats: Partial<DashboardStats>): void;
|
|
17
|
+
onCommand(handler: (cmd: Command) => void): void;
|
|
18
|
+
destroy(): void;
|
|
19
|
+
};
|
|
20
|
+
export declare function createDashboard(opts?: DashboardOptions): Dashboard;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { createLogger } from "../components/logger.js";
|
|
2
|
+
import { resolveOutputFormat } from "../internal/output-format.js";
|
|
3
|
+
import { ScreenBuffer, diff } from "./buffer.js";
|
|
4
|
+
import { renderBorder } from "./components/border.js";
|
|
5
|
+
import { defaultHints, renderFooter } from "./components/footer.js";
|
|
6
|
+
import { renderOutputPane } from "./components/output-pane.js";
|
|
7
|
+
import { renderStatsPane } from "./components/stats-pane.js";
|
|
8
|
+
import { createKeymap } from "./keymap.js";
|
|
9
|
+
import { computeDashboardLayout } from "./layout.js";
|
|
10
|
+
import { createStore } from "./store.js";
|
|
11
|
+
import { createTerminalDriver } from "./terminal.js";
|
|
12
|
+
const DEFAULT_TITLE = "Output";
|
|
13
|
+
const DEFAULT_STATS_TITLE = "Stats";
|
|
14
|
+
const DEFAULT_RIGHT_PANE_WIDTH = 25;
|
|
15
|
+
export function createDashboard(opts = {}) {
|
|
16
|
+
const stdin = opts.stdin ?? process.stdin;
|
|
17
|
+
const stdout = opts.stdout ?? process.stdout;
|
|
18
|
+
const resolveCommand = createKeymap(opts.keymap);
|
|
19
|
+
const footerHints = opts.hints ?? defaultHints();
|
|
20
|
+
const title = opts.title ?? DEFAULT_TITLE;
|
|
21
|
+
const statsTitle = opts.statsTitle ?? DEFAULT_STATS_TITLE;
|
|
22
|
+
const rightPaneWidth = opts.rightPaneWidth ?? DEFAULT_RIGHT_PANE_WIDTH;
|
|
23
|
+
const commandHandlers = new Set();
|
|
24
|
+
const fallbackLogger = createLogger((message) => {
|
|
25
|
+
stdout.write(`${message}\n`);
|
|
26
|
+
});
|
|
27
|
+
let driver;
|
|
28
|
+
let store;
|
|
29
|
+
let previousBuffer = new ScreenBuffer(0, 0);
|
|
30
|
+
let unsubscribeStore;
|
|
31
|
+
let unsubscribeKeypress;
|
|
32
|
+
let unsubscribeResize;
|
|
33
|
+
let started = false;
|
|
34
|
+
let destroyed = false;
|
|
35
|
+
function appendOutput(item) {
|
|
36
|
+
if (destroyed) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (!isTerminalMode()) {
|
|
40
|
+
writeFallbackOutput(item);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
getStore().appendOutput(item);
|
|
44
|
+
}
|
|
45
|
+
function updateStats(stats) {
|
|
46
|
+
if (destroyed || !isTerminalMode()) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
getStore().updateStats(stats);
|
|
50
|
+
}
|
|
51
|
+
function start() {
|
|
52
|
+
if (destroyed || started || !isTerminalMode()) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
driver = createTerminalDriver({ stdin, stdout });
|
|
56
|
+
started = true;
|
|
57
|
+
previousBuffer = new ScreenBuffer(0, 0);
|
|
58
|
+
driver.enterRawMode();
|
|
59
|
+
driver.enterAltScreen();
|
|
60
|
+
driver.disableLineWrap();
|
|
61
|
+
driver.hideCursor();
|
|
62
|
+
render();
|
|
63
|
+
const activeStore = getStore();
|
|
64
|
+
unsubscribeStore = activeStore.onChange(() => {
|
|
65
|
+
render();
|
|
66
|
+
});
|
|
67
|
+
unsubscribeKeypress = driver.onKeypress((event) => {
|
|
68
|
+
const command = resolveCommand(event);
|
|
69
|
+
if (command === undefined) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
emitCommand(command);
|
|
73
|
+
});
|
|
74
|
+
unsubscribeResize = driver.onResize(() => {
|
|
75
|
+
render();
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function stop() {
|
|
79
|
+
unsubscribeStore?.();
|
|
80
|
+
unsubscribeKeypress?.();
|
|
81
|
+
unsubscribeResize?.();
|
|
82
|
+
unsubscribeStore = undefined;
|
|
83
|
+
unsubscribeKeypress = undefined;
|
|
84
|
+
unsubscribeResize = undefined;
|
|
85
|
+
if (driver === undefined) {
|
|
86
|
+
started = false;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
driver.destroy();
|
|
90
|
+
driver = undefined;
|
|
91
|
+
previousBuffer = new ScreenBuffer(0, 0);
|
|
92
|
+
started = false;
|
|
93
|
+
}
|
|
94
|
+
function onCommand(handler) {
|
|
95
|
+
if (destroyed) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
commandHandlers.add(handler);
|
|
99
|
+
}
|
|
100
|
+
function destroy() {
|
|
101
|
+
if (destroyed) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
stop();
|
|
105
|
+
commandHandlers.clear();
|
|
106
|
+
store = undefined;
|
|
107
|
+
destroyed = true;
|
|
108
|
+
}
|
|
109
|
+
function getStore() {
|
|
110
|
+
store ??= createStore();
|
|
111
|
+
return store;
|
|
112
|
+
}
|
|
113
|
+
function render() {
|
|
114
|
+
if (driver === undefined) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const { cols, rows } = driver.getSize();
|
|
118
|
+
const layout = computeDashboardLayout({
|
|
119
|
+
totalWidth: cols,
|
|
120
|
+
totalHeight: rows,
|
|
121
|
+
rightPaneWidth
|
|
122
|
+
});
|
|
123
|
+
const nextBuffer = new ScreenBuffer(cols, rows);
|
|
124
|
+
const state = getStore().getState();
|
|
125
|
+
renderBorder(nextBuffer, layout, {
|
|
126
|
+
leftTitle: title,
|
|
127
|
+
rightTitle: statsTitle,
|
|
128
|
+
style: { dim: true }
|
|
129
|
+
});
|
|
130
|
+
renderOutputPane(nextBuffer, layout.leftPane, state.output);
|
|
131
|
+
renderStatsPane(nextBuffer, layout.rightPane, state.stats);
|
|
132
|
+
renderFooter(nextBuffer, layout.footer, footerHints);
|
|
133
|
+
driver.flush(diff(previousBuffer, nextBuffer));
|
|
134
|
+
previousBuffer = nextBuffer;
|
|
135
|
+
}
|
|
136
|
+
function emitCommand(command) {
|
|
137
|
+
for (const handler of commandHandlers) {
|
|
138
|
+
handler(command);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function writeFallbackOutput(item) {
|
|
142
|
+
if (item.kind === "success") {
|
|
143
|
+
fallbackLogger.success(item.text);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (item.kind === "error") {
|
|
147
|
+
fallbackLogger.error(item.text);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (item.kind === "tool") {
|
|
151
|
+
fallbackLogger.message(item.text);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
fallbackLogger.info(item.text);
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
start,
|
|
158
|
+
stop,
|
|
159
|
+
appendOutput,
|
|
160
|
+
updateStats,
|
|
161
|
+
onCommand,
|
|
162
|
+
destroy
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function isTerminalMode() {
|
|
166
|
+
return resolveOutputFormat() === "terminal";
|
|
167
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Dashboard } from "./dashboard.js";
|
|
2
|
+
type DemoDashboard = Pick<Dashboard, "appendOutput" | "updateStats">;
|
|
3
|
+
type DemoRuntime = {
|
|
4
|
+
setInterval: typeof globalThis.setInterval;
|
|
5
|
+
clearInterval: typeof globalThis.clearInterval;
|
|
6
|
+
setTimeout: typeof globalThis.setTimeout;
|
|
7
|
+
clearTimeout: typeof globalThis.clearTimeout;
|
|
8
|
+
now: () => number;
|
|
9
|
+
random: () => number;
|
|
10
|
+
};
|
|
11
|
+
export declare function startDashboardDemo(dashboard: DemoDashboard, runtime?: Partial<DemoRuntime>): () => void;
|
|
12
|
+
export declare function main(): Promise<void>;
|
|
13
|
+
export {};
|