@elizaos/tui 2.0.0-alpha.10
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 +761 -0
- package/dist/autocomplete.d.ts +48 -0
- package/dist/autocomplete.d.ts.map +1 -0
- package/dist/autocomplete.js +555 -0
- package/dist/components/box.d.ts +22 -0
- package/dist/components/box.d.ts.map +1 -0
- package/dist/components/box.js +103 -0
- package/dist/components/cancellable-loader.d.ts +22 -0
- package/dist/components/cancellable-loader.d.ts.map +1 -0
- package/dist/components/cancellable-loader.js +34 -0
- package/dist/components/editor/history.d.ts +38 -0
- package/dist/components/editor/history.d.ts.map +1 -0
- package/dist/components/editor/history.js +76 -0
- package/dist/components/editor/index.d.ts +18 -0
- package/dist/components/editor/index.d.ts.map +1 -0
- package/dist/components/editor/index.js +21 -0
- package/dist/components/editor/kill-ring.d.ts +41 -0
- package/dist/components/editor/kill-ring.d.ts.map +1 -0
- package/dist/components/editor/kill-ring.js +81 -0
- package/dist/components/editor/layout.d.ts +40 -0
- package/dist/components/editor/layout.d.ts.map +1 -0
- package/dist/components/editor/layout.js +205 -0
- package/dist/components/editor/types.d.ts +68 -0
- package/dist/components/editor/types.d.ts.map +1 -0
- package/dist/components/editor/types.js +4 -0
- package/dist/components/editor/undo.d.ts +46 -0
- package/dist/components/editor/undo.d.ts.map +1 -0
- package/dist/components/editor/undo.js +58 -0
- package/dist/components/editor.d.ts +196 -0
- package/dist/components/editor.d.ts.map +1 -0
- package/dist/components/editor.js +1707 -0
- package/dist/components/image.d.ts +28 -0
- package/dist/components/image.d.ts.map +1 -0
- package/dist/components/image.js +68 -0
- package/dist/components/input.d.ts +19 -0
- package/dist/components/input.d.ts.map +1 -0
- package/dist/components/input.js +195 -0
- package/dist/components/loader.d.ts +21 -0
- package/dist/components/loader.d.ts.map +1 -0
- package/dist/components/loader.js +49 -0
- package/dist/components/markdown/index.d.ts +13 -0
- package/dist/components/markdown/index.d.ts.map +1 -0
- package/dist/components/markdown/index.js +9 -0
- package/dist/components/markdown/inline-renderer.d.ts +22 -0
- package/dist/components/markdown/inline-renderer.d.ts.map +1 -0
- package/dist/components/markdown/inline-renderer.js +88 -0
- package/dist/components/markdown/list-renderer.d.ts +33 -0
- package/dist/components/markdown/list-renderer.d.ts.map +1 -0
- package/dist/components/markdown/list-renderer.js +110 -0
- package/dist/components/markdown/table-renderer.d.ts +43 -0
- package/dist/components/markdown/table-renderer.d.ts.map +1 -0
- package/dist/components/markdown/table-renderer.js +184 -0
- package/dist/components/markdown/types.d.ts +57 -0
- package/dist/components/markdown/types.d.ts.map +1 -0
- package/dist/components/markdown/types.js +13 -0
- package/dist/components/markdown.d.ts +44 -0
- package/dist/components/markdown.d.ts.map +1 -0
- package/dist/components/markdown.js +319 -0
- package/dist/components/progress-bar.d.ts +67 -0
- package/dist/components/progress-bar.d.ts.map +1 -0
- package/dist/components/progress-bar.js +124 -0
- package/dist/components/select-list.d.ts +32 -0
- package/dist/components/select-list.d.ts.map +1 -0
- package/dist/components/select-list.js +151 -0
- package/dist/components/settings-list.d.ts +50 -0
- package/dist/components/settings-list.d.ts.map +1 -0
- package/dist/components/settings-list.js +184 -0
- package/dist/components/spacer.d.ts +12 -0
- package/dist/components/spacer.d.ts.map +1 -0
- package/dist/components/spacer.js +22 -0
- package/dist/components/text.d.ts +19 -0
- package/dist/components/text.d.ts.map +1 -0
- package/dist/components/text.js +88 -0
- package/dist/components/toast.d.ts +73 -0
- package/dist/components/toast.d.ts.map +1 -0
- package/dist/components/toast.js +119 -0
- package/dist/components/truncated-text.d.ts +13 -0
- package/dist/components/truncated-text.d.ts.map +1 -0
- package/dist/components/truncated-text.js +50 -0
- package/dist/constants.d.ts +97 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +126 -0
- package/dist/core/container.d.ts +32 -0
- package/dist/core/container.d.ts.map +1 -0
- package/dist/core/container.js +49 -0
- package/dist/core/index.d.ts +9 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +10 -0
- package/dist/core/overlay.d.ts +44 -0
- package/dist/core/overlay.d.ts.map +1 -0
- package/dist/core/overlay.js +171 -0
- package/dist/core/types.d.ts +116 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +14 -0
- package/dist/editor-component.d.ts +37 -0
- package/dist/editor-component.d.ts.map +1 -0
- package/dist/editor-component.js +1 -0
- package/dist/fuzzy.d.ts +16 -0
- package/dist/fuzzy.d.ts.map +1 -0
- package/dist/fuzzy.js +108 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +59 -0
- package/dist/keybindings.d.ts +39 -0
- package/dist/keybindings.d.ts.map +1 -0
- package/dist/keybindings.js +113 -0
- package/dist/keys.d.ts +153 -0
- package/dist/keys.d.ts.map +1 -0
- package/dist/keys.js +951 -0
- package/dist/stdin-buffer.d.ts +48 -0
- package/dist/stdin-buffer.d.ts.map +1 -0
- package/dist/stdin-buffer.js +316 -0
- package/dist/terminal-image.d.ts +68 -0
- package/dist/terminal-image.d.ts.map +1 -0
- package/dist/terminal-image.js +287 -0
- package/dist/terminal.d.ts +71 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +216 -0
- package/dist/themes/index.d.ts +103 -0
- package/dist/themes/index.d.ts.map +1 -0
- package/dist/themes/index.js +161 -0
- package/dist/tui.d.ts +90 -0
- package/dist/tui.d.ts.map +1 -0
- package/dist/tui.js +745 -0
- package/dist/types/marked-tokens.d.ts +57 -0
- package/dist/types/marked-tokens.d.ts.map +1 -0
- package/dist/types/marked-tokens.js +17 -0
- package/dist/utils/cursor-movement.d.ts +127 -0
- package/dist/utils/cursor-movement.d.ts.map +1 -0
- package/dist/utils/cursor-movement.js +251 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +7 -0
- package/dist/utils/paste-handler.d.ts +86 -0
- package/dist/utils/paste-handler.d.ts.map +1 -0
- package/dist/utils/paste-handler.js +121 -0
- package/dist/utils.d.ts +75 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +796 -0
- package/package.json +53 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import { marked } from "marked";
|
|
2
|
+
import { isImageLine } from "../terminal-image.js";
|
|
3
|
+
import { applyBackgroundToLine, visibleWidth, wrapTextWithAnsi } from "../utils.js";
|
|
4
|
+
import { renderInlineTokens as renderInlineTokensUtil } from "./markdown/inline-renderer.js";
|
|
5
|
+
import { renderList as renderListUtil } from "./markdown/list-renderer.js";
|
|
6
|
+
import { renderTable as renderTableUtil } from "./markdown/table-renderer.js";
|
|
7
|
+
import { getStylePrefix } from "./markdown/types.js";
|
|
8
|
+
export class Markdown {
|
|
9
|
+
text;
|
|
10
|
+
paddingX; // Left/right padding
|
|
11
|
+
paddingY; // Top/bottom padding
|
|
12
|
+
defaultTextStyle;
|
|
13
|
+
theme;
|
|
14
|
+
defaultStylePrefix;
|
|
15
|
+
// Cache for rendered output
|
|
16
|
+
cachedText;
|
|
17
|
+
cachedWidth;
|
|
18
|
+
cachedLines;
|
|
19
|
+
constructor(text, paddingX, paddingY, theme, defaultTextStyle) {
|
|
20
|
+
this.text = text;
|
|
21
|
+
this.paddingX = paddingX;
|
|
22
|
+
this.paddingY = paddingY;
|
|
23
|
+
this.theme = theme;
|
|
24
|
+
this.defaultTextStyle = defaultTextStyle;
|
|
25
|
+
}
|
|
26
|
+
setText(text) {
|
|
27
|
+
this.text = text;
|
|
28
|
+
this.invalidate();
|
|
29
|
+
}
|
|
30
|
+
invalidate() {
|
|
31
|
+
this.cachedText = undefined;
|
|
32
|
+
this.cachedWidth = undefined;
|
|
33
|
+
this.cachedLines = undefined;
|
|
34
|
+
}
|
|
35
|
+
render(width) {
|
|
36
|
+
// Check cache
|
|
37
|
+
if (this.cachedLines && this.cachedText === this.text && this.cachedWidth === width) {
|
|
38
|
+
return this.cachedLines;
|
|
39
|
+
}
|
|
40
|
+
// Calculate available width for content (subtract horizontal padding)
|
|
41
|
+
const contentWidth = Math.max(1, width - this.paddingX * 2);
|
|
42
|
+
// Don't render anything if there's no actual text
|
|
43
|
+
if (!this.text || this.text.trim() === "") {
|
|
44
|
+
const result = [];
|
|
45
|
+
// Update cache
|
|
46
|
+
this.cachedText = this.text;
|
|
47
|
+
this.cachedWidth = width;
|
|
48
|
+
this.cachedLines = result;
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
// Replace tabs with 3 spaces for consistent rendering
|
|
52
|
+
const normalizedText = this.text.replace(/\t/g, " ");
|
|
53
|
+
// Parse markdown to HTML-like tokens
|
|
54
|
+
const tokens = marked.lexer(normalizedText);
|
|
55
|
+
// Convert tokens to styled terminal output
|
|
56
|
+
const renderedLines = [];
|
|
57
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
58
|
+
const token = tokens[i];
|
|
59
|
+
const nextToken = tokens[i + 1];
|
|
60
|
+
const tokenLines = this.renderToken(token, contentWidth, nextToken?.type);
|
|
61
|
+
renderedLines.push(...tokenLines);
|
|
62
|
+
}
|
|
63
|
+
// Wrap lines (NO padding, NO background yet)
|
|
64
|
+
const wrappedLines = [];
|
|
65
|
+
for (const line of renderedLines) {
|
|
66
|
+
if (isImageLine(line)) {
|
|
67
|
+
wrappedLines.push(line);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
wrappedLines.push(...wrapTextWithAnsi(line, contentWidth));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Add margins and background to each wrapped line
|
|
74
|
+
const leftMargin = " ".repeat(this.paddingX);
|
|
75
|
+
const rightMargin = " ".repeat(this.paddingX);
|
|
76
|
+
const backgroundFn = this.defaultTextStyle?.bgColor;
|
|
77
|
+
const contentLines = [];
|
|
78
|
+
for (const line of wrappedLines) {
|
|
79
|
+
if (isImageLine(line)) {
|
|
80
|
+
contentLines.push(line);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const lineWithMargins = leftMargin + line + rightMargin;
|
|
84
|
+
if (backgroundFn) {
|
|
85
|
+
contentLines.push(applyBackgroundToLine(lineWithMargins, width, backgroundFn));
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
// No background - just pad to width
|
|
89
|
+
const visibleLen = visibleWidth(lineWithMargins);
|
|
90
|
+
const paddingNeeded = Math.max(0, width - visibleLen);
|
|
91
|
+
contentLines.push(lineWithMargins + " ".repeat(paddingNeeded));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Add top/bottom padding (empty lines)
|
|
95
|
+
const emptyLine = " ".repeat(width);
|
|
96
|
+
const emptyLines = [];
|
|
97
|
+
for (let i = 0; i < this.paddingY; i++) {
|
|
98
|
+
const line = backgroundFn ? applyBackgroundToLine(emptyLine, width, backgroundFn) : emptyLine;
|
|
99
|
+
emptyLines.push(line);
|
|
100
|
+
}
|
|
101
|
+
// Combine top padding, content, and bottom padding
|
|
102
|
+
const result = [...emptyLines, ...contentLines, ...emptyLines];
|
|
103
|
+
// Update cache
|
|
104
|
+
this.cachedText = this.text;
|
|
105
|
+
this.cachedWidth = width;
|
|
106
|
+
this.cachedLines = result;
|
|
107
|
+
return result.length > 0 ? result : [""];
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Apply default text style to a string.
|
|
111
|
+
* This is the base styling applied to all text content.
|
|
112
|
+
* NOTE: Background color is NOT applied here - it's applied at the padding stage
|
|
113
|
+
* to ensure it extends to the full line width.
|
|
114
|
+
*/
|
|
115
|
+
applyDefaultStyle(text) {
|
|
116
|
+
if (!this.defaultTextStyle) {
|
|
117
|
+
return text;
|
|
118
|
+
}
|
|
119
|
+
let styled = text;
|
|
120
|
+
// Apply foreground color (NOT background - that's applied at padding stage)
|
|
121
|
+
if (this.defaultTextStyle.color) {
|
|
122
|
+
styled = this.defaultTextStyle.color(styled);
|
|
123
|
+
}
|
|
124
|
+
// Apply text decorations using this.theme
|
|
125
|
+
if (this.defaultTextStyle.bold) {
|
|
126
|
+
styled = this.theme.bold(styled);
|
|
127
|
+
}
|
|
128
|
+
if (this.defaultTextStyle.italic) {
|
|
129
|
+
styled = this.theme.italic(styled);
|
|
130
|
+
}
|
|
131
|
+
if (this.defaultTextStyle.strikethrough) {
|
|
132
|
+
styled = this.theme.strikethrough(styled);
|
|
133
|
+
}
|
|
134
|
+
if (this.defaultTextStyle.underline) {
|
|
135
|
+
styled = this.theme.underline(styled);
|
|
136
|
+
}
|
|
137
|
+
return styled;
|
|
138
|
+
}
|
|
139
|
+
getDefaultStylePrefix() {
|
|
140
|
+
if (!this.defaultTextStyle) {
|
|
141
|
+
return "";
|
|
142
|
+
}
|
|
143
|
+
if (this.defaultStylePrefix !== undefined) {
|
|
144
|
+
return this.defaultStylePrefix;
|
|
145
|
+
}
|
|
146
|
+
const sentinel = "\u0000";
|
|
147
|
+
let styled = sentinel;
|
|
148
|
+
if (this.defaultTextStyle.color) {
|
|
149
|
+
styled = this.defaultTextStyle.color(styled);
|
|
150
|
+
}
|
|
151
|
+
if (this.defaultTextStyle.bold) {
|
|
152
|
+
styled = this.theme.bold(styled);
|
|
153
|
+
}
|
|
154
|
+
if (this.defaultTextStyle.italic) {
|
|
155
|
+
styled = this.theme.italic(styled);
|
|
156
|
+
}
|
|
157
|
+
if (this.defaultTextStyle.strikethrough) {
|
|
158
|
+
styled = this.theme.strikethrough(styled);
|
|
159
|
+
}
|
|
160
|
+
if (this.defaultTextStyle.underline) {
|
|
161
|
+
styled = this.theme.underline(styled);
|
|
162
|
+
}
|
|
163
|
+
const sentinelIndex = styled.indexOf(sentinel);
|
|
164
|
+
this.defaultStylePrefix = sentinelIndex >= 0 ? styled.slice(0, sentinelIndex) : "";
|
|
165
|
+
return this.defaultStylePrefix;
|
|
166
|
+
}
|
|
167
|
+
// Use imported getStylePrefix from markdown/types.js
|
|
168
|
+
getDefaultInlineStyleContext() {
|
|
169
|
+
return {
|
|
170
|
+
applyText: (text) => this.applyDefaultStyle(text),
|
|
171
|
+
stylePrefix: this.getDefaultStylePrefix(),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
renderToken(token, width, nextTokenType) {
|
|
175
|
+
const lines = [];
|
|
176
|
+
switch (token.type) {
|
|
177
|
+
case "heading": {
|
|
178
|
+
const headingLevel = token.depth;
|
|
179
|
+
const headingPrefix = `${"#".repeat(headingLevel)} `;
|
|
180
|
+
const headingText = this.renderInlineTokens(token.tokens || []);
|
|
181
|
+
let styledHeading;
|
|
182
|
+
if (headingLevel === 1) {
|
|
183
|
+
styledHeading = this.theme.heading(this.theme.bold(this.theme.underline(headingText)));
|
|
184
|
+
}
|
|
185
|
+
else if (headingLevel === 2) {
|
|
186
|
+
styledHeading = this.theme.heading(this.theme.bold(headingText));
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
styledHeading = this.theme.heading(this.theme.bold(headingPrefix + headingText));
|
|
190
|
+
}
|
|
191
|
+
lines.push(styledHeading);
|
|
192
|
+
if (nextTokenType !== "space") {
|
|
193
|
+
lines.push(""); // Add spacing after headings (unless space token follows)
|
|
194
|
+
}
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
case "paragraph": {
|
|
198
|
+
const paragraphText = this.renderInlineTokens(token.tokens || []);
|
|
199
|
+
lines.push(paragraphText);
|
|
200
|
+
// Don't add spacing if next token is space or list
|
|
201
|
+
if (nextTokenType && nextTokenType !== "list" && nextTokenType !== "space") {
|
|
202
|
+
lines.push("");
|
|
203
|
+
}
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
case "code": {
|
|
207
|
+
const indent = this.theme.codeBlockIndent ?? " ";
|
|
208
|
+
lines.push(this.theme.codeBlockBorder(`\`\`\`${token.lang || ""}`));
|
|
209
|
+
if (this.theme.highlightCode) {
|
|
210
|
+
const highlightedLines = this.theme.highlightCode(token.text, token.lang);
|
|
211
|
+
for (const hlLine of highlightedLines) {
|
|
212
|
+
lines.push(`${indent}${hlLine}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
// Split code by newlines and style each line
|
|
217
|
+
const codeLines = token.text.split("\n");
|
|
218
|
+
for (const codeLine of codeLines) {
|
|
219
|
+
lines.push(`${indent}${this.theme.codeBlock(codeLine)}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
lines.push(this.theme.codeBlockBorder("```"));
|
|
223
|
+
if (nextTokenType !== "space") {
|
|
224
|
+
lines.push(""); // Add spacing after code blocks (unless space token follows)
|
|
225
|
+
}
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
case "list": {
|
|
229
|
+
const listLines = this.renderList(token, 0);
|
|
230
|
+
lines.push(...listLines);
|
|
231
|
+
// Don't add spacing after lists if a space token follows
|
|
232
|
+
// (the space token will handle it)
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
case "table": {
|
|
236
|
+
const tableLines = this.renderTable(token, width);
|
|
237
|
+
lines.push(...tableLines);
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
case "blockquote": {
|
|
241
|
+
const quoteStyle = (text) => this.theme.quote(this.theme.italic(text));
|
|
242
|
+
const quoteStyleContext = {
|
|
243
|
+
applyText: quoteStyle,
|
|
244
|
+
stylePrefix: getStylePrefix(quoteStyle),
|
|
245
|
+
};
|
|
246
|
+
const quoteText = this.renderInlineTokens(token.tokens || [], quoteStyleContext);
|
|
247
|
+
const quoteLines = quoteText.split("\n");
|
|
248
|
+
// Calculate available width for quote content (subtract border "│ " = 2 chars)
|
|
249
|
+
const quoteContentWidth = Math.max(1, width - 2);
|
|
250
|
+
for (const quoteLine of quoteLines) {
|
|
251
|
+
// Wrap the styled line, then add border to each wrapped line
|
|
252
|
+
const wrappedLines = wrapTextWithAnsi(quoteLine, quoteContentWidth);
|
|
253
|
+
for (const wrappedLine of wrappedLines) {
|
|
254
|
+
lines.push(this.theme.quoteBorder("│ ") + wrappedLine);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (nextTokenType !== "space") {
|
|
258
|
+
lines.push(""); // Add spacing after blockquotes (unless space token follows)
|
|
259
|
+
}
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
case "hr":
|
|
263
|
+
lines.push(this.theme.hr("─".repeat(Math.min(width, 80))));
|
|
264
|
+
if (nextTokenType !== "space") {
|
|
265
|
+
lines.push(""); // Add spacing after horizontal rules (unless space token follows)
|
|
266
|
+
}
|
|
267
|
+
break;
|
|
268
|
+
case "html":
|
|
269
|
+
// Render HTML as plain text (escaped for terminal)
|
|
270
|
+
if ("raw" in token && typeof token.raw === "string") {
|
|
271
|
+
lines.push(this.applyDefaultStyle(token.raw.trim()));
|
|
272
|
+
}
|
|
273
|
+
break;
|
|
274
|
+
case "space":
|
|
275
|
+
// Space tokens represent blank lines in markdown
|
|
276
|
+
lines.push("");
|
|
277
|
+
break;
|
|
278
|
+
default:
|
|
279
|
+
// Handle any other token types as plain text
|
|
280
|
+
if ("text" in token && typeof token.text === "string") {
|
|
281
|
+
lines.push(token.text);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return lines;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Render inline tokens to styled text.
|
|
288
|
+
* Delegates to the extracted inline-renderer utility.
|
|
289
|
+
*/
|
|
290
|
+
renderInlineTokens(tokens, styleContext) {
|
|
291
|
+
const context = {
|
|
292
|
+
theme: this.theme,
|
|
293
|
+
getDefaultInlineStyleContext: () => this.getDefaultInlineStyleContext(),
|
|
294
|
+
};
|
|
295
|
+
return renderInlineTokensUtil(tokens, context, styleContext);
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Render a list with proper nesting support.
|
|
299
|
+
* Delegates to the extracted list-renderer utility.
|
|
300
|
+
*/
|
|
301
|
+
renderList(token, depth) {
|
|
302
|
+
const context = {
|
|
303
|
+
theme: this.theme,
|
|
304
|
+
renderInlineTokens: (tokens, styleContext) => this.renderInlineTokens(tokens, styleContext),
|
|
305
|
+
};
|
|
306
|
+
return renderListUtil(token, depth, context);
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Render a table with width-aware cell wrapping.
|
|
310
|
+
* Delegates to the extracted table-renderer utility.
|
|
311
|
+
*/
|
|
312
|
+
renderTable(token, availableWidth) {
|
|
313
|
+
const context = {
|
|
314
|
+
theme: this.theme,
|
|
315
|
+
renderInlineTokens: (tokens, styleContext) => this.renderInlineTokens(tokens, styleContext),
|
|
316
|
+
};
|
|
317
|
+
return renderTableUtil(token, availableWidth, context);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProgressBar component for displaying progress indicators.
|
|
3
|
+
*/
|
|
4
|
+
import type { Component } from "../tui.js";
|
|
5
|
+
/**
|
|
6
|
+
* Theme functions for ProgressBar styling.
|
|
7
|
+
*/
|
|
8
|
+
export interface ProgressBarTheme {
|
|
9
|
+
/** Style function for the filled portion of the bar */
|
|
10
|
+
filled: (text: string) => string;
|
|
11
|
+
/** Style function for the empty portion of the bar */
|
|
12
|
+
empty: (text: string) => string;
|
|
13
|
+
/** Style function for the percentage label */
|
|
14
|
+
label?: (text: string) => string;
|
|
15
|
+
/** Style function for the optional message */
|
|
16
|
+
message?: (text: string) => string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Options for ProgressBar configuration.
|
|
20
|
+
*/
|
|
21
|
+
export interface ProgressBarOptions {
|
|
22
|
+
/** Theme for styling the progress bar */
|
|
23
|
+
theme?: ProgressBarTheme;
|
|
24
|
+
/** Character to use for the filled portion (default: "█") */
|
|
25
|
+
filledChar?: string;
|
|
26
|
+
/** Character to use for the empty portion (default: "░") */
|
|
27
|
+
emptyChar?: string;
|
|
28
|
+
/** Whether to show percentage label (default: true) */
|
|
29
|
+
showPercentage?: boolean;
|
|
30
|
+
/** Optional message to display */
|
|
31
|
+
message?: string;
|
|
32
|
+
/** Horizontal padding (default: 0) */
|
|
33
|
+
paddingX?: number;
|
|
34
|
+
/** Width of the bar portion (default: auto-calculate) */
|
|
35
|
+
barWidth?: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* ProgressBar component that displays a visual progress indicator.
|
|
39
|
+
*
|
|
40
|
+
* Features:
|
|
41
|
+
* - Configurable filled/empty characters
|
|
42
|
+
* - Optional percentage label
|
|
43
|
+
* - Optional message
|
|
44
|
+
* - Theming support
|
|
45
|
+
*/
|
|
46
|
+
export declare class ProgressBar implements Component {
|
|
47
|
+
private progress;
|
|
48
|
+
private theme;
|
|
49
|
+
private filledChar;
|
|
50
|
+
private emptyChar;
|
|
51
|
+
private showPercentage;
|
|
52
|
+
private message?;
|
|
53
|
+
private paddingX;
|
|
54
|
+
private barWidth?;
|
|
55
|
+
private cachedProgress?;
|
|
56
|
+
private cachedWidth?;
|
|
57
|
+
private cachedMessage?;
|
|
58
|
+
private cachedLines?;
|
|
59
|
+
constructor(progress?: number, options?: ProgressBarOptions);
|
|
60
|
+
setProgress(progress: number): void;
|
|
61
|
+
getProgress(): number;
|
|
62
|
+
setMessage(message?: string): void;
|
|
63
|
+
setTheme(theme: ProgressBarTheme): void;
|
|
64
|
+
invalidate(): void;
|
|
65
|
+
render(width: number): string[];
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=progress-bar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress-bar.d.ts","sourceRoot":"","sources":["../../src/components/progress-bar.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAG3C;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,uDAAuD;IACvD,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACjC,sDAAsD;IACtD,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAChC,8CAA8C;IAC9C,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACjC,8CAA8C;IAC9C,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAClC,yCAAyC;IACzC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,kCAAkC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAYD;;;;;;;;GAQG;AACH,qBAAa,WAAY,YAAW,SAAS;IAC5C,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,cAAc,CAAU;IAChC,OAAO,CAAC,OAAO,CAAC,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAC,CAAS;IAG1B,OAAO,CAAC,cAAc,CAAC,CAAS;IAChC,OAAO,CAAC,WAAW,CAAC,CAAS;IAC7B,OAAO,CAAC,aAAa,CAAC,CAAS;IAC/B,OAAO,CAAC,WAAW,CAAC,CAAW;gBAEnB,QAAQ,SAAI,EAAE,OAAO,GAAE,kBAAuB;IAW1D,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKnC,WAAW,IAAI,MAAM;IAIrB,UAAU,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;IAKlC,QAAQ,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI;IAKvC,UAAU,IAAI,IAAI;IAOlB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;CAmE/B"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProgressBar component for displaying progress indicators.
|
|
3
|
+
*/
|
|
4
|
+
import { visibleWidth } from "../utils.js";
|
|
5
|
+
/**
|
|
6
|
+
* Default theme with no styling.
|
|
7
|
+
*/
|
|
8
|
+
const DEFAULT_THEME = {
|
|
9
|
+
filled: (text) => text,
|
|
10
|
+
empty: (text) => text,
|
|
11
|
+
label: (text) => text,
|
|
12
|
+
message: (text) => text,
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* ProgressBar component that displays a visual progress indicator.
|
|
16
|
+
*
|
|
17
|
+
* Features:
|
|
18
|
+
* - Configurable filled/empty characters
|
|
19
|
+
* - Optional percentage label
|
|
20
|
+
* - Optional message
|
|
21
|
+
* - Theming support
|
|
22
|
+
*/
|
|
23
|
+
export class ProgressBar {
|
|
24
|
+
progress; // 0-1 range
|
|
25
|
+
theme;
|
|
26
|
+
filledChar;
|
|
27
|
+
emptyChar;
|
|
28
|
+
showPercentage;
|
|
29
|
+
message;
|
|
30
|
+
paddingX;
|
|
31
|
+
barWidth;
|
|
32
|
+
// Cache
|
|
33
|
+
cachedProgress;
|
|
34
|
+
cachedWidth;
|
|
35
|
+
cachedMessage;
|
|
36
|
+
cachedLines;
|
|
37
|
+
constructor(progress = 0, options = {}) {
|
|
38
|
+
this.progress = Math.max(0, Math.min(1, progress));
|
|
39
|
+
this.theme = options.theme ?? DEFAULT_THEME;
|
|
40
|
+
this.filledChar = options.filledChar ?? "█";
|
|
41
|
+
this.emptyChar = options.emptyChar ?? "░";
|
|
42
|
+
this.showPercentage = options.showPercentage ?? true;
|
|
43
|
+
this.message = options.message;
|
|
44
|
+
this.paddingX = options.paddingX ?? 0;
|
|
45
|
+
this.barWidth = options.barWidth;
|
|
46
|
+
}
|
|
47
|
+
setProgress(progress) {
|
|
48
|
+
this.progress = Math.max(0, Math.min(1, progress));
|
|
49
|
+
this.invalidate();
|
|
50
|
+
}
|
|
51
|
+
getProgress() {
|
|
52
|
+
return this.progress;
|
|
53
|
+
}
|
|
54
|
+
setMessage(message) {
|
|
55
|
+
this.message = message;
|
|
56
|
+
this.invalidate();
|
|
57
|
+
}
|
|
58
|
+
setTheme(theme) {
|
|
59
|
+
this.theme = theme;
|
|
60
|
+
this.invalidate();
|
|
61
|
+
}
|
|
62
|
+
invalidate() {
|
|
63
|
+
this.cachedProgress = undefined;
|
|
64
|
+
this.cachedWidth = undefined;
|
|
65
|
+
this.cachedMessage = undefined;
|
|
66
|
+
this.cachedLines = undefined;
|
|
67
|
+
}
|
|
68
|
+
render(width) {
|
|
69
|
+
// Check cache
|
|
70
|
+
if (this.cachedLines &&
|
|
71
|
+
this.cachedProgress === this.progress &&
|
|
72
|
+
this.cachedWidth === width &&
|
|
73
|
+
this.cachedMessage === this.message) {
|
|
74
|
+
return this.cachedLines;
|
|
75
|
+
}
|
|
76
|
+
const contentWidth = Math.max(1, width - this.paddingX * 2);
|
|
77
|
+
const leftPad = " ".repeat(this.paddingX);
|
|
78
|
+
// Calculate percentage label
|
|
79
|
+
const percentLabel = this.showPercentage ? `${Math.round(this.progress * 100)}%` : "";
|
|
80
|
+
const percentLabelWidth = this.showPercentage ? visibleWidth(percentLabel) + 1 : 0; // +1 for space
|
|
81
|
+
// Calculate bar width
|
|
82
|
+
let barWidth;
|
|
83
|
+
if (this.barWidth !== undefined) {
|
|
84
|
+
barWidth = Math.max(1, Math.min(this.barWidth, contentWidth - percentLabelWidth));
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
barWidth = Math.max(1, contentWidth - percentLabelWidth);
|
|
88
|
+
}
|
|
89
|
+
// Calculate filled/empty portions
|
|
90
|
+
const filledWidth = Math.round(barWidth * this.progress);
|
|
91
|
+
const emptyWidth = barWidth - filledWidth;
|
|
92
|
+
// Build the bar
|
|
93
|
+
const filledPart = this.theme.filled(this.filledChar.repeat(filledWidth));
|
|
94
|
+
const emptyPart = this.theme.empty(this.emptyChar.repeat(emptyWidth));
|
|
95
|
+
const bar = filledPart + emptyPart;
|
|
96
|
+
// Build the line
|
|
97
|
+
let line;
|
|
98
|
+
if (this.showPercentage) {
|
|
99
|
+
const styledLabel = this.theme.label ? this.theme.label(percentLabel) : percentLabel;
|
|
100
|
+
line = `${bar} ${styledLabel}`;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
line = bar;
|
|
104
|
+
}
|
|
105
|
+
// Pad to width
|
|
106
|
+
const lineWidth = visibleWidth(line);
|
|
107
|
+
const padding = Math.max(0, contentWidth - lineWidth);
|
|
108
|
+
const paddedLine = leftPad + line + " ".repeat(padding) + " ".repeat(this.paddingX);
|
|
109
|
+
const lines = [paddedLine];
|
|
110
|
+
// Add message line if present
|
|
111
|
+
if (this.message) {
|
|
112
|
+
const styledMessage = this.theme.message ? this.theme.message(this.message) : this.message;
|
|
113
|
+
const msgWidth = visibleWidth(styledMessage);
|
|
114
|
+
const msgPadding = Math.max(0, contentWidth - msgWidth);
|
|
115
|
+
lines.push(leftPad + styledMessage + " ".repeat(msgPadding) + " ".repeat(this.paddingX));
|
|
116
|
+
}
|
|
117
|
+
// Update cache
|
|
118
|
+
this.cachedProgress = this.progress;
|
|
119
|
+
this.cachedWidth = width;
|
|
120
|
+
this.cachedMessage = this.message;
|
|
121
|
+
this.cachedLines = lines;
|
|
122
|
+
return lines;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Component } from "../tui.js";
|
|
2
|
+
export interface SelectItem {
|
|
3
|
+
value: string;
|
|
4
|
+
label: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface SelectListTheme {
|
|
8
|
+
selectedPrefix: (text: string) => string;
|
|
9
|
+
selectedText: (text: string) => string;
|
|
10
|
+
description: (text: string) => string;
|
|
11
|
+
scrollInfo: (text: string) => string;
|
|
12
|
+
noMatch: (text: string) => string;
|
|
13
|
+
}
|
|
14
|
+
export declare class SelectList implements Component {
|
|
15
|
+
private items;
|
|
16
|
+
private filteredItems;
|
|
17
|
+
private selectedIndex;
|
|
18
|
+
private maxVisible;
|
|
19
|
+
private theme;
|
|
20
|
+
onSelect?: (item: SelectItem) => void;
|
|
21
|
+
onCancel?: () => void;
|
|
22
|
+
onSelectionChange?: (item: SelectItem) => void;
|
|
23
|
+
constructor(items: SelectItem[], maxVisible: number, theme: SelectListTheme);
|
|
24
|
+
setFilter(filter: string): void;
|
|
25
|
+
setSelectedIndex(index: number): void;
|
|
26
|
+
invalidate(): void;
|
|
27
|
+
render(width: number): string[];
|
|
28
|
+
handleInput(keyData: string): void;
|
|
29
|
+
private notifySelectionChange;
|
|
30
|
+
getSelectedItem(): SelectItem | null;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=select-list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"select-list.d.ts","sourceRoot":"","sources":["../../src/components/select-list.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAK3C,MAAM,WAAW,UAAU;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC/B,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACzC,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACvC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACrC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CAClC;AAED,qBAAa,UAAW,YAAW,SAAS;IAC3C,OAAO,CAAC,KAAK,CAAoB;IACjC,OAAO,CAAC,aAAa,CAAoB;IACzC,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,KAAK,CAAkB;IAExB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;gBAE1C,KAAK,EAAE,UAAU,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe;IAO3E,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAM/B,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIrC,UAAU,IAAI,IAAI;IAIlB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IAiG/B,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IA2BlC,OAAO,CAAC,qBAAqB;IAO7B,eAAe,IAAI,UAAU,GAAG,IAAI;CAIpC"}
|