@promptctl/rich-js 0.23.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/clock.d.ts +9 -1
- package/dist/core/clock.js +22 -2
- package/dist/core/color.d.ts +10 -0
- package/dist/core/color.js +36 -1
- package/dist/core/console.js +5 -5
- package/dist/core/easing.d.ts +138 -7
- package/dist/core/easing.js +355 -17
- 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 +28 -1
- package/dist/core/segment.js +60 -44
- package/dist/core/strip.d.ts +107 -29
- package/dist/core/strip.js +242 -131
- package/dist/core/style.d.ts +18 -1
- package/dist/core/style.js +49 -9
- package/dist/core/text.js +26 -15
- 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 +4 -6
- package/dist/host/index.d.ts +1 -1
- package/dist/host/input.d.ts +37 -13
- package/dist/host/input.js +146 -56
- package/dist/host/terminal-host.d.ts +16 -0
- package/dist/host/terminal-host.js +1 -0
- package/dist/index.d.ts +16 -11
- package/dist/index.js +15 -5
- package/dist/node/terminal-host.d.ts +8 -1
- package/dist/node/terminal-host.js +2 -0
- 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 +83 -30
- package/dist/renderables/effect.js +155 -57
- package/dist/renderables/effects.d.ts +88 -39
- package/dist/renderables/effects.js +444 -105
- package/dist/renderables/embed.d.ts +10 -0
- package/dist/renderables/embed.js +18 -9
- package/dist/renderables/flexStrip.d.ts +3 -2
- package/dist/renderables/flexStrip.js +6 -1
- package/dist/renderables/frames.d.ts +116 -0
- package/dist/renderables/frames.js +133 -0
- 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.d.ts +23 -7
- package/dist/renderables/live.js +69 -35
- 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 +7 -6
- package/dist/renderables/markdown.js +363 -499
- 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 +5 -3
- package/dist/renderables/progress.js +14 -3
- package/dist/renderables/progressBar.js +4 -4
- package/dist/renderables/python-space.d.ts +13 -0
- package/dist/renderables/python-space.js +24 -0
- package/dist/renderables/spinner.d.ts +73 -27
- package/dist/renderables/spinner.js +110 -51
- package/dist/renderables/status.d.ts +8 -3
- package/dist/renderables/status.js +8 -5
- 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.d.ts +37 -0
- package/dist/template-bindings/animation-funcs.js +83 -0
- package/dist/template-bindings/color-funcs.d.ts +13 -0
- package/dist/template-bindings/color-funcs.js +1 -1
- package/dist/template-bindings/helpers.d.ts +6 -0
- package/dist/template-bindings/helpers.js +12 -3
- package/dist/template-bindings/index.d.ts +6 -2
- package/dist/template-bindings/index.js +8 -3
- 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
package/dist/core/protocol.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* [LAW:one-source-of-truth] These interfaces are the single authority for the rendering contract.
|
|
4
4
|
*/
|
|
5
5
|
import { cellCount } from "./cells.js";
|
|
6
|
-
import { DEFAULT_THEME } from "./style.js";
|
|
6
|
+
import { DEFAULT_THEME, Style } from "./style.js";
|
|
7
7
|
/** Whether `text` is all ASCII — what an `asciiOnly` output can draw. */
|
|
8
8
|
export const isAscii = (text) => /^[\x00-\x7F]*$/.test(text);
|
|
9
9
|
export function drawable(options, chosen, ascii, glyphs = String) {
|
|
@@ -61,6 +61,8 @@ export function fitHeight(lines, height) {
|
|
|
61
61
|
export function getStyle(options, style) {
|
|
62
62
|
return (options.theme ?? DEFAULT_THEME).resolve(style);
|
|
63
63
|
}
|
|
64
|
+
/** The terminal's own foreground on its own background. */
|
|
65
|
+
const TERMINAL_GROUND = Style.parse("default on default");
|
|
64
66
|
/**
|
|
65
67
|
* `style` as the depth the render is encoded at can draw it. A style that
|
|
66
68
|
* paints its own ground in fixed colours chose its ink and ground as a pair;
|
|
@@ -69,7 +71,10 @@ export function getStyle(options, style) {
|
|
|
69
71
|
* every bundled theme. So where the depth would turn a fixed colour of the
|
|
70
72
|
* pair into a slot, the pair is dropped and the run is drawn on the
|
|
71
73
|
* terminal's own foreground and background, which its theme does make
|
|
72
|
-
* readable; its attributes still set it apart.
|
|
74
|
+
* readable; its attributes still set it apart. Both are named, not left
|
|
75
|
+
* unset, so the colour of whatever the run sits in — a quote, a link — does
|
|
76
|
+
* not show through: Rich's code keeps its own colours inside a quote, and the
|
|
77
|
+
* terminal's are the ones it keeps here. A style whose colours are
|
|
73
78
|
* slots already (`white on blue`), or that paints no ground (`bold magenta`),
|
|
74
79
|
* was written for the terminal's own colours and is drawn as written. Rich
|
|
75
80
|
* draws a highlighted line number the same way below 256 colours
|
|
@@ -79,7 +84,7 @@ export function groundedStyle(options, style) {
|
|
|
79
84
|
const drawn = style.drawnColors(options.colorSystem ?? undefined);
|
|
80
85
|
const lost = (written, shown) => written?.fixedValue !== undefined && shown?.fixedValue === undefined;
|
|
81
86
|
const pairLost = style.bgcolor !== undefined && (lost(style.color, drawn.color) || lost(style.bgcolor, drawn.bgcolor));
|
|
82
|
-
return pairLost ? style.
|
|
87
|
+
return pairLost ? style.add(TERMINAL_GROUND) : style;
|
|
83
88
|
}
|
|
84
89
|
/**
|
|
85
90
|
* The options a renderable should work from: the caller's, with `maxWidth`
|
|
@@ -104,6 +109,51 @@ export function groundedStyle(options, style) {
|
|
|
104
109
|
export function withCellWidth(options) {
|
|
105
110
|
return { ...options, maxWidth: cellCount(options.maxWidth) };
|
|
106
111
|
}
|
|
112
|
+
/** Under one column there is nothing to draw, or to measure. */
|
|
113
|
+
export function drawsNothing(options) {
|
|
114
|
+
return cellCount(options.maxWidth) < 1;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* `renderable` drawn under `options`: Rich's `Console.render`, which every
|
|
118
|
+
* renderable there is drawn through, so under one column nothing is.
|
|
119
|
+
*
|
|
120
|
+
* [LAW:single-enforcer] The one place that rule is kept. Every call of a
|
|
121
|
+
* `Renderable`'s `render` in `src/` goes through here — a container drawing
|
|
122
|
+
* its child, a renderable drawing the text it built, a host drawing a frame —
|
|
123
|
+
* and `test/seam/render-calls.test.ts` fails on one that does not. Before
|
|
124
|
+
* this, each container asked `drawsNothing` itself, and the ones that forgot
|
|
125
|
+
* drew a child handed no column anyway.
|
|
126
|
+
*
|
|
127
|
+
* It is also the one reporter of what the view holds (`onPresent`). Under one
|
|
128
|
+
* column a renderable is held, not drawn: what it holds (`Composite`) is
|
|
129
|
+
* walked and reported held in turn, and nothing renders, so no renderable
|
|
130
|
+
* that keeps state while it renders — a `Viewport`'s offset, a `TextInput`'s
|
|
131
|
+
* scroll window — ever commits it at a width that is not shown. The walk runs
|
|
132
|
+
* whether or not anyone listens. Drawing the squeezed subtree at one column
|
|
133
|
+
* instead, and dropping what it drew, did commit that state.
|
|
134
|
+
*/
|
|
135
|
+
export function render(renderable, options) {
|
|
136
|
+
const presence = drawsNothing(options) ? "held" : "drawn";
|
|
137
|
+
options.onPresent?.(renderable, presence);
|
|
138
|
+
if (presence === "drawn")
|
|
139
|
+
return renderable.render(options);
|
|
140
|
+
if (isComposite(renderable))
|
|
141
|
+
for (const content of renderable.contents())
|
|
142
|
+
render(content, options);
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* `options` as a measurement is asked with. A `measure` that renders to learn
|
|
147
|
+
* its shape draws nothing on the frame, so it reports nothing to `onPresent` —
|
|
148
|
+
* one that did would put a widget in the view at the moment a container
|
|
149
|
+
* measured it, and keep one the frame then cropped away.
|
|
150
|
+
*
|
|
151
|
+
* [LAW:single-enforcer] Every measurement starts here: `Measurement.get` and
|
|
152
|
+
* `withBoundedWidth` ask through it, and each `measure` passes it on.
|
|
153
|
+
*/
|
|
154
|
+
export function measuring(options) {
|
|
155
|
+
return { ...options, onPresent: undefined };
|
|
156
|
+
}
|
|
107
157
|
/**
|
|
108
158
|
* The options a renderable should *lay out against*: `withCellWidth`, with an
|
|
109
159
|
* unbounded offer resolved to the renderable's own natural width.
|
|
@@ -139,18 +189,6 @@ export function withCellWidth(options) {
|
|
|
139
189
|
* rather than zero, and saying so names the cause — where `String.repeat` and
|
|
140
190
|
* `new Array` only ever name their own argument.
|
|
141
191
|
*/
|
|
142
|
-
/**
|
|
143
|
-
* `options` as a measurement is asked with. A `measure` that renders to learn
|
|
144
|
-
* its shape draws nothing on the frame, so it reports no owner to `onDraw` —
|
|
145
|
-
* one that did would put a widget in document order at the moment a
|
|
146
|
-
* container measured it, and keep one the frame then cropped away.
|
|
147
|
-
*
|
|
148
|
-
* [LAW:single-enforcer] Every measurement starts here: `Measurement.get` and
|
|
149
|
-
* `withBoundedWidth` ask through it, and each `measure` passes it on.
|
|
150
|
-
*/
|
|
151
|
-
export function measuring(options) {
|
|
152
|
-
return { ...options, onDraw: undefined };
|
|
153
|
-
}
|
|
154
192
|
export function withBoundedWidth(options, self) {
|
|
155
193
|
const parsed = withCellWidth(options);
|
|
156
194
|
if (Number.isFinite(parsed.maxWidth))
|
|
@@ -182,3 +220,6 @@ export function isScrollable(obj) {
|
|
|
182
220
|
"scrollBy" in obj &&
|
|
183
221
|
typeof obj.scrollBy === "function");
|
|
184
222
|
}
|
|
223
|
+
export function isComposite(obj) {
|
|
224
|
+
return "contents" in obj && typeof obj.contents === "function";
|
|
225
|
+
}
|
package/dist/core/render.js
CHANGED
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
* `Style.render` returns bare text for a null colour system.
|
|
43
43
|
*/
|
|
44
44
|
import { ColorDepth, resolveDestination } from "./color.js";
|
|
45
|
+
import { render } from "./protocol.js";
|
|
45
46
|
import { OSC8_CLOSE, osc8Open } from "./osc8.js";
|
|
46
47
|
const DEFAULT_WIDTH = 80;
|
|
47
48
|
/**
|
|
@@ -101,5 +102,5 @@ export function renderToString(renderable, options) {
|
|
|
101
102
|
// so a renderable measures what this very call will draw.
|
|
102
103
|
colorSystem: destination.colorSystem,
|
|
103
104
|
};
|
|
104
|
-
return segmentsToString(
|
|
105
|
+
return segmentsToString(render(renderable, renderOptions), destination);
|
|
105
106
|
}
|
package/dist/core/segment.d.ts
CHANGED
|
@@ -42,6 +42,15 @@ export declare class Segment {
|
|
|
42
42
|
* whatever drew it, and its anchor says so; see `./anchor.ts`.
|
|
43
43
|
*/
|
|
44
44
|
splitCells(position: CellCol): [Segment, Segment];
|
|
45
|
+
/**
|
|
46
|
+
* Cuts at each of `positions`, non-decreasing and inside this segment's
|
|
47
|
+
* cells: one piece more than there are positions, each exactly as wide as
|
|
48
|
+
* the distance between its cuts, the way `splitCells` cuts at one.
|
|
49
|
+
*
|
|
50
|
+
* [LAW:single-enforcer] Each piece but the first starts that many cells
|
|
51
|
+
* further into whatever drew it, and its anchor says so; see `./anchor.ts`.
|
|
52
|
+
*/
|
|
53
|
+
private cutAt;
|
|
45
54
|
private static _line;
|
|
46
55
|
static line(): Segment;
|
|
47
56
|
/**
|
|
@@ -51,6 +60,16 @@ export declare class Segment {
|
|
|
51
60
|
* the owner's row that `lines[0]` is, for output that arrives in pieces.
|
|
52
61
|
*/
|
|
53
62
|
static anchorLines(lines: Segment[][], owner: object, firstRow?: number): Segment[][];
|
|
63
|
+
/**
|
|
64
|
+
* When what `segments` show next changes, in seconds on the timeline of
|
|
65
|
+
* whoever drew them: the soonest `Style.nextChange` among them, read off
|
|
66
|
+
* the composed frame the way `anchorAt` reads an owner — `t` itself for a
|
|
67
|
+
* frame something in moves every frame, `Infinity` for one that never
|
|
68
|
+
* changes. What a host that drew the frame at `t` asks before drawing
|
|
69
|
+
* again: a frame of frames answers the next frame's start, a transition
|
|
70
|
+
* `t` until it is done, still content never.
|
|
71
|
+
*/
|
|
72
|
+
static nextChange(segments: Iterable<Segment>): number;
|
|
54
73
|
/**
|
|
55
74
|
* The anchor of the cell at column `x` of row `y` in a composed frame, or
|
|
56
75
|
* `undefined` when that cell was drawn by no owner or lies outside the frame.
|
|
@@ -140,7 +159,15 @@ export declare class Segment {
|
|
|
140
159
|
*/
|
|
141
160
|
static removeColor(segments: Iterable<Segment>): Iterable<Segment>;
|
|
142
161
|
/**
|
|
143
|
-
* Divides segments at cell positions
|
|
162
|
+
* Divides segments at cell positions: a section ending at each cut, and the
|
|
163
|
+
* cells after the last cut when there are any. A segment wider than zero
|
|
164
|
+
* that starts at a cut opens the section after it; one with no cells stays
|
|
165
|
+
* in the section it ends.
|
|
166
|
+
*
|
|
167
|
+
* One walk, and each segment is cut once at every cut that falls inside it:
|
|
168
|
+
* cutting what is left of it again at each cut re-measures and copies the
|
|
169
|
+
* rest every time, so a long line wrapped into many rows took time quadratic
|
|
170
|
+
* in its length.
|
|
144
171
|
*/
|
|
145
172
|
static divide(segments: Segment[], cuts: number[]): Segment[][];
|
|
146
173
|
/**
|
package/dist/core/segment.js
CHANGED
|
@@ -59,11 +59,10 @@ export class Segment {
|
|
|
59
59
|
return [this, new Segment("")];
|
|
60
60
|
if (position <= 0)
|
|
61
61
|
return [new Segment(""), this];
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
];
|
|
62
|
+
return this.cutAt([position]);
|
|
63
|
+
}
|
|
64
|
+
cutAt(positions) {
|
|
65
|
+
return cutCells(this.text, positions).map((text, piece) => new Segment(text, piece === 0 ? this.style : this.style?.shiftedBy(positions[piece - 1])));
|
|
67
66
|
}
|
|
68
67
|
// --- Static factories ---
|
|
69
68
|
static _line;
|
|
@@ -91,6 +90,21 @@ export class Segment {
|
|
|
91
90
|
});
|
|
92
91
|
});
|
|
93
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* When what `segments` show next changes, in seconds on the timeline of
|
|
95
|
+
* whoever drew them: the soonest `Style.nextChange` among them, read off
|
|
96
|
+
* the composed frame the way `anchorAt` reads an owner — `t` itself for a
|
|
97
|
+
* frame something in moves every frame, `Infinity` for one that never
|
|
98
|
+
* changes. What a host that drew the frame at `t` asks before drawing
|
|
99
|
+
* again: a frame of frames answers the next frame's start, a transition
|
|
100
|
+
* `t` until it is done, still content never.
|
|
101
|
+
*/
|
|
102
|
+
static nextChange(segments) {
|
|
103
|
+
let soonest = Number.POSITIVE_INFINITY;
|
|
104
|
+
for (const segment of segments)
|
|
105
|
+
soonest = Math.min(soonest, segment.style?.nextChange ?? Number.POSITIVE_INFINITY);
|
|
106
|
+
return soonest;
|
|
107
|
+
}
|
|
94
108
|
/**
|
|
95
109
|
* The anchor of the cell at column `x` of row `y` in a composed frame, or
|
|
96
110
|
* `undefined` when that cell was drawn by no owner or lies outside the frame.
|
|
@@ -114,8 +128,11 @@ export class Segment {
|
|
|
114
128
|
*/
|
|
115
129
|
static *applyStyle(segments, style, postStyle) {
|
|
116
130
|
// Applying no style is the identity, so the segments pass through as they
|
|
117
|
-
// are instead of each being copied into an equal one.
|
|
118
|
-
|
|
131
|
+
// are instead of each being copied into an equal one. A null style — a
|
|
132
|
+
// theme's plain entry, such as `markdown.item` — is no style.
|
|
133
|
+
const pre = style?.isNull ? undefined : style;
|
|
134
|
+
const post = postStyle?.isNull ? undefined : postStyle;
|
|
135
|
+
if (!pre && !post) {
|
|
119
136
|
yield* segments;
|
|
120
137
|
return;
|
|
121
138
|
}
|
|
@@ -125,10 +142,10 @@ export class Segment {
|
|
|
125
142
|
continue;
|
|
126
143
|
}
|
|
127
144
|
let s = segment.style;
|
|
128
|
-
if (
|
|
129
|
-
s =
|
|
130
|
-
if (
|
|
131
|
-
s = s ? s.add(
|
|
145
|
+
if (pre)
|
|
146
|
+
s = pre.add(s);
|
|
147
|
+
if (post)
|
|
148
|
+
s = s ? s.add(post) : post;
|
|
132
149
|
yield new Segment(segment.text, s, segment.control);
|
|
133
150
|
}
|
|
134
151
|
}
|
|
@@ -383,45 +400,44 @@ export class Segment {
|
|
|
383
400
|
}
|
|
384
401
|
}
|
|
385
402
|
/**
|
|
386
|
-
* Divides segments at cell positions
|
|
403
|
+
* Divides segments at cell positions: a section ending at each cut, and the
|
|
404
|
+
* cells after the last cut when there are any. A segment wider than zero
|
|
405
|
+
* that starts at a cut opens the section after it; one with no cells stays
|
|
406
|
+
* in the section it ends.
|
|
407
|
+
*
|
|
408
|
+
* One walk, and each segment is cut once at every cut that falls inside it:
|
|
409
|
+
* cutting what is left of it again at each cut re-measures and copies the
|
|
410
|
+
* rest every time, so a long line wrapped into many rows took time quadratic
|
|
411
|
+
* in its length.
|
|
387
412
|
*/
|
|
388
413
|
static divide(segments, cuts) {
|
|
389
414
|
if (cuts.length === 0)
|
|
390
415
|
return [segments];
|
|
391
|
-
const
|
|
392
|
-
let
|
|
393
|
-
let
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
if (currentSegment && !currentSegment.isControl && cellOffset < cut) {
|
|
404
|
-
const splitAt = asCellCol(cut - cellOffset);
|
|
405
|
-
const [left, right] = currentSegment.splitCells(splitAt);
|
|
406
|
-
if (left.hasText)
|
|
407
|
-
section.push(left);
|
|
408
|
-
cellOffset += left.cellLength;
|
|
409
|
-
currentSegment = right;
|
|
416
|
+
const sections = [[]];
|
|
417
|
+
let offset = 0;
|
|
418
|
+
let next = 0;
|
|
419
|
+
for (const segment of segments) {
|
|
420
|
+
const end = offset + segment.cellLength;
|
|
421
|
+
const inside = [];
|
|
422
|
+
for (; next < cuts.length && cuts[next] < end; next++) {
|
|
423
|
+
const cut = cuts[next];
|
|
424
|
+
if (cut <= offset)
|
|
425
|
+
sections.push([]);
|
|
426
|
+
else
|
|
427
|
+
inside.push(asCellCol(cut - offset));
|
|
410
428
|
}
|
|
411
|
-
|
|
429
|
+
const [head, ...rest] = inside.length === 0 ? [segment] : segment.cutAt(inside);
|
|
430
|
+
sections[sections.length - 1].push(head);
|
|
431
|
+
// A piece between two cuts at one cell has no text: the section it is holds nothing.
|
|
432
|
+
for (const piece of rest)
|
|
433
|
+
sections.push(piece.hasText ? [piece] : []);
|
|
434
|
+
offset = end;
|
|
412
435
|
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
if (
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
while (segmentIndex < segments.length) {
|
|
419
|
-
tail.push(segments[segmentIndex]);
|
|
420
|
-
segmentIndex++;
|
|
421
|
-
}
|
|
422
|
-
if (tail.length > 0)
|
|
423
|
-
result.push(tail);
|
|
424
|
-
return result;
|
|
436
|
+
for (; next < cuts.length; next++)
|
|
437
|
+
sections.push([]);
|
|
438
|
+
if (sections[sections.length - 1].length === 0)
|
|
439
|
+
sections.pop();
|
|
440
|
+
return sections;
|
|
425
441
|
}
|
|
426
442
|
// --- Layout helpers ---
|
|
427
443
|
/**
|
package/dist/core/strip.d.ts
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
import type { Anchor } from "./anchor.js";
|
|
29
29
|
import { Segment } from "./segment.js";
|
|
30
30
|
import { Style } from "./style.js";
|
|
31
|
-
import { type Measurable, type Renderable, type RenderOptions } from "./protocol.js";
|
|
31
|
+
import { type Composite, type Measurable, type Renderable, type RenderOptions } from "./protocol.js";
|
|
32
32
|
/**
|
|
33
33
|
* Items in a `Strip` expose their per-edge style so joiners can paint the
|
|
34
34
|
* transition glyph at each boundary.
|
|
@@ -97,11 +97,12 @@ export declare function drawJoin<T extends StyledRenderable>(joiner: Joiner<T>,
|
|
|
97
97
|
* item.
|
|
98
98
|
*/
|
|
99
99
|
export declare function drawItem(item: StyledRenderable): Renderable;
|
|
100
|
-
export declare class Strip<T extends StyledRenderable = StyledRenderable> implements Renderable, Measurable {
|
|
100
|
+
export declare class Strip<T extends StyledRenderable = StyledRenderable> implements Renderable, Measurable, Composite {
|
|
101
101
|
readonly items: readonly T[];
|
|
102
102
|
readonly joiner: Joiner<T>;
|
|
103
103
|
constructor(items: readonly T[], joiner: Joiner<T>);
|
|
104
104
|
render(options: RenderOptions): Iterable<Segment>;
|
|
105
|
+
contents(): Iterable<Renderable>;
|
|
105
106
|
/**
|
|
106
107
|
* [LAW:one-source-of-truth] The width of the one row it draws, and that is
|
|
107
108
|
* both bounds: the row never wraps and no item is offered less than the whole
|
|
@@ -156,22 +157,49 @@ export interface JoinCell {
|
|
|
156
157
|
readonly bg: CellColor;
|
|
157
158
|
}
|
|
158
159
|
/**
|
|
159
|
-
*
|
|
160
|
-
*
|
|
160
|
+
* A cell of a seam, or one of the joiner's caps standing in a seam's place:
|
|
161
|
+
* `{ cap: "tail" }` closes the run the seam leaves, `{ cap: "lead" }` opens the
|
|
162
|
+
* run it enters, drawn as whatever caps the seam is joined with. That is how
|
|
163
|
+
* a seam that parts its runs — `capsule`, `metro` — ends each in the caps
|
|
164
|
+
* picked for it rather than in caps of its own.
|
|
165
|
+
*/
|
|
166
|
+
export type SeamCell = JoinCell | {
|
|
167
|
+
readonly cap: "lead" | "tail";
|
|
168
|
+
};
|
|
169
|
+
/**
|
|
170
|
+
* What a joiner draws between two neighbours, in one form:
|
|
171
|
+
* • `seam` — the cells between them;
|
|
161
172
|
* • `divider` — in the seam's place where every seam cell's ink is drawn
|
|
162
173
|
* indistinct from its own ground (see `SEAM_MIN_DELTA_E`), so the seam
|
|
163
|
-
* would vanish;
|
|
164
|
-
|
|
174
|
+
* would vanish; the same width as `seam`.
|
|
175
|
+
*/
|
|
176
|
+
export interface SeamForm {
|
|
177
|
+
readonly seam: readonly SeamCell[];
|
|
178
|
+
readonly divider: readonly SeamCell[];
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* What a joiner draws at the ends of a run, in one form:
|
|
165
182
|
* • `lead` — where a run is entered from nothing: the strip's start, or a
|
|
166
183
|
* neighbour with no ground for the seam to paint. It cannot name `left`;
|
|
167
184
|
* • `tail` — where a run leaves into nothing. It cannot name `right`.
|
|
168
185
|
*/
|
|
169
|
-
export interface
|
|
170
|
-
readonly seam: readonly JoinCell[];
|
|
171
|
-
readonly divider: readonly JoinCell[];
|
|
186
|
+
export interface CapsForm {
|
|
172
187
|
readonly lead: readonly JoinCell[];
|
|
173
188
|
readonly tail: readonly JoinCell[];
|
|
174
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* The middle of a joiner: its seam, and the `ascii` seam an ASCII-only output
|
|
192
|
+
* draws instead. Picked apart from the caps, so any seam ends in any caps.
|
|
193
|
+
*/
|
|
194
|
+
export interface JoinSeam {
|
|
195
|
+
readonly glyph: SeamForm;
|
|
196
|
+
readonly ascii: SeamForm;
|
|
197
|
+
}
|
|
198
|
+
/** The ends of a joiner: its caps, and the `ascii` caps an ASCII-only output draws instead. */
|
|
199
|
+
export interface JoinCaps {
|
|
200
|
+
readonly glyph: CapsForm;
|
|
201
|
+
readonly ascii: CapsForm;
|
|
202
|
+
}
|
|
175
203
|
/**
|
|
176
204
|
* The least ΔE_OK a seam cell's ink must differ from its own ground by for the
|
|
177
205
|
* seam to be seen. Below it the eye cannot tell the ink from the ground,
|
|
@@ -180,28 +208,70 @@ export interface JoinShape {
|
|
|
180
208
|
*/
|
|
181
209
|
export declare const SEAM_MIN_DELTA_E = 0.04;
|
|
182
210
|
/**
|
|
183
|
-
* The one joiner: what it draws
|
|
184
|
-
*
|
|
211
|
+
* The one joiner: what it draws is a `JoinSeam` between neighbours and a
|
|
212
|
+
* `JoinCaps` at the ends of each run, picked independently, so a new joiner is
|
|
213
|
+
* a pair of values, never a class.
|
|
185
214
|
*
|
|
186
215
|
* Which part a join draws is read off its two neighbours. A neighbour is
|
|
187
216
|
* missing at the strip's ends, and counts as missing — an endpoint — when the
|
|
188
|
-
*
|
|
189
|
-
* join is the seam, or the divider where every seam cell's ink is
|
|
190
|
-
* indistinct from its ground at `options.colorSystem`; with only the
|
|
191
|
-
* the left, the tail; with neither, nothing.
|
|
217
|
+
* seam paints with its ground anywhere and it has none to paint. With both
|
|
218
|
+
* present the join is the seam, or the divider where every seam cell's ink is
|
|
219
|
+
* drawn indistinct from its ground at `options.colorSystem`; with only the
|
|
220
|
+
* right, the lead; with only the left, the tail; with neither, nothing. A cap
|
|
221
|
+
* that paints with its neighbour's ground is drawn only beside a neighbour
|
|
222
|
+
* with one, so caps that need grounds never stop a seam that needs none.
|
|
192
223
|
*
|
|
193
|
-
*
|
|
224
|
+
* An ASCII-only output draws the `ascii` form of both, whole.
|
|
194
225
|
*/
|
|
195
226
|
export declare class CellJoiner<T extends StyledRenderable = StyledRenderable> implements Joiner<T> {
|
|
196
|
-
readonly
|
|
197
|
-
readonly
|
|
227
|
+
readonly seam: JoinSeam;
|
|
228
|
+
readonly caps: JoinCaps;
|
|
198
229
|
private readonly _forms;
|
|
199
|
-
constructor(
|
|
230
|
+
constructor(seam: JoinSeam, caps: JoinCaps);
|
|
200
231
|
join(left: T | null, right: T | null, { index }: JoinContext): Renderable;
|
|
201
232
|
}
|
|
202
233
|
/**
|
|
203
|
-
* The bundled
|
|
204
|
-
*
|
|
234
|
+
* The bundled seams, by name: what a joiner draws between two neighbours.
|
|
235
|
+
* What each draws is `docs/strip.md`'s "Seams"; any of them ends in any of
|
|
236
|
+
* `CAPS`.
|
|
237
|
+
*/
|
|
238
|
+
export declare const SEAMS: Readonly<{
|
|
239
|
+
powerline: JoinSeam;
|
|
240
|
+
prism: JoinSeam;
|
|
241
|
+
slant: JoinSeam;
|
|
242
|
+
backslant: JoinSeam;
|
|
243
|
+
flame: JoinSeam;
|
|
244
|
+
pixel: JoinSeam;
|
|
245
|
+
ice: JoinSeam;
|
|
246
|
+
bubble: JoinSeam;
|
|
247
|
+
capsule: JoinSeam;
|
|
248
|
+
wrapper: JoinSeam;
|
|
249
|
+
metro: JoinSeam;
|
|
250
|
+
stitch: JoinSeam;
|
|
251
|
+
torn: JoinSeam;
|
|
252
|
+
plain: JoinSeam;
|
|
253
|
+
}>;
|
|
254
|
+
/**
|
|
255
|
+
* The bundled caps, by name: what a joiner draws where a run begins and ends.
|
|
256
|
+
* What each draws is `docs/strip.md`'s "Caps"; any of them ends any of
|
|
257
|
+
* `SEAMS`.
|
|
258
|
+
*/
|
|
259
|
+
export declare const CAPS: Readonly<{
|
|
260
|
+
powerline: JoinCaps;
|
|
261
|
+
slant: JoinCaps;
|
|
262
|
+
backslant: JoinCaps;
|
|
263
|
+
flame: JoinCaps;
|
|
264
|
+
pixel: JoinCaps;
|
|
265
|
+
ice: JoinCaps;
|
|
266
|
+
bubble: JoinCaps;
|
|
267
|
+
capsule: JoinCaps;
|
|
268
|
+
wrapper: JoinCaps;
|
|
269
|
+
flat: JoinCaps;
|
|
270
|
+
}>;
|
|
271
|
+
/**
|
|
272
|
+
* The bundled joiners, by name: each a seam from `SEAMS` ended in caps from
|
|
273
|
+
* `CAPS`. What each draws is `docs/strip.md`'s "Built-in joiners"; a name from
|
|
274
|
+
* config is read with `joinerNamed`.
|
|
205
275
|
*/
|
|
206
276
|
export declare const JOINERS: Readonly<{
|
|
207
277
|
powerline: CellJoiner<StyledRenderable>;
|
|
@@ -220,16 +290,24 @@ export declare const JOINERS: Readonly<{
|
|
|
220
290
|
plain: CellJoiner<StyledRenderable>;
|
|
221
291
|
}>;
|
|
222
292
|
/**
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
|
|
293
|
+
* A joiner as config names it: one of `JOINERS`, or a seam from `SEAMS` ended
|
|
294
|
+
* in caps from `CAPS`, each named apart.
|
|
295
|
+
*/
|
|
296
|
+
export type JoinerSpec = string | {
|
|
297
|
+
readonly seam: string;
|
|
298
|
+
readonly caps: string;
|
|
299
|
+
};
|
|
300
|
+
/**
|
|
301
|
+
* The joiner `spec` names — a joiner read from config, parsed. Throws on a
|
|
302
|
+
* name its table lacks, naming the ones there are; an own-key test, so
|
|
303
|
+
* `toString` and the rest of `Object.prototype` are no one's name.
|
|
226
304
|
*/
|
|
227
|
-
export declare function joinerNamed(
|
|
305
|
+
export declare function joinerNamed(spec: JoinerSpec): CellJoiner;
|
|
228
306
|
/**
|
|
229
307
|
* A ramp of `steps` cells from the left ground to the right. Each cell is the
|
|
230
308
|
* half block U+258C, ink on its left half and ground on its right, so it
|
|
231
|
-
* carries two samples; ASCII draws a space, keeping the grounds alone.
|
|
232
|
-
*
|
|
233
|
-
* ramp
|
|
309
|
+
* carries two samples; ASCII draws a space, keeping the grounds alone. Beside
|
|
310
|
+
* a neighbour with no ground there is no colour to ramp toward, so that join
|
|
311
|
+
* draws the caps the ramp is ended in.
|
|
234
312
|
*/
|
|
235
|
-
export declare function
|
|
313
|
+
export declare function gradientSeam(steps?: number): JoinSeam;
|