@promptctl/rich-js 0.24.1 → 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/dist/core/anchor.d.ts +5 -0
- package/dist/core/anchor.js +11 -4
- package/dist/core/animation.d.ts +175 -10
- package/dist/core/animation.js +283 -15
- package/dist/core/ansi.js +2 -2
- package/dist/core/box.d.ts +7 -7
- package/dist/core/cells.d.ts +23 -13
- package/dist/core/cells.js +58 -48
- 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 +44 -39
- package/dist/core/easing.d.ts +30 -0
- package/dist/core/easing.js +65 -20
- 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 +2 -2
- package/dist/core/markup.js +16 -12
- package/dist/core/measure.js +2 -2
- 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/pretty.js +9 -10
- package/dist/core/protocol.d.ts +100 -39
- package/dist/core/protocol.js +167 -21
- package/dist/core/render.d.ts +15 -2
- package/dist/core/render.js +25 -5
- package/dist/core/segment.d.ts +13 -5
- package/dist/core/segment.js +23 -9
- 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 +13 -16
- package/dist/core/strip.js +119 -110
- package/dist/core/style.d.ts +47 -14
- package/dist/core/style.js +57 -28
- package/dist/core/text.d.ts +5 -8
- package/dist/core/text.js +116 -124
- package/dist/core/timeline.d.ts +30 -5
- package/dist/core/timeline.js +43 -7
- package/dist/core/tween.d.ts +1 -1
- package/dist/core/tween.js +5 -3
- 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 +71 -37
- package/dist/host/input.js +3 -4
- package/dist/index.d.ts +24 -12
- package/dist/index.js +9 -4
- package/dist/node/traceback.js +4 -3
- package/dist/renderables/align.d.ts +2 -3
- package/dist/renderables/align.js +0 -3
- package/dist/renderables/change.d.ts +42 -0
- package/dist/renderables/change.js +112 -0
- package/dist/renderables/columns.d.ts +3 -4
- package/dist/renderables/columns.js +2 -7
- package/dist/renderables/constrain.d.ts +2 -3
- package/dist/renderables/constrain.js +0 -3
- package/dist/renderables/effect.d.ts +132 -54
- package/dist/renderables/effect.js +188 -115
- package/dist/renderables/effects.d.ts +122 -11
- package/dist/renderables/effects.js +238 -94
- package/dist/renderables/embed.d.ts +1 -1
- package/dist/renderables/embed.js +16 -8
- package/dist/renderables/flexStrip.d.ts +2 -3
- package/dist/renderables/flexStrip.js +0 -4
- package/dist/renderables/frames.d.ts +35 -54
- package/dist/renderables/frames.js +43 -58
- 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 +3 -4
- package/dist/renderables/group.js +0 -3
- package/dist/renderables/layout.d.ts +12 -12
- package/dist/renderables/layout.js +40 -46
- package/dist/renderables/lifecycle.d.ts +77 -0
- package/dist/renderables/lifecycle.js +132 -0
- package/dist/renderables/list-view.d.ts +4 -5
- package/dist/renderables/list-view.js +9 -13
- package/dist/renderables/live.d.ts +36 -21
- package/dist/renderables/live.js +88 -41
- package/dist/renderables/markdown-parse.d.ts +4 -4
- package/dist/renderables/markdown-parse.js +52 -46
- package/dist/renderables/markdown-url.js +11 -11
- package/dist/renderables/padding.d.ts +4 -5
- package/dist/renderables/padding.js +0 -3
- package/dist/renderables/panel.d.ts +14 -16
- package/dist/renderables/panel.js +6 -14
- package/dist/renderables/progress.d.ts +83 -34
- package/dist/renderables/progress.js +182 -95
- package/dist/renderables/progressBar.d.ts +22 -12
- package/dist/renderables/progressBar.js +32 -9
- package/dist/renderables/prompt.js +3 -2
- package/dist/renderables/python-space.js +2 -1
- package/dist/renderables/scroll.d.ts +50 -0
- package/dist/renderables/scroll.js +106 -0
- package/dist/renderables/spinner.d.ts +35 -14
- package/dist/renderables/spinner.js +65 -25
- package/dist/renderables/status.js +1 -1
- package/dist/renderables/syntax.d.ts +10 -9
- package/dist/renderables/syntax.js +3 -2
- package/dist/renderables/table.d.ts +34 -40
- package/dist/renderables/table.js +41 -63
- package/dist/renderables/traceback.d.ts +2 -2
- package/dist/renderables/traceback.js +6 -5
- package/dist/renderables/tree.d.ts +27 -25
- package/dist/renderables/tree.js +104 -58
- package/dist/renderables/viewport.d.ts +75 -44
- package/dist/renderables/viewport.js +145 -82
- 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 +5 -5
- package/dist/themes/ramp.d.ts +46 -3
- package/dist/themes/ramp.js +61 -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 +2 -0
- package/dist/widgets/focus-manager.js +6 -4
- package/dist/widgets/hit.d.ts +6 -0
- package/dist/widgets/hit.js +8 -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 +2 -3
- package/dist/widgets/static-item.js +0 -3
- package/dist/widgets/text-input.d.ts +28 -32
- package/dist/widgets/text-input.js +360 -346
- 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 +14 -4
- package/dist/widgets/widget-app.js +36 -7
- package/dist/widgets/widget-base.d.ts +2 -2
- package/dist/widgets/widget-base.js +29 -18
- package/package.json +8 -1
package/dist/core/text.js
CHANGED
|
@@ -6,7 +6,9 @@ import { divideLine, wordWidths } from "./wrap.js";
|
|
|
6
6
|
import { Segment } from "./segment.js";
|
|
7
7
|
import { Style, NULL_STYLE, StyleSyntaxError } from "./style.js";
|
|
8
8
|
import { stripOscTerminators } from "./osc8.js";
|
|
9
|
-
import { drawable, getStyle, withBoundedWidth } from "./protocol.js";
|
|
9
|
+
import { betweenPasses, drawable, getStyle, withBoundedWidth } from "./protocol.js";
|
|
10
|
+
import { pattern } from "./pattern.js";
|
|
11
|
+
import { ListState, State } from "./state.js";
|
|
10
12
|
// Every C0 control and DEL except \t and \n. Rich's `Text` strips only
|
|
11
13
|
// `STRIP_CONTROL_CODES` (BEL, BS, VT, FF, CR); the rest of the set is the
|
|
12
14
|
// port's extension, so no byte of content can start an escape sequence or move
|
|
@@ -22,11 +24,10 @@ import { drawable, getStyle, withBoundedWidth } from "./protocol.js";
|
|
|
22
24
|
// pass, since right-to-left text needs them.
|
|
23
25
|
// [LAW:single-enforcer] Single place where control chars are sanitized: every
|
|
24
26
|
// string that becomes part of a Text's characters passes here.
|
|
25
|
-
const CONTROL_CHARS_RE = /[\x00-\x08\x0B-\x1F\x7F-\x9F\u202A-\u202E\u2066-\u206F]/g;
|
|
26
27
|
export function stripControlChars(text) {
|
|
27
|
-
return text.replace(
|
|
28
|
+
return text.replace(/[\x00-\x08\x0B-\x1F\x7F-\x9F\u202A-\u202E\u2066-\u206F]/g, "");
|
|
28
29
|
}
|
|
29
|
-
const TRAILING_WHITESPACE_RE = /\s
|
|
30
|
+
const TRAILING_WHITESPACE_RE = pattern(/\s+$/);
|
|
30
31
|
/**
|
|
31
32
|
* The plain text of one line of segments, in the coordinate system
|
|
32
33
|
* `divideLine` and `Segment.divide` share: cell offsets into the styled line
|
|
@@ -84,9 +85,9 @@ function sanitizeStyleLink(style) {
|
|
|
84
85
|
* lays its text out anew, so a stamp naming where a cell sat in the output it
|
|
85
86
|
* came from would name the wrong cell once it wraps, slices or overlaps; the
|
|
86
87
|
* owner that renders the RichText is the one that stamps its cells. A
|
|
87
|
-
* `
|
|
88
|
-
* wherever the text lays them, so a frame
|
|
89
|
-
* when it turns.
|
|
88
|
+
* hold stays — `lastChange` and `nextChange`: when a span's cells began and
|
|
89
|
+
* next change is true of them wherever the text lays them, so a frame
|
|
90
|
+
* appended into a line still says when it turns.
|
|
90
91
|
*/
|
|
91
92
|
function admitStyle(style) {
|
|
92
93
|
if (typeof style === "string")
|
|
@@ -129,7 +130,7 @@ function resolveDefinition(options, style) {
|
|
|
129
130
|
catch (err) {
|
|
130
131
|
if (!(err instanceof StyleSyntaxError))
|
|
131
132
|
throw err;
|
|
132
|
-
options.onStyleError?.(err, style);
|
|
133
|
+
betweenPasses(() => options.onStyleError?.(err, style));
|
|
133
134
|
return NULL_STYLE;
|
|
134
135
|
}
|
|
135
136
|
}
|
|
@@ -170,41 +171,45 @@ export class Span {
|
|
|
170
171
|
}
|
|
171
172
|
}
|
|
172
173
|
export class RichText {
|
|
174
|
+
// Rich's `Text` is changed in place — appended to, stylized, padded — so its
|
|
175
|
+
// characters, spans and format are state, and a write to one while
|
|
176
|
+
// something is drawn throws. Three holders rather than one per field: a
|
|
177
|
+
// `RichText` is the most-made object in the library.
|
|
173
178
|
_text;
|
|
174
|
-
_spans;
|
|
175
|
-
|
|
176
|
-
_justify;
|
|
177
|
-
_overflow;
|
|
178
|
-
_end;
|
|
179
|
+
_spans = new ListState();
|
|
180
|
+
_format;
|
|
179
181
|
_tabSize;
|
|
180
|
-
_noWrap;
|
|
181
182
|
constructor(text, options) {
|
|
182
|
-
this._text = text ? stripControlChars(text) : "";
|
|
183
|
-
this.
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
183
|
+
this._text = new State(text ? stripControlChars(text) : "");
|
|
184
|
+
this._format = new State({
|
|
185
|
+
// [LAW:single-enforcer] `admitStyle` is the boundary that sanitizes
|
|
186
|
+
// any link URL crossing into a RichText; downstream trusts the invariant.
|
|
187
|
+
style: admitStyle(options?.style ?? NULL_STYLE),
|
|
188
|
+
justify: options?.justify,
|
|
189
|
+
overflow: options?.overflow,
|
|
190
|
+
end: options?.end ?? "\n",
|
|
191
|
+
noWrap: options?.noWrap ?? false,
|
|
192
|
+
});
|
|
190
193
|
this._tabSize = parseTabSize(options?.tabSize ?? 8);
|
|
191
|
-
|
|
194
|
+
}
|
|
195
|
+
_reformat(change) {
|
|
196
|
+
this._format.set({ ...this._format.get(), ...change });
|
|
192
197
|
}
|
|
193
198
|
// --- Properties ---
|
|
194
199
|
get plain() {
|
|
195
|
-
return this._text;
|
|
200
|
+
return this._text.get();
|
|
196
201
|
}
|
|
197
202
|
set plain(value) {
|
|
198
203
|
const sanitized = stripControlChars(value);
|
|
199
|
-
this._text
|
|
204
|
+
this._text.set(sanitized);
|
|
200
205
|
// Trim spans that extend beyond new length
|
|
201
206
|
const len = sanitized.length;
|
|
202
|
-
this._spans
|
|
207
|
+
this._spans.set(this._spans.get()
|
|
203
208
|
.map((s) => (s.end > len ? new Span(s.start, Math.min(s.end, len), s.style) : s))
|
|
204
|
-
.filter((s) => s.start < len);
|
|
209
|
+
.filter((s) => s.start < len));
|
|
205
210
|
}
|
|
206
211
|
get length() {
|
|
207
|
-
return this._text.length;
|
|
212
|
+
return this._text.get().length;
|
|
208
213
|
}
|
|
209
214
|
/**
|
|
210
215
|
* The cells of the plain text with each tab widened to its stop counted from
|
|
@@ -215,44 +220,44 @@ export class RichText {
|
|
|
215
220
|
* no render draws.
|
|
216
221
|
*/
|
|
217
222
|
get cellLength() {
|
|
218
|
-
return cellLen(expandTabs(this._text, this._tabSize).text);
|
|
223
|
+
return cellLen(expandTabs(this._text.get(), this._tabSize).text);
|
|
219
224
|
}
|
|
220
225
|
get hasContent() {
|
|
221
|
-
return this._text.length > 0;
|
|
226
|
+
return this._text.get().length > 0;
|
|
222
227
|
}
|
|
223
228
|
/** The base style every span layers over, resolved at render unless it is a `Style`. */
|
|
224
229
|
get style() {
|
|
225
|
-
return this.
|
|
230
|
+
return this._format.get().style;
|
|
226
231
|
}
|
|
227
232
|
set style(value) {
|
|
228
|
-
this.
|
|
233
|
+
this._reformat({ style: admitStyle(value) });
|
|
229
234
|
}
|
|
230
235
|
get justify() {
|
|
231
|
-
return this.
|
|
236
|
+
return this._format.get().justify;
|
|
232
237
|
}
|
|
233
238
|
set justify(value) {
|
|
234
|
-
this.
|
|
239
|
+
this._reformat({ justify: value });
|
|
235
240
|
}
|
|
236
241
|
get overflow() {
|
|
237
|
-
return this.
|
|
242
|
+
return this._format.get().overflow;
|
|
238
243
|
}
|
|
239
244
|
set overflow(value) {
|
|
240
|
-
this.
|
|
245
|
+
this._reformat({ overflow: value });
|
|
241
246
|
}
|
|
242
247
|
get end() {
|
|
243
|
-
return this.
|
|
248
|
+
return this._format.get().end;
|
|
244
249
|
}
|
|
245
250
|
set end(value) {
|
|
246
|
-
this.
|
|
251
|
+
this._reformat({ end: value });
|
|
247
252
|
}
|
|
248
253
|
get noWrap() {
|
|
249
|
-
return this.
|
|
254
|
+
return this._format.get().noWrap;
|
|
250
255
|
}
|
|
251
256
|
set noWrap(value) {
|
|
252
|
-
this.
|
|
257
|
+
this._reformat({ noWrap: value });
|
|
253
258
|
}
|
|
254
259
|
get spans() {
|
|
255
|
-
return this._spans;
|
|
260
|
+
return this._spans.get();
|
|
256
261
|
}
|
|
257
262
|
/**
|
|
258
263
|
* This text's own style, spans aside, as the render drawing it resolves it.
|
|
@@ -260,7 +265,7 @@ export class RichText {
|
|
|
260
265
|
* text forgives a missing name.
|
|
261
266
|
*/
|
|
262
267
|
resolvedStyle(options) {
|
|
263
|
-
return resolveStyle(options, this.
|
|
268
|
+
return resolveStyle(options, this._format.get().style);
|
|
264
269
|
}
|
|
265
270
|
/**
|
|
266
271
|
* The style of the cell-column at the named edge — base style merged with
|
|
@@ -284,11 +289,11 @@ export class RichText {
|
|
|
284
289
|
*/
|
|
285
290
|
edgeStyle(side, options) {
|
|
286
291
|
const base = this.resolvedStyle(options);
|
|
287
|
-
if (this._text.length === 0)
|
|
292
|
+
if (this._text.get().length === 0)
|
|
288
293
|
return base;
|
|
289
|
-
const pos = side === "left" ? 0 : this._text.length - 1;
|
|
294
|
+
const pos = side === "left" ? 0 : this._text.get().length - 1;
|
|
290
295
|
let result = base;
|
|
291
|
-
for (const span of this._spans) {
|
|
296
|
+
for (const span of this._spans.get()) {
|
|
292
297
|
if (span.start <= pos && pos < span.end) {
|
|
293
298
|
result = result.add(resolveStyle(options, span.style));
|
|
294
299
|
}
|
|
@@ -301,35 +306,36 @@ export class RichText {
|
|
|
301
306
|
if (style !== undefined) {
|
|
302
307
|
throw new Error("Style argument must not be provided when appending RichText");
|
|
303
308
|
}
|
|
304
|
-
const offset = this._text.length;
|
|
305
|
-
|
|
309
|
+
const offset = this._text.get().length;
|
|
310
|
+
// Read before anything is appended, so a text appended to itself takes its spans as they were.
|
|
311
|
+
const moved = content._spans.get().map((span) => span.move(offset));
|
|
312
|
+
this._text.set(this._text.get() + content._text.get());
|
|
306
313
|
// Its base style becomes a span under its own spans, as the reference's
|
|
307
314
|
// `append` does, so appended text looks as it did alone.
|
|
308
|
-
this._addSpan(offset, this._text.length, content.
|
|
309
|
-
for (const span of
|
|
310
|
-
this._spans.push(span
|
|
311
|
-
}
|
|
315
|
+
this._addSpan(offset, this._text.get().length, content._format.get().style);
|
|
316
|
+
for (const span of moved)
|
|
317
|
+
this._spans.push(span);
|
|
312
318
|
return this;
|
|
313
319
|
}
|
|
314
320
|
const sanitized = stripControlChars(content);
|
|
315
|
-
const start = this._text.length;
|
|
316
|
-
this._text
|
|
317
|
-
this._addSpan(start, this._text.length, style ?? "");
|
|
321
|
+
const start = this._text.get().length;
|
|
322
|
+
this._text.set(this._text.get() + sanitized);
|
|
323
|
+
this._addSpan(start, this._text.get().length, style ?? "");
|
|
318
324
|
return this;
|
|
319
325
|
}
|
|
320
326
|
contains(needle) {
|
|
321
|
-
const searchText = needle instanceof RichText ? needle._text : needle;
|
|
322
|
-
return this._text.includes(searchText);
|
|
327
|
+
const searchText = needle instanceof RichText ? needle._text.get() : needle;
|
|
328
|
+
return this._text.get().includes(searchText);
|
|
323
329
|
}
|
|
324
330
|
at(index) {
|
|
325
|
-
const resolved = index < 0 ? this._text.length + index : index;
|
|
326
|
-
const char = this._text[resolved];
|
|
331
|
+
const resolved = index < 0 ? this._text.get().length + index : index;
|
|
332
|
+
const char = this._text.get()[resolved];
|
|
327
333
|
if (char === undefined)
|
|
328
334
|
return new RichText("");
|
|
329
335
|
return this.slice(resolved, resolved + 1);
|
|
330
336
|
}
|
|
331
337
|
slice(start, end) {
|
|
332
|
-
const text = this._text;
|
|
338
|
+
const text = this._text.get();
|
|
333
339
|
const len = text.length;
|
|
334
340
|
const s = start ?? 0;
|
|
335
341
|
const e = end ?? len;
|
|
@@ -340,7 +346,7 @@ export class RichText {
|
|
|
340
346
|
}
|
|
341
347
|
const slicedText = text.slice(resolvedStart, resolvedEnd);
|
|
342
348
|
const result = this.blankCopy(slicedText);
|
|
343
|
-
for (const span of this._spans) {
|
|
349
|
+
for (const span of this._spans.get()) {
|
|
344
350
|
const spanStart = Math.max(span.start, resolvedStart) - resolvedStart;
|
|
345
351
|
const spanEnd = Math.min(span.end, resolvedEnd) - resolvedStart;
|
|
346
352
|
if (spanStart < spanEnd) {
|
|
@@ -362,7 +368,7 @@ export class RichText {
|
|
|
362
368
|
this._spans.push(new Span(start, end, admitted));
|
|
363
369
|
}
|
|
364
370
|
stylize(style, start, end) {
|
|
365
|
-
const len = this._text.length;
|
|
371
|
+
const len = this._text.get().length;
|
|
366
372
|
const s = start !== undefined ? (start < 0 ? len + start : start) : 0;
|
|
367
373
|
const e = end !== undefined ? (end < 0 ? len + end : end) : len;
|
|
368
374
|
if (s >= e || s >= len || e <= 0)
|
|
@@ -373,7 +379,7 @@ export class RichText {
|
|
|
373
379
|
return this;
|
|
374
380
|
}
|
|
375
381
|
highlightRegex(pattern, style) {
|
|
376
|
-
const text = this._text;
|
|
382
|
+
const text = this._text.get();
|
|
377
383
|
let count = 0;
|
|
378
384
|
// Ensure global flag
|
|
379
385
|
const flags = pattern.flags.includes("g") ? pattern.flags : pattern.flags + "g";
|
|
@@ -415,7 +421,7 @@ export class RichText {
|
|
|
415
421
|
const flags = caseSensitive ? "g" : "gi";
|
|
416
422
|
const re = new RegExp(`\\b${escaped}\\b`, flags);
|
|
417
423
|
let match;
|
|
418
|
-
while ((match = re.exec(this._text)) !== null) {
|
|
424
|
+
while ((match = re.exec(this._text.get())) !== null) {
|
|
419
425
|
this._addSpan(match.index, match.index + match[0].length, style);
|
|
420
426
|
count++;
|
|
421
427
|
}
|
|
@@ -424,31 +430,17 @@ export class RichText {
|
|
|
424
430
|
}
|
|
425
431
|
// --- Copy Operations ---
|
|
426
432
|
copy() {
|
|
427
|
-
const result =
|
|
428
|
-
|
|
429
|
-
justify: this._justify,
|
|
430
|
-
overflow: this._overflow,
|
|
431
|
-
end: this._end,
|
|
432
|
-
tabSize: this._tabSize,
|
|
433
|
-
noWrap: this._noWrap,
|
|
434
|
-
});
|
|
435
|
-
result._spans = this._spans.slice();
|
|
433
|
+
const result = this.blankCopy(this._text.get());
|
|
434
|
+
result._spans.set(this._spans.get());
|
|
436
435
|
return result;
|
|
437
436
|
}
|
|
438
437
|
blankCopy(text) {
|
|
439
|
-
return new RichText(text ?? "", {
|
|
440
|
-
style: this._style,
|
|
441
|
-
justify: this._justify,
|
|
442
|
-
overflow: this._overflow,
|
|
443
|
-
end: this._end,
|
|
444
|
-
tabSize: this._tabSize,
|
|
445
|
-
noWrap: this._noWrap,
|
|
446
|
-
});
|
|
438
|
+
return new RichText(text ?? "", { ...this._format.get(), tabSize: this._tabSize });
|
|
447
439
|
}
|
|
448
440
|
// --- Splitting ---
|
|
449
441
|
split(separator) {
|
|
450
442
|
const sep = separator ?? "\n";
|
|
451
|
-
const text = this._text;
|
|
443
|
+
const text = this._text.get();
|
|
452
444
|
const parts = [];
|
|
453
445
|
let start = 0;
|
|
454
446
|
while (true) {
|
|
@@ -476,8 +468,8 @@ export class RichText {
|
|
|
476
468
|
}
|
|
477
469
|
// --- Whitespace Operations ---
|
|
478
470
|
rstrip() {
|
|
479
|
-
const trimmed = this._text.replace(/\s+$/, "");
|
|
480
|
-
if (trimmed.length < this._text.length) {
|
|
471
|
+
const trimmed = this._text.get().replace(/\s+$/, "");
|
|
472
|
+
if (trimmed.length < this._text.get().length) {
|
|
481
473
|
this.plain = trimmed;
|
|
482
474
|
}
|
|
483
475
|
return this;
|
|
@@ -485,8 +477,8 @@ export class RichText {
|
|
|
485
477
|
pad(count, char) {
|
|
486
478
|
const padding = stripControlChars(char ?? " ").repeat(count);
|
|
487
479
|
// Shift all spans right by what the left side gained.
|
|
488
|
-
this._spans
|
|
489
|
-
this._text
|
|
480
|
+
this._spans.set(this._spans.get().map((s) => s.move(padding.length)));
|
|
481
|
+
this._text.set(padding + this._text.get() + padding);
|
|
490
482
|
return this;
|
|
491
483
|
}
|
|
492
484
|
/**
|
|
@@ -495,35 +487,35 @@ export class RichText {
|
|
|
495
487
|
* the text measures what it will draw.
|
|
496
488
|
*/
|
|
497
489
|
expandTabs() {
|
|
498
|
-
const expanded = expandTabs(this._text, this._tabSize);
|
|
499
|
-
this._spans
|
|
500
|
-
this._text
|
|
490
|
+
const expanded = expandTabs(this._text.get(), this._tabSize);
|
|
491
|
+
this._spans.set(this._spans.get().map((s) => new Span(expanded.offsetOf(s.start), expanded.offsetOf(s.end), s.style)));
|
|
492
|
+
this._text.set(expanded.text);
|
|
501
493
|
return this;
|
|
502
494
|
}
|
|
503
495
|
padLeft(count, char) {
|
|
504
496
|
const padding = stripControlChars(char ?? " ").repeat(count);
|
|
505
|
-
this._spans
|
|
506
|
-
this._text
|
|
497
|
+
this._spans.set(this._spans.get().map((s) => s.move(padding.length)));
|
|
498
|
+
this._text.set(padding + this._text.get());
|
|
507
499
|
return this;
|
|
508
500
|
}
|
|
509
501
|
padRight(count, char) {
|
|
510
|
-
this._text
|
|
502
|
+
this._text.set(this._text.get() + stripControlChars(char ?? " ").repeat(count));
|
|
511
503
|
return this;
|
|
512
504
|
}
|
|
513
505
|
setLength(length) {
|
|
514
|
-
if (this._text.length < length) {
|
|
515
|
-
this._text
|
|
506
|
+
if (this._text.get().length < length) {
|
|
507
|
+
this._text.set(this._text.get() + " ".repeat(length - this._text.get().length));
|
|
516
508
|
}
|
|
517
|
-
else if (this._text.length > length) {
|
|
518
|
-
this.plain = this._text.slice(0, length);
|
|
509
|
+
else if (this._text.get().length > length) {
|
|
510
|
+
this.plain = this._text.get().slice(0, length);
|
|
519
511
|
}
|
|
520
512
|
return this;
|
|
521
513
|
}
|
|
522
514
|
extendStyle(count) {
|
|
523
|
-
const oldLen = this._text.length;
|
|
524
|
-
this._text
|
|
515
|
+
const oldLen = this._text.get().length;
|
|
516
|
+
this._text.set(this._text.get() + " ".repeat(count));
|
|
525
517
|
// Extend existing spans to cover new spaces
|
|
526
|
-
this._spans
|
|
518
|
+
this._spans.set(this._spans.get().map((s) => s.end === oldLen ? s.extend(count) : s));
|
|
527
519
|
return this;
|
|
528
520
|
}
|
|
529
521
|
// --- Truncation ---
|
|
@@ -571,14 +563,14 @@ export class RichText {
|
|
|
571
563
|
if (mode === "right") {
|
|
572
564
|
this._cropRightTo(budget);
|
|
573
565
|
if (marker)
|
|
574
|
-
this._text
|
|
566
|
+
this._text.set(this._text.get() + marker);
|
|
575
567
|
return this;
|
|
576
568
|
}
|
|
577
569
|
if (mode === "left") {
|
|
578
570
|
this._cropLeftTo(budget);
|
|
579
571
|
if (marker) {
|
|
580
|
-
this._spans
|
|
581
|
-
this._text
|
|
572
|
+
this._spans.set(this._spans.get().map((s) => s.move(marker.length)));
|
|
573
|
+
this._text.set(marker + this._text.get());
|
|
582
574
|
}
|
|
583
575
|
return this;
|
|
584
576
|
}
|
|
@@ -586,17 +578,17 @@ export class RichText {
|
|
|
586
578
|
const leftBudget = asCellCol(Math.floor(budget / 2));
|
|
587
579
|
const rightBudget = asCellCol(budget - leftBudget);
|
|
588
580
|
// Find the char-index ranges to keep from each side.
|
|
589
|
-
const leftEndCharIdx = cellFit(this._text, leftBudget).length;
|
|
590
|
-
const rightStartCharIdx = this._text.length - cellFitEnd(this._text, rightBudget).length;
|
|
591
|
-
const leftText = this._text.slice(0, leftEndCharIdx);
|
|
592
|
-
const rightText = this._text.slice(rightStartCharIdx);
|
|
581
|
+
const leftEndCharIdx = cellFit(this._text.get(), leftBudget).length;
|
|
582
|
+
const rightStartCharIdx = this._text.get().length - cellFitEnd(this._text.get(), rightBudget).length;
|
|
583
|
+
const leftText = this._text.get().slice(0, leftEndCharIdx);
|
|
584
|
+
const rightText = this._text.get().slice(rightStartCharIdx);
|
|
593
585
|
const droppedStart = leftEndCharIdx;
|
|
594
586
|
const droppedEnd = rightStartCharIdx;
|
|
595
587
|
const shift = marker.length - (droppedEnd - droppedStart);
|
|
596
588
|
// Spans falling entirely before the drop stay; entirely after shift by
|
|
597
589
|
// (marker.length - dropped chars); spans crossing the drop are clipped.
|
|
598
590
|
const newSpans = [];
|
|
599
|
-
for (const s of this._spans) {
|
|
591
|
+
for (const s of this._spans.get()) {
|
|
600
592
|
if (s.end <= droppedStart) {
|
|
601
593
|
newSpans.push(s);
|
|
602
594
|
}
|
|
@@ -613,18 +605,18 @@ export class RichText {
|
|
|
613
605
|
}
|
|
614
606
|
}
|
|
615
607
|
}
|
|
616
|
-
this._text
|
|
617
|
-
this._spans
|
|
608
|
+
this._text.set(leftText + marker + rightText);
|
|
609
|
+
this._spans.set(newSpans);
|
|
618
610
|
return this;
|
|
619
611
|
}
|
|
620
612
|
_cropRightTo(targetWidth) {
|
|
621
|
-
this.plain = cellFit(this._text, targetWidth);
|
|
613
|
+
this.plain = cellFit(this._text.get(), targetWidth);
|
|
622
614
|
}
|
|
623
615
|
_cropLeftTo(targetWidth) {
|
|
624
|
-
const charIdx = this._text.length - cellFitEnd(this._text, targetWidth).length;
|
|
616
|
+
const charIdx = this._text.get().length - cellFitEnd(this._text.get(), targetWidth).length;
|
|
625
617
|
// Shift spans left by charIdx; clip spans that started before.
|
|
626
618
|
const shift = -charIdx;
|
|
627
|
-
this._spans
|
|
619
|
+
this._spans.set(this._spans.get()
|
|
628
620
|
.map((s) => {
|
|
629
621
|
if (s.end <= charIdx)
|
|
630
622
|
return undefined;
|
|
@@ -632,8 +624,8 @@ export class RichText {
|
|
|
632
624
|
const newEnd = s.end + shift;
|
|
633
625
|
return new Span(newStart, newEnd, s.style);
|
|
634
626
|
})
|
|
635
|
-
.filter((s) => s !== undefined);
|
|
636
|
-
this._text
|
|
627
|
+
.filter((s) => s !== undefined));
|
|
628
|
+
this._text.set(this._text.get().slice(charIdx));
|
|
637
629
|
}
|
|
638
630
|
// --- Alignment ---
|
|
639
631
|
align(justify, width) {
|
|
@@ -663,8 +655,8 @@ export class RichText {
|
|
|
663
655
|
}
|
|
664
656
|
// --- Suffix Removal ---
|
|
665
657
|
removeSuffix(suffix) {
|
|
666
|
-
if (this._text.endsWith(suffix)) {
|
|
667
|
-
this.plain = this._text.slice(0, -suffix.length);
|
|
658
|
+
if (this._text.get().endsWith(suffix)) {
|
|
659
|
+
this.plain = this._text.get().slice(0, -suffix.length);
|
|
668
660
|
}
|
|
669
661
|
return this;
|
|
670
662
|
}
|
|
@@ -721,7 +713,7 @@ export class RichText {
|
|
|
721
713
|
}
|
|
722
714
|
// --- Renderable ---
|
|
723
715
|
*render(options) {
|
|
724
|
-
const expanded = expandTabs(this._text, this._tabSize);
|
|
716
|
+
const expanded = expandTabs(this._text.get(), this._tabSize);
|
|
725
717
|
const base = this.resolvedStyle(options);
|
|
726
718
|
const { segments: allSegments, covered } = this._buildSegments(expanded, base, options);
|
|
727
719
|
const spanFree = spanFreeLines(allSegments, covered);
|
|
@@ -739,8 +731,8 @@ export class RichText {
|
|
|
739
731
|
// overflow arm a no-op, but passed an unbounded one through to `justify`,
|
|
740
732
|
// which pads — and `" ".repeat(Infinity)` throws.
|
|
741
733
|
const maxWidth = cellCount(withBoundedWidth(options, this).maxWidth);
|
|
742
|
-
const overflow = this.
|
|
743
|
-
const noWrap = this.
|
|
734
|
+
const overflow = this._format.get().overflow ?? options.overflow ?? "fold";
|
|
735
|
+
const noWrap = this._format.get().noWrap || (options.noWrap ?? false);
|
|
744
736
|
// Two widths, and they are the reference's `no_wrap` and `"ignore"` told
|
|
745
737
|
// apart. `edge` is where a line is cut: gone under `"ignore"`, so the line
|
|
746
738
|
// leaves at its natural width and whatever drew it decides about the
|
|
@@ -759,7 +751,7 @@ export class RichText {
|
|
|
759
751
|
// A line with no edge has no width to be justified in, so under `"ignore"`
|
|
760
752
|
// it is not justified — the reference's `Text.wrap` returns that arm before
|
|
761
753
|
// `justify`. `undefined` is the placement that leaves a line as it is.
|
|
762
|
-
const justify = overflow === "ignore" ? undefined : this.
|
|
754
|
+
const justify = overflow === "ignore" ? undefined : this._format.get().justify ?? options.justify;
|
|
763
755
|
// One cell either way, so the marker's stand-in leaves the cut where it was.
|
|
764
756
|
const ellipsis = drawable(options, "\u2026", ".");
|
|
765
757
|
for (let index = 0; index < logicalLines.length; index += 1) {
|
|
@@ -787,13 +779,13 @@ export class RichText {
|
|
|
787
779
|
// (truthy) and again for non-empty text (truthy and not the default
|
|
788
780
|
// "\n"), which gave `end` two meanings depending on whether the text was
|
|
789
781
|
// empty (rich-text-5ai).
|
|
790
|
-
if (this.
|
|
791
|
-
yield new Segment(this.
|
|
782
|
+
if (this._format.get().end) {
|
|
783
|
+
yield new Segment(this._format.get().end);
|
|
792
784
|
}
|
|
793
785
|
}
|
|
794
786
|
// --- Measurable ---
|
|
795
787
|
measure(options) {
|
|
796
|
-
const lines = expandTabs(this._text, this._tabSize).text.split("\n");
|
|
788
|
+
const lines = expandTabs(this._text.get(), this._tabSize).text.split("\n");
|
|
797
789
|
const maxLineWidth = lines.reduce((widest, line) => Math.max(widest, cellLen(line)), 0);
|
|
798
790
|
// [LAW:one-source-of-truth] The minimum is the widest piece the wrapper
|
|
799
791
|
// never breaks, asked of the wrapper — so a line's indent, which wraps
|
|
@@ -848,7 +840,7 @@ export class RichText {
|
|
|
848
840
|
// and it is what makes empty text one line to everything that splits a
|
|
849
841
|
// render into lines — a panel's body, a table cell — where Rich draws one.
|
|
850
842
|
const inner = new Set();
|
|
851
|
-
for (const span of this._spans) {
|
|
843
|
+
for (const span of this._spans.get()) {
|
|
852
844
|
inner.add(clamp(span.start));
|
|
853
845
|
inner.add(clamp(span.end));
|
|
854
846
|
}
|
|
@@ -862,7 +854,7 @@ export class RichText {
|
|
|
862
854
|
const pieceAt = new Map(boundaries.map((position, piece) => [position, piece]).reverse());
|
|
863
855
|
const styles = boundaries.slice(0, -1).map(() => base);
|
|
864
856
|
const covered = styles.map(() => false);
|
|
865
|
-
for (const span of this._spans) {
|
|
857
|
+
for (const span of this._spans.get()) {
|
|
866
858
|
const end = clamp(span.end);
|
|
867
859
|
const style = resolveStyle(options, span.style);
|
|
868
860
|
const opensAt = pieceAt.get(clamp(span.start));
|
package/dist/core/timeline.d.ts
CHANGED
|
@@ -32,12 +32,25 @@
|
|
|
32
32
|
* why it is a search and not the ulp walk a sum on one clock takes. A value
|
|
33
33
|
* moving at the mapped time changes at the host time itself.
|
|
34
34
|
*
|
|
35
|
+
* A stagger — a Group's children pulsing in sequence, a light walking down
|
|
36
|
+
* a Table's rows — is the same map once per child: `stagger` runs each
|
|
37
|
+
* child's animation on a timeline playing from its index times the delay,
|
|
38
|
+
* and the group changes when the soonest child does, each child's answer
|
|
39
|
+
* carried onto the group's clock by `run`. Before its turn a child is asked
|
|
40
|
+
* for a time before 0, which is its own business: a loop is shifted in
|
|
41
|
+
* phase. Tree, Table, Group and Layout take the children's frames as they
|
|
42
|
+
* take any renderables, through `Animation.map`, so none of them knows a
|
|
43
|
+
* stagger exists. [LAW:composability] A lifecycle is not staggered here: it
|
|
44
|
+
* carries its turn as `entered`, on the host's clock its `leave` and `gone`
|
|
45
|
+
* take, so moving it onto a shifted clock would lose them
|
|
46
|
+
* ([LAW:one-source-of-truth] for when it arrives).
|
|
47
|
+
*
|
|
35
48
|
* [LAW:no-ambient-temporal-coupling] Nothing here reads a clock. The host
|
|
36
49
|
* time is an argument everywhere, and a timeline is built from one the host
|
|
37
50
|
* hands in, so the same host time maps to the same animation time at one
|
|
38
51
|
* sample a second or sixty.
|
|
39
52
|
*/
|
|
40
|
-
import
|
|
53
|
+
import { Animation, type Timed } from "./animation.js";
|
|
41
54
|
/**
|
|
42
55
|
* A map from host time to animation time. Playing, it is `time` at `origin`
|
|
43
56
|
* moving `rate` animation seconds a host second; paused, it is `time` at
|
|
@@ -76,9 +89,21 @@ export declare const Timeline: {
|
|
|
76
89
|
* host time to, changing at the host moment the mapped time reaches the
|
|
77
90
|
* animation's own answer — backwards, when it drops under where the value
|
|
78
91
|
* began. Paused, the value never changes and never did. The value is the
|
|
79
|
-
* animation's own, on no clock:
|
|
80
|
-
*
|
|
81
|
-
*
|
|
92
|
+
* animation's own, on no clock: placed, it is stamped at the frame's time
|
|
93
|
+
* (`placedRender`, `./animation.ts`) with this animation's answers, so a
|
|
94
|
+
* value's contents follow the frame's clock and not this map.
|
|
95
|
+
* Reduced, it is the animation reduced on the same timeline: what shows
|
|
96
|
+
* still follows the timeline, and only the motion is gone.
|
|
97
|
+
*/
|
|
98
|
+
run<T>(timeline: Timeline, animation: Timed<T>): Animation<T>;
|
|
99
|
+
/**
|
|
100
|
+
* Every one of `animations` at once, each on a timeline playing from its
|
|
101
|
+
* index times `delay` seconds after the earliest, their values in order, on
|
|
102
|
+
* a clock of the stagger's own from 0. It changes when the soonest of them
|
|
103
|
+
* does, on that clock (`Animation.all` of `run`s). A delay of 0 starts them
|
|
104
|
+
* together and a negative one starts the last at 0 and the first last.
|
|
105
|
+
* Reduced, the offsets are the cascade's choreography, which is motion: every
|
|
106
|
+
* child reduced at once, so from 0 everyone has arrived.
|
|
82
107
|
*/
|
|
83
|
-
|
|
108
|
+
stagger<T>(animations: readonly Timed<T>[], delay: number): Animation<readonly T[]>;
|
|
84
109
|
};
|