poe-code 3.0.178 → 3.0.179
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/index.js +6 -2
- package/dist/index.js.map +2 -2
- package/package.json +6 -2
- package/packages/cmdkit/dist/cli.compile-check.d.ts +1 -0
- package/packages/cmdkit/dist/cli.compile-check.js +26 -0
- package/packages/cmdkit/dist/cli.d.ts +12 -0
- package/packages/cmdkit/dist/cli.js +2306 -0
- package/packages/cmdkit/dist/cli.js.map +7 -0
- package/packages/cmdkit/dist/index.compile-check.d.ts +1 -0
- package/packages/cmdkit/dist/index.compile-check.js +50 -0
- package/packages/cmdkit/dist/index.d.ts +164 -0
- package/packages/cmdkit/dist/index.js +518 -0
- package/packages/cmdkit/dist/index.js.map +7 -0
- package/packages/cmdkit/dist/mcp.compile-check.d.ts +1 -0
- package/packages/cmdkit/dist/mcp.compile-check.js +26 -0
- package/packages/cmdkit/dist/mcp.d.ts +16 -0
- package/packages/cmdkit/dist/mcp.js +1153 -0
- package/packages/cmdkit/dist/mcp.js.map +7 -0
- package/packages/cmdkit/dist/renderer.d.ts +5 -0
- package/packages/cmdkit/dist/renderer.js +164 -0
- package/packages/cmdkit/dist/renderer.js.map +7 -0
- package/packages/cmdkit/dist/sdk.compile-check.d.ts +1 -0
- package/packages/cmdkit/dist/sdk.compile-check.js +79 -0
- package/packages/cmdkit/dist/sdk.d.ts +63 -0
- package/packages/cmdkit/dist/sdk.js +314 -0
- package/packages/cmdkit/dist/sdk.js.map +7 -0
- package/packages/cmdkit-schema/dist/index.compile-check.d.ts +1 -0
- package/packages/cmdkit-schema/dist/index.compile-check.js +11 -0
- package/packages/cmdkit-schema/dist/index.d.ts +81 -0
- package/packages/cmdkit-schema/dist/index.js +130 -0
- package/packages/design-system/dist/acp/components.d.ts +11 -0
- package/packages/design-system/dist/acp/components.js +121 -0
- package/packages/design-system/dist/acp/index.d.ts +3 -0
- package/packages/design-system/dist/acp/index.js +2 -0
- package/packages/design-system/dist/acp/writer.d.ts +13 -0
- package/packages/design-system/dist/acp/writer.js +21 -0
- package/packages/design-system/dist/components/command-errors.d.ts +16 -0
- package/packages/design-system/dist/components/command-errors.js +22 -0
- package/packages/design-system/dist/components/help-formatter.d.ts +20 -0
- package/packages/design-system/dist/components/help-formatter.js +27 -0
- package/packages/design-system/dist/components/index.d.ts +10 -0
- package/packages/design-system/dist/components/index.js +7 -0
- package/packages/design-system/dist/components/logger.d.ts +11 -0
- package/packages/design-system/dist/components/logger.js +60 -0
- package/packages/design-system/dist/components/symbols.d.ts +12 -0
- package/packages/design-system/dist/components/symbols.js +71 -0
- package/packages/design-system/dist/components/table.d.ts +13 -0
- package/packages/design-system/dist/components/table.js +74 -0
- package/packages/design-system/dist/components/text.d.ts +14 -0
- package/packages/design-system/dist/components/text.js +104 -0
- package/packages/design-system/dist/dashboard/ansi.d.ts +18 -0
- package/packages/design-system/dist/dashboard/ansi.js +298 -0
- package/packages/design-system/dist/dashboard/buffer.d.ts +25 -0
- package/packages/design-system/dist/dashboard/buffer.js +189 -0
- package/packages/design-system/dist/dashboard/components/border.d.ts +9 -0
- package/packages/design-system/dist/dashboard/components/border.js +123 -0
- package/packages/design-system/dist/dashboard/components/footer.d.ts +8 -0
- package/packages/design-system/dist/dashboard/components/footer.js +58 -0
- package/packages/design-system/dist/dashboard/components/output-pane.d.ts +21 -0
- package/packages/design-system/dist/dashboard/components/output-pane.js +323 -0
- package/packages/design-system/dist/dashboard/components/stats-pane.d.ts +7 -0
- package/packages/design-system/dist/dashboard/components/stats-pane.js +120 -0
- package/packages/design-system/dist/dashboard/dashboard.d.ts +20 -0
- package/packages/design-system/dist/dashboard/dashboard.js +187 -0
- package/packages/design-system/dist/dashboard/demo.d.ts +13 -0
- package/packages/design-system/dist/dashboard/demo.js +145 -0
- package/packages/design-system/dist/dashboard/index.d.ts +7 -0
- package/packages/design-system/dist/dashboard/index.js +3 -0
- package/packages/design-system/dist/dashboard/keymap.d.ts +3 -0
- package/packages/design-system/dist/dashboard/keymap.js +114 -0
- package/packages/design-system/dist/dashboard/layout.d.ts +25 -0
- package/packages/design-system/dist/dashboard/layout.js +79 -0
- package/packages/design-system/dist/dashboard/snapshot.d.ts +10 -0
- package/packages/design-system/dist/dashboard/snapshot.js +72 -0
- package/packages/design-system/dist/dashboard/store.d.ts +9 -0
- package/packages/design-system/dist/dashboard/store.js +107 -0
- package/packages/design-system/dist/dashboard/terminal.d.ts +35 -0
- package/packages/design-system/dist/dashboard/terminal.js +215 -0
- package/packages/design-system/dist/dashboard/types.d.ts +45 -0
- package/packages/design-system/dist/dashboard/types.js +1 -0
- package/packages/design-system/dist/index.d.ts +33 -0
- package/packages/design-system/dist/index.js +31 -0
- package/packages/design-system/dist/internal/output-format.d.ts +6 -0
- package/packages/design-system/dist/internal/output-format.js +22 -0
- package/packages/design-system/dist/internal/strip-ansi.d.ts +1 -0
- package/packages/design-system/dist/internal/strip-ansi.js +3 -0
- package/packages/design-system/dist/internal/theme-detect.d.ts +11 -0
- package/packages/design-system/dist/internal/theme-detect.js +49 -0
- package/packages/design-system/dist/prompts/index.d.ts +66 -0
- package/packages/design-system/dist/prompts/index.js +132 -0
- package/packages/design-system/dist/prompts/primitives/cancel.d.ts +2 -0
- package/packages/design-system/dist/prompts/primitives/cancel.js +9 -0
- package/packages/design-system/dist/prompts/primitives/intro.d.ts +1 -0
- package/packages/design-system/dist/prompts/primitives/intro.js +15 -0
- package/packages/design-system/dist/prompts/primitives/log.d.ts +18 -0
- package/packages/design-system/dist/prompts/primitives/log.js +101 -0
- package/packages/design-system/dist/prompts/primitives/note.d.ts +1 -0
- package/packages/design-system/dist/prompts/primitives/note.js +39 -0
- package/packages/design-system/dist/prompts/primitives/outro.d.ts +1 -0
- package/packages/design-system/dist/prompts/primitives/outro.js +16 -0
- package/packages/design-system/dist/prompts/primitives/spinner.d.ts +6 -0
- package/packages/design-system/dist/prompts/primitives/spinner.js +74 -0
- package/packages/design-system/dist/prompts/theme.d.ts +11 -0
- package/packages/design-system/dist/prompts/theme.js +12 -0
- package/packages/design-system/dist/static/index.d.ts +4 -0
- package/packages/design-system/dist/static/index.js +2 -0
- package/packages/design-system/dist/static/menu.d.ts +11 -0
- package/packages/design-system/dist/static/menu.js +36 -0
- package/packages/design-system/dist/static/spinner.d.ts +14 -0
- package/packages/design-system/dist/static/spinner.js +46 -0
- package/packages/design-system/dist/terminal-markdown/ast.d.ts +84 -0
- package/packages/design-system/dist/terminal-markdown/ast.js +1 -0
- package/packages/design-system/dist/terminal-markdown/demo-content.d.ts +2 -0
- package/packages/design-system/dist/terminal-markdown/demo-content.js +139 -0
- package/packages/design-system/dist/terminal-markdown/index.d.ts +6 -0
- package/packages/design-system/dist/terminal-markdown/index.js +8 -0
- package/packages/design-system/dist/terminal-markdown/parser/block.d.ts +6 -0
- package/packages/design-system/dist/terminal-markdown/parser/block.js +1205 -0
- package/packages/design-system/dist/terminal-markdown/parser/frontmatter.d.ts +6 -0
- package/packages/design-system/dist/terminal-markdown/parser/frontmatter.js +395 -0
- package/packages/design-system/dist/terminal-markdown/parser/inline.d.ts +6 -0
- package/packages/design-system/dist/terminal-markdown/parser/inline.js +1087 -0
- package/packages/design-system/dist/terminal-markdown/parser.d.ts +5 -0
- package/packages/design-system/dist/terminal-markdown/parser.js +13 -0
- package/packages/design-system/dist/terminal-markdown/renderer.d.ts +6 -0
- package/packages/design-system/dist/terminal-markdown/renderer.js +572 -0
- package/packages/design-system/dist/terminal-markdown/testing/theme-render-fixture.d.ts +1 -0
- package/packages/design-system/dist/terminal-markdown/testing/theme-render-fixture.js +27 -0
- package/packages/design-system/dist/tokens/colors.d.ts +35 -0
- package/packages/design-system/dist/tokens/colors.js +34 -0
- package/packages/design-system/dist/tokens/index.d.ts +4 -0
- package/packages/design-system/dist/tokens/index.js +4 -0
- package/packages/design-system/dist/tokens/spacing.d.ts +6 -0
- package/packages/design-system/dist/tokens/spacing.js +6 -0
- package/packages/design-system/dist/tokens/typography.d.ts +7 -0
- package/packages/design-system/dist/tokens/typography.js +8 -0
- package/packages/design-system/dist/tokens/widths.d.ts +5 -0
- package/packages/design-system/dist/tokens/widths.js +5 -0
- package/packages/tiny-stdio-mcp-server/dist/content/audio.d.ts +14 -0
- package/packages/tiny-stdio-mcp-server/dist/content/audio.js +76 -0
- package/packages/tiny-stdio-mcp-server/dist/content/convert.d.ts +16 -0
- package/packages/tiny-stdio-mcp-server/dist/content/convert.js +42 -0
- package/packages/tiny-stdio-mcp-server/dist/content/file-type.d.ts +11 -0
- package/packages/tiny-stdio-mcp-server/dist/content/file-type.js +93 -0
- package/packages/tiny-stdio-mcp-server/dist/content/file.d.ts +26 -0
- package/packages/tiny-stdio-mcp-server/dist/content/file.js +94 -0
- package/packages/tiny-stdio-mcp-server/dist/content/image.d.ts +14 -0
- package/packages/tiny-stdio-mcp-server/dist/content/image.js +64 -0
- package/packages/tiny-stdio-mcp-server/dist/content/index.d.ts +5 -0
- package/packages/tiny-stdio-mcp-server/dist/content/index.js +8 -0
- package/packages/tiny-stdio-mcp-server/dist/content/mime.d.ts +1 -0
- package/packages/tiny-stdio-mcp-server/dist/content/mime.js +1 -0
- package/packages/tiny-stdio-mcp-server/dist/index.d.ts +9 -0
- package/packages/tiny-stdio-mcp-server/dist/index.js +7 -0
- package/packages/tiny-stdio-mcp-server/dist/jsonrpc.d.ts +14 -0
- package/packages/tiny-stdio-mcp-server/dist/jsonrpc.js +99 -0
- package/packages/tiny-stdio-mcp-server/dist/schema.d.ts +19 -0
- package/packages/tiny-stdio-mcp-server/dist/schema.js +18 -0
- package/packages/tiny-stdio-mcp-server/dist/server.d.ts +13 -0
- package/packages/tiny-stdio-mcp-server/dist/server.js +226 -0
- package/packages/tiny-stdio-mcp-server/dist/testing.d.ts +7 -0
- package/packages/tiny-stdio-mcp-server/dist/testing.js +20 -0
- package/packages/tiny-stdio-mcp-server/dist/types.d.ts +119 -0
- package/packages/tiny-stdio-mcp-server/dist/types.js +16 -0
|
@@ -0,0 +1,58 @@
|
|
|
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: "p", label: "Pause" },
|
|
20
|
+
{ key: "r", label: "Retry" },
|
|
21
|
+
{ key: "↑↓", label: "Scroll" },
|
|
22
|
+
{ key: "F", label: "Follow" }
|
|
23
|
+
];
|
|
24
|
+
}
|
|
25
|
+
function hintsToCells(hints) {
|
|
26
|
+
const accentStyle = getAccentStyle();
|
|
27
|
+
const cells = [];
|
|
28
|
+
hints.forEach((hint, hintIndex) => {
|
|
29
|
+
if (hintIndex > 0) {
|
|
30
|
+
cells.push({ ch: " ", style: {} }, { ch: " ", style: {} });
|
|
31
|
+
}
|
|
32
|
+
for (const ch of hint.key) {
|
|
33
|
+
cells.push({ ch, style: accentStyle });
|
|
34
|
+
}
|
|
35
|
+
cells.push({ ch: " ", style: {} });
|
|
36
|
+
for (const ch of hint.label) {
|
|
37
|
+
cells.push({ ch, style: {} });
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
return cells;
|
|
41
|
+
}
|
|
42
|
+
function truncateCells(cells, width) {
|
|
43
|
+
if (cells.length <= width) {
|
|
44
|
+
return cells;
|
|
45
|
+
}
|
|
46
|
+
if (width <= 3) {
|
|
47
|
+
return Array.from({ length: Math.max(0, width) }, () => ({ ch: ".", style: {} }));
|
|
48
|
+
}
|
|
49
|
+
return [
|
|
50
|
+
...cells.slice(0, width - 3),
|
|
51
|
+
{ ch: ".", style: {} },
|
|
52
|
+
{ ch: ".", style: {} },
|
|
53
|
+
{ ch: ".", style: {} }
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
function getAccentStyle() {
|
|
57
|
+
return getTheme() === light ? { fg: "#006699", bold: true } : { fg: "cyan", bold: true };
|
|
58
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
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 OutputPaneState = {
|
|
5
|
+
items: OutputItem[];
|
|
6
|
+
scrollOffset: number;
|
|
7
|
+
autoFollow: boolean;
|
|
8
|
+
};
|
|
9
|
+
export type VisualLine = {
|
|
10
|
+
text: string;
|
|
11
|
+
style: CellStyle;
|
|
12
|
+
prefix: string;
|
|
13
|
+
prefixStyle: CellStyle;
|
|
14
|
+
segments?: StyledSegment[];
|
|
15
|
+
};
|
|
16
|
+
export declare function renderOutputPane(buffer: ScreenBuffer, rect: Rect, state: OutputPaneState): void;
|
|
17
|
+
export declare function computeVisualLines(items: OutputItem[], width: number): VisualLine[];
|
|
18
|
+
export declare function scrollUp(state: OutputPaneState, lines: number): OutputPaneState;
|
|
19
|
+
export declare function scrollDown(state: OutputPaneState, lines: number, totalVisualLines: number): OutputPaneState;
|
|
20
|
+
export declare function scrollToTop(state: OutputPaneState): OutputPaneState;
|
|
21
|
+
export declare function scrollToBottom(state: OutputPaneState, totalVisualLines: number, paneHeight: number): OutputPaneState;
|
|
@@ -0,0 +1,323 @@
|
|
|
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, state) {
|
|
6
|
+
buffer.clearRect(rect);
|
|
7
|
+
if (rect.width <= 0 || rect.height <= 0) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const visualLines = computeVisualLines(state.items, rect.width);
|
|
11
|
+
const showBanner = !state.autoFollow && rect.height >= 2;
|
|
12
|
+
const contentHeight = showBanner ? rect.height - 1 : rect.height;
|
|
13
|
+
const startLine = state.autoFollow
|
|
14
|
+
? Math.max(visualLines.length - contentHeight, 0)
|
|
15
|
+
: clampScrollOffset(state.scrollOffset, visualLines.length);
|
|
16
|
+
const textRect = {
|
|
17
|
+
x: rect.x + TEXT_OFFSET,
|
|
18
|
+
y: rect.y,
|
|
19
|
+
width: rect.width - TEXT_OFFSET,
|
|
20
|
+
height: contentHeight
|
|
21
|
+
};
|
|
22
|
+
for (let row = 0; row < contentHeight; row += 1) {
|
|
23
|
+
const line = visualLines[startLine + row];
|
|
24
|
+
if (line === undefined) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
buffer.putInRect(rect, row, line.prefix, line.prefixStyle);
|
|
28
|
+
if (line.segments && line.segments.length > 0) {
|
|
29
|
+
let offsetX = 0;
|
|
30
|
+
for (const segment of line.segments) {
|
|
31
|
+
if (segment.text.length === 0) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const remaining = textRect.width - offsetX;
|
|
35
|
+
if (remaining <= 0) {
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
buffer.putInRect({
|
|
39
|
+
x: textRect.x + offsetX,
|
|
40
|
+
y: textRect.y,
|
|
41
|
+
width: remaining,
|
|
42
|
+
height: textRect.height
|
|
43
|
+
}, row, segment.text, segment.style);
|
|
44
|
+
offsetX += countCells(segment.text);
|
|
45
|
+
}
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
buffer.putInRect(textRect, row, line.text, line.style);
|
|
49
|
+
}
|
|
50
|
+
if (showBanner) {
|
|
51
|
+
const hiddenBelow = Math.max(0, visualLines.length - (startLine + contentHeight));
|
|
52
|
+
renderFollowBanner(buffer, rect, hiddenBelow);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function renderFollowBanner(buffer, rect, hiddenBelow) {
|
|
56
|
+
const bannerRow = rect.height - 1;
|
|
57
|
+
const themeName = resolveThemeName();
|
|
58
|
+
const keyStyle = themeName === "light"
|
|
59
|
+
? { fg: "#006699", bold: true }
|
|
60
|
+
: { fg: "cyan", bold: true };
|
|
61
|
+
const labelStyle = getMutedStyle(themeName);
|
|
62
|
+
const message = hiddenBelow > 0
|
|
63
|
+
? `↓ F to follow · ${hiddenBelow} more`
|
|
64
|
+
: "↓ F to follow";
|
|
65
|
+
const cells = buildBannerCells(message, keyStyle, labelStyle);
|
|
66
|
+
const clipped = cells.slice(0, rect.width);
|
|
67
|
+
const startX = rect.x + Math.max(0, Math.floor((rect.width - clipped.length) / 2));
|
|
68
|
+
const y = rect.y + bannerRow;
|
|
69
|
+
for (let index = 0; index < clipped.length; index += 1) {
|
|
70
|
+
const cell = clipped[index];
|
|
71
|
+
buffer.put(startX + index, y, cell.ch, cell.style);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function buildBannerCells(message, keyStyle, labelStyle) {
|
|
75
|
+
const cells = [];
|
|
76
|
+
for (const ch of message) {
|
|
77
|
+
cells.push({ ch, style: ch === "F" ? keyStyle : labelStyle });
|
|
78
|
+
}
|
|
79
|
+
return cells;
|
|
80
|
+
}
|
|
81
|
+
export function computeVisualLines(items, width) {
|
|
82
|
+
if (width <= 0) {
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
const themeName = resolveThemeName();
|
|
86
|
+
const mutedStyle = getMutedStyle(themeName);
|
|
87
|
+
const textWidth = Math.max(width - TEXT_OFFSET, 0);
|
|
88
|
+
const visualLines = [];
|
|
89
|
+
for (const item of items) {
|
|
90
|
+
const itemStyle = getItemStyle(item.kind, themeName);
|
|
91
|
+
if (hasAnsi(item.text)) {
|
|
92
|
+
const styledLines = parseAnsi(item.text, {});
|
|
93
|
+
let firstRow = true;
|
|
94
|
+
for (const styledLine of styledLines) {
|
|
95
|
+
const rows = hardWrapSegments(styledLine.segments, textWidth);
|
|
96
|
+
for (const rowSegments of rows) {
|
|
97
|
+
visualLines.push({
|
|
98
|
+
prefix: firstRow ? getPrefix(item.kind) : CONTINUATION_PREFIX,
|
|
99
|
+
prefixStyle: firstRow ? itemStyle : mutedStyle,
|
|
100
|
+
style: itemStyle,
|
|
101
|
+
text: rowSegments.map((segment) => segment.text).join(""),
|
|
102
|
+
segments: rowSegments
|
|
103
|
+
});
|
|
104
|
+
firstRow = false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const wrappedLines = wrapText(item.text, textWidth);
|
|
110
|
+
for (let index = 0; index < wrappedLines.length; index += 1) {
|
|
111
|
+
visualLines.push({
|
|
112
|
+
prefix: index === 0 ? getPrefix(item.kind) : CONTINUATION_PREFIX,
|
|
113
|
+
prefixStyle: index === 0 ? itemStyle : mutedStyle,
|
|
114
|
+
style: itemStyle,
|
|
115
|
+
text: wrappedLines[index] ?? ""
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return visualLines;
|
|
120
|
+
}
|
|
121
|
+
function hardWrapSegments(segments, width) {
|
|
122
|
+
if (width <= 0) {
|
|
123
|
+
return [[]];
|
|
124
|
+
}
|
|
125
|
+
const rows = [[]];
|
|
126
|
+
let rowWidth = 0;
|
|
127
|
+
for (const segment of segments) {
|
|
128
|
+
if (segment.text.length === 0) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const chars = [...segment.text];
|
|
132
|
+
let cursor = 0;
|
|
133
|
+
while (cursor < chars.length) {
|
|
134
|
+
const space = width - rowWidth;
|
|
135
|
+
if (space <= 0) {
|
|
136
|
+
rows.push([]);
|
|
137
|
+
rowWidth = 0;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
const take = chars.slice(cursor, cursor + space).join("");
|
|
141
|
+
const currentRow = rows[rows.length - 1];
|
|
142
|
+
currentRow.push({ text: take, style: { ...segment.style } });
|
|
143
|
+
rowWidth += Math.min(space, chars.length - cursor);
|
|
144
|
+
cursor += space;
|
|
145
|
+
if (cursor < chars.length) {
|
|
146
|
+
rows.push([]);
|
|
147
|
+
rowWidth = 0;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return rows;
|
|
152
|
+
}
|
|
153
|
+
function countCells(text) {
|
|
154
|
+
return Array.from(text).length;
|
|
155
|
+
}
|
|
156
|
+
export function scrollUp(state, lines) {
|
|
157
|
+
return {
|
|
158
|
+
...state,
|
|
159
|
+
autoFollow: false,
|
|
160
|
+
scrollOffset: Math.max(0, state.scrollOffset - normalizeCount(lines))
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
export function scrollDown(state, lines, totalVisualLines) {
|
|
164
|
+
return {
|
|
165
|
+
...state,
|
|
166
|
+
autoFollow: false,
|
|
167
|
+
scrollOffset: Math.min(clampScrollOffset(totalVisualLines - 1, totalVisualLines), state.scrollOffset + normalizeCount(lines))
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
export function scrollToTop(state) {
|
|
171
|
+
return {
|
|
172
|
+
...state,
|
|
173
|
+
autoFollow: false,
|
|
174
|
+
scrollOffset: 0
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
export function scrollToBottom(state, totalVisualLines, paneHeight) {
|
|
178
|
+
return {
|
|
179
|
+
...state,
|
|
180
|
+
autoFollow: true,
|
|
181
|
+
scrollOffset: Math.max(0, normalizeCount(totalVisualLines) - normalizeCount(paneHeight))
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function getPrefix(kind) {
|
|
185
|
+
if (kind === "success") {
|
|
186
|
+
return "◆";
|
|
187
|
+
}
|
|
188
|
+
if (kind === "error") {
|
|
189
|
+
return "■";
|
|
190
|
+
}
|
|
191
|
+
if (kind === "tool") {
|
|
192
|
+
return CONTINUATION_PREFIX;
|
|
193
|
+
}
|
|
194
|
+
if (kind === "status") {
|
|
195
|
+
return "●";
|
|
196
|
+
}
|
|
197
|
+
return "◇";
|
|
198
|
+
}
|
|
199
|
+
function getItemStyle(kind, themeName) {
|
|
200
|
+
if (kind === "success") {
|
|
201
|
+
return themeName === "light" ? { fg: "#008800" } : { fg: "green" };
|
|
202
|
+
}
|
|
203
|
+
if (kind === "error") {
|
|
204
|
+
return themeName === "light" ? { fg: "#cc0000" } : { fg: "red" };
|
|
205
|
+
}
|
|
206
|
+
if (kind === "tool") {
|
|
207
|
+
return getMutedStyle(themeName);
|
|
208
|
+
}
|
|
209
|
+
if (kind === "status") {
|
|
210
|
+
return themeName === "light" ? { fg: "#a200ff" } : { fg: "magenta" };
|
|
211
|
+
}
|
|
212
|
+
return themeName === "light" ? { fg: "#a200ff" } : { fg: "magenta" };
|
|
213
|
+
}
|
|
214
|
+
function getMutedStyle(themeName) {
|
|
215
|
+
return themeName === "light" ? { fg: "#666666" } : { dim: true };
|
|
216
|
+
}
|
|
217
|
+
function wrapText(value, width) {
|
|
218
|
+
const logicalLines = splitLogicalLines(value);
|
|
219
|
+
if (logicalLines.length === 0) {
|
|
220
|
+
return [""];
|
|
221
|
+
}
|
|
222
|
+
if (width <= 0) {
|
|
223
|
+
return logicalLines.map(() => "");
|
|
224
|
+
}
|
|
225
|
+
return logicalLines.flatMap((line) => wrapParagraph(line, width));
|
|
226
|
+
}
|
|
227
|
+
function wrapParagraph(value, width) {
|
|
228
|
+
if (value.length === 0) {
|
|
229
|
+
return [""];
|
|
230
|
+
}
|
|
231
|
+
const tokens = tokenize(value);
|
|
232
|
+
const lines = [];
|
|
233
|
+
let currentLine = "";
|
|
234
|
+
let pendingSpace = "";
|
|
235
|
+
const flushLine = () => {
|
|
236
|
+
lines.push(currentLine);
|
|
237
|
+
currentLine = "";
|
|
238
|
+
pendingSpace = "";
|
|
239
|
+
};
|
|
240
|
+
for (const token of tokens) {
|
|
241
|
+
if (token.kind === "space") {
|
|
242
|
+
if (currentLine.length > 0) {
|
|
243
|
+
pendingSpace += token.value;
|
|
244
|
+
}
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
const chunks = splitWord(token.value, width);
|
|
248
|
+
for (let index = 0; index < chunks.length; index += 1) {
|
|
249
|
+
const chunk = chunks[index] ?? "";
|
|
250
|
+
const gap = index === 0 ? pendingSpace : "";
|
|
251
|
+
if (currentLine.length > 0 && currentLine.length + gap.length + chunk.length > width) {
|
|
252
|
+
flushLine();
|
|
253
|
+
}
|
|
254
|
+
if (currentLine.length > 0 && gap.length > 0) {
|
|
255
|
+
currentLine += gap;
|
|
256
|
+
}
|
|
257
|
+
currentLine += chunk;
|
|
258
|
+
pendingSpace = "";
|
|
259
|
+
if (index < chunks.length - 1) {
|
|
260
|
+
flushLine();
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (currentLine.length > 0 || lines.length === 0) {
|
|
265
|
+
lines.push(currentLine);
|
|
266
|
+
}
|
|
267
|
+
return lines;
|
|
268
|
+
}
|
|
269
|
+
function splitWord(value, width) {
|
|
270
|
+
if (value.length <= width) {
|
|
271
|
+
return [value];
|
|
272
|
+
}
|
|
273
|
+
const chunks = [];
|
|
274
|
+
for (let index = 0; index < value.length; index += width) {
|
|
275
|
+
chunks.push(value.slice(index, index + width));
|
|
276
|
+
}
|
|
277
|
+
return chunks;
|
|
278
|
+
}
|
|
279
|
+
function tokenize(value) {
|
|
280
|
+
const tokens = [];
|
|
281
|
+
let current = "";
|
|
282
|
+
let currentKind;
|
|
283
|
+
for (const ch of value) {
|
|
284
|
+
const nextKind = isWrappingSpace(ch) ? "space" : "word";
|
|
285
|
+
if (currentKind !== undefined && currentKind !== nextKind) {
|
|
286
|
+
tokens.push({ kind: currentKind, value: current });
|
|
287
|
+
current = "";
|
|
288
|
+
}
|
|
289
|
+
currentKind = nextKind;
|
|
290
|
+
current += ch;
|
|
291
|
+
}
|
|
292
|
+
if (currentKind !== undefined) {
|
|
293
|
+
tokens.push({ kind: currentKind, value: current });
|
|
294
|
+
}
|
|
295
|
+
return tokens;
|
|
296
|
+
}
|
|
297
|
+
function splitLogicalLines(value) {
|
|
298
|
+
const lines = [];
|
|
299
|
+
let currentLine = "";
|
|
300
|
+
for (const ch of value) {
|
|
301
|
+
if (ch === "\r") {
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
if (ch === "\n") {
|
|
305
|
+
lines.push(currentLine);
|
|
306
|
+
currentLine = "";
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
currentLine += ch;
|
|
310
|
+
}
|
|
311
|
+
lines.push(currentLine);
|
|
312
|
+
return lines;
|
|
313
|
+
}
|
|
314
|
+
function clampScrollOffset(scrollOffset, totalVisualLines) {
|
|
315
|
+
const maxOffset = Math.max(0, normalizeCount(totalVisualLines) - 1);
|
|
316
|
+
return Math.max(0, Math.min(normalizeCount(scrollOffset), maxOffset));
|
|
317
|
+
}
|
|
318
|
+
function normalizeCount(value) {
|
|
319
|
+
return Math.max(0, Math.floor(value));
|
|
320
|
+
}
|
|
321
|
+
function isWrappingSpace(ch) {
|
|
322
|
+
return ch === " " || ch === "\t";
|
|
323
|
+
}
|
|
@@ -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,120 @@
|
|
|
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 lines = [
|
|
42
|
+
createKeyValueLine("Status", formatStatus(stats.status), width, getStatusStyle(stats.status)),
|
|
43
|
+
createKeyValueLine("Iteration", formatNumber(stats.iterations), width),
|
|
44
|
+
createKeyValueLine("Elapsed", formatElapsed(stats.elapsedMs), width),
|
|
45
|
+
createBlankLine(),
|
|
46
|
+
createKeyValueLine("Tokens In", formatNumber(stats.tokensIn), width),
|
|
47
|
+
createKeyValueLine("Tokens Out", formatNumber(stats.tokensOut), width),
|
|
48
|
+
createKeyValueLine("Total", formatNumber(totalTokens), width)
|
|
49
|
+
];
|
|
50
|
+
if (stats.currentAction !== undefined) {
|
|
51
|
+
lines.push(createBlankLine(), {
|
|
52
|
+
prefix: clipText("Current:", width),
|
|
53
|
+
prefixStyle: {},
|
|
54
|
+
style: {},
|
|
55
|
+
text: ""
|
|
56
|
+
}, {
|
|
57
|
+
prefix: width > 0 ? clipText(" ", width) : "",
|
|
58
|
+
prefixStyle: mutedStyle,
|
|
59
|
+
style: mutedStyle,
|
|
60
|
+
text: clipText(stats.currentAction, Math.max(width - 2, 0))
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return lines;
|
|
64
|
+
}
|
|
65
|
+
function createBlankLine() {
|
|
66
|
+
return {
|
|
67
|
+
prefix: "",
|
|
68
|
+
prefixStyle: {},
|
|
69
|
+
style: {},
|
|
70
|
+
text: ""
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function createKeyValueLine(label, value, width, valueStyle = {}) {
|
|
74
|
+
const clippedValue = clipText(value, width);
|
|
75
|
+
const availableBeforeValue = Math.max(width - clippedValue.length, 0);
|
|
76
|
+
const clippedLabel = clipText(label, Math.max(availableBeforeValue - 1, 0));
|
|
77
|
+
return {
|
|
78
|
+
prefix: clippedLabel + " ".repeat(Math.max(availableBeforeValue - clippedLabel.length, 0)),
|
|
79
|
+
prefixStyle: {},
|
|
80
|
+
style: valueStyle,
|
|
81
|
+
text: clippedValue
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function clipText(value, width) {
|
|
85
|
+
return width <= 0 ? "" : value.slice(0, width);
|
|
86
|
+
}
|
|
87
|
+
function formatStatus(status) {
|
|
88
|
+
return `${status.slice(0, 1).toUpperCase()}${status.slice(1)}`;
|
|
89
|
+
}
|
|
90
|
+
function getStatusStyle(status) {
|
|
91
|
+
if (status === "running") {
|
|
92
|
+
return getToneStyle("info");
|
|
93
|
+
}
|
|
94
|
+
if (status === "paused") {
|
|
95
|
+
return getToneStyle("warning");
|
|
96
|
+
}
|
|
97
|
+
if (status === "error") {
|
|
98
|
+
return getToneStyle("error");
|
|
99
|
+
}
|
|
100
|
+
if (status === "done") {
|
|
101
|
+
return getToneStyle("success");
|
|
102
|
+
}
|
|
103
|
+
return getToneStyle("muted");
|
|
104
|
+
}
|
|
105
|
+
function getToneStyle(tone) {
|
|
106
|
+
const isLightTheme = getTheme() === light;
|
|
107
|
+
if (tone === "muted") {
|
|
108
|
+
return isLightTheme ? { fg: "#666666" } : { dim: true };
|
|
109
|
+
}
|
|
110
|
+
if (tone === "info") {
|
|
111
|
+
return isLightTheme ? { fg: "#a200ff" } : { fg: "magenta" };
|
|
112
|
+
}
|
|
113
|
+
if (tone === "warning") {
|
|
114
|
+
return isLightTheme ? { fg: "#cc6600" } : { fg: "yellow" };
|
|
115
|
+
}
|
|
116
|
+
if (tone === "error") {
|
|
117
|
+
return isLightTheme ? { fg: "#cc0000" } : { fg: "red" };
|
|
118
|
+
}
|
|
119
|
+
return isLightTheme ? { fg: "#008800" } : { fg: "green" };
|
|
120
|
+
}
|
|
@@ -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;
|