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.
Files changed (135) hide show
  1. package/README.md +91 -0
  2. package/dist/cli.compile-check.d.ts +1 -0
  3. package/dist/cli.compile-check.js +26 -0
  4. package/dist/cli.d.ts +12 -0
  5. package/dist/cli.js +1312 -0
  6. package/dist/index.compile-check.d.ts +1 -0
  7. package/dist/index.compile-check.js +50 -0
  8. package/dist/index.d.ts +164 -0
  9. package/dist/index.js +366 -0
  10. package/dist/mcp.compile-check.d.ts +1 -0
  11. package/dist/mcp.compile-check.js +26 -0
  12. package/dist/mcp.d.ts +31 -0
  13. package/dist/mcp.js +354 -0
  14. package/dist/number-schema.d.ts +3 -0
  15. package/dist/number-schema.js +8 -0
  16. package/dist/renderer.d.ts +5 -0
  17. package/dist/renderer.js +148 -0
  18. package/dist/schema-scope.d.ts +4 -0
  19. package/dist/schema-scope.js +34 -0
  20. package/dist/sdk.compile-check.d.ts +1 -0
  21. package/dist/sdk.compile-check.js +79 -0
  22. package/dist/sdk.d.ts +63 -0
  23. package/dist/sdk.js +218 -0
  24. package/node_modules/@poe-code/design-system/dist/acp/components.d.ts +11 -0
  25. package/node_modules/@poe-code/design-system/dist/acp/components.js +121 -0
  26. package/node_modules/@poe-code/design-system/dist/acp/index.d.ts +3 -0
  27. package/node_modules/@poe-code/design-system/dist/acp/index.js +2 -0
  28. package/node_modules/@poe-code/design-system/dist/acp/writer.d.ts +13 -0
  29. package/node_modules/@poe-code/design-system/dist/acp/writer.js +21 -0
  30. package/node_modules/@poe-code/design-system/dist/components/command-errors.d.ts +16 -0
  31. package/node_modules/@poe-code/design-system/dist/components/command-errors.js +22 -0
  32. package/node_modules/@poe-code/design-system/dist/components/help-formatter.d.ts +20 -0
  33. package/node_modules/@poe-code/design-system/dist/components/help-formatter.js +27 -0
  34. package/node_modules/@poe-code/design-system/dist/components/index.d.ts +10 -0
  35. package/node_modules/@poe-code/design-system/dist/components/index.js +7 -0
  36. package/node_modules/@poe-code/design-system/dist/components/logger.d.ts +11 -0
  37. package/node_modules/@poe-code/design-system/dist/components/logger.js +60 -0
  38. package/node_modules/@poe-code/design-system/dist/components/symbols.d.ts +12 -0
  39. package/node_modules/@poe-code/design-system/dist/components/symbols.js +71 -0
  40. package/node_modules/@poe-code/design-system/dist/components/table.d.ts +13 -0
  41. package/node_modules/@poe-code/design-system/dist/components/table.js +74 -0
  42. package/node_modules/@poe-code/design-system/dist/components/text.d.ts +14 -0
  43. package/node_modules/@poe-code/design-system/dist/components/text.js +104 -0
  44. package/node_modules/@poe-code/design-system/dist/dashboard/ansi.d.ts +18 -0
  45. package/node_modules/@poe-code/design-system/dist/dashboard/ansi.js +298 -0
  46. package/node_modules/@poe-code/design-system/dist/dashboard/buffer.d.ts +25 -0
  47. package/node_modules/@poe-code/design-system/dist/dashboard/buffer.js +189 -0
  48. package/node_modules/@poe-code/design-system/dist/dashboard/components/border.d.ts +9 -0
  49. package/node_modules/@poe-code/design-system/dist/dashboard/components/border.js +123 -0
  50. package/node_modules/@poe-code/design-system/dist/dashboard/components/footer.d.ts +8 -0
  51. package/node_modules/@poe-code/design-system/dist/dashboard/components/footer.js +57 -0
  52. package/node_modules/@poe-code/design-system/dist/dashboard/components/output-pane.d.ts +12 -0
  53. package/node_modules/@poe-code/design-system/dist/dashboard/components/output-pane.js +254 -0
  54. package/node_modules/@poe-code/design-system/dist/dashboard/components/stats-pane.d.ts +7 -0
  55. package/node_modules/@poe-code/design-system/dist/dashboard/components/stats-pane.js +121 -0
  56. package/node_modules/@poe-code/design-system/dist/dashboard/dashboard.d.ts +20 -0
  57. package/node_modules/@poe-code/design-system/dist/dashboard/dashboard.js +167 -0
  58. package/node_modules/@poe-code/design-system/dist/dashboard/demo.d.ts +13 -0
  59. package/node_modules/@poe-code/design-system/dist/dashboard/demo.js +145 -0
  60. package/node_modules/@poe-code/design-system/dist/dashboard/index.d.ts +8 -0
  61. package/node_modules/@poe-code/design-system/dist/dashboard/index.js +4 -0
  62. package/node_modules/@poe-code/design-system/dist/dashboard/keymap.d.ts +3 -0
  63. package/node_modules/@poe-code/design-system/dist/dashboard/keymap.js +99 -0
  64. package/node_modules/@poe-code/design-system/dist/dashboard/layout.d.ts +25 -0
  65. package/node_modules/@poe-code/design-system/dist/dashboard/layout.js +79 -0
  66. package/node_modules/@poe-code/design-system/dist/dashboard/should-use-dashboard.d.ts +10 -0
  67. package/node_modules/@poe-code/design-system/dist/dashboard/should-use-dashboard.js +7 -0
  68. package/node_modules/@poe-code/design-system/dist/dashboard/snapshot.d.ts +10 -0
  69. package/node_modules/@poe-code/design-system/dist/dashboard/snapshot.js +68 -0
  70. package/node_modules/@poe-code/design-system/dist/dashboard/store.d.ts +8 -0
  71. package/node_modules/@poe-code/design-system/dist/dashboard/store.js +51 -0
  72. package/node_modules/@poe-code/design-system/dist/dashboard/terminal.d.ts +37 -0
  73. package/node_modules/@poe-code/design-system/dist/dashboard/terminal.js +233 -0
  74. package/node_modules/@poe-code/design-system/dist/dashboard/types.d.ts +36 -0
  75. package/node_modules/@poe-code/design-system/dist/dashboard/types.js +1 -0
  76. package/node_modules/@poe-code/design-system/dist/index.d.ts +33 -0
  77. package/node_modules/@poe-code/design-system/dist/index.js +31 -0
  78. package/node_modules/@poe-code/design-system/dist/internal/output-format.d.ts +6 -0
  79. package/node_modules/@poe-code/design-system/dist/internal/output-format.js +22 -0
  80. package/node_modules/@poe-code/design-system/dist/internal/strip-ansi.d.ts +1 -0
  81. package/node_modules/@poe-code/design-system/dist/internal/strip-ansi.js +3 -0
  82. package/node_modules/@poe-code/design-system/dist/internal/theme-detect.d.ts +11 -0
  83. package/node_modules/@poe-code/design-system/dist/internal/theme-detect.js +49 -0
  84. package/node_modules/@poe-code/design-system/dist/prompts/index.d.ts +66 -0
  85. package/node_modules/@poe-code/design-system/dist/prompts/index.js +132 -0
  86. package/node_modules/@poe-code/design-system/dist/prompts/primitives/cancel.d.ts +2 -0
  87. package/node_modules/@poe-code/design-system/dist/prompts/primitives/cancel.js +9 -0
  88. package/node_modules/@poe-code/design-system/dist/prompts/primitives/intro.d.ts +1 -0
  89. package/node_modules/@poe-code/design-system/dist/prompts/primitives/intro.js +15 -0
  90. package/node_modules/@poe-code/design-system/dist/prompts/primitives/log.d.ts +18 -0
  91. package/node_modules/@poe-code/design-system/dist/prompts/primitives/log.js +101 -0
  92. package/node_modules/@poe-code/design-system/dist/prompts/primitives/note.d.ts +1 -0
  93. package/node_modules/@poe-code/design-system/dist/prompts/primitives/note.js +39 -0
  94. package/node_modules/@poe-code/design-system/dist/prompts/primitives/outro.d.ts +1 -0
  95. package/node_modules/@poe-code/design-system/dist/prompts/primitives/outro.js +16 -0
  96. package/node_modules/@poe-code/design-system/dist/prompts/primitives/spinner.d.ts +6 -0
  97. package/node_modules/@poe-code/design-system/dist/prompts/primitives/spinner.js +74 -0
  98. package/node_modules/@poe-code/design-system/dist/prompts/theme.d.ts +11 -0
  99. package/node_modules/@poe-code/design-system/dist/prompts/theme.js +12 -0
  100. package/node_modules/@poe-code/design-system/dist/static/index.d.ts +4 -0
  101. package/node_modules/@poe-code/design-system/dist/static/index.js +2 -0
  102. package/node_modules/@poe-code/design-system/dist/static/menu.d.ts +11 -0
  103. package/node_modules/@poe-code/design-system/dist/static/menu.js +36 -0
  104. package/node_modules/@poe-code/design-system/dist/static/spinner.d.ts +14 -0
  105. package/node_modules/@poe-code/design-system/dist/static/spinner.js +46 -0
  106. package/node_modules/@poe-code/design-system/dist/terminal-markdown/ast.d.ts +92 -0
  107. package/node_modules/@poe-code/design-system/dist/terminal-markdown/ast.js +1 -0
  108. package/node_modules/@poe-code/design-system/dist/terminal-markdown/demo-content.d.ts +2 -0
  109. package/node_modules/@poe-code/design-system/dist/terminal-markdown/demo-content.js +139 -0
  110. package/node_modules/@poe-code/design-system/dist/terminal-markdown/index.d.ts +6 -0
  111. package/node_modules/@poe-code/design-system/dist/terminal-markdown/index.js +8 -0
  112. package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/block.d.ts +7 -0
  113. package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/block.js +1495 -0
  114. package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/frontmatter.d.ts +8 -0
  115. package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/frontmatter.js +412 -0
  116. package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/inline.d.ts +10 -0
  117. package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser/inline.js +1166 -0
  118. package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser.d.ts +5 -0
  119. package/node_modules/@poe-code/design-system/dist/terminal-markdown/parser.js +42 -0
  120. package/node_modules/@poe-code/design-system/dist/terminal-markdown/renderer.d.ts +6 -0
  121. package/node_modules/@poe-code/design-system/dist/terminal-markdown/renderer.js +572 -0
  122. package/node_modules/@poe-code/design-system/dist/terminal-markdown/testing/theme-render-fixture.d.ts +1 -0
  123. package/node_modules/@poe-code/design-system/dist/terminal-markdown/testing/theme-render-fixture.js +27 -0
  124. package/node_modules/@poe-code/design-system/dist/tokens/colors.d.ts +35 -0
  125. package/node_modules/@poe-code/design-system/dist/tokens/colors.js +34 -0
  126. package/node_modules/@poe-code/design-system/dist/tokens/index.d.ts +4 -0
  127. package/node_modules/@poe-code/design-system/dist/tokens/index.js +4 -0
  128. package/node_modules/@poe-code/design-system/dist/tokens/spacing.d.ts +6 -0
  129. package/node_modules/@poe-code/design-system/dist/tokens/spacing.js +6 -0
  130. package/node_modules/@poe-code/design-system/dist/tokens/typography.d.ts +7 -0
  131. package/node_modules/@poe-code/design-system/dist/tokens/typography.js +8 -0
  132. package/node_modules/@poe-code/design-system/dist/tokens/widths.d.ts +5 -0
  133. package/node_modules/@poe-code/design-system/dist/tokens/widths.js +5 -0
  134. package/node_modules/@poe-code/design-system/package.json +25 -0
  135. 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[];