@promptctl/rich-js 0.16.0 → 0.18.0
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 +14 -27
- package/package.json +7 -3
- package/dist/core/box.d.ts +0 -144
- package/dist/core/box.js +0 -391
- package/dist/core/cells.d.ts +0 -148
- package/dist/core/cells.js +0 -266
- package/dist/core/color.d.ts +0 -229
- package/dist/core/color.js +0 -992
- package/dist/core/console.d.ts +0 -142
- package/dist/core/console.js +0 -477
- package/dist/core/emoji.d.ts +0 -24
- package/dist/core/emoji.js +0 -391
- package/dist/core/export-html.d.ts +0 -24
- package/dist/core/export-html.js +0 -106
- package/dist/core/export-lines.d.ts +0 -121
- package/dist/core/export-lines.js +0 -141
- package/dist/core/highlighter.d.ts +0 -33
- package/dist/core/highlighter.js +0 -124
- package/dist/core/json.d.ts +0 -27
- package/dist/core/json.js +0 -53
- package/dist/core/markup.d.ts +0 -97
- package/dist/core/markup.js +0 -619
- package/dist/core/measure.d.ts +0 -27
- package/dist/core/measure.js +0 -61
- package/dist/core/oklch.d.ts +0 -161
- package/dist/core/oklch.js +0 -312
- package/dist/core/osc8.d.ts +0 -68
- package/dist/core/osc8.js +0 -90
- package/dist/core/pretty.d.ts +0 -141
- package/dist/core/pretty.js +0 -472
- package/dist/core/protocol.d.ts +0 -137
- package/dist/core/protocol.js +0 -101
- package/dist/core/render.d.ts +0 -69
- package/dist/core/render.js +0 -140
- package/dist/core/segment.d.ts +0 -142
- package/dist/core/segment.js +0 -433
- package/dist/core/spinnerData.d.ts +0 -10
- package/dist/core/spinnerData.js +0 -115
- package/dist/core/strip.d.ts +0 -139
- package/dist/core/strip.js +0 -302
- package/dist/core/style.d.ts +0 -149
- package/dist/core/style.js +0 -741
- package/dist/core/subscription.d.ts +0 -12
- package/dist/core/subscription.js +0 -12
- package/dist/core/text.d.ts +0 -255
- package/dist/core/text.js +0 -990
- package/dist/core/wrap.d.ts +0 -41
- package/dist/core/wrap.js +0 -127
- package/dist/host/host-stream.d.ts +0 -30
- package/dist/host/host-stream.js +0 -35
- package/dist/host/index.d.ts +0 -3
- package/dist/host/index.js +0 -2
- package/dist/host/terminal-host.d.ts +0 -143
- package/dist/host/terminal-host.js +0 -132
- package/dist/index.d.ts +0 -87
- package/dist/index.js +0 -104
- package/dist/node/prompt.d.ts +0 -21
- package/dist/node/prompt.js +0 -30
- package/dist/node/save.d.ts +0 -17
- package/dist/node/save.js +0 -21
- package/dist/node/terminal-host.d.ts +0 -84
- package/dist/node/terminal-host.js +0 -160
- package/dist/node/traceback.d.ts +0 -27
- package/dist/node/traceback.js +0 -112
- package/dist/renderables/align.d.ts +0 -16
- package/dist/renderables/align.js +0 -46
- package/dist/renderables/columns.d.ts +0 -61
- package/dist/renderables/columns.js +0 -168
- package/dist/renderables/constrain.d.ts +0 -15
- package/dist/renderables/constrain.js +0 -37
- package/dist/renderables/flexStrip.d.ts +0 -41
- package/dist/renderables/flexStrip.js +0 -200
- package/dist/renderables/group.d.ts +0 -11
- package/dist/renderables/group.js +0 -15
- package/dist/renderables/layout.d.ts +0 -92
- package/dist/renderables/layout.js +0 -315
- package/dist/renderables/live.d.ts +0 -47
- package/dist/renderables/live.js +0 -131
- package/dist/renderables/markdown.d.ts +0 -24
- package/dist/renderables/markdown.js +0 -182
- package/dist/renderables/padding.d.ts +0 -38
- package/dist/renderables/padding.js +0 -140
- package/dist/renderables/panel.d.ts +0 -116
- package/dist/renderables/panel.js +0 -322
- package/dist/renderables/progress.d.ts +0 -90
- package/dist/renderables/progress.js +0 -241
- package/dist/renderables/progressBar.d.ts +0 -31
- package/dist/renderables/progressBar.js +0 -52
- package/dist/renderables/prompt.d.ts +0 -49
- package/dist/renderables/prompt.js +0 -125
- package/dist/renderables/rule.d.ts +0 -24
- package/dist/renderables/rule.js +0 -90
- package/dist/renderables/spinner.d.ts +0 -29
- package/dist/renderables/spinner.js +0 -64
- package/dist/renderables/status.d.ts +0 -23
- package/dist/renderables/status.js +0 -60
- package/dist/renderables/syntax.d.ts +0 -35
- package/dist/renderables/syntax.js +0 -114
- package/dist/renderables/table.d.ts +0 -182
- package/dist/renderables/table.js +0 -697
- package/dist/renderables/traceback.d.ts +0 -31
- package/dist/renderables/traceback.js +0 -109
- package/dist/renderables/tree.d.ts +0 -54
- package/dist/renderables/tree.js +0 -159
- package/dist/template-bindings/color-funcs.d.ts +0 -87
- package/dist/template-bindings/color-funcs.js +0 -219
- package/dist/template-bindings/helpers.d.ts +0 -24
- package/dist/template-bindings/helpers.js +0 -48
- package/dist/template-bindings/index.d.ts +0 -107
- package/dist/template-bindings/index.js +0 -144
- package/dist/template-bindings/palette-funcs.d.ts +0 -94
- package/dist/template-bindings/palette-funcs.js +0 -151
- package/dist/template-bindings/style-funcs.d.ts +0 -28
- package/dist/template-bindings/style-funcs.js +0 -151
- package/dist/themes/buildPalette.d.ts +0 -36
- package/dist/themes/buildPalette.js +0 -56
- package/dist/themes/colorMath.d.ts +0 -82
- package/dist/themes/colorMath.js +0 -254
- package/dist/themes/colorRef.d.ts +0 -51
- package/dist/themes/colorRef.js +0 -96
- package/dist/themes/data/atom-one-dark.d.ts +0 -3
- package/dist/themes/data/atom-one-dark.js +0 -158
- package/dist/themes/data/atom-one-light.d.ts +0 -3
- package/dist/themes/data/atom-one-light.js +0 -158
- package/dist/themes/data/catppuccin-frappe.d.ts +0 -3
- package/dist/themes/data/catppuccin-frappe.js +0 -158
- package/dist/themes/data/catppuccin-latte.d.ts +0 -3
- package/dist/themes/data/catppuccin-latte.js +0 -158
- package/dist/themes/data/catppuccin-macchiato.d.ts +0 -3
- package/dist/themes/data/catppuccin-macchiato.js +0 -158
- package/dist/themes/data/catppuccin-mocha.d.ts +0 -3
- package/dist/themes/data/catppuccin-mocha.js +0 -158
- package/dist/themes/data/cyberpunk.d.ts +0 -17
- package/dist/themes/data/cyberpunk.js +0 -191
- package/dist/themes/data/default.d.ts +0 -3
- package/dist/themes/data/default.js +0 -158
- package/dist/themes/data/dracula.d.ts +0 -3
- package/dist/themes/data/dracula.js +0 -158
- package/dist/themes/data/flexoki.d.ts +0 -3
- package/dist/themes/data/flexoki.js +0 -158
- package/dist/themes/data/gruvbox.d.ts +0 -3
- package/dist/themes/data/gruvbox.js +0 -158
- package/dist/themes/data/index.d.ts +0 -35
- package/dist/themes/data/index.js +0 -64
- package/dist/themes/data/monokai.d.ts +0 -3
- package/dist/themes/data/monokai.js +0 -158
- package/dist/themes/data/nord.d.ts +0 -3
- package/dist/themes/data/nord.js +0 -158
- package/dist/themes/data/rose-pine-dawn.d.ts +0 -3
- package/dist/themes/data/rose-pine-dawn.js +0 -158
- package/dist/themes/data/rose-pine-moon.d.ts +0 -3
- package/dist/themes/data/rose-pine-moon.js +0 -158
- package/dist/themes/data/rose-pine.d.ts +0 -3
- package/dist/themes/data/rose-pine.js +0 -158
- package/dist/themes/data/solarized-dark.d.ts +0 -3
- package/dist/themes/data/solarized-dark.js +0 -158
- package/dist/themes/data/solarized-light.d.ts +0 -3
- package/dist/themes/data/solarized-light.js +0 -158
- package/dist/themes/data/svg-export.d.ts +0 -3
- package/dist/themes/data/svg-export.js +0 -158
- package/dist/themes/data/textual-ansi.d.ts +0 -3
- package/dist/themes/data/textual-ansi.js +0 -158
- package/dist/themes/data/textual-dark.d.ts +0 -3
- package/dist/themes/data/textual-dark.js +0 -158
- package/dist/themes/data/textual-light.d.ts +0 -3
- package/dist/themes/data/textual-light.js +0 -158
- package/dist/themes/data/tokyo-night.d.ts +0 -3
- package/dist/themes/data/tokyo-night.js +0 -158
- package/dist/themes/data/types.d.ts +0 -15
- package/dist/themes/data/types.js +0 -1
- package/dist/themes/palette.d.ts +0 -21
- package/dist/themes/palette.js +0 -28
- package/dist/themes/ramp.d.ts +0 -83
- package/dist/themes/ramp.js +0 -122
- package/dist/themes/registry.d.ts +0 -43
- package/dist/themes/registry.js +0 -82
- package/dist/themes/terminalThemes.d.ts +0 -54
- package/dist/themes/terminalThemes.js +0 -68
- package/dist/themes/transpose.d.ts +0 -87
- package/dist/themes/transpose.js +0 -138
- package/dist/widgets/button.d.ts +0 -49
- package/dist/widgets/button.js +0 -206
- package/dist/widgets/checkbox.d.ts +0 -41
- package/dist/widgets/checkbox.js +0 -155
- package/dist/widgets/dropdown.d.ts +0 -67
- package/dist/widgets/dropdown.js +0 -453
- package/dist/widgets/event-router.d.ts +0 -78
- package/dist/widgets/event-router.js +0 -622
- package/dist/widgets/focus-manager.d.ts +0 -26
- package/dist/widgets/focus-manager.js +0 -193
- package/dist/widgets/index.d.ts +0 -22
- package/dist/widgets/index.js +0 -15
- package/dist/widgets/screen.d.ts +0 -98
- package/dist/widgets/screen.js +0 -542
- package/dist/widgets/slider.d.ts +0 -57
- package/dist/widgets/slider.js +0 -270
- package/dist/widgets/static-item.d.ts +0 -40
- package/dist/widgets/static-item.js +0 -60
- package/dist/widgets/text-input.d.ts +0 -274
- package/dist/widgets/text-input.js +0 -1162
- package/dist/widgets/toggle.d.ts +0 -44
- package/dist/widgets/toggle.js +0 -179
- package/dist/widgets/types.d.ts +0 -108
- package/dist/widgets/types.js +0 -31
- package/dist/widgets/widget-base.d.ts +0 -42
- package/dist/widgets/widget-base.js +0 -173
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Markdown — renders Markdown content to the terminal.
|
|
3
|
-
* Uses built-in parsing (no external dependency).
|
|
4
|
-
*/
|
|
5
|
-
import { Segment } from "../core/segment.js";
|
|
6
|
-
import { Style } from "../core/style.js";
|
|
7
|
-
import { RichText } from "../core/text.js";
|
|
8
|
-
import { Rule } from "./rule.js";
|
|
9
|
-
import { getStyle } from "../core/protocol.js";
|
|
10
|
-
function tokenize(markdown) {
|
|
11
|
-
const lines = markdown.split("\n");
|
|
12
|
-
const tokens = [];
|
|
13
|
-
let i = 0;
|
|
14
|
-
while (i < lines.length) {
|
|
15
|
-
const line = lines[i];
|
|
16
|
-
// Blank line
|
|
17
|
-
if (line.trim() === "") {
|
|
18
|
-
tokens.push({ type: "blank" });
|
|
19
|
-
i++;
|
|
20
|
-
continue;
|
|
21
|
-
}
|
|
22
|
-
// Heading
|
|
23
|
-
const headingMatch = /^(#{1,6})\s+(.+)$/.exec(line);
|
|
24
|
-
if (headingMatch) {
|
|
25
|
-
tokens.push({ type: "heading", level: headingMatch[1].length, text: headingMatch[2] });
|
|
26
|
-
i++;
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
// Horizontal rule
|
|
30
|
-
if (/^(?:---+|===+|\*\*\*+)$/.test(line.trim())) {
|
|
31
|
-
tokens.push({ type: "hr" });
|
|
32
|
-
i++;
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
// Fenced code block
|
|
36
|
-
const codeMatch = /^```(\w*)/.exec(line);
|
|
37
|
-
if (codeMatch) {
|
|
38
|
-
const lang = codeMatch[1] ?? "";
|
|
39
|
-
const codeLines = [];
|
|
40
|
-
i++;
|
|
41
|
-
while (i < lines.length && !lines[i].startsWith("```")) {
|
|
42
|
-
codeLines.push(lines[i]);
|
|
43
|
-
i++;
|
|
44
|
-
}
|
|
45
|
-
i++; // skip closing ```
|
|
46
|
-
tokens.push({ type: "code_block", language: lang, code: codeLines.join("\n") });
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
// Blockquote
|
|
50
|
-
if (line.startsWith("> ")) {
|
|
51
|
-
const quoteLines = [];
|
|
52
|
-
while (i < lines.length && lines[i].startsWith("> ")) {
|
|
53
|
-
quoteLines.push(lines[i].slice(2));
|
|
54
|
-
i++;
|
|
55
|
-
}
|
|
56
|
-
tokens.push({ type: "blockquote", text: quoteLines.join("\n") });
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
// Unordered list
|
|
60
|
-
const ulMatch = /^([*\-+])\s+(.+)$/.exec(line);
|
|
61
|
-
if (ulMatch) {
|
|
62
|
-
tokens.push({ type: "list_item", ordered: false, index: 0, text: ulMatch[2] });
|
|
63
|
-
i++;
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
// Ordered list
|
|
67
|
-
const olMatch = /^(\d+)\.\s+(.+)$/.exec(line);
|
|
68
|
-
if (olMatch) {
|
|
69
|
-
tokens.push({ type: "list_item", ordered: true, index: parseInt(olMatch[1], 10), text: olMatch[2] });
|
|
70
|
-
i++;
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
// Paragraph (collect consecutive non-blank lines)
|
|
74
|
-
const paraLines = [];
|
|
75
|
-
while (i < lines.length && lines[i].trim() !== "" && !/^#{1,6}\s/.test(lines[i]) && !/^```/.test(lines[i])) {
|
|
76
|
-
paraLines.push(lines[i]);
|
|
77
|
-
i++;
|
|
78
|
-
}
|
|
79
|
-
tokens.push({ type: "paragraph", text: paraLines.join(" ") });
|
|
80
|
-
}
|
|
81
|
-
return tokens;
|
|
82
|
-
}
|
|
83
|
-
function applyInlineStyles(text) {
|
|
84
|
-
const result = new RichText("", { end: "" });
|
|
85
|
-
// Process inline patterns
|
|
86
|
-
const inlineRe = /(\*\*(.+?)\*\*|\*(.+?)\*|`(.+?)`|\[(.+?)\]\((.+?)\))/g;
|
|
87
|
-
let lastIdx = 0;
|
|
88
|
-
let match;
|
|
89
|
-
while ((match = inlineRe.exec(text)) !== null) {
|
|
90
|
-
// Add text before match
|
|
91
|
-
if (match.index > lastIdx) {
|
|
92
|
-
result.append(text.slice(lastIdx, match.index));
|
|
93
|
-
}
|
|
94
|
-
if (match[2]) {
|
|
95
|
-
// Bold: **text**
|
|
96
|
-
result.append(match[2], "bold");
|
|
97
|
-
}
|
|
98
|
-
else if (match[3]) {
|
|
99
|
-
// Italic: *text*
|
|
100
|
-
result.append(match[3], "italic");
|
|
101
|
-
}
|
|
102
|
-
else if (match[4]) {
|
|
103
|
-
// Inline code: `text`
|
|
104
|
-
result.append(match[4], "markdown.code");
|
|
105
|
-
}
|
|
106
|
-
else if (match[5] && match[6]) {
|
|
107
|
-
// Link: [text](url)
|
|
108
|
-
result.append(match[5], new Style({ link: match[6] }));
|
|
109
|
-
}
|
|
110
|
-
lastIdx = match.index + match[0].length;
|
|
111
|
-
}
|
|
112
|
-
// Remaining text
|
|
113
|
-
if (lastIdx < text.length) {
|
|
114
|
-
result.append(text.slice(lastIdx));
|
|
115
|
-
}
|
|
116
|
-
return result;
|
|
117
|
-
}
|
|
118
|
-
export class Markdown {
|
|
119
|
-
markdown;
|
|
120
|
-
inlineCodeStyle;
|
|
121
|
-
hyperlinks;
|
|
122
|
-
constructor(markdown, options) {
|
|
123
|
-
this.markdown = markdown;
|
|
124
|
-
this.inlineCodeStyle = options?.inlineCodeStyle ?? "markdown.code";
|
|
125
|
-
this.hyperlinks = options?.hyperlinks !== false;
|
|
126
|
-
}
|
|
127
|
-
*render(options) {
|
|
128
|
-
const tokens = tokenize(this.markdown);
|
|
129
|
-
for (const token of tokens) {
|
|
130
|
-
switch (token.type) {
|
|
131
|
-
case "heading": {
|
|
132
|
-
const style = getStyle(options, `markdown.h${Math.min(token.level, 4)}`);
|
|
133
|
-
const text = applyInlineStyles(token.text);
|
|
134
|
-
yield* Segment.applyStyle([...text.render(options)], style);
|
|
135
|
-
yield Segment.line();
|
|
136
|
-
break;
|
|
137
|
-
}
|
|
138
|
-
case "paragraph": {
|
|
139
|
-
const text = applyInlineStyles(token.text);
|
|
140
|
-
yield* text.render(options);
|
|
141
|
-
yield Segment.line();
|
|
142
|
-
break;
|
|
143
|
-
}
|
|
144
|
-
case "code_block": {
|
|
145
|
-
const codeStyle = getStyle(options, "markdown.code");
|
|
146
|
-
const lines = token.code.split("\n");
|
|
147
|
-
for (const line of lines) {
|
|
148
|
-
yield new Segment(line, codeStyle);
|
|
149
|
-
yield Segment.line();
|
|
150
|
-
}
|
|
151
|
-
break;
|
|
152
|
-
}
|
|
153
|
-
case "hr": {
|
|
154
|
-
const rule = new Rule(undefined, { style: "markdown.hr" });
|
|
155
|
-
yield* rule.render(options);
|
|
156
|
-
break;
|
|
157
|
-
}
|
|
158
|
-
case "list_item": {
|
|
159
|
-
const bullet = token.ordered ? `${token.index}. ` : " • ";
|
|
160
|
-
yield new Segment(bullet);
|
|
161
|
-
const text = applyInlineStyles(token.text);
|
|
162
|
-
yield* text.render({ ...options, maxWidth: options.maxWidth - bullet.length });
|
|
163
|
-
break;
|
|
164
|
-
}
|
|
165
|
-
case "blockquote": {
|
|
166
|
-
const quoteStyle = Style.parse("dim italic");
|
|
167
|
-
yield new Segment("▎ ", getStyle(options, "markdown.hr"));
|
|
168
|
-
const text = applyInlineStyles(token.text);
|
|
169
|
-
yield* Segment.applyStyle([...text.render(options)], quoteStyle);
|
|
170
|
-
yield Segment.line();
|
|
171
|
-
break;
|
|
172
|
-
}
|
|
173
|
-
case "blank":
|
|
174
|
-
yield Segment.line();
|
|
175
|
-
break;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
measure(options) {
|
|
180
|
-
return { minimum: 1, maximum: options.maxWidth };
|
|
181
|
-
}
|
|
182
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Padding — wraps a renderable with whitespace padding on all four sides.
|
|
3
|
-
*/
|
|
4
|
-
import { Segment } from "../core/segment.js";
|
|
5
|
-
import { Style } from "../core/style.js";
|
|
6
|
-
import type { Renderable, Measurable, RenderOptions } from "../core/protocol.js";
|
|
7
|
-
export type PaddingDimensions = number | [number, number] | [number, number, number, number];
|
|
8
|
-
/**
|
|
9
|
-
* Parses the shapes a caller may write padding in — one number, a vertical/
|
|
10
|
-
* horizontal pair, or all four sides — into the four-sided form every
|
|
11
|
-
* renderable indexes by, flooring each side at zero.
|
|
12
|
-
*
|
|
13
|
-
* [LAW:parse-dont-validate] This is the one crossing between the public
|
|
14
|
-
* padding vocabulary and the internal tuple, which is why the floor lives
|
|
15
|
-
* here and nowhere downstream: past this point `" ".repeat(left)` is safe in
|
|
16
|
-
* any renderable without asking what the caller passed. A negative side used
|
|
17
|
-
* to reach the renderers, where `Panel` drew content rows wider than its own
|
|
18
|
-
* border and `Table` threw outright.
|
|
19
|
-
*/
|
|
20
|
-
export declare function normalizePadding(padding: PaddingDimensions): [number, number, number, number];
|
|
21
|
-
export declare class Padding implements Renderable, Measurable {
|
|
22
|
-
readonly renderable: Renderable;
|
|
23
|
-
readonly top: number;
|
|
24
|
-
readonly right: number;
|
|
25
|
-
readonly bottom: number;
|
|
26
|
-
readonly left: number;
|
|
27
|
-
readonly style: string | Style;
|
|
28
|
-
readonly expand: boolean;
|
|
29
|
-
constructor(renderable: Renderable, padding: PaddingDimensions, options?: {
|
|
30
|
-
style?: string | Style;
|
|
31
|
-
expand?: boolean;
|
|
32
|
-
});
|
|
33
|
-
render(rawOptions: RenderOptions): Iterable<Segment>;
|
|
34
|
-
measure(rawOptions: RenderOptions): {
|
|
35
|
-
minimum: number;
|
|
36
|
-
maximum: number;
|
|
37
|
-
};
|
|
38
|
-
}
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Padding — wraps a renderable with whitespace padding on all four sides.
|
|
3
|
-
*/
|
|
4
|
-
import { Segment } from "../core/segment.js";
|
|
5
|
-
import { cellCount } from "../core/cells.js";
|
|
6
|
-
import { NULL_STYLE } from "../core/style.js";
|
|
7
|
-
import { Measurement } from "../core/measure.js";
|
|
8
|
-
import { getStyle, isMeasurable, withBoundedWidth, withCellWidth } from "../core/protocol.js";
|
|
9
|
-
/**
|
|
10
|
-
* Parses the shapes a caller may write padding in — one number, a vertical/
|
|
11
|
-
* horizontal pair, or all four sides — into the four-sided form every
|
|
12
|
-
* renderable indexes by, flooring each side at zero.
|
|
13
|
-
*
|
|
14
|
-
* [LAW:parse-dont-validate] This is the one crossing between the public
|
|
15
|
-
* padding vocabulary and the internal tuple, which is why the floor lives
|
|
16
|
-
* here and nowhere downstream: past this point `" ".repeat(left)` is safe in
|
|
17
|
-
* any renderable without asking what the caller passed. A negative side used
|
|
18
|
-
* to reach the renderers, where `Panel` drew content rows wider than its own
|
|
19
|
-
* border and `Table` threw outright.
|
|
20
|
-
*/
|
|
21
|
-
export function normalizePadding(padding) {
|
|
22
|
-
// A padding side is a count of cells like any other, so it is parsed by the
|
|
23
|
-
// same rule rather than by a second copy of it: a fractional side reaches
|
|
24
|
-
// `" ".repeat`, which truncates, while the rows around it are measured
|
|
25
|
-
// arithmetically — so the padding and the border disagree and the frame
|
|
26
|
-
// breaks. `cellCount`'s header carries why the comparison, not `Math.max`.
|
|
27
|
-
//
|
|
28
|
-
// `Infinity` is where a side stops being an offer and the rule needs the one
|
|
29
|
-
// addition. An unbounded *offer* is a question a renderable can answer, so
|
|
30
|
-
// `cellCount` passes it through; an unbounded *side* is a quantity and there
|
|
31
|
-
// is nothing to draw. Unparsed it reached `budget -= got` as
|
|
32
|
-
// `Infinity - Infinity`, and `new Padding(text, Infinity).measure(...)`
|
|
33
|
-
// handed back `{NaN, NaN}` from a public API.
|
|
34
|
-
const side = (n) => (Number.isFinite(n) ? cellCount(n) : 0);
|
|
35
|
-
const sides = typeof padding === "number"
|
|
36
|
-
? [padding, padding, padding, padding]
|
|
37
|
-
: padding.length === 2
|
|
38
|
-
? [padding[0], padding[1], padding[0], padding[1]]
|
|
39
|
-
: padding;
|
|
40
|
-
return [side(sides[0]), side(sides[1]), side(sides[2]), side(sides[3])];
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Divides the requested width once into the three spans a padded row is made
|
|
44
|
-
* of, in the order they are worth spending cells on.
|
|
45
|
-
*
|
|
46
|
-
* The three used to be derived separately and disagree where it matters:
|
|
47
|
-
* `render` took the canvas as `Math.max(1, maxWidth - left - right)`, which
|
|
48
|
-
* clamps *up*, so a padding of 1 inside a 2-cell request drew 3-cell content
|
|
49
|
-
* rows beside 2-cell blank rows — an open frame the terminal then soft-wraps.
|
|
50
|
-
* Content takes its first cell before padding takes any, for the reason
|
|
51
|
-
* `layoutPanel` gives: a squeeze should cost you the decoration, not the thing
|
|
52
|
-
* being decorated.
|
|
53
|
-
*/
|
|
54
|
-
function layoutPadding(outerWidth, leftWanted, rightWanted) {
|
|
55
|
-
let budget = cellCount(outerWidth);
|
|
56
|
-
const take = (want) => {
|
|
57
|
-
const got = Math.min(want, budget);
|
|
58
|
-
budget -= got;
|
|
59
|
-
return got;
|
|
60
|
-
};
|
|
61
|
-
const firstContentCell = take(1);
|
|
62
|
-
const left = take(leftWanted);
|
|
63
|
-
const right = take(rightWanted);
|
|
64
|
-
return { left, contentWidth: firstContentCell + budget, right };
|
|
65
|
-
}
|
|
66
|
-
/** The full width of a padded row — the division, added back up. */
|
|
67
|
-
function rowWidth(geometry) {
|
|
68
|
-
return geometry.left + geometry.contentWidth + geometry.right;
|
|
69
|
-
}
|
|
70
|
-
export class Padding {
|
|
71
|
-
renderable;
|
|
72
|
-
top;
|
|
73
|
-
right;
|
|
74
|
-
bottom;
|
|
75
|
-
left;
|
|
76
|
-
style;
|
|
77
|
-
expand;
|
|
78
|
-
constructor(renderable, padding, options) {
|
|
79
|
-
const [top, right, bottom, left] = normalizePadding(padding);
|
|
80
|
-
this.renderable = renderable;
|
|
81
|
-
this.top = top;
|
|
82
|
-
this.right = right;
|
|
83
|
-
this.bottom = bottom;
|
|
84
|
-
this.left = left;
|
|
85
|
-
this.style = options?.style ?? NULL_STYLE;
|
|
86
|
-
this.expand = options?.expand !== false;
|
|
87
|
-
}
|
|
88
|
-
*render(rawOptions) {
|
|
89
|
-
const options = withBoundedWidth(rawOptions, this);
|
|
90
|
-
const geometry = layoutPadding(options.maxWidth, this.left, this.right);
|
|
91
|
-
const innerOptions = {
|
|
92
|
-
...options,
|
|
93
|
-
maxWidth: geometry.contentWidth,
|
|
94
|
-
};
|
|
95
|
-
const segments = [...this.renderable.render(innerOptions)];
|
|
96
|
-
const lines = Segment.splitLines(segments);
|
|
97
|
-
const resolved = getStyle(options, this.style);
|
|
98
|
-
const style = resolved.isNull ? undefined : resolved;
|
|
99
|
-
// Zero-length spans need no branch to suppress: the wire boundary drops
|
|
100
|
-
// empty segments, so a padding the width could not afford emits nothing.
|
|
101
|
-
const leftPad = new Segment(" ".repeat(geometry.left), style);
|
|
102
|
-
const rightPad = new Segment(" ".repeat(geometry.right), style);
|
|
103
|
-
const blankLine = new Segment(" ".repeat(rowWidth(geometry)), style);
|
|
104
|
-
for (let i = 0; i < this.top; i++) {
|
|
105
|
-
yield blankLine;
|
|
106
|
-
yield Segment.line();
|
|
107
|
-
}
|
|
108
|
-
for (const line of lines) {
|
|
109
|
-
yield leftPad;
|
|
110
|
-
// `expand` is the pad half of this call; the crop half is unconditional,
|
|
111
|
-
// because a child that ignored the canvas it was handed (a Table at its
|
|
112
|
-
// natural width) would otherwise burst the padding around it.
|
|
113
|
-
yield* Segment.adjustLineLength(line, geometry.contentWidth, style, this.expand);
|
|
114
|
-
yield rightPad;
|
|
115
|
-
yield Segment.line();
|
|
116
|
-
}
|
|
117
|
-
for (let i = 0; i < this.bottom; i++) {
|
|
118
|
-
yield blankLine;
|
|
119
|
-
yield Segment.line();
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
measure(rawOptions) {
|
|
123
|
-
const options = withCellWidth(rawOptions);
|
|
124
|
-
const geometry = layoutPadding(options.maxWidth, this.left, this.right);
|
|
125
|
-
const overhead = geometry.left + geometry.right;
|
|
126
|
-
if (isMeasurable(this.renderable)) {
|
|
127
|
-
const measurement = Measurement.get({ ...options, maxWidth: geometry.contentWidth }, this.renderable);
|
|
128
|
-
// The ceiling is what was offered, and it wins over what the child asks
|
|
129
|
-
// for: a range whose floor sits above its own ceiling — this returned
|
|
130
|
-
// {8, 8} for three cells of padding inside a two-cell offer — is one no
|
|
131
|
-
// parent layout can divide.
|
|
132
|
-
const maximum = Math.min(options.maxWidth, measurement.maximum + overhead);
|
|
133
|
-
return {
|
|
134
|
-
minimum: Math.min(measurement.minimum + overhead, maximum),
|
|
135
|
-
maximum,
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
return { minimum: overhead, maximum: options.maxWidth };
|
|
139
|
-
}
|
|
140
|
-
}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Panel — a bordered box that wraps content, with optional title and subtitle.
|
|
3
|
-
*/
|
|
4
|
-
import { Segment } from "../core/segment.js";
|
|
5
|
-
import { Style } from "../core/style.js";
|
|
6
|
-
import { Box } from "../core/box.js";
|
|
7
|
-
import { RichText } from "../core/text.js";
|
|
8
|
-
import type { PaddingDimensions } from "./padding.js";
|
|
9
|
-
import type { Renderable, Measurable, RenderOptions } from "../core/protocol.js";
|
|
10
|
-
/**
|
|
11
|
-
* A lazily-resolved border accessory. Strings render inline in the
|
|
12
|
-
* border style; a `RichText` accessory contributes ONLY its wrapping
|
|
13
|
-
* `style` (per-range spans within the RichText are not preserved — the
|
|
14
|
-
* accessory is a small status indicator, not an arbitrary span carrier).
|
|
15
|
-
* A function form is evaluated at render time, *after* content has been
|
|
16
|
-
* rendered for the current frame — use this when the accessory mirrors
|
|
17
|
-
* state that the wrapped renderable populates during its own `render()`
|
|
18
|
-
* (e.g. a widget's post-render scroll position).
|
|
19
|
-
*/
|
|
20
|
-
export type BorderAccessory = string | RichText | (() => string | RichText | undefined);
|
|
21
|
-
export interface PanelOptions {
|
|
22
|
-
box?: Box;
|
|
23
|
-
title?: string | RichText;
|
|
24
|
-
subtitle?: string | RichText;
|
|
25
|
-
/**
|
|
26
|
-
* Right-aligned accessory in the bottom border, just left of the
|
|
27
|
-
* `bottomRight` corner. Coexists with `subtitle` — the subtitle
|
|
28
|
-
* remains centered in the remaining space. Padded with a leading/
|
|
29
|
-
* trailing space like `title`/`subtitle`, so passing `"[14/102]"`
|
|
30
|
-
* renders as `─ [14/102] ┘`.
|
|
31
|
-
*/
|
|
32
|
-
bottomRightAccessory?: BorderAccessory;
|
|
33
|
-
expand?: boolean;
|
|
34
|
-
style?: string | Style;
|
|
35
|
-
borderStyle?: string | Style;
|
|
36
|
-
/**
|
|
37
|
-
* Style for the title text in the top border. Defaults to `borderStyle`
|
|
38
|
-
* (i.e. title inherits the border color). Set independently when the
|
|
39
|
-
* title should pop relative to the border.
|
|
40
|
-
*/
|
|
41
|
-
titleStyle?: string | Style;
|
|
42
|
-
/**
|
|
43
|
-
* Style for the subtitle text in the bottom border. Defaults to
|
|
44
|
-
* `borderStyle`.
|
|
45
|
-
*/
|
|
46
|
-
subtitleStyle?: string | Style;
|
|
47
|
-
width?: number;
|
|
48
|
-
padding?: PaddingDimensions;
|
|
49
|
-
}
|
|
50
|
-
export declare class Panel implements Renderable, Measurable {
|
|
51
|
-
readonly renderable: Renderable;
|
|
52
|
-
readonly box: Box;
|
|
53
|
-
readonly title: string | RichText | undefined;
|
|
54
|
-
readonly subtitle: string | RichText | undefined;
|
|
55
|
-
readonly bottomRightAccessory: BorderAccessory | undefined;
|
|
56
|
-
readonly expand: boolean;
|
|
57
|
-
readonly style: string | Style;
|
|
58
|
-
readonly borderStyle: string | Style;
|
|
59
|
-
readonly titleStyle: string | Style | undefined;
|
|
60
|
-
readonly subtitleStyle: string | Style | undefined;
|
|
61
|
-
readonly width: number | undefined;
|
|
62
|
-
readonly padding: [number, number, number, number];
|
|
63
|
-
constructor(content: string | RichText | Renderable, options?: PanelOptions);
|
|
64
|
-
render(rawOptions: RenderOptions): Iterable<Segment>;
|
|
65
|
-
/**
|
|
66
|
-
* The wrapped renderable's lines, laid out on a canvas `contentWidth` cells
|
|
67
|
-
* wide. Below width 3 a panel is all frame and the canvas holds no lines —
|
|
68
|
-
* but the render still happens, because a `bottomRightAccessory` thunk
|
|
69
|
-
* fires at every width and reads state this render populates.
|
|
70
|
-
*/
|
|
71
|
-
private _renderContent;
|
|
72
|
-
/**
|
|
73
|
-
* One row of the panel body: frame column, span, frame column.
|
|
74
|
-
*
|
|
75
|
-
* [LAW:single-enforcer] `adjustLineLength` is the one place a width is
|
|
76
|
-
* decided here, and it runs twice against two different widths. The first
|
|
77
|
-
* pass crops content that rendered wider than the canvas it was handed (a
|
|
78
|
-
* `Table` at its natural width, say) back to the canvas, so an oversized
|
|
79
|
-
* child is trimmed rather than allowed to eat the right-hand padding. The
|
|
80
|
-
* second fills the rest of the span, which is the trailing padding and any
|
|
81
|
-
* shortfall in one stroke.
|
|
82
|
-
*/
|
|
83
|
-
private _renderRow;
|
|
84
|
-
measure(rawOptions: RenderOptions): {
|
|
85
|
-
minimum: number;
|
|
86
|
-
maximum: number;
|
|
87
|
-
};
|
|
88
|
-
/**
|
|
89
|
-
* The width this panel wants when nothing declared one for it: its content
|
|
90
|
-
* plus its own frame, both read off the division it will render against.
|
|
91
|
-
*
|
|
92
|
-
* [LAW:one-source-of-truth] `measure` and `_getPanelWidth` ask this same
|
|
93
|
-
* question, and each used to work it out itself — the same `layoutPanel`, the
|
|
94
|
-
* same `frameOverhead`, the same `Math.min(maxWidth, maximum + overhead)`,
|
|
95
|
-
* written twice. That is the pattern `_declaredWidth` below was extracted to
|
|
96
|
-
* stop, left standing for the fit case; changing how overhead is derived in
|
|
97
|
-
* one copy is all it would take to put `measure` and `render` back into the
|
|
98
|
-
* disagreement this epic spent itself closing.
|
|
99
|
-
*/
|
|
100
|
-
private _fitRange;
|
|
101
|
-
/**
|
|
102
|
-
* The width this panel was told to be, as a count of cells.
|
|
103
|
-
*
|
|
104
|
-
* [LAW:one-source-of-truth] `measure` and `_getPanelWidth` answer the same
|
|
105
|
-
* question about the same field, so they read it from here. Answered
|
|
106
|
-
* separately, `measure` reported nine cells of content while `render` drew the
|
|
107
|
-
* declared twelve, and the parent that divided space from the range got a
|
|
108
|
-
* panel three cells wider than the share it granted.
|
|
109
|
-
*/
|
|
110
|
-
private get _declaredWidth();
|
|
111
|
-
private _getPanelWidth;
|
|
112
|
-
private _renderTopBorder;
|
|
113
|
-
private _renderBottomBorder;
|
|
114
|
-
private _resolveAccessory;
|
|
115
|
-
static fit(content: string | RichText | Renderable, options?: Omit<PanelOptions, "expand">): Panel;
|
|
116
|
-
}
|