@promptctl/rich-js 0.24.0 → 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/THIRD-PARTY-NOTICES +29 -1
- package/dist/core/animation.d.ts +78 -0
- package/dist/core/animation.js +107 -0
- package/dist/core/cell-widths.d.ts +9 -0
- package/dist/core/cell-widths.js +140 -0
- package/dist/core/cells.d.ts +17 -11
- package/dist/core/cells.js +100 -25
- package/dist/core/console.js +5 -5
- package/dist/core/easing.d.ts +110 -10
- package/dist/core/easing.js +269 -36
- package/dist/core/json.js +2 -1
- package/dist/core/measure.js +2 -2
- package/dist/core/place.js +2 -1
- package/dist/core/pretty.js +2 -2
- package/dist/core/protocol.d.ts +73 -19
- package/dist/core/protocol.js +56 -15
- package/dist/core/render.js +2 -1
- package/dist/core/segment.d.ts +18 -1
- package/dist/core/segment.js +45 -44
- package/dist/core/strip.d.ts +3 -2
- package/dist/core/strip.js +11 -7
- package/dist/core/style.js +17 -6
- package/dist/core/text.js +22 -14
- package/dist/core/timeline.d.ts +84 -0
- package/dist/core/timeline.js +144 -0
- package/dist/core/tween.d.ts +49 -0
- package/dist/core/tween.js +69 -0
- package/dist/host/app.js +2 -2
- package/dist/index.d.ts +10 -7
- package/dist/index.js +13 -4
- package/dist/renderables/align.d.ts +3 -2
- package/dist/renderables/align.js +3 -0
- package/dist/renderables/columns.d.ts +3 -2
- package/dist/renderables/columns.js +7 -2
- package/dist/renderables/constrain.d.ts +3 -2
- package/dist/renderables/constrain.js +5 -1
- package/dist/renderables/effect.d.ts +43 -58
- package/dist/renderables/effect.js +58 -64
- package/dist/renderables/effects.d.ts +16 -14
- package/dist/renderables/effects.js +64 -45
- package/dist/renderables/embed.js +3 -2
- package/dist/renderables/flexStrip.d.ts +3 -2
- package/dist/renderables/flexStrip.js +6 -1
- package/dist/renderables/frames.d.ts +40 -19
- package/dist/renderables/frames.js +48 -21
- package/dist/renderables/group.d.ts +3 -2
- package/dist/renderables/group.js +5 -2
- package/dist/renderables/html-entities.d.ts +8 -0
- package/dist/renderables/html-entities.js +386 -0
- package/dist/renderables/layout.d.ts +3 -2
- package/dist/renderables/layout.js +15 -2
- package/dist/renderables/list-view.d.ts +3 -2
- package/dist/renderables/list-view.js +12 -5
- package/dist/renderables/live.js +2 -2
- package/dist/renderables/markdown-parse.d.ts +109 -0
- package/dist/renderables/markdown-parse.js +1421 -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 +8 -20
- package/dist/renderables/padding.js +32 -50
- package/dist/renderables/panel.d.ts +26 -26
- package/dist/renderables/panel.js +67 -103
- package/dist/renderables/progress.d.ts +4 -2
- package/dist/renderables/progress.js +11 -2
- package/dist/renderables/progressBar.js +2 -2
- package/dist/renderables/python-space.d.ts +13 -0
- package/dist/renderables/python-space.js +24 -0
- package/dist/renderables/spinner.d.ts +26 -14
- package/dist/renderables/spinner.js +49 -37
- package/dist/renderables/syntax.js +2 -1
- package/dist/renderables/table.d.ts +19 -11
- package/dist/renderables/table.js +140 -156
- package/dist/renderables/traceback.d.ts +1 -1
- package/dist/renderables/traceback.js +6 -5
- package/dist/renderables/tree.d.ts +6 -5
- package/dist/renderables/tree.js +11 -7
- package/dist/renderables/viewport.d.ts +13 -5
- package/dist/renderables/viewport.js +41 -20
- package/dist/template-bindings/animation-funcs.js +2 -2
- package/dist/template-bindings/index.js +2 -1
- package/dist/themes/ramp.d.ts +18 -3
- package/dist/themes/ramp.js +23 -2
- package/dist/widgets/focus-manager.d.ts +21 -14
- package/dist/widgets/focus-manager.js +28 -29
- package/dist/widgets/index.d.ts +1 -1
- package/dist/widgets/static-item.d.ts +4 -3
- package/dist/widgets/static-item.js +9 -7
- package/dist/widgets/text-input.d.ts +28 -18
- package/dist/widgets/text-input.js +144 -101
- package/dist/widgets/widget-app.d.ts +19 -3
- package/dist/widgets/widget-app.js +39 -21
- package/dist/widgets/widget-base.js +0 -3
- package/package.json +2 -5
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { Segment } from "../core/segment.js";
|
|
5
5
|
import { Style } from "../core/style.js";
|
|
6
6
|
import type { RichText } from "../core/text.js";
|
|
7
|
-
import type { Renderable, Measurable, RenderOptions } from "../core/protocol.js";
|
|
7
|
+
import type { Composite, Renderable, Measurable, RenderOptions } from "../core/protocol.js";
|
|
8
8
|
export type PaddingDimensions = number | [number, number] | [number, number, number, number];
|
|
9
9
|
/**
|
|
10
10
|
* Parses the shapes a caller may write padding in — one number, a vertical/
|
|
@@ -19,26 +19,14 @@ export type PaddingDimensions = number | [number, number] | [number, number, num
|
|
|
19
19
|
* border and `Table` threw outright.
|
|
20
20
|
*/
|
|
21
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
|
-
}
|
|
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
|
|
41
|
-
export declare class Padding implements Renderable, Measurable {
|
|
28
|
+
export declare function paddedContentWidth(outerWidth: number, left: number, right: number): number;
|
|
29
|
+
export declare class Padding implements Renderable, Measurable, Composite {
|
|
42
30
|
readonly renderable: Renderable;
|
|
43
31
|
readonly top: number;
|
|
44
32
|
readonly right: number;
|
|
@@ -50,10 +38,10 @@ export declare class Padding implements Renderable, Measurable {
|
|
|
50
38
|
style?: string | Style;
|
|
51
39
|
expand?: boolean;
|
|
52
40
|
});
|
|
41
|
+
contents(): Iterable<Renderable>;
|
|
53
42
|
render(rawOptions: RenderOptions): Iterable<Segment>;
|
|
54
43
|
measure(rawOptions: RenderOptions): {
|
|
55
44
|
minimum: number;
|
|
56
45
|
maximum: number;
|
|
57
46
|
};
|
|
58
47
|
}
|
|
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;
|
|
@@ -86,6 +67,9 @@ export class Padding {
|
|
|
86
67
|
this.style = options?.style ?? NULL_STYLE;
|
|
87
68
|
this.expand = options?.expand !== false;
|
|
88
69
|
}
|
|
70
|
+
*contents() {
|
|
71
|
+
yield this.renderable;
|
|
72
|
+
}
|
|
89
73
|
*render(rawOptions) {
|
|
90
74
|
const options = withBoundedWidth(rawOptions, this);
|
|
91
75
|
// `expand` decides how wide the block is, never whether its rows reach
|
|
@@ -94,10 +78,10 @@ export class Padding {
|
|
|
94
78
|
// rows instead left the content rows ragged under a full-width blank row,
|
|
95
79
|
// and a `style` ground with holes down its right-hand side.
|
|
96
80
|
const width = this.expand ? options.maxWidth : Measurement.get(options, this).maximum;
|
|
97
|
-
const
|
|
81
|
+
const contentWidth = paddedContentWidth(width, this.left, this.right);
|
|
98
82
|
const innerOptions = {
|
|
99
83
|
...options,
|
|
100
|
-
maxWidth:
|
|
84
|
+
maxWidth: contentWidth,
|
|
101
85
|
height: insetHeight(options.height, this.top + this.bottom),
|
|
102
86
|
};
|
|
103
87
|
const resolved = getStyle(options, this.style);
|
|
@@ -105,35 +89,33 @@ export class Padding {
|
|
|
105
89
|
// The style is the ground the content is drawn on, not only the spaces
|
|
106
90
|
// around it — Rich's `render_lines(..., style=style)`. Applied to the
|
|
107
91
|
// 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
|
-
|
|
92
|
+
const drawn = Segment.splitLines(Segment.applyStyle(render(this.renderable, innerOptions), style));
|
|
93
|
+
const lines = fitHeight(drawn, innerOptions.height);
|
|
94
|
+
const leftPad = new Segment(" ".repeat(this.left), style);
|
|
95
|
+
const rightPad = new Segment(" ".repeat(this.right), style);
|
|
96
|
+
for (let i = 0; i < this.top; i++)
|
|
97
|
+
yield* row([]);
|
|
98
|
+
// Every row is the block's width: content rows pad and crop the content to
|
|
99
|
+
// its canvas — a child that ignored the canvas it was handed (a Table at
|
|
100
|
+
// its natural width) would otherwise burst the padding — and the row to
|
|
101
|
+
// the block, so the rows a region asks for under sides the width cannot
|
|
102
|
+
// hold are cut at its edge rather than drawn past it.
|
|
119
103
|
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();
|
|
104
|
+
yield* row([leftPad, ...Segment.adjustLineLength(line, contentWidth, style), rightPad]);
|
|
126
105
|
}
|
|
127
|
-
for (let i = 0; i < this.bottom; i++)
|
|
128
|
-
yield
|
|
106
|
+
for (let i = 0; i < this.bottom; i++)
|
|
107
|
+
yield* row([]);
|
|
108
|
+
function* row(cells) {
|
|
109
|
+
yield* Segment.adjustLineLength(cells, width, style);
|
|
129
110
|
yield Segment.line();
|
|
130
111
|
}
|
|
131
112
|
}
|
|
132
113
|
measure(rawOptions) {
|
|
133
114
|
const options = withCellWidth(rawOptions);
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
115
|
+
const overhead = this.left + this.right;
|
|
116
|
+
// Content left no column measures as nothing, so the padding asks for the
|
|
117
|
+
// whole offer — Rich's `Padding.__rich_measure__`.
|
|
118
|
+
const measurement = Measurement.get({ ...options, maxWidth: paddedContentWidth(options.maxWidth, this.left, this.right) }, this.renderable);
|
|
137
119
|
// The ceiling is what was offered, and it wins over what the child asks
|
|
138
120
|
// for: a range whose floor sits above its own ceiling — this returned
|
|
139
121
|
// {8, 8} for three cells of padding inside a two-cell offer — is one no
|
|
@@ -6,7 +6,7 @@ import { Style } from "../core/style.js";
|
|
|
6
6
|
import { Box } from "../core/box.js";
|
|
7
7
|
import { RichText } from "../core/text.js";
|
|
8
8
|
import type { PaddingDimensions } from "./padding.js";
|
|
9
|
-
import type { Renderable, Measurable, RenderOptions } from "../core/protocol.js";
|
|
9
|
+
import type { Composite, Renderable, Measurable, RenderOptions } from "../core/protocol.js";
|
|
10
10
|
/**
|
|
11
11
|
* A lazily-resolved border accessory. Strings render inline in the
|
|
12
12
|
* border style; a `RichText` accessory lays ONLY its wrapping `style` over
|
|
@@ -15,7 +15,9 @@ import type { Renderable, Measurable, RenderOptions } from "../core/protocol.js"
|
|
|
15
15
|
* A function form is evaluated at render time, *after* content has been
|
|
16
16
|
* rendered for the current frame — use this when the accessory mirrors
|
|
17
17
|
* state that the wrapped renderable populates during its own `render()`
|
|
18
|
-
* (e.g. a widget's post-render scroll position).
|
|
18
|
+
* (e.g. a widget's post-render scroll position). A width that leaves the
|
|
19
|
+
* content no column does not render it, as Rich's does not, and the thunk
|
|
20
|
+
* then reads the state of the last frame that did.
|
|
19
21
|
*/
|
|
20
22
|
export type BorderAccessory = string | RichText | (() => string | RichText | undefined);
|
|
21
23
|
export interface PanelOptions {
|
|
@@ -46,7 +48,7 @@ export interface PanelOptions {
|
|
|
46
48
|
width?: number;
|
|
47
49
|
padding?: PaddingDimensions;
|
|
48
50
|
}
|
|
49
|
-
export declare class Panel implements Renderable, Measurable {
|
|
51
|
+
export declare class Panel implements Renderable, Measurable, Composite {
|
|
50
52
|
readonly renderable: Renderable;
|
|
51
53
|
readonly box: Box;
|
|
52
54
|
readonly title: string | RichText | undefined;
|
|
@@ -58,39 +60,37 @@ 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>;
|
|
76
|
+
contents(): Iterable<Renderable>;
|
|
84
77
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
78
|
+
* The body's rows, held to the region as Rich's `render_lines` holds them —
|
|
79
|
+
* padded below, cropped from the bottom — and each exactly `spanWidth`
|
|
80
|
+
* cells: `Padding` makes every row its block's width, cropping content that
|
|
81
|
+
* ignored its canvas. A span of no cells renders nothing, as Rich's
|
|
82
|
+
* `Console.render` renders nothing under one column, so outside a region it
|
|
83
|
+
* draws no rows, padding rows included, and inside one the frame stands
|
|
84
|
+
* around empty rows. The render happens before either border is drawn,
|
|
85
|
+
* because a `bottomRightAccessory` thunk reads state the content's render
|
|
86
|
+
* populates.
|
|
87
87
|
*/
|
|
88
|
-
private
|
|
88
|
+
private _renderBody;
|
|
89
89
|
measure(rawOptions: RenderOptions): {
|
|
90
90
|
minimum: number;
|
|
91
91
|
maximum: number;
|
|
92
92
|
};
|
|
93
|
-
/** The
|
|
93
|
+
/** The body plus the frame around it, both read off the division it will render against. */
|
|
94
94
|
private _contentRange;
|
|
95
95
|
/** The cells the title's label takes, its padding included; none without a title. */
|
|
96
96
|
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,50 @@ 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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
yield
|
|
156
|
-
|
|
157
|
-
for (let i = 0; i < padBottom; i++) {
|
|
158
|
-
yield* this._renderPaddingRow(box, geometry, border, contentStyle);
|
|
136
|
+
const frame = box.getContentChars("row");
|
|
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();
|
|
159
142
|
}
|
|
160
143
|
yield* this._renderBottomBorder(options, box, geometry, border);
|
|
161
144
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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;
|
|
145
|
+
// The body is the one thing a panel draws through `render`: its title and
|
|
146
|
+
// subtitle are text it builds.
|
|
147
|
+
*contents() {
|
|
148
|
+
yield this._body;
|
|
179
149
|
}
|
|
180
150
|
/**
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
151
|
+
* The body's rows, held to the region as Rich's `render_lines` holds them —
|
|
152
|
+
* padded below, cropped from the bottom — and each exactly `spanWidth`
|
|
153
|
+
* cells: `Padding` makes every row its block's width, cropping content that
|
|
154
|
+
* ignored its canvas. A span of no cells renders nothing, as Rich's
|
|
155
|
+
* `Console.render` renders nothing under one column, so outside a region it
|
|
156
|
+
* draws no rows, padding rows included, and inside one the frame stands
|
|
157
|
+
* around empty rows. The render happens before either border is drawn,
|
|
158
|
+
* because a `bottomRightAccessory` thunk reads state the content's render
|
|
159
|
+
* populates.
|
|
189
160
|
*/
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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();
|
|
161
|
+
_renderBody(options, spanWidth) {
|
|
162
|
+
// Two border rows are the panel's own, and the vertical padding is the
|
|
163
|
+
// body's. Handed on as a region, the rest is the panel's to shape, which
|
|
164
|
+
// is what stretches its frame down a pane and keeps its bottom border
|
|
165
|
+
// inside one. No highlighter: what the panel holds is drawn plain, as
|
|
166
|
+
// Rich's `Panel` hands its body `highlight=False`.
|
|
167
|
+
const bodyOptions = {
|
|
168
|
+
...options,
|
|
169
|
+
highlight: false,
|
|
170
|
+
maxWidth: spanWidth,
|
|
171
|
+
height: insetHeight(options.height, 2),
|
|
172
|
+
};
|
|
173
|
+
const lines = Segment.splitLines(render(this._body, bodyOptions));
|
|
174
|
+
return fitHeight(lines, bodyOptions.height);
|
|
209
175
|
}
|
|
210
176
|
measure(rawOptions) {
|
|
211
177
|
const options = withCellWidth(rawOptions);
|
|
@@ -220,18 +186,17 @@ export class Panel {
|
|
|
220
186
|
return { minimum: width, maximum: width };
|
|
221
187
|
}
|
|
222
188
|
const content = this._contentRange(options);
|
|
223
|
-
const overhead =
|
|
189
|
+
const overhead = frameWidth(layoutPanel(options.maxWidth)) + this._body.left + this._body.right;
|
|
224
190
|
const maximum = Math.min(options.maxWidth, Math.max(content.maximum, this._labelWidth(options) + overhead));
|
|
225
191
|
return { minimum: Math.min(content.minimum, maximum), maximum };
|
|
226
192
|
}
|
|
227
|
-
/** The
|
|
193
|
+
/** The body plus the frame around it, both read off the division it will render against. */
|
|
228
194
|
_contentRange(options) {
|
|
229
|
-
const geometry = layoutPanel(options.maxWidth
|
|
230
|
-
const
|
|
231
|
-
const measurement = Measurement.get({ ...options, maxWidth: geometry.contentWidth }, this.renderable);
|
|
195
|
+
const geometry = layoutPanel(options.maxWidth);
|
|
196
|
+
const measurement = Measurement.get({ ...options, maxWidth: geometry.spanWidth }, this._body);
|
|
232
197
|
return {
|
|
233
|
-
minimum: measurement.minimum +
|
|
234
|
-
maximum:
|
|
198
|
+
minimum: measurement.minimum + frameWidth(geometry),
|
|
199
|
+
maximum: measurement.maximum + frameWidth(geometry),
|
|
235
200
|
};
|
|
236
201
|
}
|
|
237
202
|
/** The cells the title's label takes, its padding included; none without a title. */
|
|
@@ -266,9 +231,8 @@ export class Panel {
|
|
|
266
231
|
yield Segment.line();
|
|
267
232
|
}
|
|
268
233
|
*_renderBottomBorder(options, box, geometry, border) {
|
|
269
|
-
// Resolve the right accessory *now
|
|
270
|
-
//
|
|
271
|
-
// before yielding any borders), so the thunk sees fresh widget state.
|
|
234
|
+
// Resolve the right accessory *now*, after the body has rendered —
|
|
235
|
+
// `BorderAccessory` owns what a thunk sees.
|
|
272
236
|
const accessory = this._resolveAccessory(this.bottomRightAccessory);
|
|
273
237
|
const accessoryDisplay = accessory === undefined
|
|
274
238
|
? ""
|
|
@@ -8,7 +8,7 @@ import type { Style } from "../core/style.js";
|
|
|
8
8
|
import { ProgressBar } from "./progressBar.js";
|
|
9
9
|
import { type Clock } from "../core/clock.js";
|
|
10
10
|
import { type ColumnOptions } from "./table.js";
|
|
11
|
-
import type { Renderable, RenderOptions } from "../core/protocol.js";
|
|
11
|
+
import type { Composite, Renderable, RenderOptions } from "../core/protocol.js";
|
|
12
12
|
export interface TaskOptions {
|
|
13
13
|
total?: number;
|
|
14
14
|
start?: boolean;
|
|
@@ -156,7 +156,7 @@ export interface ProgressOptions {
|
|
|
156
156
|
*/
|
|
157
157
|
clock?: Clock;
|
|
158
158
|
}
|
|
159
|
-
export declare class Progress implements Renderable {
|
|
159
|
+
export declare class Progress implements Renderable, Composite {
|
|
160
160
|
private _columns;
|
|
161
161
|
private _tasks;
|
|
162
162
|
private _nextId;
|
|
@@ -176,6 +176,8 @@ export declare class Progress implements Renderable {
|
|
|
176
176
|
stop(): void;
|
|
177
177
|
refresh(): void;
|
|
178
178
|
render(options: RenderOptions): Iterable<Segment>;
|
|
179
|
+
contents(): Iterable<Renderable>;
|
|
180
|
+
private _table;
|
|
179
181
|
}
|
|
180
182
|
export declare function track<T>(iterable: Iterable<T>, options?: {
|
|
181
183
|
description?: string;
|
|
@@ -9,6 +9,7 @@ import { Spinner } from "./spinner.js";
|
|
|
9
9
|
import { Live } from "./live.js";
|
|
10
10
|
import { systemClock } from "../core/clock.js";
|
|
11
11
|
import { Table } from "./table.js";
|
|
12
|
+
import { render } from "../core/protocol.js";
|
|
12
13
|
/**
|
|
13
14
|
* Rich's defaults for `speed_estimate_period` and its sample deque's
|
|
14
15
|
* `maxlen`: an update forgets samples older than the period, and the newest
|
|
@@ -328,7 +329,15 @@ export class Progress {
|
|
|
328
329
|
this._live.refresh();
|
|
329
330
|
}
|
|
330
331
|
*render(options) {
|
|
331
|
-
|
|
332
|
+
yield* render(this._table(), options);
|
|
333
|
+
}
|
|
334
|
+
// What each column renders for each task is what the table holds, and so is
|
|
335
|
+
// all a progress display holds.
|
|
336
|
+
*contents() {
|
|
337
|
+
yield this._table();
|
|
338
|
+
}
|
|
339
|
+
// Rich's `make_tasks_table`: each column laid out as it says.
|
|
340
|
+
_table() {
|
|
332
341
|
const table = Table.grid({ expand: this.expand });
|
|
333
342
|
for (const col of this._columns) {
|
|
334
343
|
table.addColumn(undefined, col.tableColumn);
|
|
@@ -349,7 +358,7 @@ export class Progress {
|
|
|
349
358
|
};
|
|
350
359
|
table.addRow(...this._columns.map((col) => col.render(task)));
|
|
351
360
|
}
|
|
352
|
-
|
|
361
|
+
return table;
|
|
353
362
|
}
|
|
354
363
|
}
|
|
355
364
|
// --- track ---
|
|
@@ -5,7 +5,7 @@ import { cellCount } from "../core/cells.js";
|
|
|
5
5
|
import { ColorDepth, ColorRgba, ColorSpec, resolveTerminal } from "../core/color.js";
|
|
6
6
|
import { Segment } from "../core/segment.js";
|
|
7
7
|
import { Style } from "../core/style.js";
|
|
8
|
-
import { drawable, getStyle } from "../core/protocol.js";
|
|
8
|
+
import { drawable, getStyle, render } from "../core/protocol.js";
|
|
9
9
|
import { Effected } from "./effect.js";
|
|
10
10
|
import { EFFECT_CURVES, atShares, shimmer } from "./effects.js";
|
|
11
11
|
const BAR = "━";
|
|
@@ -59,7 +59,7 @@ export class ProgressBar {
|
|
|
59
59
|
// [LAW:no-ambient-temporal-coupling] The moment is the caller's; the bar
|
|
60
60
|
// only says what it looks like then. An empty track, lit by the shimmer.
|
|
61
61
|
const track = { render: (o) => this.track(o, width, 0, back) };
|
|
62
|
-
yield* new Effected(track, this.pulseEffect(options, back, depth, theme), { t: this.pulse.t, key: "progress-bar", theme })
|
|
62
|
+
yield* render(new Effected(track, this.pulseEffect(options, back, depth, theme), { t: this.pulse.t, key: "progress-bar", theme }), options);
|
|
63
63
|
}
|
|
64
64
|
/**
|
|
65
65
|
* The shimmer over this bar's back colour as `Effected` sees it drawn at
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whitespace as Python reads it — `str.isspace`, `str.strip`, and `\s` in a
|
|
3
|
+
* `str` pattern of `re`, which are one set. markdown-it-py, the markdown-it
|
|
4
|
+
* Rich runs, reads whitespace through all three, and that set is not
|
|
5
|
+
* JavaScript's: `\s` and `trim` take U+FEFF, which Python does not, and leave
|
|
6
|
+
* U+001C–U+001F and U+0085, which Python takes.
|
|
7
|
+
*/
|
|
8
|
+
/** The set as a regex character class, every member of it a single UTF-16 unit, for composing into a pattern where markdown-it-py's has `\s`. */
|
|
9
|
+
export declare const PY_WHITESPACE: string;
|
|
10
|
+
/** Python's `str.strip`, a scan in from each end, so a run of interior whitespace costs one pass, as it does there. */
|
|
11
|
+
export declare function pyStrip(text: string): string;
|
|
12
|
+
/** Python's `str.rstrip`, a scan in from the end — not a `+$` pattern, which retries from every cell of an interior run. */
|
|
13
|
+
export declare function pyRstrip(text: string): string;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whitespace as Python reads it — `str.isspace`, `str.strip`, and `\s` in a
|
|
3
|
+
* `str` pattern of `re`, which are one set. markdown-it-py, the markdown-it
|
|
4
|
+
* Rich runs, reads whitespace through all three, and that set is not
|
|
5
|
+
* JavaScript's: `\s` and `trim` take U+FEFF, which Python does not, and leave
|
|
6
|
+
* U+001C–U+001F and U+0085, which Python takes.
|
|
7
|
+
*/
|
|
8
|
+
/** The set as a regex character class, every member of it a single UTF-16 unit, for composing into a pattern where markdown-it-py's has `\s`. */
|
|
9
|
+
export const PY_WHITESPACE = String.raw `[\t-\r\x1c-\x20\x85\xa0 -
]`;
|
|
10
|
+
const PY_SPACE = new RegExp(`^${PY_WHITESPACE}$`);
|
|
11
|
+
/** Python's `str.strip`, a scan in from each end, so a run of interior whitespace costs one pass, as it does there. */
|
|
12
|
+
export function pyStrip(text) {
|
|
13
|
+
let start = 0;
|
|
14
|
+
while (start < text.length && PY_SPACE.test(text[start]))
|
|
15
|
+
start++;
|
|
16
|
+
return pyRstrip(text.slice(start));
|
|
17
|
+
}
|
|
18
|
+
/** Python's `str.rstrip`, a scan in from the end — not a `+$` pattern, which retries from every cell of an interior run. */
|
|
19
|
+
export function pyRstrip(text) {
|
|
20
|
+
let end = text.length;
|
|
21
|
+
while (end > 0 && PY_SPACE.test(text[end - 1]))
|
|
22
|
+
end--;
|
|
23
|
+
return text.slice(0, end);
|
|
24
|
+
}
|