toolcraft 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 +91 -0
- package/dist/cli.compile-check.d.ts +1 -0
- package/dist/cli.compile-check.js +26 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.js +1312 -0
- package/dist/index.compile-check.d.ts +1 -0
- package/dist/index.compile-check.js +50 -0
- package/dist/index.d.ts +164 -0
- package/dist/index.js +366 -0
- package/dist/mcp.compile-check.d.ts +1 -0
- package/dist/mcp.compile-check.js +26 -0
- package/dist/mcp.d.ts +31 -0
- package/dist/mcp.js +354 -0
- package/dist/number-schema.d.ts +3 -0
- package/dist/number-schema.js +8 -0
- package/dist/renderer.d.ts +5 -0
- package/dist/renderer.js +148 -0
- package/dist/schema-scope.d.ts +4 -0
- package/dist/schema-scope.js +34 -0
- package/dist/sdk.compile-check.d.ts +1 -0
- package/dist/sdk.compile-check.js +79 -0
- package/dist/sdk.d.ts +63 -0
- package/dist/sdk.js +218 -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/package.json +57 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
const EMPTY_CELL = { ch: " ", style: {} };
|
|
3
|
+
export class ScreenBuffer {
|
|
4
|
+
_width;
|
|
5
|
+
_height;
|
|
6
|
+
_cells;
|
|
7
|
+
constructor(width, height) {
|
|
8
|
+
this._width = normalizeSize(width);
|
|
9
|
+
this._height = normalizeSize(height);
|
|
10
|
+
this._cells = createCells(this._width, this._height);
|
|
11
|
+
}
|
|
12
|
+
get width() {
|
|
13
|
+
return this._width;
|
|
14
|
+
}
|
|
15
|
+
get height() {
|
|
16
|
+
return this._height;
|
|
17
|
+
}
|
|
18
|
+
put(x, y, text, style) {
|
|
19
|
+
if (!this.isInBoundsY(y) || text.length === 0) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const normalizedStyle = normalizeStyle(style);
|
|
23
|
+
let offset = 0;
|
|
24
|
+
for (const ch of text) {
|
|
25
|
+
const targetX = x + offset;
|
|
26
|
+
offset += 1;
|
|
27
|
+
if (!this.isInBoundsX(targetX)) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
this._cells[this.index(targetX, y)] = { ch, style: normalizedStyle };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
get(x, y) {
|
|
34
|
+
if (!this.isInBounds(x, y)) {
|
|
35
|
+
return cloneCell(EMPTY_CELL);
|
|
36
|
+
}
|
|
37
|
+
return cloneCell(this._cells[this.index(x, y)] ?? EMPTY_CELL);
|
|
38
|
+
}
|
|
39
|
+
clear(style) {
|
|
40
|
+
this._cells = createCells(this._width, this._height, style);
|
|
41
|
+
}
|
|
42
|
+
clearRect(rect, style) {
|
|
43
|
+
const startX = Math.max(0, rect.x);
|
|
44
|
+
const startY = Math.max(0, rect.y);
|
|
45
|
+
const endX = Math.min(this._width, rect.x + Math.max(0, rect.width));
|
|
46
|
+
const endY = Math.min(this._height, rect.y + Math.max(0, rect.height));
|
|
47
|
+
const normalizedStyle = normalizeStyle(style);
|
|
48
|
+
for (let y = startY; y < endY; y += 1) {
|
|
49
|
+
for (let x = startX; x < endX; x += 1) {
|
|
50
|
+
this._cells[this.index(x, y)] = { ch: " ", style: normalizedStyle };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
resize(width, height) {
|
|
55
|
+
const nextWidth = normalizeSize(width);
|
|
56
|
+
const nextHeight = normalizeSize(height);
|
|
57
|
+
const nextCells = createCells(nextWidth, nextHeight);
|
|
58
|
+
const copyWidth = Math.min(this._width, nextWidth);
|
|
59
|
+
const copyHeight = Math.min(this._height, nextHeight);
|
|
60
|
+
for (let y = 0; y < copyHeight; y += 1) {
|
|
61
|
+
for (let x = 0; x < copyWidth; x += 1) {
|
|
62
|
+
nextCells[(y * nextWidth) + x] = cloneCell(this._cells[this.index(x, y)] ?? EMPTY_CELL);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
this._width = nextWidth;
|
|
66
|
+
this._height = nextHeight;
|
|
67
|
+
this._cells = nextCells;
|
|
68
|
+
}
|
|
69
|
+
putInRect(rect, row, text, style) {
|
|
70
|
+
if (row < 0 || row >= rect.height || text.length === 0 || rect.width <= 0) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const y = rect.y + row;
|
|
74
|
+
if (!this.isInBoundsY(y)) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const normalizedStyle = normalizeStyle(style);
|
|
78
|
+
const rectEndX = rect.x + rect.width;
|
|
79
|
+
let offset = 0;
|
|
80
|
+
for (const ch of text) {
|
|
81
|
+
const targetX = rect.x + offset;
|
|
82
|
+
offset += 1;
|
|
83
|
+
if (targetX >= rectEndX) {
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
if (!this.isInBoundsX(targetX)) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
this._cells[this.index(targetX, y)] = { ch, style: normalizedStyle };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
index(x, y) {
|
|
93
|
+
return (y * this._width) + x;
|
|
94
|
+
}
|
|
95
|
+
isInBounds(x, y) {
|
|
96
|
+
return this.isInBoundsX(x) && this.isInBoundsY(y);
|
|
97
|
+
}
|
|
98
|
+
isInBoundsX(x) {
|
|
99
|
+
return x >= 0 && x < this._width;
|
|
100
|
+
}
|
|
101
|
+
isInBoundsY(y) {
|
|
102
|
+
return y >= 0 && y < this._height;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
export function diff(prev, next) {
|
|
106
|
+
const changes = [];
|
|
107
|
+
const width = Math.max(prev.width, next.width);
|
|
108
|
+
const height = Math.max(prev.height, next.height);
|
|
109
|
+
for (let y = 0; y < height; y += 1) {
|
|
110
|
+
for (let x = 0; x < width; x += 1) {
|
|
111
|
+
const previousCell = prev.get(x, y);
|
|
112
|
+
const nextCell = next.get(x, y);
|
|
113
|
+
if (!cellsEqual(previousCell, nextCell)) {
|
|
114
|
+
changes.push({ x, y, cell: nextCell });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return changes;
|
|
119
|
+
}
|
|
120
|
+
export function cellToAnsi(cell) {
|
|
121
|
+
const style = cell.style ?? {};
|
|
122
|
+
let painter = chalk;
|
|
123
|
+
if (style.bold) {
|
|
124
|
+
painter = painter.bold;
|
|
125
|
+
}
|
|
126
|
+
if (style.dim) {
|
|
127
|
+
painter = painter.dim;
|
|
128
|
+
}
|
|
129
|
+
if (style.fg) {
|
|
130
|
+
painter = applyForegroundColor(painter, style.fg);
|
|
131
|
+
}
|
|
132
|
+
if (style.bg) {
|
|
133
|
+
painter = applyBackgroundColor(painter, style.bg);
|
|
134
|
+
}
|
|
135
|
+
return painter(cell.ch);
|
|
136
|
+
}
|
|
137
|
+
function createCells(width, height, style) {
|
|
138
|
+
const normalizedStyle = normalizeStyle(style);
|
|
139
|
+
return Array.from({ length: width * height }, () => ({ ch: " ", style: normalizedStyle }));
|
|
140
|
+
}
|
|
141
|
+
function cloneCell(cell) {
|
|
142
|
+
return {
|
|
143
|
+
ch: cell.ch,
|
|
144
|
+
style: normalizeStyle(cell.style)
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function normalizeStyle(style) {
|
|
148
|
+
const next = {};
|
|
149
|
+
if (style?.fg !== undefined) {
|
|
150
|
+
next.fg = style.fg;
|
|
151
|
+
}
|
|
152
|
+
if (style?.bg !== undefined) {
|
|
153
|
+
next.bg = style.bg;
|
|
154
|
+
}
|
|
155
|
+
if (style?.bold !== undefined) {
|
|
156
|
+
next.bold = style.bold;
|
|
157
|
+
}
|
|
158
|
+
if (style?.dim !== undefined) {
|
|
159
|
+
next.dim = style.dim;
|
|
160
|
+
}
|
|
161
|
+
return next;
|
|
162
|
+
}
|
|
163
|
+
function normalizeSize(value) {
|
|
164
|
+
return Math.max(0, Math.floor(value));
|
|
165
|
+
}
|
|
166
|
+
function cellsEqual(left, right) {
|
|
167
|
+
return left.ch === right.ch
|
|
168
|
+
&& left.style.fg === right.style.fg
|
|
169
|
+
&& left.style.bg === right.style.bg
|
|
170
|
+
&& left.style.bold === right.style.bold
|
|
171
|
+
&& left.style.dim === right.style.dim;
|
|
172
|
+
}
|
|
173
|
+
function applyForegroundColor(instance, color) {
|
|
174
|
+
if (color.startsWith("#")) {
|
|
175
|
+
return instance.hex(color);
|
|
176
|
+
}
|
|
177
|
+
const painter = instance[color];
|
|
178
|
+
return typeof painter === "function" ? painter : instance;
|
|
179
|
+
}
|
|
180
|
+
function applyBackgroundColor(instance, color) {
|
|
181
|
+
if (color.startsWith("#")) {
|
|
182
|
+
return instance.bgHex(color);
|
|
183
|
+
}
|
|
184
|
+
const methodName = color.startsWith("bg")
|
|
185
|
+
? color
|
|
186
|
+
: `bg${color.charAt(0).toUpperCase()}${color.slice(1)}`;
|
|
187
|
+
const painter = instance[methodName];
|
|
188
|
+
return typeof painter === "function" ? painter : instance;
|
|
189
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ScreenBuffer } from "../buffer.js";
|
|
2
|
+
import type { DashboardLayout } from "../layout.js";
|
|
3
|
+
import type { CellStyle } from "../types.js";
|
|
4
|
+
export type BorderOptions = {
|
|
5
|
+
leftTitle?: string;
|
|
6
|
+
rightTitle?: string;
|
|
7
|
+
style: CellStyle;
|
|
8
|
+
};
|
|
9
|
+
export declare function renderBorder(buffer: ScreenBuffer, layout: DashboardLayout, opts: BorderOptions): void;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
export function renderBorder(buffer, layout, opts) {
|
|
2
|
+
const outer = layout.outerBorder;
|
|
3
|
+
if (outer.width <= 0 || outer.height <= 0) {
|
|
4
|
+
return;
|
|
5
|
+
}
|
|
6
|
+
const left = outer.x;
|
|
7
|
+
const top = outer.y;
|
|
8
|
+
const right = outer.x + outer.width - 1;
|
|
9
|
+
const bottom = outer.y + outer.height - 1;
|
|
10
|
+
renderTopBorder(buffer, left, top, right, layout, opts);
|
|
11
|
+
renderSideBorders(buffer, left, right, top, bottom, opts.style);
|
|
12
|
+
renderBottomBorder(buffer, left, top, right, bottom, layout, opts.style);
|
|
13
|
+
renderVerticalDivider(buffer, layout, opts.style);
|
|
14
|
+
renderFooterDivider(buffer, left, right, bottom, layout, opts.style);
|
|
15
|
+
}
|
|
16
|
+
function renderTopBorder(buffer, left, top, right, layout, opts) {
|
|
17
|
+
if (left === right) {
|
|
18
|
+
buffer.put(left, top, "┌", opts.style);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const hasTopJunction = hasInteriorRows(top, layout.outerBorder.y + layout.outerBorder.height - 1)
|
|
22
|
+
&& hasPaneDivider(layout, left, right)
|
|
23
|
+
&& layout.divider.top <= top + 1
|
|
24
|
+
&& layout.divider.bottom >= top + 1;
|
|
25
|
+
if (!hasTopJunction) {
|
|
26
|
+
buffer.put(left, top, `┌${renderTopSegment(right - left - 1, opts.leftTitle)}┐`, opts.style);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const leftWidth = layout.divider.x - left - 1;
|
|
30
|
+
const rightWidth = right - layout.divider.x - 1;
|
|
31
|
+
const row = [
|
|
32
|
+
"┌",
|
|
33
|
+
renderTopSegment(leftWidth, opts.leftTitle),
|
|
34
|
+
"┬",
|
|
35
|
+
renderTopSegment(rightWidth, opts.rightTitle),
|
|
36
|
+
"┐"
|
|
37
|
+
].join("");
|
|
38
|
+
buffer.put(left, top, row, opts.style);
|
|
39
|
+
}
|
|
40
|
+
function renderSideBorders(buffer, left, right, top, bottom, style) {
|
|
41
|
+
for (let y = top + 1; y < bottom; y += 1) {
|
|
42
|
+
buffer.put(left, y, "│", style);
|
|
43
|
+
buffer.put(right, y, "│", style);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function renderBottomBorder(buffer, left, top, right, bottom, layout, style) {
|
|
47
|
+
if (bottom <= top) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (left === right) {
|
|
51
|
+
buffer.put(left, bottom, "└", style);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
buffer.put(left, bottom, "└", style);
|
|
55
|
+
buffer.put(left + 1, bottom, "─".repeat(Math.max(0, right - left - 1)), style);
|
|
56
|
+
buffer.put(right, bottom, "┘", style);
|
|
57
|
+
if (hasInteriorRows(top, bottom)
|
|
58
|
+
&& hasPaneDivider(layout, left, right)
|
|
59
|
+
&& layout.divider.top <= bottom - 1
|
|
60
|
+
&& layout.divider.bottom >= bottom - 1) {
|
|
61
|
+
buffer.put(layout.divider.x, bottom, "┴", style);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function renderVerticalDivider(buffer, layout, style) {
|
|
65
|
+
const left = layout.outerBorder.x;
|
|
66
|
+
const right = layout.outerBorder.x + layout.outerBorder.width - 1;
|
|
67
|
+
if (!hasPaneDivider(layout, left, right)) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const startY = Math.max(layout.divider.top, layout.outerBorder.y + 1);
|
|
71
|
+
const endY = Math.min(layout.divider.bottom, layout.outerBorder.y + layout.outerBorder.height - 2);
|
|
72
|
+
for (let y = startY; y <= endY; y += 1) {
|
|
73
|
+
buffer.put(layout.divider.x, y, "│", style);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function renderFooterDivider(buffer, left, right, bottom, layout, style) {
|
|
77
|
+
const y = layout.footerDivider.y;
|
|
78
|
+
if (y <= layout.outerBorder.y || y >= bottom) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const width = layout.footerDivider.right - layout.footerDivider.left + 1;
|
|
82
|
+
if (width > 0) {
|
|
83
|
+
buffer.put(layout.footerDivider.left, y, "─".repeat(width), style);
|
|
84
|
+
}
|
|
85
|
+
buffer.put(left, y, "├", style);
|
|
86
|
+
buffer.put(right, y, "┤", style);
|
|
87
|
+
if (!hasPaneDivider(layout, left, right)
|
|
88
|
+
|| layout.divider.x < layout.footerDivider.left
|
|
89
|
+
|| layout.divider.x > layout.footerDivider.right) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const connectsAbove = layout.divider.bottom >= y - 1 && layout.divider.top <= y;
|
|
93
|
+
const connectsBelow = layout.divider.bottom > y;
|
|
94
|
+
if (connectsAbove && connectsBelow) {
|
|
95
|
+
buffer.put(layout.divider.x, y, "┼", style);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (connectsAbove) {
|
|
99
|
+
buffer.put(layout.divider.x, y, "┴", style);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function renderTopSegment(width, title) {
|
|
103
|
+
if (width <= 0) {
|
|
104
|
+
return "";
|
|
105
|
+
}
|
|
106
|
+
if (!title) {
|
|
107
|
+
return "─".repeat(width);
|
|
108
|
+
}
|
|
109
|
+
const content = `─ ${title} `;
|
|
110
|
+
if (content.length >= width) {
|
|
111
|
+
return content.slice(0, width);
|
|
112
|
+
}
|
|
113
|
+
return `${content}${"─".repeat(width - content.length)}`;
|
|
114
|
+
}
|
|
115
|
+
function hasInteriorRows(top, bottom) {
|
|
116
|
+
return (bottom - top) > 1;
|
|
117
|
+
}
|
|
118
|
+
function hasPaneDivider(layout, left, right) {
|
|
119
|
+
return layout.leftPane.width > 0
|
|
120
|
+
&& layout.rightPane.width > 0
|
|
121
|
+
&& layout.divider.x > left
|
|
122
|
+
&& layout.divider.x < right;
|
|
123
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ScreenBuffer } from "../buffer.js";
|
|
2
|
+
import type { Rect } from "../types.js";
|
|
3
|
+
export type FooterHint = {
|
|
4
|
+
key: string;
|
|
5
|
+
label: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function renderFooter(buffer: ScreenBuffer, rect: Rect, hints: FooterHint[]): void;
|
|
8
|
+
export declare function defaultHints(): FooterHint[];
|
|
@@ -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[];
|