@promptctl/rich-js 0.18.0 → 0.19.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 +9 -7
- package/dist/core/anchor.d.ts +45 -0
- package/dist/core/anchor.js +39 -0
- package/dist/core/ansi.d.ts +65 -0
- package/dist/core/ansi.js +231 -0
- package/dist/core/box.d.ts +145 -0
- package/dist/core/box.js +372 -0
- package/dist/core/cells.d.ts +192 -0
- package/dist/core/cells.js +334 -0
- package/dist/core/clock.d.ts +48 -0
- package/dist/core/clock.js +42 -0
- package/dist/core/color.d.ts +326 -0
- package/dist/core/color.js +1225 -0
- package/dist/core/console.d.ts +196 -0
- package/dist/core/console.js +596 -0
- package/dist/core/easing.d.ts +86 -0
- package/dist/core/easing.js +171 -0
- package/dist/core/emoji.d.ts +24 -0
- package/dist/core/emoji.js +391 -0
- package/dist/core/env.d.ts +16 -0
- package/dist/core/env.js +16 -0
- package/dist/core/export-html.d.ts +59 -0
- package/dist/core/export-html.js +197 -0
- package/dist/core/export-lines.d.ts +130 -0
- package/dist/core/export-lines.js +159 -0
- package/dist/core/export-svg.d.ts +49 -0
- package/dist/core/export-svg.js +213 -0
- package/dist/core/fnv1a.d.ts +10 -0
- package/dist/core/fnv1a.js +17 -0
- package/dist/core/highlighter.d.ts +33 -0
- package/dist/core/highlighter.js +190 -0
- package/dist/core/json.d.ts +27 -0
- package/dist/core/json.js +53 -0
- package/dist/core/markup.d.ts +140 -0
- package/dist/core/markup.js +654 -0
- package/dist/core/measure.d.ts +27 -0
- package/dist/core/measure.js +67 -0
- package/dist/core/memo.d.ts +19 -0
- package/dist/core/memo.js +34 -0
- package/dist/core/oklch.d.ts +161 -0
- package/dist/core/oklch.js +312 -0
- package/dist/core/osc8.d.ts +68 -0
- package/dist/core/osc8.js +78 -0
- package/dist/core/paint.d.ts +107 -0
- package/dist/core/paint.js +150 -0
- package/dist/core/place.d.ts +30 -0
- package/dist/core/place.js +49 -0
- package/dist/core/pretty.d.ts +247 -0
- package/dist/core/pretty.js +906 -0
- package/dist/core/protocol.d.ts +305 -0
- package/dist/core/protocol.js +183 -0
- package/dist/core/render.d.ts +87 -0
- package/dist/core/render.js +105 -0
- package/dist/core/segment.d.ts +162 -0
- package/dist/core/segment.js +488 -0
- package/dist/core/spinnerData.d.ts +12 -0
- package/dist/core/spinnerData.js +117 -0
- package/dist/core/strip.d.ts +152 -0
- package/dist/core/strip.js +326 -0
- package/dist/core/style.d.ts +189 -0
- package/dist/core/style.js +758 -0
- package/dist/core/subscription.d.ts +12 -0
- package/dist/core/subscription.js +12 -0
- package/dist/core/text.d.ts +283 -0
- package/dist/core/text.js +1097 -0
- package/dist/core/wrap.d.ts +46 -0
- package/dist/core/wrap.js +140 -0
- package/dist/host/app.d.ts +133 -0
- package/dist/host/app.js +285 -0
- package/dist/host/host-environment.d.ts +26 -0
- package/dist/host/host-environment.js +44 -0
- package/dist/host/index.d.ts +6 -0
- package/dist/host/index.js +3 -0
- package/dist/host/terminal-host.d.ts +180 -0
- package/dist/host/terminal-host.js +186 -0
- package/dist/index.d.ts +98 -0
- package/dist/index.js +114 -0
- package/dist/node/prompt.d.ts +34 -0
- package/dist/node/prompt.js +51 -0
- package/dist/node/save.d.ts +18 -0
- package/dist/node/save.js +24 -0
- package/dist/node/terminal-host.d.ts +114 -0
- package/dist/node/terminal-host.js +227 -0
- package/dist/node/traceback.d.ts +38 -0
- package/dist/node/traceback.js +98 -0
- package/dist/renderables/align.d.ts +16 -0
- package/dist/renderables/align.js +26 -0
- package/dist/renderables/columns.d.ts +77 -0
- package/dist/renderables/columns.js +191 -0
- package/dist/renderables/constrain.d.ts +15 -0
- package/dist/renderables/constrain.js +31 -0
- package/dist/renderables/effect.d.ts +98 -0
- package/dist/renderables/effect.js +160 -0
- package/dist/renderables/embed.d.ts +107 -0
- package/dist/renderables/embed.js +160 -0
- package/dist/renderables/flexStrip.d.ts +41 -0
- package/dist/renderables/flexStrip.js +200 -0
- package/dist/renderables/group.d.ts +14 -0
- package/dist/renderables/group.js +22 -0
- package/dist/renderables/layout.d.ts +75 -0
- package/dist/renderables/layout.js +223 -0
- package/dist/renderables/live.d.ts +74 -0
- package/dist/renderables/live.js +188 -0
- package/dist/renderables/markdown.d.ts +38 -0
- package/dist/renderables/markdown.js +587 -0
- package/dist/renderables/padding.d.ts +59 -0
- package/dist/renderables/padding.js +147 -0
- package/dist/renderables/panel.d.ts +114 -0
- package/dist/renderables/panel.js +303 -0
- package/dist/renderables/progress.d.ts +178 -0
- package/dist/renderables/progress.js +362 -0
- package/dist/renderables/progressBar.d.ts +31 -0
- package/dist/renderables/progressBar.js +64 -0
- package/dist/renderables/prompt.d.ts +71 -0
- package/dist/renderables/prompt.js +165 -0
- package/dist/renderables/ratio.d.ts +123 -0
- package/dist/renderables/ratio.js +213 -0
- package/dist/renderables/rule.d.ts +26 -0
- package/dist/renderables/rule.js +86 -0
- package/dist/renderables/spinner.d.ts +57 -0
- package/dist/renderables/spinner.js +103 -0
- package/dist/renderables/status.d.ts +29 -0
- package/dist/renderables/status.js +46 -0
- package/dist/renderables/syntax.d.ts +65 -0
- package/dist/renderables/syntax.js +166 -0
- package/dist/renderables/table.d.ts +232 -0
- package/dist/renderables/table.js +973 -0
- package/dist/renderables/traceback.d.ts +32 -0
- package/dist/renderables/traceback.js +279 -0
- package/dist/renderables/tree.d.ts +76 -0
- package/dist/renderables/tree.js +177 -0
- package/dist/renderables/viewport.d.ts +114 -0
- package/dist/renderables/viewport.js +196 -0
- package/dist/template-bindings/color-funcs.d.ts +109 -0
- package/dist/template-bindings/color-funcs.js +243 -0
- package/dist/template-bindings/helpers.d.ts +26 -0
- package/dist/template-bindings/helpers.js +33 -0
- package/dist/template-bindings/index.d.ts +119 -0
- package/dist/template-bindings/index.js +162 -0
- package/dist/template-bindings/palette-funcs.d.ts +96 -0
- package/dist/template-bindings/palette-funcs.js +145 -0
- package/dist/template-bindings/style-funcs.d.ts +28 -0
- package/dist/template-bindings/style-funcs.js +157 -0
- package/dist/themes/buildPalette.d.ts +57 -0
- package/dist/themes/buildPalette.js +100 -0
- package/dist/themes/colorMath.d.ts +94 -0
- package/dist/themes/colorMath.js +280 -0
- package/dist/themes/colorRef.d.ts +36 -0
- package/dist/themes/colorRef.js +78 -0
- package/dist/themes/data/ansi-tables.d.ts +14 -0
- package/dist/themes/data/ansi-tables.js +49 -0
- package/dist/themes/data/atom-one-dark.d.ts +3 -0
- package/dist/themes/data/atom-one-dark.js +177 -0
- package/dist/themes/data/atom-one-light.d.ts +3 -0
- package/dist/themes/data/atom-one-light.js +177 -0
- package/dist/themes/data/catppuccin-frappe.d.ts +3 -0
- package/dist/themes/data/catppuccin-frappe.js +177 -0
- package/dist/themes/data/catppuccin-latte.d.ts +3 -0
- package/dist/themes/data/catppuccin-latte.js +177 -0
- package/dist/themes/data/catppuccin-macchiato.d.ts +3 -0
- package/dist/themes/data/catppuccin-macchiato.js +177 -0
- package/dist/themes/data/catppuccin-mocha.d.ts +3 -0
- package/dist/themes/data/catppuccin-mocha.js +177 -0
- package/dist/themes/data/cyberpunk.d.ts +17 -0
- package/dist/themes/data/cyberpunk.js +193 -0
- package/dist/themes/data/default.d.ts +3 -0
- package/dist/themes/data/default.js +160 -0
- package/dist/themes/data/dracula.d.ts +3 -0
- package/dist/themes/data/dracula.js +177 -0
- package/dist/themes/data/flexoki.d.ts +3 -0
- package/dist/themes/data/flexoki.js +177 -0
- package/dist/themes/data/gruvbox.d.ts +3 -0
- package/dist/themes/data/gruvbox.js +177 -0
- package/dist/themes/data/index.d.ts +35 -0
- package/dist/themes/data/index.js +64 -0
- package/dist/themes/data/monokai.d.ts +3 -0
- package/dist/themes/data/monokai.js +177 -0
- package/dist/themes/data/nord.d.ts +3 -0
- package/dist/themes/data/nord.js +177 -0
- package/dist/themes/data/rose-pine-dawn.d.ts +3 -0
- package/dist/themes/data/rose-pine-dawn.js +177 -0
- package/dist/themes/data/rose-pine-moon.d.ts +3 -0
- package/dist/themes/data/rose-pine-moon.js +177 -0
- package/dist/themes/data/rose-pine.d.ts +3 -0
- package/dist/themes/data/rose-pine.js +177 -0
- package/dist/themes/data/solarized-dark.d.ts +3 -0
- package/dist/themes/data/solarized-dark.js +177 -0
- package/dist/themes/data/solarized-light.d.ts +3 -0
- package/dist/themes/data/solarized-light.js +177 -0
- package/dist/themes/data/svg-export.d.ts +3 -0
- package/dist/themes/data/svg-export.js +177 -0
- package/dist/themes/data/textual-ansi.d.ts +3 -0
- package/dist/themes/data/textual-ansi.js +160 -0
- package/dist/themes/data/textual-dark.d.ts +3 -0
- package/dist/themes/data/textual-dark.js +160 -0
- package/dist/themes/data/textual-light.d.ts +3 -0
- package/dist/themes/data/textual-light.js +160 -0
- package/dist/themes/data/tokyo-night.d.ts +3 -0
- package/dist/themes/data/tokyo-night.js +177 -0
- package/dist/themes/data/types.d.ts +31 -0
- package/dist/themes/data/types.js +10 -0
- package/dist/themes/palette.d.ts +45 -0
- package/dist/themes/palette.js +71 -0
- package/dist/themes/ramp.d.ts +70 -0
- package/dist/themes/ramp.js +103 -0
- package/dist/themes/registry.d.ts +44 -0
- package/dist/themes/registry.js +105 -0
- package/dist/themes/terminalThemes.d.ts +49 -0
- package/dist/themes/terminalThemes.js +63 -0
- package/dist/themes/transpose.d.ts +87 -0
- package/dist/themes/transpose.js +138 -0
- package/dist/widgets/button.d.ts +45 -0
- package/dist/widgets/button.js +177 -0
- package/dist/widgets/checkbox.d.ts +38 -0
- package/dist/widgets/checkbox.js +135 -0
- package/dist/widgets/dropdown.d.ts +64 -0
- package/dist/widgets/dropdown.js +416 -0
- package/dist/widgets/event-router.d.ts +71 -0
- package/dist/widgets/event-router.js +629 -0
- package/dist/widgets/focus-manager.d.ts +45 -0
- package/dist/widgets/focus-manager.js +191 -0
- package/dist/widgets/hit.d.ts +64 -0
- package/dist/widgets/hit.js +113 -0
- package/dist/widgets/index.d.ts +20 -0
- package/dist/widgets/index.js +14 -0
- package/dist/widgets/ink.d.ts +22 -0
- package/dist/widgets/ink.js +31 -0
- package/dist/widgets/slider.d.ts +54 -0
- package/dist/widgets/slider.js +247 -0
- package/dist/widgets/static-item.d.ts +34 -0
- package/dist/widgets/static-item.js +54 -0
- package/dist/widgets/text-input.d.ts +283 -0
- package/dist/widgets/text-input.js +1174 -0
- package/dist/widgets/themed-widget.d.ts +20 -0
- package/dist/widgets/themed-widget.js +89 -0
- package/dist/widgets/toggle.d.ts +41 -0
- package/dist/widgets/toggle.js +157 -0
- package/dist/widgets/types.d.ts +87 -0
- package/dist/widgets/types.js +30 -0
- package/dist/widgets/widget-app.d.ts +65 -0
- package/dist/widgets/widget-app.js +206 -0
- package/dist/widgets/widget-base.d.ts +42 -0
- package/dist/widgets/widget-base.js +172 -0
- package/package.json +16 -2
|
@@ -0,0 +1,147 @@
|
|
|
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 { fitHeight, getStyle, insetHeight, withBoundedWidth, withCellWidth } from "../core/protocol.js";
|
|
9
|
+
import { embed } from "./embed.js";
|
|
10
|
+
/**
|
|
11
|
+
* Parses the shapes a caller may write padding in — one number, a vertical/
|
|
12
|
+
* horizontal pair, or all four sides — into the four-sided form every
|
|
13
|
+
* renderable indexes by, flooring each side at zero.
|
|
14
|
+
*
|
|
15
|
+
* [LAW:parse-dont-validate] This is the one crossing between the public
|
|
16
|
+
* padding vocabulary and the internal tuple, which is why the floor lives
|
|
17
|
+
* here and nowhere downstream: past this point `" ".repeat(left)` is safe in
|
|
18
|
+
* any renderable without asking what the caller passed. A negative side used
|
|
19
|
+
* to reach the renderers, where `Panel` drew content rows wider than its own
|
|
20
|
+
* border and `Table` threw outright.
|
|
21
|
+
*/
|
|
22
|
+
export function normalizePadding(padding) {
|
|
23
|
+
// A padding side is a count of cells like any other, so it is parsed by the
|
|
24
|
+
// same rule rather than by a second copy of it: a fractional side reaches
|
|
25
|
+
// `" ".repeat`, which truncates, while the rows around it are measured
|
|
26
|
+
// arithmetically — so the padding and the border disagree and the frame
|
|
27
|
+
// breaks. `cellCount`'s header carries why the comparison, not `Math.max`.
|
|
28
|
+
//
|
|
29
|
+
// `Infinity` is where a side stops being an offer and the rule needs the one
|
|
30
|
+
// addition. An unbounded *offer* is a question a renderable can answer, so
|
|
31
|
+
// `cellCount` passes it through; an unbounded *side* is a quantity and there
|
|
32
|
+
// is nothing to draw. Unparsed it reached `budget -= got` as
|
|
33
|
+
// `Infinity - Infinity`, and `new Padding(text, Infinity).measure(...)`
|
|
34
|
+
// handed back `{NaN, NaN}` from a public API.
|
|
35
|
+
const side = (n) => (Number.isFinite(n) ? cellCount(n) : 0);
|
|
36
|
+
const sides = typeof padding === "number"
|
|
37
|
+
? [padding, padding, padding, padding]
|
|
38
|
+
: padding.length === 2
|
|
39
|
+
? [padding[0], padding[1], padding[0], padding[1]]
|
|
40
|
+
: padding;
|
|
41
|
+
return [side(sides[0]), side(sides[1]), side(sides[2]), side(sides[3])];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Divides the requested width once into the three spans a padded row is made
|
|
45
|
+
* of, in the order they are worth spending cells on.
|
|
46
|
+
*
|
|
47
|
+
* The three used to be derived separately and disagree where it matters:
|
|
48
|
+
* `render` took the canvas as `Math.max(1, maxWidth - left - right)`, which
|
|
49
|
+
* clamps *up*, so a padding of 1 inside a 2-cell request drew 3-cell content
|
|
50
|
+
* rows beside 2-cell blank rows — an open frame the terminal then soft-wraps.
|
|
51
|
+
* Content takes its first cell before padding takes any, for the reason
|
|
52
|
+
* `layoutPanel` gives: a squeeze should cost you the decoration, not the thing
|
|
53
|
+
* being decorated.
|
|
54
|
+
*/
|
|
55
|
+
export function layoutPadding(outerWidth, leftWanted, rightWanted) {
|
|
56
|
+
let budget = cellCount(outerWidth);
|
|
57
|
+
const take = (want) => {
|
|
58
|
+
const got = Math.min(want, budget);
|
|
59
|
+
budget -= got;
|
|
60
|
+
return got;
|
|
61
|
+
};
|
|
62
|
+
const firstContentCell = take(1);
|
|
63
|
+
const left = take(leftWanted);
|
|
64
|
+
const right = take(rightWanted);
|
|
65
|
+
return { left, contentWidth: firstContentCell + budget, right };
|
|
66
|
+
}
|
|
67
|
+
/** The full width of a padded row — the division, added back up. */
|
|
68
|
+
function rowWidth(geometry) {
|
|
69
|
+
return geometry.left + geometry.contentWidth + geometry.right;
|
|
70
|
+
}
|
|
71
|
+
export class Padding {
|
|
72
|
+
renderable;
|
|
73
|
+
top;
|
|
74
|
+
right;
|
|
75
|
+
bottom;
|
|
76
|
+
left;
|
|
77
|
+
style;
|
|
78
|
+
expand;
|
|
79
|
+
constructor(content, padding, options) {
|
|
80
|
+
const [top, right, bottom, left] = normalizePadding(padding);
|
|
81
|
+
this.renderable = embed(content);
|
|
82
|
+
this.top = top;
|
|
83
|
+
this.right = right;
|
|
84
|
+
this.bottom = bottom;
|
|
85
|
+
this.left = left;
|
|
86
|
+
this.style = options?.style ?? NULL_STYLE;
|
|
87
|
+
this.expand = options?.expand !== false;
|
|
88
|
+
}
|
|
89
|
+
*render(rawOptions) {
|
|
90
|
+
const options = withBoundedWidth(rawOptions, this);
|
|
91
|
+
// `expand` decides how wide the block is, never whether its rows reach
|
|
92
|
+
// that width: as Rich's does, a padding that does not expand narrows to
|
|
93
|
+
// what it measures, and every row is then padded out to it. Deciding the
|
|
94
|
+
// rows instead left the content rows ragged under a full-width blank row,
|
|
95
|
+
// and a `style` ground with holes down its right-hand side.
|
|
96
|
+
const width = this.expand ? options.maxWidth : Measurement.get(options, this).maximum;
|
|
97
|
+
const geometry = layoutPadding(width, this.left, this.right);
|
|
98
|
+
const innerOptions = {
|
|
99
|
+
...options,
|
|
100
|
+
maxWidth: geometry.contentWidth,
|
|
101
|
+
height: insetHeight(options.height, this.top + this.bottom),
|
|
102
|
+
};
|
|
103
|
+
const resolved = getStyle(options, this.style);
|
|
104
|
+
const style = resolved.isNull ? undefined : resolved;
|
|
105
|
+
// The style is the ground the content is drawn on, not only the spaces
|
|
106
|
+
// around it — Rich's `render_lines(..., style=style)`. Applied to the
|
|
107
|
+
// padding alone, a background left a hole behind the content.
|
|
108
|
+
const segments = [...Segment.applyStyle(this.renderable.render(innerOptions), style)];
|
|
109
|
+
const lines = fitHeight(Segment.splitLines(segments), innerOptions.height);
|
|
110
|
+
// Zero-length spans need no branch to suppress: the wire boundary drops
|
|
111
|
+
// empty segments, so a padding the width could not afford emits nothing.
|
|
112
|
+
const leftPad = new Segment(" ".repeat(geometry.left), style);
|
|
113
|
+
const rightPad = new Segment(" ".repeat(geometry.right), style);
|
|
114
|
+
const blankLine = new Segment(" ".repeat(rowWidth(geometry)), style);
|
|
115
|
+
for (let i = 0; i < this.top; i++) {
|
|
116
|
+
yield blankLine;
|
|
117
|
+
yield Segment.line();
|
|
118
|
+
}
|
|
119
|
+
for (const line of lines) {
|
|
120
|
+
yield leftPad;
|
|
121
|
+
// Crops as well as pads: a child that ignored the canvas it was handed
|
|
122
|
+
// (a Table at its natural width) would otherwise burst the padding.
|
|
123
|
+
yield* Segment.adjustLineLength(line, geometry.contentWidth, style);
|
|
124
|
+
yield rightPad;
|
|
125
|
+
yield Segment.line();
|
|
126
|
+
}
|
|
127
|
+
for (let i = 0; i < this.bottom; i++) {
|
|
128
|
+
yield blankLine;
|
|
129
|
+
yield Segment.line();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
measure(rawOptions) {
|
|
133
|
+
const options = withCellWidth(rawOptions);
|
|
134
|
+
const geometry = layoutPadding(options.maxWidth, this.left, this.right);
|
|
135
|
+
const overhead = geometry.left + geometry.right;
|
|
136
|
+
const measurement = Measurement.get({ ...options, maxWidth: geometry.contentWidth }, this.renderable);
|
|
137
|
+
// The ceiling is what was offered, and it wins over what the child asks
|
|
138
|
+
// for: a range whose floor sits above its own ceiling — this returned
|
|
139
|
+
// {8, 8} for three cells of padding inside a two-cell offer — is one no
|
|
140
|
+
// parent layout can divide.
|
|
141
|
+
const maximum = Math.min(options.maxWidth, measurement.maximum + overhead);
|
|
142
|
+
return {
|
|
143
|
+
minimum: Math.min(measurement.minimum + overhead, maximum),
|
|
144
|
+
maximum,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
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 lays ONLY its wrapping `style` over
|
|
13
|
+
* the border's (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, laid over `borderStyle`:
|
|
38
|
+
* what it sets wins, and what it leaves unset the border's supplies.
|
|
39
|
+
*/
|
|
40
|
+
titleStyle?: string | Style;
|
|
41
|
+
/**
|
|
42
|
+
* Style for the subtitle text in the bottom border, laid over
|
|
43
|
+
* `borderStyle` as `titleStyle` is.
|
|
44
|
+
*/
|
|
45
|
+
subtitleStyle?: string | Style;
|
|
46
|
+
width?: number;
|
|
47
|
+
padding?: PaddingDimensions;
|
|
48
|
+
}
|
|
49
|
+
export declare class Panel implements Renderable, Measurable {
|
|
50
|
+
readonly renderable: Renderable;
|
|
51
|
+
readonly box: Box;
|
|
52
|
+
readonly title: string | RichText | undefined;
|
|
53
|
+
readonly subtitle: string | RichText | undefined;
|
|
54
|
+
readonly bottomRightAccessory: BorderAccessory | undefined;
|
|
55
|
+
readonly expand: boolean;
|
|
56
|
+
readonly style: string | Style;
|
|
57
|
+
readonly borderStyle: string | Style;
|
|
58
|
+
readonly titleStyle: string | Style | undefined;
|
|
59
|
+
readonly subtitleStyle: string | Style | undefined;
|
|
60
|
+
readonly width: number | undefined;
|
|
61
|
+
readonly padding: [number, number, number, number];
|
|
62
|
+
private readonly _titleLabel;
|
|
63
|
+
private readonly _subtitleLabel;
|
|
64
|
+
constructor(content: string | RichText | Renderable, options?: PanelOptions);
|
|
65
|
+
render(rawOptions: RenderOptions): Iterable<Segment>;
|
|
66
|
+
/**
|
|
67
|
+
* The wrapped renderable's lines, laid out on a canvas `contentWidth` cells
|
|
68
|
+
* wide. Below width 3 a panel is all frame and the canvas holds no lines —
|
|
69
|
+
* but the render still happens, because a `bottomRightAccessory` thunk
|
|
70
|
+
* fires at every width and reads state this render populates.
|
|
71
|
+
*/
|
|
72
|
+
private _renderContent;
|
|
73
|
+
/**
|
|
74
|
+
* One row of the panel body, cut where Rich cuts it: frame column, left
|
|
75
|
+
* padding, the content line made exactly `contentWidth` wide, right padding,
|
|
76
|
+
* frame column — each its own segment, so each its own SGR run.
|
|
77
|
+
*
|
|
78
|
+
* [LAW:single-enforcer] `adjustLineLength` is the one place the content's
|
|
79
|
+
* width is decided: content that rendered wider than its canvas (a `Table`
|
|
80
|
+
* at its natural width, say) is cropped back to it, and a short line is
|
|
81
|
+
* filled out to it, so the right padding always starts at the same column.
|
|
82
|
+
*/
|
|
83
|
+
private _renderRow;
|
|
84
|
+
/**
|
|
85
|
+
* A row of vertical padding: one blank run across the whole span, as Rich's
|
|
86
|
+
* `Padding` draws its blank lines, not a content row with nothing in it.
|
|
87
|
+
*/
|
|
88
|
+
private _renderPaddingRow;
|
|
89
|
+
measure(rawOptions: RenderOptions): {
|
|
90
|
+
minimum: number;
|
|
91
|
+
maximum: number;
|
|
92
|
+
};
|
|
93
|
+
/** The content plus its own frame, both read off the division it will render against. */
|
|
94
|
+
private _contentRange;
|
|
95
|
+
/** The cells the title's label takes, its padding included; none without a title. */
|
|
96
|
+
private _labelWidth;
|
|
97
|
+
/**
|
|
98
|
+
* The width this panel was told to be, as a count of cells.
|
|
99
|
+
*
|
|
100
|
+
* [LAW:one-source-of-truth] `measure` and `_getPanelWidth` read the field
|
|
101
|
+
* from here, so neither can count it differently from the other.
|
|
102
|
+
*/
|
|
103
|
+
private get _declaredWidth();
|
|
104
|
+
/**
|
|
105
|
+
* As Rich's `__rich_console__` sizes it: a declared width is a ceiling that
|
|
106
|
+
* `expand` fills and a fitted panel fits its content inside, and the title
|
|
107
|
+
* widens either.
|
|
108
|
+
*/
|
|
109
|
+
private _getPanelWidth;
|
|
110
|
+
private _renderTopBorder;
|
|
111
|
+
private _renderBottomBorder;
|
|
112
|
+
private _resolveAccessory;
|
|
113
|
+
static fit(content: string | RichText | Renderable, options?: Omit<PanelOptions, "expand">): Panel;
|
|
114
|
+
}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Panel — a bordered box that wraps content, with optional title and subtitle.
|
|
3
|
+
*/
|
|
4
|
+
import { cellLen, setCellSize, asCellCol, cellCount } from "../core/cells.js";
|
|
5
|
+
import { Segment } from "../core/segment.js";
|
|
6
|
+
import { NULL_STYLE } from "../core/style.js";
|
|
7
|
+
import { ROUNDED } from "../core/box.js";
|
|
8
|
+
import { Measurement } from "../core/measure.js";
|
|
9
|
+
import { RichText } from "../core/text.js";
|
|
10
|
+
import { cutLabel, drawLabel, embed, inlineLabel } from "./embed.js";
|
|
11
|
+
import { normalizePadding } from "./padding.js";
|
|
12
|
+
import { drawable, fitHeight, getStyle, insetHeight, withBoundedWidth, withCellWidth } from "../core/protocol.js";
|
|
13
|
+
function layoutPanel(outerWidth, padding) {
|
|
14
|
+
const [, padRightWanted, , padLeftWanted] = padding;
|
|
15
|
+
// Panel's one division point, and the width reaching it is not always one
|
|
16
|
+
// this file produced: `_getPanelWidth` derives the fit-mode width from
|
|
17
|
+
// `Measurement.get` on an arbitrary `Measurable`, so a renderable reporting a
|
|
18
|
+
// negative maximum arrives here unparsed and reaches `repeat` as a negative
|
|
19
|
+
// count.
|
|
20
|
+
let budget = cellCount(outerWidth);
|
|
21
|
+
const take = (want) => {
|
|
22
|
+
const got = Math.min(want, budget);
|
|
23
|
+
budget -= got;
|
|
24
|
+
return got;
|
|
25
|
+
};
|
|
26
|
+
const left = take(1);
|
|
27
|
+
const right = take(1);
|
|
28
|
+
const firstContentCell = take(1);
|
|
29
|
+
const padLeft = take(padLeftWanted);
|
|
30
|
+
const padRight = take(padRightWanted);
|
|
31
|
+
const contentWidth = firstContentCell + budget;
|
|
32
|
+
return {
|
|
33
|
+
left,
|
|
34
|
+
right,
|
|
35
|
+
padLeft,
|
|
36
|
+
contentWidth,
|
|
37
|
+
padRight,
|
|
38
|
+
spanWidth: padLeft + contentWidth + padRight,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Every cell of a panel that is not content canvas. Read off the geometry
|
|
43
|
+
* rather than recomputed as `2 + padLeft + padRight`, because the two differ
|
|
44
|
+
* exactly where this panel is squeezed: a width that cannot afford its right
|
|
45
|
+
* frame column or its padding does not spend cells on them, and a measurement
|
|
46
|
+
* that assumed it did would report a minimum larger than its own maximum.
|
|
47
|
+
*/
|
|
48
|
+
function frameOverhead(geometry) {
|
|
49
|
+
return geometry.left + geometry.right + geometry.padLeft + geometry.padRight;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* How a title or subtitle string is read: always as markup and never
|
|
53
|
+
* highlighted, whatever the console says, because Rich's `Panel` reads its
|
|
54
|
+
* title with `Text.from_markup` rather than through the console.
|
|
55
|
+
*/
|
|
56
|
+
function borderLabel(options) {
|
|
57
|
+
return { ...options, markup: true, highlight: false };
|
|
58
|
+
}
|
|
59
|
+
/** A title or subtitle as Rich's `Panel._title` sets it: read as a border reads it, a space either side. */
|
|
60
|
+
function borderTitle(label, options) {
|
|
61
|
+
return label.text(borderLabel(options)).pad(1);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The style of text set into a border — title, subtitle and accessory alike:
|
|
65
|
+
* its own laid over the border's, the one rule for "what colour is the title
|
|
66
|
+
* text in". Laid over rather than in place of it, as Rich's `stylize_before`
|
|
67
|
+
* does, because the border's style carries the panel's own: a title that
|
|
68
|
+
* replaced it cut a hole in the panel's background on the top row.
|
|
69
|
+
*/
|
|
70
|
+
function borderTextStyle(border, own) {
|
|
71
|
+
return own.isNull ? border : (border ?? NULL_STYLE).add(own);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* A border row: the opening corner, the rule across `span` cells with `label`
|
|
75
|
+
* centred in it, and `close` — the closing corner, or nothing when the caller
|
|
76
|
+
* sets something between the rule and its corner.
|
|
77
|
+
*
|
|
78
|
+
* Cut as Rich cuts it. An unlabelled rule is one run, corners included. A
|
|
79
|
+
* label is set into the `span - 2` cells between one rule cell either side,
|
|
80
|
+
* truncated to them as Rich's `align_text` truncates it — marked only when the
|
|
81
|
+
* label's own overflow is `"ellipsis"` — and centred with the rule filling the
|
|
82
|
+
* rest, so the row leaves as corner-and-rule, rule, label, rule,
|
|
83
|
+
* rule-and-corner. Rich draws no label in a span of two cells or fewer
|
|
84
|
+
* (`width <= 4`), where it would sit on the corners.
|
|
85
|
+
*/
|
|
86
|
+
function borderRow(options, edge, geometry, span, label, labelStyle, border, close) {
|
|
87
|
+
const corner = edge.left.repeat(geometry.left);
|
|
88
|
+
if (label === undefined || span <= 2) {
|
|
89
|
+
return [new Segment(corner + edge.horizontal.repeat(span) + close, border)];
|
|
90
|
+
}
|
|
91
|
+
const canvas = span - 2;
|
|
92
|
+
const text = borderTitle(label, options);
|
|
93
|
+
cutLabel(text, canvas, text.overflow === "ellipsis" ? drawable(options, "\u2026", ".") : "");
|
|
94
|
+
const fitted = drawLabel(text, borderLabel(options), labelStyle);
|
|
95
|
+
const excess = canvas - Segment.getLineLength(fitted);
|
|
96
|
+
const before = Math.floor(excess / 2);
|
|
97
|
+
return [
|
|
98
|
+
new Segment(corner + edge.horizontal, border),
|
|
99
|
+
new Segment(edge.horizontal.repeat(before), border),
|
|
100
|
+
...fitted,
|
|
101
|
+
new Segment(edge.horizontal.repeat(excess - before), border),
|
|
102
|
+
new Segment(edge.horizontal + close, border),
|
|
103
|
+
];
|
|
104
|
+
}
|
|
105
|
+
export class Panel {
|
|
106
|
+
renderable;
|
|
107
|
+
box;
|
|
108
|
+
title;
|
|
109
|
+
subtitle;
|
|
110
|
+
bottomRightAccessory;
|
|
111
|
+
expand;
|
|
112
|
+
style;
|
|
113
|
+
borderStyle;
|
|
114
|
+
titleStyle;
|
|
115
|
+
subtitleStyle;
|
|
116
|
+
width;
|
|
117
|
+
padding;
|
|
118
|
+
_titleLabel;
|
|
119
|
+
_subtitleLabel;
|
|
120
|
+
constructor(content, options) {
|
|
121
|
+
this.renderable = embed(content);
|
|
122
|
+
this.box = options?.box ?? ROUNDED;
|
|
123
|
+
this.title = options?.title;
|
|
124
|
+
this.subtitle = options?.subtitle;
|
|
125
|
+
this._titleLabel = inlineLabel(this.title);
|
|
126
|
+
this._subtitleLabel = inlineLabel(this.subtitle);
|
|
127
|
+
this.bottomRightAccessory = options?.bottomRightAccessory;
|
|
128
|
+
this.expand = options?.expand !== false;
|
|
129
|
+
this.style = options?.style ?? NULL_STYLE;
|
|
130
|
+
this.borderStyle = options?.borderStyle ?? NULL_STYLE;
|
|
131
|
+
this.titleStyle = options?.titleStyle;
|
|
132
|
+
this.subtitleStyle = options?.subtitleStyle;
|
|
133
|
+
this.width = options?.width;
|
|
134
|
+
this.padding = normalizePadding(options?.padding ?? [0, 1, 0, 1]);
|
|
135
|
+
}
|
|
136
|
+
*render(rawOptions) {
|
|
137
|
+
const options = withBoundedWidth(rawOptions, this);
|
|
138
|
+
const box = this.box.substitute(options);
|
|
139
|
+
// [LAW:one-source-of-truth] `style` is the ground the whole panel is drawn
|
|
140
|
+
// on, as Rich's is: the frame's style is the border style laid over it,
|
|
141
|
+
// and the content is rendered on top of it. Applied to the padding alone,
|
|
142
|
+
// "white on dark_blue" drew a blue ring with a hole where the text was.
|
|
143
|
+
const style = getStyle(options, this.style);
|
|
144
|
+
const borderStyle = style.add(getStyle(options, this.borderStyle));
|
|
145
|
+
const border = borderStyle.isNull ? undefined : borderStyle;
|
|
146
|
+
const contentStyle = style.isNull ? undefined : style;
|
|
147
|
+
const geometry = layoutPanel(this._getPanelWidth(options), this.padding);
|
|
148
|
+
const [padTop, , padBottom] = this.padding;
|
|
149
|
+
const contentLines = this._renderContent(options, geometry.contentWidth, contentStyle);
|
|
150
|
+
yield* this._renderTopBorder(options, box, geometry, border);
|
|
151
|
+
for (let i = 0; i < padTop; i++) {
|
|
152
|
+
yield* this._renderPaddingRow(box, geometry, border, contentStyle);
|
|
153
|
+
}
|
|
154
|
+
for (const line of contentLines) {
|
|
155
|
+
yield* this._renderRow(box, geometry, line, border, contentStyle);
|
|
156
|
+
}
|
|
157
|
+
for (let i = 0; i < padBottom; i++) {
|
|
158
|
+
yield* this._renderPaddingRow(box, geometry, border, contentStyle);
|
|
159
|
+
}
|
|
160
|
+
yield* this._renderBottomBorder(options, box, geometry, border);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* The wrapped renderable's lines, laid out on a canvas `contentWidth` cells
|
|
164
|
+
* wide. Below width 3 a panel is all frame and the canvas holds no lines —
|
|
165
|
+
* but the render still happens, because a `bottomRightAccessory` thunk
|
|
166
|
+
* fires at every width and reads state this render populates.
|
|
167
|
+
*/
|
|
168
|
+
_renderContent(options, contentWidth, style) {
|
|
169
|
+
// Two border rows and the vertical padding are the panel's own. Handed on
|
|
170
|
+
// as a region, the rest is the panel's to shape, which is what stretches
|
|
171
|
+
// its frame down a pane and keeps its bottom border inside one.
|
|
172
|
+
const [padTop, , padBottom] = this.padding;
|
|
173
|
+
const height = insetHeight(options.height, 2 + padTop + padBottom);
|
|
174
|
+
// No highlighter: what the panel holds is drawn plain, as Rich's `Panel`
|
|
175
|
+
// hands its body `highlight=False`.
|
|
176
|
+
const innerOptions = { ...options, highlight: false, maxWidth: contentWidth, height };
|
|
177
|
+
const lines = fitHeight(Segment.splitLines([...Segment.applyStyle(this.renderable.render(innerOptions), style)]), height);
|
|
178
|
+
return contentWidth === 0 ? [] : lines;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* One row of the panel body, cut where Rich cuts it: frame column, left
|
|
182
|
+
* padding, the content line made exactly `contentWidth` wide, right padding,
|
|
183
|
+
* frame column — each its own segment, so each its own SGR run.
|
|
184
|
+
*
|
|
185
|
+
* [LAW:single-enforcer] `adjustLineLength` is the one place the content's
|
|
186
|
+
* width is decided: content that rendered wider than its canvas (a `Table`
|
|
187
|
+
* at its natural width, say) is cropped back to it, and a short line is
|
|
188
|
+
* filled out to it, so the right padding always starts at the same column.
|
|
189
|
+
*/
|
|
190
|
+
*_renderRow(box, geometry, line, border, contentStyle) {
|
|
191
|
+
const frame = box.getContentChars("row");
|
|
192
|
+
yield new Segment(frame.left.repeat(geometry.left), border);
|
|
193
|
+
yield new Segment(" ".repeat(geometry.padLeft), contentStyle);
|
|
194
|
+
yield* Segment.adjustLineLength(line, geometry.contentWidth, contentStyle);
|
|
195
|
+
yield new Segment(" ".repeat(geometry.padRight), contentStyle);
|
|
196
|
+
yield new Segment(frame.right.repeat(geometry.right), border);
|
|
197
|
+
yield Segment.line();
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* A row of vertical padding: one blank run across the whole span, as Rich's
|
|
201
|
+
* `Padding` draws its blank lines, not a content row with nothing in it.
|
|
202
|
+
*/
|
|
203
|
+
*_renderPaddingRow(box, geometry, border, contentStyle) {
|
|
204
|
+
const frame = box.getContentChars("row");
|
|
205
|
+
yield new Segment(frame.left.repeat(geometry.left), border);
|
|
206
|
+
yield new Segment(" ".repeat(geometry.spanWidth), contentStyle);
|
|
207
|
+
yield new Segment(frame.right.repeat(geometry.right), border);
|
|
208
|
+
yield Segment.line();
|
|
209
|
+
}
|
|
210
|
+
measure(rawOptions) {
|
|
211
|
+
const options = withCellWidth(rawOptions);
|
|
212
|
+
// Rich's `__rich_measure__`, which is not its render's arithmetic: a
|
|
213
|
+
// declared width is the measurement whatever the panel then draws inside
|
|
214
|
+
// it, and a title counts as content beside the body, with the padding and
|
|
215
|
+
// frame around it, where the render holds it between one rule cell either
|
|
216
|
+
// side. A parent sizing from it sizes as Rich's parent does.
|
|
217
|
+
const declared = this._declaredWidth;
|
|
218
|
+
if (declared !== undefined) {
|
|
219
|
+
const width = Math.min(options.maxWidth, declared);
|
|
220
|
+
return { minimum: width, maximum: width };
|
|
221
|
+
}
|
|
222
|
+
const content = this._contentRange(options);
|
|
223
|
+
const overhead = frameOverhead(layoutPanel(options.maxWidth, this.padding));
|
|
224
|
+
const maximum = Math.min(options.maxWidth, Math.max(content.maximum, this._labelWidth(options) + overhead));
|
|
225
|
+
return { minimum: Math.min(content.minimum, maximum), maximum };
|
|
226
|
+
}
|
|
227
|
+
/** The content plus its own frame, both read off the division it will render against. */
|
|
228
|
+
_contentRange(options) {
|
|
229
|
+
const geometry = layoutPanel(options.maxWidth, this.padding);
|
|
230
|
+
const overhead = frameOverhead(geometry);
|
|
231
|
+
const measurement = Measurement.get({ ...options, maxWidth: geometry.contentWidth }, this.renderable);
|
|
232
|
+
return {
|
|
233
|
+
minimum: measurement.minimum + overhead,
|
|
234
|
+
maximum: Math.min(options.maxWidth, measurement.maximum + overhead),
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
/** The cells the title's label takes, its padding included; none without a title. */
|
|
238
|
+
_labelWidth(options) {
|
|
239
|
+
return this._titleLabel === undefined ? 0 : borderTitle(this._titleLabel, options).cellLength;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* The width this panel was told to be, as a count of cells.
|
|
243
|
+
*
|
|
244
|
+
* [LAW:one-source-of-truth] `measure` and `_getPanelWidth` read the field
|
|
245
|
+
* from here, so neither can count it differently from the other.
|
|
246
|
+
*/
|
|
247
|
+
get _declaredWidth() {
|
|
248
|
+
return this.width === undefined ? undefined : cellCount(this.width);
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* As Rich's `__rich_console__` sizes it: a declared width is a ceiling that
|
|
252
|
+
* `expand` fills and a fitted panel fits its content inside, and the title
|
|
253
|
+
* widens either.
|
|
254
|
+
*/
|
|
255
|
+
_getPanelWidth(options) {
|
|
256
|
+
const ceiling = Math.min(options.maxWidth, this._declaredWidth ?? options.maxWidth);
|
|
257
|
+
const body = this.expand ? ceiling : this._contentRange({ ...options, maxWidth: ceiling }).maximum;
|
|
258
|
+
// The title holds its label whole between one rule cell either side —
|
|
259
|
+
// past a declared width too, up to the width the panel was offered.
|
|
260
|
+
const floor = this._titleLabel === undefined ? 0 : this._labelWidth(options) + 4;
|
|
261
|
+
return Math.min(options.maxWidth, Math.max(body, floor));
|
|
262
|
+
}
|
|
263
|
+
*_renderTopBorder(options, box, geometry, border) {
|
|
264
|
+
const titleStyle = borderTextStyle(border, getStyle(options, this.titleStyle ?? NULL_STYLE));
|
|
265
|
+
yield* borderRow(options, box.top, geometry, geometry.spanWidth, this._titleLabel, titleStyle, border, box.top.right.repeat(geometry.right));
|
|
266
|
+
yield Segment.line();
|
|
267
|
+
}
|
|
268
|
+
*_renderBottomBorder(options, box, geometry, border) {
|
|
269
|
+
// Resolve the right accessory *now*. Function form evaluates after
|
|
270
|
+
// content has been rendered (Panel.render collects content segments
|
|
271
|
+
// before yielding any borders), so the thunk sees fresh widget state.
|
|
272
|
+
const accessory = this._resolveAccessory(this.bottomRightAccessory);
|
|
273
|
+
const accessoryDisplay = accessory === undefined
|
|
274
|
+
? ""
|
|
275
|
+
: typeof accessory === "string"
|
|
276
|
+
? ` ${accessory} `
|
|
277
|
+
: ` ${accessory.plain} `;
|
|
278
|
+
// Cell-aware clip: the accessory never claims more than the span.
|
|
279
|
+
const accessoryText = setCellSize(accessoryDisplay, asCellCol(Math.min(cellLen(accessoryDisplay), geometry.spanWidth)));
|
|
280
|
+
const accessoryOwn = accessory instanceof RichText ? accessory.resolvedStyle(options) : NULL_STYLE;
|
|
281
|
+
const subtitleStyle = borderTextStyle(border, getStyle(options, this.subtitleStyle ?? NULL_STYLE));
|
|
282
|
+
// The accessory hugs the bottom-right corner; the rule and subtitle are
|
|
283
|
+
// laid out, as Rich lays them, across the span it leaves. With no
|
|
284
|
+
// accessory the corner closes the rule's last run, as Rich's does.
|
|
285
|
+
const corner = box.bottom.right.repeat(geometry.right);
|
|
286
|
+
const [close, afterAccessory] = accessoryText === "" ? [corner, ""] : ["", corner];
|
|
287
|
+
yield* borderRow(options, box.bottom, geometry, geometry.spanWidth - cellLen(accessoryText), this._subtitleLabel, subtitleStyle, border, close);
|
|
288
|
+
yield new Segment(accessoryText, borderTextStyle(border, accessoryOwn));
|
|
289
|
+
yield new Segment(afterAccessory, border);
|
|
290
|
+
yield Segment.line();
|
|
291
|
+
}
|
|
292
|
+
_resolveAccessory(a) {
|
|
293
|
+
if (a === undefined)
|
|
294
|
+
return undefined;
|
|
295
|
+
if (typeof a === "function")
|
|
296
|
+
return a();
|
|
297
|
+
return a;
|
|
298
|
+
}
|
|
299
|
+
// --- Static factory ---
|
|
300
|
+
static fit(content, options) {
|
|
301
|
+
return new Panel(content, { ...options, expand: false });
|
|
302
|
+
}
|
|
303
|
+
}
|