@promptctl/rich-js 0.27.0 → 0.28.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 +1 -0
- package/THIRD-PARTY-NOTICES +42 -0
- package/dist/core/animation.d.ts +36 -17
- package/dist/core/animation.js +61 -37
- package/dist/core/ansi.d.ts +15 -8
- package/dist/core/ansi.js +106 -28
- package/dist/core/bidi-data.d.ts +14 -0
- package/dist/core/bidi-data.js +385 -0
- package/dist/core/bidi.d.ts +89 -0
- package/dist/core/bidi.js +615 -0
- package/dist/core/box.d.ts +9 -1
- package/dist/core/box.js +11 -0
- package/dist/core/cell-widths.d.ts +3 -2
- package/dist/core/cell-widths.js +93 -93
- package/dist/core/cells.d.ts +20 -0
- package/dist/core/cells.js +93 -5
- package/dist/core/color.d.ts +6 -7
- package/dist/core/color.js +11 -13
- package/dist/core/console.d.ts +18 -1
- package/dist/core/console.js +24 -5
- package/dist/core/control-codes.d.ts +61 -0
- package/dist/core/control-codes.js +157 -0
- package/dist/core/env.d.ts +6 -0
- package/dist/core/env.js +9 -1
- package/dist/core/export-html.js +4 -3
- package/dist/core/export-lines.d.ts +2 -0
- package/dist/core/export-lines.js +7 -3
- package/dist/core/export-svg.js +6 -5
- package/dist/core/json.js +9 -2
- package/dist/core/markup.js +4 -3
- package/dist/core/orient.d.ts +68 -0
- package/dist/core/orient.js +224 -0
- package/dist/core/osc8.d.ts +12 -5
- package/dist/core/osc8.js +14 -7
- package/dist/core/pretty.js +22 -18
- package/dist/core/protocol.d.ts +27 -6
- package/dist/core/protocol.js +14 -6
- package/dist/core/render.d.ts +8 -3
- package/dist/core/render.js +18 -5
- package/dist/core/seal.d.ts +26 -0
- package/dist/core/seal.js +75 -0
- package/dist/core/segment.d.ts +9 -1
- package/dist/core/segment.js +11 -1
- package/dist/core/style.d.ts +29 -1
- package/dist/core/style.js +41 -5
- package/dist/core/text-order.d.ts +51 -0
- package/dist/core/text-order.js +50 -0
- package/dist/core/text.d.ts +10 -0
- package/dist/core/text.js +133 -87
- package/dist/core/timeline.js +6 -6
- package/dist/core/view.d.ts +1 -1
- package/dist/core/view.js +5 -4
- package/dist/core/wrap.d.ts +24 -6
- package/dist/core/wrap.js +82 -16
- package/dist/host/app.d.ts +44 -3
- package/dist/host/app.js +111 -6
- package/dist/host/index.d.ts +3 -2
- package/dist/host/index.js +1 -0
- package/dist/host/input.d.ts +35 -2
- package/dist/host/input.js +88 -4
- package/dist/host/terminal-colors.d.ts +18 -0
- package/dist/host/terminal-colors.js +46 -0
- package/dist/host/terminal-host.d.ts +2 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +2 -2
- package/dist/renderables/change.d.ts +1 -3
- package/dist/renderables/change.js +4 -6
- package/dist/renderables/effect.d.ts +60 -59
- package/dist/renderables/effect.js +91 -81
- package/dist/renderables/effects.d.ts +37 -8
- package/dist/renderables/effects.js +185 -52
- package/dist/renderables/frames.d.ts +1 -10
- package/dist/renderables/frames.js +1 -16
- package/dist/renderables/glyph-effect.d.ts +12 -13
- package/dist/renderables/glyph-effect.js +34 -32
- package/dist/renderables/glyph-effects.d.ts +27 -8
- package/dist/renderables/glyph-effects.js +75 -7
- package/dist/renderables/lifecycle.d.ts +5 -7
- package/dist/renderables/lifecycle.js +6 -7
- package/dist/renderables/markdown.js +4 -2
- package/dist/renderables/panel.js +6 -4
- package/dist/renderables/progressBar.d.ts +3 -3
- package/dist/renderables/progressBar.js +16 -13
- package/dist/renderables/syntax.js +7 -2
- package/dist/renderables/table.js +2 -1
- package/dist/template-bindings/animation-funcs.d.ts +49 -18
- package/dist/template-bindings/animation-funcs.js +115 -32
- package/dist/template-bindings/index.d.ts +7 -5
- package/dist/template-bindings/index.js +8 -5
- package/dist/template-bindings/palette-funcs.d.ts +1 -1
- package/dist/template-bindings/palette-funcs.js +1 -1
- package/dist/themes/buildPalette.d.ts +3 -2
- package/dist/themes/buildPalette.js +2 -2
- package/dist/themes/data/index.js +1 -4
- package/dist/themes/data/textual-ansi.js +0 -143
- package/dist/themes/ramp.d.ts +1 -1
- package/dist/themes/ramp.js +1 -1
- package/dist/themes/registry.d.ts +6 -8
- package/dist/themes/registry.js +2 -4
- package/dist/themes/terminalThemes.d.ts +4 -11
- package/dist/themes/terminalThemes.js +31 -47
- package/dist/widgets/button.d.ts +1 -1
- package/dist/widgets/button.js +6 -6
- package/dist/widgets/checkbox.js +4 -4
- package/dist/widgets/dropdown.d.ts +1 -1
- package/dist/widgets/dropdown.js +7 -5
- package/dist/widgets/themed-widget.js +3 -3
- package/dist/widgets/toggle.d.ts +1 -1
- package/dist/widgets/toggle.js +5 -5
- package/package.json +5 -4
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sealed` — the one total enforcer of what the wire holds.
|
|
3
|
+
*
|
|
4
|
+
* [LAW:single-enforcer] An explicit bidi control reaches the bytes only inside
|
|
5
|
+
* an isolate that accounts for it. The rule has no list of who wrote what: in a
|
|
6
|
+
* segment with no logical stamp every explicit control is deleted, in every
|
|
7
|
+
* order, so a raw `new Segment(text)` is contained and merely shows no effect.
|
|
8
|
+
* What the census withholds goes here too (`wireText`), a segment at a time, on
|
|
9
|
+
* text nothing cuts again, which is why a rule that reads neighbours can live here
|
|
10
|
+
* and not in the sanitizer. A span edge is therefore where that reading stops. A
|
|
11
|
+
* flag cut by one loses its tags and stays a flag; a selector after one is kept;
|
|
12
|
+
* a ZWJ before one is not taken with a deletion past it, so a join can form that
|
|
13
|
+
* `cellLen`, which reads the whole string, did not measure. Segments have always
|
|
14
|
+
* been measured apart and written together, so the same edge in the same text with
|
|
15
|
+
* nothing deleted draws the same row.
|
|
16
|
+
*
|
|
17
|
+
* This runs on the way out, below every cut, because a row is cut after it is
|
|
18
|
+
* drawn: `scroll` divides it, `mergeHorizontal` and a viewport crop it, an
|
|
19
|
+
* overlay splices into it. Controls written at drawing time would be cut with
|
|
20
|
+
* it. `Style.bidi` is a fact on the cells instead, so what survives still names
|
|
21
|
+
* its stamp, and the controls are written around the survivors.
|
|
22
|
+
*/
|
|
23
|
+
import { advance, closersOf, opened, withoutExplicit } from "./bidi.js";
|
|
24
|
+
import { wireText } from "./cells.js";
|
|
25
|
+
import { Segment } from "./segment.js";
|
|
26
|
+
const LRI = "\u2066";
|
|
27
|
+
const RLI = "\u2067";
|
|
28
|
+
const PDI = "\u2069";
|
|
29
|
+
/** `text` with closers that match nothing in `open` dropped, so content cannot end the isolate around it. */
|
|
30
|
+
function balanced(text, open) {
|
|
31
|
+
return Array.from(text, (character) => (advance(open, character) ? character : "")).join("");
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* One row's run of one stamp in its isolate. The initiator is stated from the
|
|
35
|
+
* whole line's direction rather than FSI, so a wrapped row does not re-detect
|
|
36
|
+
* it. Embeddings close by PDF ahead of the PDI because VTE discards a zero-width
|
|
37
|
+
* character at column 0, initiator included, and a PDI matching nothing there
|
|
38
|
+
* closes nothing.
|
|
39
|
+
*/
|
|
40
|
+
function* isolated(run, { level, opens }) {
|
|
41
|
+
const open = opened(opens);
|
|
42
|
+
yield new Segment((level === 1 ? RLI : LRI) + opens);
|
|
43
|
+
for (const segment of run) {
|
|
44
|
+
const text = balanced(segment.text, open);
|
|
45
|
+
yield text === segment.text ? segment : new Segment(text, segment.style, segment.control);
|
|
46
|
+
}
|
|
47
|
+
yield new Segment(closersOf(open.stack.join("")) + PDI);
|
|
48
|
+
}
|
|
49
|
+
/** `segments` as the wire may hold them for `order`. */
|
|
50
|
+
export function* sealed(segments, order) {
|
|
51
|
+
let run = [];
|
|
52
|
+
let stamp;
|
|
53
|
+
const flush = function* () {
|
|
54
|
+
if (stamp !== undefined)
|
|
55
|
+
yield* isolated(run, stamp);
|
|
56
|
+
run = [];
|
|
57
|
+
stamp = undefined;
|
|
58
|
+
};
|
|
59
|
+
for (const raw of segments) {
|
|
60
|
+
const drawn = wireText(raw.text);
|
|
61
|
+
const segment = drawn === raw.text ? raw : new Segment(drawn, raw.style, raw.control);
|
|
62
|
+
const own = order === "logical" && !segment.isControl ? segment.style?.bidi : undefined;
|
|
63
|
+
if (own?.order === "logical") {
|
|
64
|
+
if (own !== stamp)
|
|
65
|
+
yield* flush();
|
|
66
|
+
stamp = own;
|
|
67
|
+
run.push(segment);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
yield* flush();
|
|
71
|
+
const text = withoutExplicit(segment.text);
|
|
72
|
+
yield text === segment.text ? segment : new Segment(text, segment.style, segment.control);
|
|
73
|
+
}
|
|
74
|
+
yield* flush();
|
|
75
|
+
}
|
package/dist/core/segment.d.ts
CHANGED
|
@@ -130,6 +130,14 @@ export declare class Segment {
|
|
|
130
130
|
* short rows padded with spaces, long ones cut.
|
|
131
131
|
*/
|
|
132
132
|
static splitAndCropLines(segments: Iterable<Segment>, width: number): Segment[][];
|
|
133
|
+
/**
|
|
134
|
+
* The plain text of one line of segments, in the coordinate system
|
|
135
|
+
* `divideLine` and `Segment.divide` share: cell offsets into the styled line
|
|
136
|
+
* are cell offsets into this string. [LAW:one-source-of-truth] for that
|
|
137
|
+
* correspondence — the offsets are found in this text and applied to the
|
|
138
|
+
* segments it came from.
|
|
139
|
+
*/
|
|
140
|
+
static plainOf(line: readonly Segment[]): string;
|
|
133
141
|
/**
|
|
134
142
|
* Returns total cell width of a line. Ignores control segments.
|
|
135
143
|
*/
|
|
@@ -177,7 +185,7 @@ export declare class Segment {
|
|
|
177
185
|
* rest every time, so a long line wrapped into many rows took time quadratic
|
|
178
186
|
* in its length.
|
|
179
187
|
*/
|
|
180
|
-
static divide(segments: Segment[], cuts: number[]): Segment[][];
|
|
188
|
+
static divide(segments: readonly Segment[], cuts: number[]): Segment[][];
|
|
181
189
|
/**
|
|
182
190
|
* Pads below to fill height.
|
|
183
191
|
*/
|
package/dist/core/segment.js
CHANGED
|
@@ -307,6 +307,16 @@ export class Segment {
|
|
|
307
307
|
static splitAndCropLines(segments, width) {
|
|
308
308
|
return Segment.splitLines(segments).map((line) => Segment.adjustLineLength(line, width));
|
|
309
309
|
}
|
|
310
|
+
/**
|
|
311
|
+
* The plain text of one line of segments, in the coordinate system
|
|
312
|
+
* `divideLine` and `Segment.divide` share: cell offsets into the styled line
|
|
313
|
+
* are cell offsets into this string. [LAW:one-source-of-truth] for that
|
|
314
|
+
* correspondence — the offsets are found in this text and applied to the
|
|
315
|
+
* segments it came from.
|
|
316
|
+
*/
|
|
317
|
+
static plainOf(line) {
|
|
318
|
+
return line.map((segment) => segment.text).join("");
|
|
319
|
+
}
|
|
310
320
|
/**
|
|
311
321
|
* Returns total cell width of a line. Ignores control segments.
|
|
312
322
|
*/
|
|
@@ -426,7 +436,7 @@ export class Segment {
|
|
|
426
436
|
*/
|
|
427
437
|
static divide(segments, cuts) {
|
|
428
438
|
if (cuts.length === 0)
|
|
429
|
-
return [segments];
|
|
439
|
+
return [[...segments]];
|
|
430
440
|
const sections = [[]];
|
|
431
441
|
let offset = 0;
|
|
432
442
|
let next = 0;
|
package/dist/core/style.d.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { ColorSpec, ColorDepth } from "./color.js";
|
|
5
5
|
import { type Anchor } from "./anchor.js";
|
|
6
|
+
import type { BidiStamp } from "./text-order.js";
|
|
6
7
|
/**
|
|
7
8
|
* Canonical text-attribute inventory. Single source of truth consumed by
|
|
8
9
|
* `Style.parse` / `Style.toString` and the template bindings — adding an
|
|
@@ -45,6 +46,8 @@ export interface StyleOptions {
|
|
|
45
46
|
link?: string;
|
|
46
47
|
meta?: Record<string, unknown>;
|
|
47
48
|
anchor?: Anchor;
|
|
49
|
+
boxFrame?: object;
|
|
50
|
+
bidi?: BidiStamp;
|
|
48
51
|
/**
|
|
49
52
|
* The hold the style's cells are in, both ends or neither: a cell saying
|
|
50
53
|
* only when it next changes would claim, by default, that it always was,
|
|
@@ -58,6 +61,10 @@ export interface Hold {
|
|
|
58
61
|
readonly lastChange: number;
|
|
59
62
|
readonly nextChange: number;
|
|
60
63
|
}
|
|
64
|
+
/** The hold of a cell that never changes: always was, and always will be. */
|
|
65
|
+
export declare const NEVER_CHANGES: Hold;
|
|
66
|
+
/** The moments both `a` and `b` hold through: the later `lastChange` and the sooner `nextChange`. */
|
|
67
|
+
export declare function intersection(a: Hold, b: Hold): Hold;
|
|
61
68
|
export declare class StyleSyntaxError extends Error {
|
|
62
69
|
constructor(message: string, options?: ErrorOptions);
|
|
63
70
|
}
|
|
@@ -85,6 +92,23 @@ export declare class Style {
|
|
|
85
92
|
* unequal, because they came from different places.
|
|
86
93
|
*/
|
|
87
94
|
readonly anchor: Anchor | undefined;
|
|
95
|
+
/**
|
|
96
|
+
* The drawing of a box these cells are the frame of (`Box.frame`), compared
|
|
97
|
+
* by identity. A frame is told by what drew it, never by its glyphs, as a
|
|
98
|
+
* strip's seam is (`JOIN`, `./strip.ts`): a box may draw its frame in blanks,
|
|
99
|
+
* and what it draws in ink an element's own content may draw too. It is a
|
|
100
|
+
* drawing and not a box, so two panels of one `ROUNDED` are two frames, and
|
|
101
|
+
* it is no `Anchor`: a frame's cells say whose they are and not where, since
|
|
102
|
+
* a table draws its top rule before it knows its height. It draws nothing.
|
|
103
|
+
*/
|
|
104
|
+
readonly boxFrame: object | undefined;
|
|
105
|
+
/**
|
|
106
|
+
* What drawing said of these cells' direction, compared by identity, as a
|
|
107
|
+
* link is a fact the encoder turns into bytes: `Segment` never holds a control
|
|
108
|
+
* the library wrote, so no cut of a row can unbalance one, and what survives a
|
|
109
|
+
* cut still names its stamp (`./seal.ts`). It draws nothing.
|
|
110
|
+
*/
|
|
111
|
+
readonly bidi: BidiStamp | undefined;
|
|
88
112
|
/**
|
|
89
113
|
* The hold this style's cells are in, on the timeline of whoever drew the
|
|
90
114
|
* frame: since when what they show has been what it is (`lastChange`), and
|
|
@@ -117,6 +141,10 @@ export declare class Style {
|
|
|
117
141
|
* The same look, drawn from the place `anchor` names, or from nowhere.
|
|
118
142
|
*/
|
|
119
143
|
withAnchor(anchor: Anchor | undefined): Style;
|
|
144
|
+
/** The same look, as the frame of the drawing `boxFrame` names, or of none. */
|
|
145
|
+
withBoxFrame(boxFrame: object | undefined): Style;
|
|
146
|
+
/** The same look, its cells' direction as `bidi` says. */
|
|
147
|
+
withBidi(bidi: BidiStamp | undefined): Style;
|
|
120
148
|
/** The style's text attributes, each as it sets it. */
|
|
121
149
|
get attributes(): Attributes;
|
|
122
150
|
/**
|
|
@@ -163,7 +191,7 @@ export declare class Style {
|
|
|
163
191
|
/**
|
|
164
192
|
* The look as a canonical string that round-trips through `Style.parse()`:
|
|
165
193
|
* colours, attributes and the link, which is all a style string can spell.
|
|
166
|
-
* What a style carries beyond its look — `meta`, `anchor`, `hold` —
|
|
194
|
+
* What a style carries beyond its look — `meta`, `anchor`, `boxFrame`, `bidi`, `hold` —
|
|
167
195
|
* has no spelling and is not here, so two styles that differ only there
|
|
168
196
|
* read alike; `equals` tells them apart.
|
|
169
197
|
*/
|
package/dist/core/style.js
CHANGED
|
@@ -63,7 +63,11 @@ export const ATTRIBUTE_SHORT_ALIASES = {
|
|
|
63
63
|
uu: "underline2",
|
|
64
64
|
};
|
|
65
65
|
/** The hold of a cell that never changes: always was, and always will be. */
|
|
66
|
-
const NEVER_CHANGES = { lastChange: Number.NEGATIVE_INFINITY, nextChange: Number.POSITIVE_INFINITY };
|
|
66
|
+
export const NEVER_CHANGES = { lastChange: Number.NEGATIVE_INFINITY, nextChange: Number.POSITIVE_INFINITY };
|
|
67
|
+
/** The moments both `a` and `b` hold through: the later `lastChange` and the sooner `nextChange`. */
|
|
68
|
+
export function intersection(a, b) {
|
|
69
|
+
return { lastChange: Math.max(a.lastChange, b.lastChange), nextChange: Math.min(a.nextChange, b.nextChange) };
|
|
70
|
+
}
|
|
67
71
|
export class StyleSyntaxError extends Error {
|
|
68
72
|
constructor(message, options) {
|
|
69
73
|
super(message, options);
|
|
@@ -97,6 +101,23 @@ export class Style {
|
|
|
97
101
|
* unequal, because they came from different places.
|
|
98
102
|
*/
|
|
99
103
|
anchor;
|
|
104
|
+
/**
|
|
105
|
+
* The drawing of a box these cells are the frame of (`Box.frame`), compared
|
|
106
|
+
* by identity. A frame is told by what drew it, never by its glyphs, as a
|
|
107
|
+
* strip's seam is (`JOIN`, `./strip.ts`): a box may draw its frame in blanks,
|
|
108
|
+
* and what it draws in ink an element's own content may draw too. It is a
|
|
109
|
+
* drawing and not a box, so two panels of one `ROUNDED` are two frames, and
|
|
110
|
+
* it is no `Anchor`: a frame's cells say whose they are and not where, since
|
|
111
|
+
* a table draws its top rule before it knows its height. It draws nothing.
|
|
112
|
+
*/
|
|
113
|
+
boxFrame;
|
|
114
|
+
/**
|
|
115
|
+
* What drawing said of these cells' direction, compared by identity, as a
|
|
116
|
+
* link is a fact the encoder turns into bytes: `Segment` never holds a control
|
|
117
|
+
* the library wrote, so no cut of a row can unbalance one, and what survives a
|
|
118
|
+
* cut still names its stamp (`./seal.ts`). It draws nothing.
|
|
119
|
+
*/
|
|
120
|
+
bidi;
|
|
100
121
|
/**
|
|
101
122
|
* The hold this style's cells are in, on the timeline of whoever drew the
|
|
102
123
|
* frame: since when what they show has been what it is (`lastChange`), and
|
|
@@ -142,6 +163,8 @@ export class Style {
|
|
|
142
163
|
this.link = options.link || undefined;
|
|
143
164
|
this.meta = options.meta;
|
|
144
165
|
this.anchor = options.anchor;
|
|
166
|
+
this.boxFrame = options.boxFrame;
|
|
167
|
+
this.bidi = options.bidi;
|
|
145
168
|
}
|
|
146
169
|
get isNull() {
|
|
147
170
|
return (this.color === undefined &&
|
|
@@ -162,6 +185,8 @@ export class Style {
|
|
|
162
185
|
this.link === undefined &&
|
|
163
186
|
this.meta === undefined &&
|
|
164
187
|
this.anchor === undefined &&
|
|
188
|
+
this.boxFrame === undefined &&
|
|
189
|
+
this.bidi === undefined &&
|
|
165
190
|
this.hold.nextChange === Number.POSITIVE_INFINITY &&
|
|
166
191
|
this.hold.lastChange === Number.NEGATIVE_INFINITY);
|
|
167
192
|
}
|
|
@@ -199,6 +224,14 @@ export class Style {
|
|
|
199
224
|
withAnchor(anchor) {
|
|
200
225
|
return this.with({ anchor });
|
|
201
226
|
}
|
|
227
|
+
/** The same look, as the frame of the drawing `boxFrame` names, or of none. */
|
|
228
|
+
withBoxFrame(boxFrame) {
|
|
229
|
+
return this.with({ boxFrame });
|
|
230
|
+
}
|
|
231
|
+
/** The same look, its cells' direction as `bidi` says. */
|
|
232
|
+
withBidi(bidi) {
|
|
233
|
+
return this.with({ bidi });
|
|
234
|
+
}
|
|
202
235
|
/** The style's text attributes, each as it sets it. */
|
|
203
236
|
get attributes() {
|
|
204
237
|
// [LAW:one-source-of-truth] Keyed by `ATTRIBUTE_NAMES`, whose every name is a key here: the cast is that list's own claim.
|
|
@@ -257,9 +290,10 @@ export class Style {
|
|
|
257
290
|
? { ...this.meta, ...other.meta }
|
|
258
291
|
: other.meta ?? this.meta,
|
|
259
292
|
anchor: other.anchor ?? this.anchor,
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
293
|
+
boxFrame: other.boxFrame ?? this.boxFrame,
|
|
294
|
+
bidi: other.bidi ?? this.bidi,
|
|
295
|
+
// Cells under both change when either does.
|
|
296
|
+
hold: intersection(this.hold, other.hold),
|
|
263
297
|
});
|
|
264
298
|
}
|
|
265
299
|
equals(other) {
|
|
@@ -282,6 +316,8 @@ export class Style {
|
|
|
282
316
|
this.overline === other.overline &&
|
|
283
317
|
this.link === other.link &&
|
|
284
318
|
anchorsEqual(this.anchor, other.anchor) &&
|
|
319
|
+
this.boxFrame === other.boxFrame &&
|
|
320
|
+
this.bidi === other.bidi &&
|
|
285
321
|
this.hold.nextChange === other.hold.nextChange &&
|
|
286
322
|
this.hold.lastChange === other.hold.lastChange);
|
|
287
323
|
}
|
|
@@ -333,7 +369,7 @@ export class Style {
|
|
|
333
369
|
/**
|
|
334
370
|
* The look as a canonical string that round-trips through `Style.parse()`:
|
|
335
371
|
* colours, attributes and the link, which is all a style string can spell.
|
|
336
|
-
* What a style carries beyond its look — `meta`, `anchor`, `hold` —
|
|
372
|
+
* What a style carries beyond its look — `meta`, `anchor`, `boxFrame`, `bidi`, `hold` —
|
|
337
373
|
* has no spelling and is not here, so two styles that differ only there
|
|
338
374
|
* read alike; `equals` tells them apart.
|
|
339
375
|
*/
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a destination does with text direction.
|
|
3
|
+
*
|
|
4
|
+
* Every order above `plain` is a claim about the terminal, and a wrong claim
|
|
5
|
+
* costs more than none. Visual order on a terminal that also reorders reverses
|
|
6
|
+
* the text twice, and no query answers for every such terminal. An isolate on a
|
|
7
|
+
* terminal whose width table predates it advances the cursor (xterm.js 6 does),
|
|
8
|
+
* which moves a table's border and the rows a painter goes back over. So what
|
|
9
|
+
* nothing is known about gets bare text, and `detectTextOrder` grants more only
|
|
10
|
+
* on evidence.
|
|
11
|
+
*/
|
|
12
|
+
import { type Env } from "./env.js";
|
|
13
|
+
/**
|
|
14
|
+
* - `plain`: nothing is known of the destination, or it is not a display: a
|
|
15
|
+
* pipe, a file, an unknown terminal. Characters only, in logical order, with
|
|
16
|
+
* no explicit bidi control.
|
|
17
|
+
* - `logical`: a display known to reorder and to hold the controls at zero
|
|
18
|
+
* width. Rows are sealed in isolates.
|
|
19
|
+
* - `visual`: a display known not to reorder. Rows are written reordered and
|
|
20
|
+
* shaped, with no controls.
|
|
21
|
+
*/
|
|
22
|
+
export type TextOrder = "plain" | "logical" | "visual";
|
|
23
|
+
/** A row's paragraph direction: 0 left to right, 1 right to left. */
|
|
24
|
+
export type ParagraphLevel = 0 | 1;
|
|
25
|
+
/** Cells written for a terminal that reorders them: their row's direction, and what is still open where they begin. */
|
|
26
|
+
export interface LogicalStamp {
|
|
27
|
+
readonly order: "logical";
|
|
28
|
+
readonly level: ParagraphLevel;
|
|
29
|
+
readonly opens: string;
|
|
30
|
+
}
|
|
31
|
+
/** Cells already in visual order, for an export not to reverse again. */
|
|
32
|
+
export interface VisualStamp {
|
|
33
|
+
readonly order: "visual";
|
|
34
|
+
}
|
|
35
|
+
export type BidiStamp = LogicalStamp | VisualStamp;
|
|
36
|
+
/**
|
|
37
|
+
* The order `env` gives evidence for.
|
|
38
|
+
*
|
|
39
|
+
* An environment is inherited: `code .` from Windows Terminal carries
|
|
40
|
+
* `WT_SESSION` into VS Code's xterm.js terminal, and Alacritty clears nothing
|
|
41
|
+
* from GNOME Terminal. `TERM` is the one variable every terminal writes for its
|
|
42
|
+
* own child, so it is read first, and a variable a parent could have left counts
|
|
43
|
+
* only beside the `TERM` its own terminal sets. The rows are in the order that
|
|
44
|
+
* makes that true; a terminal naming itself off this list is `plain`.
|
|
45
|
+
*
|
|
46
|
+
* Not listed, each for a reason: xterm.js lays a row out through the browser and
|
|
47
|
+
* advances for an isolate, and VS Code is xterm.js. WezTerm's bidi is a setting
|
|
48
|
+
* the environment cannot show. iTerm2, Konsole, Terminal.app, mintty and mlterm
|
|
49
|
+
* are unmeasured. Any program may state its order.
|
|
50
|
+
*/
|
|
51
|
+
export declare function detectTextOrder(env: Env, isTTY: boolean): TextOrder;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a destination does with text direction.
|
|
3
|
+
*
|
|
4
|
+
* Every order above `plain` is a claim about the terminal, and a wrong claim
|
|
5
|
+
* costs more than none. Visual order on a terminal that also reorders reverses
|
|
6
|
+
* the text twice, and no query answers for every such terminal. An isolate on a
|
|
7
|
+
* terminal whose width table predates it advances the cursor (xterm.js 6 does),
|
|
8
|
+
* which moves a table's border and the rows a painter goes back over. So what
|
|
9
|
+
* nothing is known about gets bare text, and `detectTextOrder` grants more only
|
|
10
|
+
* on evidence.
|
|
11
|
+
*/
|
|
12
|
+
import { isDumbTerminal } from "./env.js";
|
|
13
|
+
import { pattern } from "./pattern.js";
|
|
14
|
+
// Terminals that paint their own cells and do not reorder, by the `TERM` each
|
|
15
|
+
// writes for its child. Ghostty forces the embedding level to 0
|
|
16
|
+
// (font/shaper/coretext.zig); Alacritty (#663) and foot (#756) run no bidi.
|
|
17
|
+
const VISUAL_TERM = pattern(/^(alacritty|foot|xterm-ghostty)/);
|
|
18
|
+
/**
|
|
19
|
+
* The order `env` gives evidence for.
|
|
20
|
+
*
|
|
21
|
+
* An environment is inherited: `code .` from Windows Terminal carries
|
|
22
|
+
* `WT_SESSION` into VS Code's xterm.js terminal, and Alacritty clears nothing
|
|
23
|
+
* from GNOME Terminal. `TERM` is the one variable every terminal writes for its
|
|
24
|
+
* own child, so it is read first, and a variable a parent could have left counts
|
|
25
|
+
* only beside the `TERM` its own terminal sets. The rows are in the order that
|
|
26
|
+
* makes that true; a terminal naming itself off this list is `plain`.
|
|
27
|
+
*
|
|
28
|
+
* Not listed, each for a reason: xterm.js lays a row out through the browser and
|
|
29
|
+
* advances for an isolate, and VS Code is xterm.js. WezTerm's bidi is a setting
|
|
30
|
+
* the environment cannot show. iTerm2, Konsole, Terminal.app, mintty and mlterm
|
|
31
|
+
* are unmeasured. Any program may state its order.
|
|
32
|
+
*/
|
|
33
|
+
export function detectTextOrder(env, isTTY) {
|
|
34
|
+
if (!isTTY || isDumbTerminal(env))
|
|
35
|
+
return "plain";
|
|
36
|
+
if (env["TMUX"] || env["STY"] || env["ZELLIJ"] || env["INSIDE_EMACS"] || env["NVIM"] || env["VIM_TERMINAL"])
|
|
37
|
+
return "plain";
|
|
38
|
+
if ((env["TERM"] ?? "").match(VISUAL_TERM) !== null)
|
|
39
|
+
return "visual";
|
|
40
|
+
if (env["TERM_PROGRAM"] || env["TERMINAL_EMULATOR"] || env["KONSOLE_VERSION"])
|
|
41
|
+
return "plain";
|
|
42
|
+
// VTE reorders, and takes the controls at zero width past column 0
|
|
43
|
+
// (`src/unicode-width.hh`, `Terminal::insert_char`).
|
|
44
|
+
if (env["VTE_VERSION"] && env["TERM"] === "xterm-256color")
|
|
45
|
+
return "logical";
|
|
46
|
+
// Windows Terminal (microsoft/terminal#538): a console there has no `TERM`, WSL has this one.
|
|
47
|
+
if (env["WT_SESSION"] && (env["TERM"] === undefined || env["TERM"] === "xterm-256color"))
|
|
48
|
+
return "visual";
|
|
49
|
+
return "plain";
|
|
50
|
+
}
|
package/dist/core/text.d.ts
CHANGED
|
@@ -5,6 +5,13 @@ import { Segment } from "./segment.js";
|
|
|
5
5
|
import { Style, type TextStyle } from "./style.js";
|
|
6
6
|
import type { Renderable, Measurable, OverflowMethod, RenderOptions } from "./protocol.js";
|
|
7
7
|
export declare function stripControlChars(text: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* `text` as a Text's characters hold it: stripped, every line ending an LF, and
|
|
10
|
+
* every control its picture. One code unit for one, so an offset into stripped
|
|
11
|
+
* text holds, and every reader of a Text finds one line break without having to
|
|
12
|
+
* know the others.
|
|
13
|
+
*/
|
|
14
|
+
export declare function sanitized(text: string): string;
|
|
8
15
|
/**
|
|
9
16
|
* The style a stored `TextStyle` stands for in this render.
|
|
10
17
|
*
|
|
@@ -35,9 +42,12 @@ export interface RichTextOptions {
|
|
|
35
42
|
export declare class RichText implements Renderable, Measurable {
|
|
36
43
|
private readonly _text;
|
|
37
44
|
private readonly _spans;
|
|
45
|
+
private readonly _closure;
|
|
38
46
|
private readonly _format;
|
|
39
47
|
private readonly _tabSize;
|
|
40
48
|
constructor(text?: string, options?: RichTextOptions);
|
|
49
|
+
/** The part of the text whose direction state nothing has closed. */
|
|
50
|
+
private _unclosed;
|
|
41
51
|
private _reformat;
|
|
42
52
|
get plain(): string;
|
|
43
53
|
set plain(value: string);
|