@promptctl/rich-js 0.24.0 → 0.25.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 +7 -0
- package/THIRD-PARTY-NOTICES +29 -1
- package/dist/core/anchor.d.ts +5 -0
- package/dist/core/anchor.js +11 -4
- package/dist/core/animation.d.ts +243 -0
- package/dist/core/animation.js +375 -0
- package/dist/core/ansi.js +2 -2
- package/dist/core/box.d.ts +7 -7
- package/dist/core/cell-widths.d.ts +9 -0
- package/dist/core/cell-widths.js +140 -0
- package/dist/core/cells.d.ts +40 -24
- package/dist/core/cells.js +151 -66
- package/dist/core/clock.d.ts +2 -2
- package/dist/core/clock.js +6 -4
- package/dist/core/color.d.ts +27 -10
- package/dist/core/color.js +21 -29
- package/dist/core/console.d.ts +28 -17
- package/dist/core/console.js +48 -43
- package/dist/core/easing.d.ts +140 -10
- package/dist/core/easing.js +328 -50
- package/dist/core/emoji.d.ts +1 -1
- package/dist/core/emoji.js +1 -2
- package/dist/core/export-html.js +2 -1
- package/dist/core/export-lines.js +3 -3
- package/dist/core/highlighter.d.ts +9 -8
- package/dist/core/highlighter.js +3 -2
- package/dist/core/json.js +3 -2
- package/dist/core/markup.js +16 -12
- package/dist/core/measure.js +3 -3
- package/dist/core/memo.d.ts +11 -2
- package/dist/core/memo.js +16 -0
- package/dist/core/oklch.js +17 -6
- package/dist/core/osc8.d.ts +1 -1
- package/dist/core/osc8.js +4 -5
- package/dist/core/paint.d.ts +2 -1
- package/dist/core/paint.js +4 -3
- package/dist/core/pattern.d.ts +21 -0
- package/dist/core/pattern.js +21 -0
- package/dist/core/place.js +2 -1
- package/dist/core/pretty.js +11 -12
- package/dist/core/protocol.d.ts +136 -21
- package/dist/core/protocol.js +203 -16
- package/dist/core/render.d.ts +15 -2
- package/dist/core/render.js +27 -6
- package/dist/core/segment.d.ts +31 -6
- package/dist/core/segment.js +68 -53
- package/dist/core/spinnerData.d.ts +1 -1
- package/dist/core/state.d.ts +53 -0
- package/dist/core/state.js +82 -0
- package/dist/core/strip.d.ts +11 -13
- package/dist/core/strip.js +126 -113
- package/dist/core/style.d.ts +47 -14
- package/dist/core/style.js +74 -34
- package/dist/core/text.d.ts +5 -8
- package/dist/core/text.js +133 -133
- package/dist/core/timeline.d.ts +109 -0
- package/dist/core/timeline.js +180 -0
- package/dist/core/tween.d.ts +49 -0
- package/dist/core/tween.js +71 -0
- package/dist/core/view.d.ts +92 -0
- package/dist/core/view.js +83 -0
- package/dist/host/app.d.ts +49 -21
- package/dist/host/app.js +72 -38
- package/dist/host/input.js +3 -4
- package/dist/index.d.ts +30 -15
- package/dist/index.js +20 -6
- package/dist/node/traceback.js +4 -3
- package/dist/renderables/change.d.ts +42 -0
- package/dist/renderables/change.js +112 -0
- package/dist/renderables/columns.d.ts +1 -1
- package/dist/renderables/columns.js +3 -3
- package/dist/renderables/constrain.js +2 -1
- package/dist/renderables/effect.d.ts +155 -92
- package/dist/renderables/effect.js +203 -136
- package/dist/renderables/effects.d.ts +137 -24
- package/dist/renderables/effects.js +276 -113
- package/dist/renderables/embed.d.ts +1 -1
- package/dist/renderables/embed.js +18 -9
- package/dist/renderables/flexStrip.js +2 -1
- package/dist/renderables/frames.d.ts +45 -43
- package/dist/renderables/frames.js +57 -45
- package/dist/renderables/glyph-effect.d.ts +158 -0
- package/dist/renderables/glyph-effect.js +237 -0
- package/dist/renderables/glyph-effects.d.ts +147 -0
- package/dist/renderables/glyph-effects.js +283 -0
- package/dist/renderables/group.d.ts +1 -1
- package/dist/renderables/group.js +2 -2
- package/dist/renderables/html-entities.d.ts +8 -0
- package/dist/renderables/html-entities.js +386 -0
- package/dist/renderables/layout.d.ts +10 -9
- package/dist/renderables/layout.js +42 -35
- package/dist/renderables/lifecycle.d.ts +77 -0
- package/dist/renderables/lifecycle.js +132 -0
- package/dist/renderables/list-view.d.ts +2 -2
- package/dist/renderables/list-view.js +11 -8
- package/dist/renderables/live.d.ts +36 -21
- package/dist/renderables/live.js +89 -42
- package/dist/renderables/markdown-parse.d.ts +109 -0
- package/dist/renderables/markdown-parse.js +1427 -0
- package/dist/renderables/markdown-url.d.ts +19 -0
- package/dist/renderables/markdown-url.js +314 -0
- package/dist/renderables/markdown.d.ts +5 -7
- package/dist/renderables/markdown.js +356 -683
- package/dist/renderables/padding.d.ts +7 -20
- package/dist/renderables/padding.js +29 -50
- package/dist/renderables/panel.d.ts +30 -32
- package/dist/renderables/panel.js +65 -109
- package/dist/renderables/progress.d.ts +83 -32
- package/dist/renderables/progress.js +186 -90
- package/dist/renderables/progressBar.d.ts +22 -12
- package/dist/renderables/progressBar.js +33 -10
- package/dist/renderables/prompt.js +3 -2
- package/dist/renderables/python-space.d.ts +13 -0
- package/dist/renderables/python-space.js +25 -0
- package/dist/renderables/scroll.d.ts +50 -0
- package/dist/renderables/scroll.js +106 -0
- package/dist/renderables/spinner.d.ts +57 -24
- package/dist/renderables/spinner.js +101 -49
- package/dist/renderables/status.js +1 -1
- package/dist/renderables/syntax.d.ts +10 -9
- package/dist/renderables/syntax.js +5 -3
- package/dist/renderables/table.d.ts +48 -46
- package/dist/renderables/table.js +170 -208
- package/dist/renderables/traceback.d.ts +3 -3
- package/dist/renderables/traceback.js +12 -10
- package/dist/renderables/tree.d.ts +28 -25
- package/dist/renderables/tree.js +108 -58
- package/dist/renderables/viewport.d.ts +77 -38
- package/dist/renderables/viewport.js +156 -72
- package/dist/template-bindings/animation-funcs.d.ts +78 -9
- package/dist/template-bindings/animation-funcs.js +132 -14
- package/dist/template-bindings/index.d.ts +5 -5
- package/dist/template-bindings/index.js +7 -6
- package/dist/themes/ramp.d.ts +62 -4
- package/dist/themes/ramp.js +82 -4
- package/dist/themes/registry.js +5 -7
- package/dist/widgets/button.js +3 -2
- package/dist/widgets/checkbox.js +4 -3
- package/dist/widgets/dropdown.d.ts +3 -1
- package/dist/widgets/dropdown.js +20 -15
- package/dist/widgets/event-router.js +4 -2
- package/dist/widgets/focus-manager.d.ts +23 -14
- package/dist/widgets/focus-manager.js +34 -33
- package/dist/widgets/hit.d.ts +6 -0
- package/dist/widgets/hit.js +8 -1
- package/dist/widgets/index.d.ts +1 -1
- package/dist/widgets/observable.d.ts +23 -0
- package/dist/widgets/observable.js +43 -0
- package/dist/widgets/slider.d.ts +1 -1
- package/dist/widgets/slider.js +12 -3
- package/dist/widgets/static-item.d.ts +1 -1
- package/dist/widgets/static-item.js +6 -7
- package/dist/widgets/text-input.d.ts +53 -47
- package/dist/widgets/text-input.js +456 -399
- package/dist/widgets/themed-widget.js +2 -1
- package/dist/widgets/toggle.js +4 -3
- package/dist/widgets/types.d.ts +10 -4
- package/dist/widgets/types.js +8 -2
- package/dist/widgets/widget-app.d.ts +32 -6
- package/dist/widgets/widget-app.js +74 -27
- package/dist/widgets/widget-base.d.ts +2 -2
- package/dist/widgets/widget-base.js +29 -21
- package/package.json +9 -5
|
@@ -5,7 +5,7 @@ import { Segment } from "../core/segment.js";
|
|
|
5
5
|
import { Style } from "../core/style.js";
|
|
6
6
|
import type { RichText } from "../core/text.js";
|
|
7
7
|
import type { Renderable, Measurable, RenderOptions } from "../core/protocol.js";
|
|
8
|
-
export type PaddingDimensions = number | [number, number] | [number, number, number, number];
|
|
8
|
+
export type PaddingDimensions = number | readonly [number, number] | readonly [number, number, number, number];
|
|
9
9
|
/**
|
|
10
10
|
* Parses the shapes a caller may write padding in — one number, a vertical/
|
|
11
11
|
* horizontal pair, or all four sides — into the four-sided form every
|
|
@@ -18,26 +18,14 @@ export type PaddingDimensions = number | [number, number] | [number, number, num
|
|
|
18
18
|
* to reach the renderers, where `Panel` drew content rows wider than its own
|
|
19
19
|
* border and `Table` threw outright.
|
|
20
20
|
*/
|
|
21
|
-
export declare function normalizePadding(padding: PaddingDimensions): [number, number, number, number];
|
|
22
|
-
/** The three spans of a padded row, summing to exactly the requested width. */
|
|
23
|
-
interface PaddingGeometry {
|
|
24
|
-
readonly left: number;
|
|
25
|
-
readonly contentWidth: number;
|
|
26
|
-
readonly right: number;
|
|
27
|
-
}
|
|
21
|
+
export declare function normalizePadding(padding: PaddingDimensions): readonly [number, number, number, number];
|
|
28
22
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* `render` took the canvas as `Math.max(1, maxWidth - left - right)`, which
|
|
34
|
-
* clamps *up*, so a padding of 1 inside a 2-cell request drew 3-cell content
|
|
35
|
-
* rows beside 2-cell blank rows — an open frame the terminal then soft-wraps.
|
|
36
|
-
* Content takes its first cell before padding takes any, for the reason
|
|
37
|
-
* `layoutPanel` gives: a squeeze should cost you the decoration, not the thing
|
|
38
|
-
* being decorated.
|
|
23
|
+
* The width a padding leaves its content: what the request holds once both
|
|
24
|
+
* sides are paid for, as Rich's `Padding` divides it. The sides are paid in
|
|
25
|
+
* full, so a request too narrow for them leaves the content no column, and it
|
|
26
|
+
* is drawn as nothing — Rich's `Console.render` under one column.
|
|
39
27
|
*/
|
|
40
|
-
export declare function
|
|
28
|
+
export declare function paddedContentWidth(outerWidth: number, left: number, right: number): number;
|
|
41
29
|
export declare class Padding implements Renderable, Measurable {
|
|
42
30
|
readonly renderable: Renderable;
|
|
43
31
|
readonly top: number;
|
|
@@ -56,4 +44,3 @@ export declare class Padding implements Renderable, Measurable {
|
|
|
56
44
|
maximum: number;
|
|
57
45
|
};
|
|
58
46
|
}
|
|
59
|
-
export {};
|
|
@@ -5,7 +5,7 @@ import { Segment } from "../core/segment.js";
|
|
|
5
5
|
import { cellCount } from "../core/cells.js";
|
|
6
6
|
import { NULL_STYLE } from "../core/style.js";
|
|
7
7
|
import { Measurement } from "../core/measure.js";
|
|
8
|
-
import { fitHeight, getStyle, insetHeight, withBoundedWidth, withCellWidth } from "../core/protocol.js";
|
|
8
|
+
import { fitHeight, getStyle, insetHeight, render, withBoundedWidth, withCellWidth } from "../core/protocol.js";
|
|
9
9
|
import { embed } from "./embed.js";
|
|
10
10
|
/**
|
|
11
11
|
* Parses the shapes a caller may write padding in — one number, a vertical/
|
|
@@ -41,32 +41,13 @@ export function normalizePadding(padding) {
|
|
|
41
41
|
return [side(sides[0]), side(sides[1]), side(sides[2]), side(sides[3])];
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
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.
|
|
44
|
+
* The width a padding leaves its content: what the request holds once both
|
|
45
|
+
* sides are paid for, as Rich's `Padding` divides it. The sides are paid in
|
|
46
|
+
* full, so a request too narrow for them leaves the content no column, and it
|
|
47
|
+
* is drawn as nothing — Rich's `Console.render` under one column.
|
|
54
48
|
*/
|
|
55
|
-
export function
|
|
56
|
-
|
|
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;
|
|
49
|
+
export function paddedContentWidth(outerWidth, left, right) {
|
|
50
|
+
return cellCount(cellCount(outerWidth) - left - right);
|
|
70
51
|
}
|
|
71
52
|
export class Padding {
|
|
72
53
|
renderable;
|
|
@@ -94,10 +75,10 @@ export class Padding {
|
|
|
94
75
|
// rows instead left the content rows ragged under a full-width blank row,
|
|
95
76
|
// and a `style` ground with holes down its right-hand side.
|
|
96
77
|
const width = this.expand ? options.maxWidth : Measurement.get(options, this).maximum;
|
|
97
|
-
const
|
|
78
|
+
const contentWidth = paddedContentWidth(width, this.left, this.right);
|
|
98
79
|
const innerOptions = {
|
|
99
80
|
...options,
|
|
100
|
-
maxWidth:
|
|
81
|
+
maxWidth: contentWidth,
|
|
101
82
|
height: insetHeight(options.height, this.top + this.bottom),
|
|
102
83
|
};
|
|
103
84
|
const resolved = getStyle(options, this.style);
|
|
@@ -105,35 +86,33 @@ export class Padding {
|
|
|
105
86
|
// The style is the ground the content is drawn on, not only the spaces
|
|
106
87
|
// around it — Rich's `render_lines(..., style=style)`. Applied to the
|
|
107
88
|
// padding alone, a background left a hole behind the content.
|
|
108
|
-
const
|
|
109
|
-
const lines = fitHeight(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
89
|
+
const drawn = Segment.splitLines(Segment.applyStyle(render(this.renderable, innerOptions), style));
|
|
90
|
+
const lines = fitHeight(drawn, innerOptions.height);
|
|
91
|
+
const leftPad = new Segment(" ".repeat(this.left), style);
|
|
92
|
+
const rightPad = new Segment(" ".repeat(this.right), style);
|
|
93
|
+
for (let i = 0; i < this.top; i++)
|
|
94
|
+
yield* row([]);
|
|
95
|
+
// Every row is the block's width: content rows pad and crop the content to
|
|
96
|
+
// its canvas — a child that ignored the canvas it was handed (a Table at
|
|
97
|
+
// its natural width) would otherwise burst the padding — and the row to
|
|
98
|
+
// the block, so the rows a region asks for under sides the width cannot
|
|
99
|
+
// hold are cut at its edge rather than drawn past it.
|
|
119
100
|
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();
|
|
101
|
+
yield* row([leftPad, ...Segment.adjustLineLength(line, contentWidth, style), rightPad]);
|
|
126
102
|
}
|
|
127
|
-
for (let i = 0; i < this.bottom; i++)
|
|
128
|
-
yield
|
|
103
|
+
for (let i = 0; i < this.bottom; i++)
|
|
104
|
+
yield* row([]);
|
|
105
|
+
function* row(cells) {
|
|
106
|
+
yield* Segment.adjustLineLength(cells, width, style);
|
|
129
107
|
yield Segment.line();
|
|
130
108
|
}
|
|
131
109
|
}
|
|
132
110
|
measure(rawOptions) {
|
|
133
111
|
const options = withCellWidth(rawOptions);
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
112
|
+
const overhead = this.left + this.right;
|
|
113
|
+
// Content left no column measures as nothing, so the padding asks for the
|
|
114
|
+
// whole offer — Rich's `Padding.__rich_measure__`.
|
|
115
|
+
const measurement = Measurement.get({ ...options, maxWidth: paddedContentWidth(options.maxWidth, this.left, this.right) }, this.renderable);
|
|
137
116
|
// The ceiling is what was offered, and it wins over what the child asks
|
|
138
117
|
// for: a range whose floor sits above its own ceiling — this returned
|
|
139
118
|
// {8, 8} for three cells of padding inside a two-cell offer — is one no
|
|
@@ -8,16 +8,18 @@ import { RichText } from "../core/text.js";
|
|
|
8
8
|
import type { PaddingDimensions } from "./padding.js";
|
|
9
9
|
import type { Renderable, Measurable, RenderOptions } from "../core/protocol.js";
|
|
10
10
|
/**
|
|
11
|
-
* A
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
11
|
+
* A border accessory. Strings render inline in the border style; a
|
|
12
|
+
* `RichText` accessory lays ONLY its wrapping `style` over the border's
|
|
13
|
+
* (per-range spans within the RichText are not preserved — the accessory is a
|
|
14
|
+
* small status indicator, not an arbitrary span carrier).
|
|
15
|
+
*
|
|
16
|
+
* [LAW:no-ambient-temporal-coupling] A function form is handed the rows the
|
|
17
|
+
* body drew this frame, so an accessory that mirrors the content's layout (a
|
|
18
|
+
* widget's scroll position) reads it off the frame it labels, not off state
|
|
19
|
+
* the content kept from an earlier one. A width that leaves the content no column does not render it,
|
|
20
|
+
* as Rich's does not, and the rows it is handed are then blank.
|
|
19
21
|
*/
|
|
20
|
-
export type BorderAccessory = string | RichText | (() => string | RichText | undefined);
|
|
22
|
+
export type BorderAccessory = string | RichText | ((body: readonly (readonly Segment[])[]) => string | RichText | undefined);
|
|
21
23
|
export interface PanelOptions {
|
|
22
24
|
box?: Box;
|
|
23
25
|
title?: string | RichText;
|
|
@@ -58,39 +60,35 @@ export declare class Panel implements Renderable, Measurable {
|
|
|
58
60
|
readonly titleStyle: string | Style | undefined;
|
|
59
61
|
readonly subtitleStyle: string | Style | undefined;
|
|
60
62
|
readonly width: number | undefined;
|
|
61
|
-
readonly padding: [number, number, number, number];
|
|
62
63
|
private readonly _titleLabel;
|
|
63
64
|
private readonly _subtitleLabel;
|
|
64
|
-
constructor(content: string | RichText | Renderable, options?: PanelOptions);
|
|
65
|
-
render(rawOptions: RenderOptions): Iterable<Segment>;
|
|
66
65
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
66
|
+
* What the frame is drawn around: the content inside its padding, on the
|
|
67
|
+
* panel's ground. Rich's `Panel` draws its body as this same `Padding`, so
|
|
68
|
+
* how a squeezed width divides between padding and content is answered once,
|
|
69
|
+
* by `Padding`, for both.
|
|
71
70
|
*/
|
|
72
|
-
private
|
|
73
|
-
/**
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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;
|
|
71
|
+
private readonly _body;
|
|
72
|
+
/** The padding inside the frame, top, right, bottom, left — the body's, read back. */
|
|
73
|
+
get padding(): readonly [number, number, number, number];
|
|
74
|
+
constructor(content: string | RichText | Renderable, options?: PanelOptions);
|
|
75
|
+
render(rawOptions: RenderOptions): Iterable<Segment>;
|
|
84
76
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
77
|
+
* The body's rows, held to the region as Rich's `render_lines` holds them —
|
|
78
|
+
* padded below, cropped from the bottom — and each exactly `spanWidth`
|
|
79
|
+
* cells: `Padding` makes every row its block's width, cropping content that
|
|
80
|
+
* ignored its canvas. A span of no cells renders nothing, as Rich's
|
|
81
|
+
* `Console.render` renders nothing under one column, so outside a region it
|
|
82
|
+
* draws no rows, padding rows included, and inside one the frame stands
|
|
83
|
+
* around empty rows. The render happens before either border is drawn,
|
|
84
|
+
* because a `bottomRightAccessory` function is handed the rows it drew.
|
|
87
85
|
*/
|
|
88
|
-
private
|
|
86
|
+
private _renderBody;
|
|
89
87
|
measure(rawOptions: RenderOptions): {
|
|
90
88
|
minimum: number;
|
|
91
89
|
maximum: number;
|
|
92
90
|
};
|
|
93
|
-
/** The
|
|
91
|
+
/** The body plus the frame around it, both read off the division it will render against. */
|
|
94
92
|
private _contentRange;
|
|
95
93
|
/** The cells the title's label takes, its padding included; none without a title. */
|
|
96
94
|
private _labelWidth;
|
|
@@ -8,45 +8,22 @@ import { ROUNDED } from "../core/box.js";
|
|
|
8
8
|
import { Measurement } from "../core/measure.js";
|
|
9
9
|
import { RichText } from "../core/text.js";
|
|
10
10
|
import { cutLabel, drawLabel, embed, inlineLabel } from "./embed.js";
|
|
11
|
-
import {
|
|
12
|
-
import { drawable, fitHeight, getStyle, insetHeight, withBoundedWidth, withCellWidth } from "../core/protocol.js";
|
|
13
|
-
function layoutPanel(outerWidth
|
|
14
|
-
const [, padRightWanted, , padLeftWanted] = padding;
|
|
11
|
+
import { Padding } from "./padding.js";
|
|
12
|
+
import { drawable, fitHeight, getStyle, insetHeight, render, withBoundedWidth, withCellWidth } from "../core/protocol.js";
|
|
13
|
+
function layoutPanel(outerWidth) {
|
|
15
14
|
// Panel's one division point, and the width reaching it is not always one
|
|
16
15
|
// this file produced: `_getPanelWidth` derives the fit-mode width from
|
|
17
16
|
// `Measurement.get` on an arbitrary `Measurable`, so a renderable reporting a
|
|
18
17
|
// negative maximum arrives here unparsed and reaches `repeat` as a negative
|
|
19
18
|
// count.
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
};
|
|
19
|
+
const width = cellCount(outerWidth);
|
|
20
|
+
const left = Math.min(1, width);
|
|
21
|
+
const right = Math.min(1, width - left);
|
|
22
|
+
return { left, right, spanWidth: width - left - right };
|
|
40
23
|
}
|
|
41
|
-
/**
|
|
42
|
-
|
|
43
|
-
|
|
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;
|
|
24
|
+
/** The cells a panel's frame takes from the width it is given. */
|
|
25
|
+
function frameWidth(geometry) {
|
|
26
|
+
return geometry.left + geometry.right;
|
|
50
27
|
}
|
|
51
28
|
/**
|
|
52
29
|
* How a title or subtitle string is read: always as markup and never
|
|
@@ -114,9 +91,20 @@ export class Panel {
|
|
|
114
91
|
titleStyle;
|
|
115
92
|
subtitleStyle;
|
|
116
93
|
width;
|
|
117
|
-
padding;
|
|
118
94
|
_titleLabel;
|
|
119
95
|
_subtitleLabel;
|
|
96
|
+
/**
|
|
97
|
+
* What the frame is drawn around: the content inside its padding, on the
|
|
98
|
+
* panel's ground. Rich's `Panel` draws its body as this same `Padding`, so
|
|
99
|
+
* how a squeezed width divides between padding and content is answered once,
|
|
100
|
+
* by `Padding`, for both.
|
|
101
|
+
*/
|
|
102
|
+
_body;
|
|
103
|
+
/** The padding inside the frame, top, right, bottom, left — the body's, read back. */
|
|
104
|
+
get padding() {
|
|
105
|
+
const body = this._body;
|
|
106
|
+
return [body.top, body.right, body.bottom, body.left];
|
|
107
|
+
}
|
|
120
108
|
constructor(content, options) {
|
|
121
109
|
this.renderable = embed(content);
|
|
122
110
|
this.box = options?.box ?? ROUNDED;
|
|
@@ -131,7 +119,7 @@ export class Panel {
|
|
|
131
119
|
this.titleStyle = options?.titleStyle;
|
|
132
120
|
this.subtitleStyle = options?.subtitleStyle;
|
|
133
121
|
this.width = options?.width;
|
|
134
|
-
this.
|
|
122
|
+
this._body = new Padding(this.renderable, options?.padding ?? [0, 1, 0, 1], { style: this.style });
|
|
135
123
|
}
|
|
136
124
|
*render(rawOptions) {
|
|
137
125
|
const options = withBoundedWidth(rawOptions, this);
|
|
@@ -140,72 +128,44 @@ export class Panel {
|
|
|
140
128
|
// on, as Rich's is: the frame's style is the border style laid over it,
|
|
141
129
|
// and the content is rendered on top of it. Applied to the padding alone,
|
|
142
130
|
// "white on dark_blue" drew a blue ring with a hole where the text was.
|
|
143
|
-
const
|
|
144
|
-
const borderStyle = style.add(getStyle(options, this.borderStyle));
|
|
131
|
+
const borderStyle = getStyle(options, this.style).add(getStyle(options, this.borderStyle));
|
|
145
132
|
const border = borderStyle.isNull ? undefined : borderStyle;
|
|
146
|
-
const
|
|
147
|
-
const
|
|
148
|
-
const [padTop, , padBottom] = this.padding;
|
|
149
|
-
const contentLines = this._renderContent(options, geometry.contentWidth, contentStyle);
|
|
133
|
+
const geometry = layoutPanel(this._getPanelWidth(options));
|
|
134
|
+
const bodyLines = this._renderBody(options, geometry.spanWidth);
|
|
150
135
|
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
136
|
const frame = box.getContentChars("row");
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
137
|
+
for (const line of bodyLines) {
|
|
138
|
+
yield new Segment(frame.left.repeat(geometry.left), border);
|
|
139
|
+
yield* line;
|
|
140
|
+
yield new Segment(frame.right.repeat(geometry.right), border);
|
|
141
|
+
yield Segment.line();
|
|
142
|
+
}
|
|
143
|
+
yield* this._renderBottomBorder(options, box, geometry, border, bodyLines);
|
|
198
144
|
}
|
|
199
145
|
/**
|
|
200
|
-
*
|
|
201
|
-
*
|
|
146
|
+
* The body's rows, held to the region as Rich's `render_lines` holds them —
|
|
147
|
+
* padded below, cropped from the bottom — and each exactly `spanWidth`
|
|
148
|
+
* cells: `Padding` makes every row its block's width, cropping content that
|
|
149
|
+
* ignored its canvas. A span of no cells renders nothing, as Rich's
|
|
150
|
+
* `Console.render` renders nothing under one column, so outside a region it
|
|
151
|
+
* draws no rows, padding rows included, and inside one the frame stands
|
|
152
|
+
* around empty rows. The render happens before either border is drawn,
|
|
153
|
+
* because a `bottomRightAccessory` function is handed the rows it drew.
|
|
202
154
|
*/
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
155
|
+
_renderBody(options, spanWidth) {
|
|
156
|
+
// Two border rows are the panel's own, and the vertical padding is the
|
|
157
|
+
// body's. Handed on as a region, the rest is the panel's to shape, which
|
|
158
|
+
// is what stretches its frame down a pane and keeps its bottom border
|
|
159
|
+
// inside one. No highlighter: what the panel holds is drawn plain, as
|
|
160
|
+
// Rich's `Panel` hands its body `highlight=False`.
|
|
161
|
+
const bodyOptions = {
|
|
162
|
+
...options,
|
|
163
|
+
highlight: false,
|
|
164
|
+
maxWidth: spanWidth,
|
|
165
|
+
height: insetHeight(options.height, 2),
|
|
166
|
+
};
|
|
167
|
+
const lines = Segment.splitLines(render(this._body, bodyOptions));
|
|
168
|
+
return fitHeight(lines, bodyOptions.height);
|
|
209
169
|
}
|
|
210
170
|
measure(rawOptions) {
|
|
211
171
|
const options = withCellWidth(rawOptions);
|
|
@@ -220,18 +180,17 @@ export class Panel {
|
|
|
220
180
|
return { minimum: width, maximum: width };
|
|
221
181
|
}
|
|
222
182
|
const content = this._contentRange(options);
|
|
223
|
-
const overhead =
|
|
183
|
+
const overhead = frameWidth(layoutPanel(options.maxWidth)) + this._body.left + this._body.right;
|
|
224
184
|
const maximum = Math.min(options.maxWidth, Math.max(content.maximum, this._labelWidth(options) + overhead));
|
|
225
185
|
return { minimum: Math.min(content.minimum, maximum), maximum };
|
|
226
186
|
}
|
|
227
|
-
/** The
|
|
187
|
+
/** The body plus the frame around it, both read off the division it will render against. */
|
|
228
188
|
_contentRange(options) {
|
|
229
|
-
const geometry = layoutPanel(options.maxWidth
|
|
230
|
-
const
|
|
231
|
-
const measurement = Measurement.get({ ...options, maxWidth: geometry.contentWidth }, this.renderable);
|
|
189
|
+
const geometry = layoutPanel(options.maxWidth);
|
|
190
|
+
const measurement = Measurement.get({ ...options, maxWidth: geometry.spanWidth }, this._body);
|
|
232
191
|
return {
|
|
233
|
-
minimum: measurement.minimum +
|
|
234
|
-
maximum:
|
|
192
|
+
minimum: measurement.minimum + frameWidth(geometry),
|
|
193
|
+
maximum: measurement.maximum + frameWidth(geometry),
|
|
235
194
|
};
|
|
236
195
|
}
|
|
237
196
|
/** The cells the title's label takes, its padding included; none without a title. */
|
|
@@ -265,11 +224,8 @@ export class Panel {
|
|
|
265
224
|
yield* borderRow(options, box.top, geometry, geometry.spanWidth, this._titleLabel, titleStyle, border, box.top.right.repeat(geometry.right));
|
|
266
225
|
yield Segment.line();
|
|
267
226
|
}
|
|
268
|
-
*_renderBottomBorder(options, box, geometry, border) {
|
|
269
|
-
|
|
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);
|
|
227
|
+
*_renderBottomBorder(options, box, geometry, border, body) {
|
|
228
|
+
const accessory = this._resolveAccessory(this.bottomRightAccessory, body);
|
|
273
229
|
const accessoryDisplay = accessory === undefined
|
|
274
230
|
? ""
|
|
275
231
|
: typeof accessory === "string"
|
|
@@ -289,11 +245,11 @@ export class Panel {
|
|
|
289
245
|
yield new Segment(afterAccessory, border);
|
|
290
246
|
yield Segment.line();
|
|
291
247
|
}
|
|
292
|
-
_resolveAccessory(a) {
|
|
248
|
+
_resolveAccessory(a, body) {
|
|
293
249
|
if (a === undefined)
|
|
294
250
|
return undefined;
|
|
295
251
|
if (typeof a === "function")
|
|
296
|
-
return a();
|
|
252
|
+
return a(body);
|
|
297
253
|
return a;
|
|
298
254
|
}
|
|
299
255
|
// --- Static factory ---
|