@promptctl/rich-js 0.27.0 → 0.29.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.
Files changed (185) hide show
  1. package/README.md +7 -6
  2. package/THIRD-PARTY-NOTICES +42 -0
  3. package/dist/core/animation.d.ts +65 -46
  4. package/dist/core/animation.js +91 -67
  5. package/dist/core/ansi.d.ts +19 -12
  6. package/dist/core/ansi.js +114 -36
  7. package/dist/core/bidi-data.d.ts +14 -0
  8. package/dist/core/bidi-data.js +385 -0
  9. package/dist/core/bidi.d.ts +89 -0
  10. package/dist/core/bidi.js +615 -0
  11. package/dist/core/box.d.ts +9 -1
  12. package/dist/core/box.js +11 -0
  13. package/dist/core/cell-widths.d.ts +3 -2
  14. package/dist/core/cell-widths.js +93 -93
  15. package/dist/core/cells.d.ts +20 -0
  16. package/dist/core/cells.js +94 -6
  17. package/dist/core/clock.d.ts +1 -1
  18. package/dist/core/color.d.ts +56 -57
  19. package/dist/core/color.js +79 -81
  20. package/dist/core/console.d.ts +24 -7
  21. package/dist/core/console.js +27 -8
  22. package/dist/core/control-codes.d.ts +61 -0
  23. package/dist/core/control-codes.js +157 -0
  24. package/dist/core/easing.d.ts +40 -40
  25. package/dist/core/easing.js +40 -40
  26. package/dist/core/env.d.ts +6 -0
  27. package/dist/core/env.js +9 -1
  28. package/dist/core/export-html.js +5 -4
  29. package/dist/core/export-lines.d.ts +13 -11
  30. package/dist/core/export-lines.js +19 -15
  31. package/dist/core/export-svg.d.ts +3 -3
  32. package/dist/core/export-svg.js +10 -9
  33. package/dist/core/json.js +9 -2
  34. package/dist/core/markup.js +5 -4
  35. package/dist/core/memo.d.ts +1 -1
  36. package/dist/core/memo.js +1 -1
  37. package/dist/core/oklch.d.ts +14 -14
  38. package/dist/core/oklch.js +10 -10
  39. package/dist/core/orient.d.ts +68 -0
  40. package/dist/core/orient.js +224 -0
  41. package/dist/core/osc8.d.ts +12 -5
  42. package/dist/core/osc8.js +14 -7
  43. package/dist/core/paint.d.ts +9 -9
  44. package/dist/core/paint.js +3 -3
  45. package/dist/core/pretty.d.ts +1 -1
  46. package/dist/core/pretty.js +23 -19
  47. package/dist/core/protocol.d.ts +47 -26
  48. package/dist/core/protocol.js +31 -23
  49. package/dist/core/render.d.ts +19 -14
  50. package/dist/core/render.js +30 -17
  51. package/dist/core/seal.d.ts +26 -0
  52. package/dist/core/seal.js +75 -0
  53. package/dist/core/segment.d.ts +12 -4
  54. package/dist/core/segment.js +16 -6
  55. package/dist/core/strip.d.ts +83 -83
  56. package/dist/core/strip.js +70 -70
  57. package/dist/core/style.d.ts +46 -18
  58. package/dist/core/style.js +92 -56
  59. package/dist/core/text-order.d.ts +51 -0
  60. package/dist/core/text-order.js +50 -0
  61. package/dist/core/text.d.ts +12 -2
  62. package/dist/core/text.js +136 -90
  63. package/dist/core/timeline.d.ts +5 -5
  64. package/dist/core/timeline.js +11 -11
  65. package/dist/core/tween.d.ts +6 -6
  66. package/dist/core/tween.js +10 -10
  67. package/dist/core/view.d.ts +5 -5
  68. package/dist/core/view.js +15 -14
  69. package/dist/core/wrap.d.ts +24 -6
  70. package/dist/core/wrap.js +82 -16
  71. package/dist/host/app.d.ts +50 -9
  72. package/dist/host/app.js +120 -15
  73. package/dist/host/host-environment.d.ts +2 -2
  74. package/dist/host/host-environment.js +2 -2
  75. package/dist/host/index.d.ts +3 -2
  76. package/dist/host/index.js +1 -0
  77. package/dist/host/input.d.ts +42 -9
  78. package/dist/host/input.js +91 -7
  79. package/dist/host/terminal-colors.d.ts +18 -0
  80. package/dist/host/terminal-colors.js +46 -0
  81. package/dist/host/terminal-host.d.ts +4 -3
  82. package/dist/host/terminal-host.js +1 -1
  83. package/dist/index.d.ts +17 -16
  84. package/dist/index.js +11 -11
  85. package/dist/node/prompt.d.ts +1 -1
  86. package/dist/node/prompt.js +1 -1
  87. package/dist/node/terminal-host.d.ts +1 -1
  88. package/dist/node/traceback.js +2 -2
  89. package/dist/renderables/change.d.ts +1 -3
  90. package/dist/renderables/change.js +6 -8
  91. package/dist/renderables/effect.d.ts +88 -87
  92. package/dist/renderables/effect.js +124 -114
  93. package/dist/renderables/effects.d.ts +105 -76
  94. package/dist/renderables/effects.js +309 -176
  95. package/dist/renderables/embed.d.ts +1 -1
  96. package/dist/renderables/embed.js +2 -2
  97. package/dist/renderables/flexStrip.d.ts +7 -7
  98. package/dist/renderables/flexStrip.js +15 -15
  99. package/dist/renderables/frames.d.ts +7 -16
  100. package/dist/renderables/frames.js +12 -27
  101. package/dist/renderables/glyph-effect.d.ts +22 -23
  102. package/dist/renderables/glyph-effect.js +43 -41
  103. package/dist/renderables/glyph-effects.d.ts +33 -14
  104. package/dist/renderables/glyph-effects.js +90 -22
  105. package/dist/renderables/html-entities.d.ts +1 -1
  106. package/dist/renderables/html-entities.js +1 -1
  107. package/dist/renderables/lifecycle.d.ts +8 -10
  108. package/dist/renderables/lifecycle.js +17 -18
  109. package/dist/renderables/live.d.ts +2 -2
  110. package/dist/renderables/live.js +7 -7
  111. package/dist/renderables/markdown-parse.js +3 -3
  112. package/dist/renderables/markdown.js +4 -2
  113. package/dist/renderables/panel.js +7 -5
  114. package/dist/renderables/progress.d.ts +1 -1
  115. package/dist/renderables/progress.js +1 -1
  116. package/dist/renderables/progressBar.d.ts +3 -3
  117. package/dist/renderables/progressBar.js +21 -18
  118. package/dist/renderables/prompt.d.ts +1 -1
  119. package/dist/renderables/scroll.d.ts +1 -1
  120. package/dist/renderables/scroll.js +3 -3
  121. package/dist/renderables/spinner.d.ts +4 -4
  122. package/dist/renderables/spinner.js +10 -10
  123. package/dist/renderables/syntax.d.ts +2 -2
  124. package/dist/renderables/syntax.js +9 -4
  125. package/dist/renderables/table.js +5 -4
  126. package/dist/renderables/traceback.d.ts +1 -1
  127. package/dist/renderables/traceback.js +1 -1
  128. package/dist/renderables/tree.js +3 -3
  129. package/dist/template-bindings/animation-funcs.d.ts +60 -29
  130. package/dist/template-bindings/animation-funcs.js +126 -43
  131. package/dist/template-bindings/color-funcs.d.ts +14 -14
  132. package/dist/template-bindings/color-funcs.js +25 -25
  133. package/dist/template-bindings/index.d.ts +16 -14
  134. package/dist/template-bindings/index.js +17 -14
  135. package/dist/template-bindings/palette-funcs.d.ts +2 -2
  136. package/dist/template-bindings/palette-funcs.js +4 -4
  137. package/dist/template-bindings/style-funcs.d.ts +5 -5
  138. package/dist/template-bindings/style-funcs.js +16 -16
  139. package/dist/themes/{transpose.d.ts → adjust.d.ts} +23 -23
  140. package/dist/themes/{transpose.js → adjust.js} +28 -28
  141. package/dist/themes/buildPalette.d.ts +6 -5
  142. package/dist/themes/buildPalette.js +22 -22
  143. package/dist/themes/colorMath.d.ts +16 -16
  144. package/dist/themes/colorMath.js +32 -32
  145. package/dist/themes/data/ansi-tables.d.ts +2 -2
  146. package/dist/themes/data/ansi-tables.js +2 -2
  147. package/dist/themes/data/index.js +1 -4
  148. package/dist/themes/data/textual-ansi.js +0 -143
  149. package/dist/themes/data/types.d.ts +4 -4
  150. package/dist/themes/data/types.js +2 -2
  151. package/dist/themes/palette.d.ts +12 -12
  152. package/dist/themes/palette.js +16 -16
  153. package/dist/themes/ramp.d.ts +17 -17
  154. package/dist/themes/ramp.js +12 -12
  155. package/dist/themes/registry.d.ts +6 -8
  156. package/dist/themes/registry.js +6 -8
  157. package/dist/themes/terminalThemes.d.ts +4 -11
  158. package/dist/themes/terminalThemes.js +31 -47
  159. package/dist/widgets/button.d.ts +3 -3
  160. package/dist/widgets/button.js +8 -8
  161. package/dist/widgets/checkbox.d.ts +2 -2
  162. package/dist/widgets/checkbox.js +4 -4
  163. package/dist/widgets/dropdown.d.ts +3 -3
  164. package/dist/widgets/dropdown.js +7 -5
  165. package/dist/widgets/event-router.d.ts +6 -6
  166. package/dist/widgets/event-router.js +2 -2
  167. package/dist/widgets/focus-manager.d.ts +3 -3
  168. package/dist/widgets/focus-manager.js +3 -3
  169. package/dist/widgets/index.d.ts +2 -2
  170. package/dist/widgets/index.js +1 -1
  171. package/dist/widgets/ink.d.ts +3 -3
  172. package/dist/widgets/ink.js +4 -4
  173. package/dist/widgets/slider.d.ts +2 -2
  174. package/dist/widgets/static-item.d.ts +2 -2
  175. package/dist/widgets/text-input.d.ts +2 -2
  176. package/dist/widgets/text-input.js +5 -5
  177. package/dist/widgets/themed-widget.js +4 -4
  178. package/dist/widgets/toggle.d.ts +4 -4
  179. package/dist/widgets/toggle.js +6 -6
  180. package/dist/widgets/types.d.ts +6 -6
  181. package/dist/widgets/types.js +2 -2
  182. package/dist/widgets/widget-app.d.ts +10 -10
  183. package/dist/widgets/widget-app.js +8 -8
  184. package/dist/widgets/widget-base.d.ts +2 -2
  185. package/package.json +5 -4
@@ -6,7 +6,7 @@ import { shiftAnchor } from "./anchor.js";
6
6
  import { Memo } from "./memo.js";
7
7
  import { pattern } from "./pattern.js";
8
8
  // [LAW:one-way-deps] `core/style` depends only on `core/color`. The substrate
9
- // a translucent colour is flattened over is `SURFACE_BLACK`, defined beside
9
+ // a translucent color is flattened over is `SURFACE_BLACK`, defined beside
10
10
  // `compositeOver`.
11
11
  // --- Attribute definitions ---
12
12
  /**
@@ -21,11 +21,11 @@ export const ATTRIBUTE_NAMES = [
21
21
  "italic",
22
22
  "underline",
23
23
  "blink",
24
- "blink2",
24
+ "rapidBlink",
25
25
  "reverse",
26
26
  "conceal",
27
27
  "strike",
28
- "underline2",
28
+ "doubleUnderline",
29
29
  "frame",
30
30
  "encircle",
31
31
  "overline",
@@ -37,11 +37,11 @@ export const ATTRIBUTE_SGR = {
37
37
  italic: 3,
38
38
  underline: 4,
39
39
  blink: 5,
40
- blink2: 6,
40
+ rapidBlink: 6,
41
41
  reverse: 7,
42
42
  conceal: 8,
43
43
  strike: 9,
44
- underline2: 21,
44
+ doubleUnderline: 21,
45
45
  frame: 51,
46
46
  encircle: 52,
47
47
  overline: 53,
@@ -60,10 +60,14 @@ export const ATTRIBUTE_SHORT_ALIASES = {
60
60
  s: "strike",
61
61
  r: "reverse",
62
62
  o: "overline",
63
- uu: "underline2",
63
+ uu: "doubleUnderline",
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 = { validFrom: Number.NEGATIVE_INFINITY, validUntil: Number.POSITIVE_INFINITY };
67
+ /** The moments both `a` and `b` hold through: the later `validFrom` and the sooner `validUntil`. */
68
+ export function intersection(a, b) {
69
+ return { validFrom: Math.max(a.validFrom, b.validFrom), validUntil: Math.min(a.validUntil, b.validUntil) };
70
+ }
67
71
  export class StyleSyntaxError extends Error {
68
72
  constructor(message, options) {
69
73
  super(message, options);
@@ -81,11 +85,11 @@ export class Style {
81
85
  italic;
82
86
  underline;
83
87
  blink;
84
- blink2;
88
+ rapidBlink;
85
89
  reverse;
86
90
  conceal;
87
91
  strike;
88
- underline2;
92
+ doubleUnderline;
89
93
  frame;
90
94
  encircle;
91
95
  overline;
@@ -97,32 +101,49 @@ 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
- * frame: since when what they show has been what it is (`lastChange`), and
103
- * when it next changes (`nextChange`) — both `t` for a cell drawn by
123
+ * frame: since when what they show has been what it is (`validFrom`), and
124
+ * when it next changes (`validUntil`) — both `t` for a cell drawn by
104
125
  * something that moves every frame, and `-Infinity`/`Infinity` for one
105
126
  * that never changes. It draws nothing. Whatever draws for a handed time
106
127
  * stamps its cells with it (`placedRender`, `Effected`), the stamp rides up
107
128
  * through every container as the anchor does (`./anchor.ts`), and whoever
108
- * holds the composed frame reads it off (`Segment.nextChange`, and
109
- * `Segment.lastChange` for a host running its timeline backwards), so a
129
+ * holds the composed frame reads it off (`Segment.validUntil`, and
130
+ * `Segment.validFrom` for a host running its timeline backwards), so a
110
131
  * host knows when to draw again without asking each container. Two styles
111
132
  * laid together are in the intersection of their holds.
112
133
  */
113
- hold;
134
+ validity;
114
135
  constructor(options = {}) {
115
- const hold = options.hold ?? NEVER_CHANGES;
116
- const { lastChange, nextChange } = hold;
136
+ const validity = options.validity ?? NEVER_CHANGES;
137
+ const { validFrom, validUntil } = validity;
117
138
  // [LAW:parse-dont-validate] A time or never, settled where the style is
118
139
  // made: NaN and the far infinity fail here, so no reader of a frame meets one.
119
- if (!(nextChange > Number.NEGATIVE_INFINITY)) {
120
- throw new RangeError(`Style nextChange must be a time in seconds or Infinity, got ${nextChange}`);
140
+ if (!(validUntil > Number.NEGATIVE_INFINITY)) {
141
+ throw new RangeError(`Style validUntil must be a time in seconds or Infinity, got ${validUntil}`);
121
142
  }
122
- if (!(lastChange < Number.POSITIVE_INFINITY)) {
123
- throw new RangeError(`Style lastChange must be a time in seconds or -Infinity, got ${lastChange}`);
143
+ if (!(validFrom < Number.POSITIVE_INFINITY)) {
144
+ throw new RangeError(`Style validFrom must be a time in seconds or -Infinity, got ${validFrom}`);
124
145
  }
125
- this.hold = hold;
146
+ this.validity = validity;
126
147
  this.color = resolveColor(options.color);
127
148
  this.bgcolor = resolveBackground(options.bgcolor);
128
149
  this.bold = options.bold;
@@ -130,11 +151,11 @@ export class Style {
130
151
  this.italic = options.italic;
131
152
  this.underline = options.underline;
132
153
  this.blink = options.blink;
133
- this.blink2 = options.blink2;
154
+ this.rapidBlink = options.rapidBlink;
134
155
  this.reverse = options.reverse;
135
156
  this.conceal = options.conceal;
136
157
  this.strike = options.strike;
137
- this.underline2 = options.underline2;
158
+ this.doubleUnderline = options.doubleUnderline;
138
159
  this.frame = options.frame;
139
160
  this.encircle = options.encircle;
140
161
  this.overline = options.overline;
@@ -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 &&
@@ -151,19 +174,21 @@ export class Style {
151
174
  this.italic === undefined &&
152
175
  this.underline === undefined &&
153
176
  this.blink === undefined &&
154
- this.blink2 === undefined &&
177
+ this.rapidBlink === undefined &&
155
178
  this.reverse === undefined &&
156
179
  this.conceal === undefined &&
157
180
  this.strike === undefined &&
158
- this.underline2 === undefined &&
181
+ this.doubleUnderline === undefined &&
159
182
  this.frame === undefined &&
160
183
  this.encircle === undefined &&
161
184
  this.overline === undefined &&
162
185
  this.link === undefined &&
163
186
  this.meta === undefined &&
164
187
  this.anchor === undefined &&
165
- this.hold.nextChange === Number.POSITIVE_INFINITY &&
166
- this.hold.lastChange === Number.NEGATIVE_INFINITY);
188
+ this.boxFrame === undefined &&
189
+ this.bidi === undefined &&
190
+ this.validity.validUntil === Number.POSITIVE_INFINITY &&
191
+ this.validity.validFrom === Number.NEGATIVE_INFINITY);
167
192
  }
168
193
  get transparentBackground() {
169
194
  return this.bgcolor === undefined || this.bgcolor.isDefault;
@@ -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.
@@ -210,16 +243,16 @@ export class Style {
210
243
  */
211
244
  withAttributes(attributes) {
212
245
  // Only the attribute names are read: a wider object — a `Style`, say —
213
- // satisfies the type and must not carry its colours or anchor in with it.
246
+ // satisfies the type and must not carry its colors or anchor in with it.
214
247
  return this.with(Object.fromEntries(ATTRIBUTE_NAMES.filter((name) => name in attributes).map((name) => [name, attributes[name]])));
215
248
  }
216
249
  /**
217
250
  * This style with its motion taken out, what reduced motion draws: `blink`
218
- * and `blink2`, the motion a terminal makes on its own, unset, and every
251
+ * and `rapidBlink`, the motion a terminal makes on its own, unset, and every
219
252
  * other field as it was. A style that does not blink is its own.
220
253
  */
221
254
  reduced() {
222
- return this.blink || this.blink2 ? this.withAttributes({ blink: undefined, blink2: undefined }) : this;
255
+ return this.blink || this.rapidBlink ? this.withAttributes({ blink: undefined, rapidBlink: undefined }) : this;
223
256
  }
224
257
  /**
225
258
  * This style on the cell `cells` to the right: its anchor shifted along its
@@ -244,11 +277,11 @@ export class Style {
244
277
  italic: other.italic ?? this.italic,
245
278
  underline: other.underline ?? this.underline,
246
279
  blink: other.blink ?? this.blink,
247
- blink2: other.blink2 ?? this.blink2,
280
+ rapidBlink: other.rapidBlink ?? this.rapidBlink,
248
281
  reverse: other.reverse ?? this.reverse,
249
282
  conceal: other.conceal ?? this.conceal,
250
283
  strike: other.strike ?? this.strike,
251
- underline2: other.underline2 ?? this.underline2,
284
+ doubleUnderline: other.doubleUnderline ?? this.doubleUnderline,
252
285
  frame: other.frame ?? this.frame,
253
286
  encircle: other.encircle ?? this.encircle,
254
287
  overline: other.overline ?? this.overline,
@@ -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
- // Cells under both change when either does: the sooner, and have been
261
- // what they are since the later — the intersection of the two holds.
262
- hold: { lastChange: Math.max(this.hold.lastChange, other.hold.lastChange), nextChange: Math.min(this.hold.nextChange, other.hold.nextChange) },
293
+ boxFrame: other.boxFrame ?? this.boxFrame,
294
+ bidi: other.bidi ?? this.bidi,
295
+ // Cells under both change when either does.
296
+ validity: intersection(this.validity, other.validity),
263
297
  });
264
298
  }
265
299
  equals(other) {
@@ -272,21 +306,23 @@ export class Style {
272
306
  this.italic === other.italic &&
273
307
  this.underline === other.underline &&
274
308
  this.blink === other.blink &&
275
- this.blink2 === other.blink2 &&
309
+ this.rapidBlink === other.rapidBlink &&
276
310
  this.reverse === other.reverse &&
277
311
  this.conceal === other.conceal &&
278
312
  this.strike === other.strike &&
279
- this.underline2 === other.underline2 &&
313
+ this.doubleUnderline === other.doubleUnderline &&
280
314
  this.frame === other.frame &&
281
315
  this.encircle === other.encircle &&
282
316
  this.overline === other.overline &&
283
317
  this.link === other.link &&
284
318
  anchorsEqual(this.anchor, other.anchor) &&
285
- this.hold.nextChange === other.hold.nextChange &&
286
- this.hold.lastChange === other.hold.lastChange);
319
+ this.boxFrame === other.boxFrame &&
320
+ this.bidi === other.bidi &&
321
+ this.validity.validUntil === other.validity.validUntil &&
322
+ this.validity.validFrom === other.validity.validFrom);
287
323
  }
288
324
  /**
289
- * The colours this style puts on screen at `colorSystem`: each flattened
325
+ * The colors this style puts on screen at `colorSystem`: each flattened
290
326
  * onto what lies beneath it — the background onto the terminal's black, the
291
327
  * foreground onto that background — then downgraded to the depth. The one
292
328
  * account of what a style draws: `toSgrCodes` encodes exactly these, and a
@@ -332,8 +368,8 @@ export class Style {
332
368
  }
333
369
  /**
334
370
  * The look as a canonical string that round-trips through `Style.parse()`:
335
- * colours, attributes and the link, which is all a style string can spell.
336
- * What a style carries beyond its look — `meta`, `anchor`, `hold` —
371
+ * colors, attributes and the link, which is all a style string can spell.
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
  */
@@ -480,7 +516,7 @@ export class Theme {
480
516
  // --- Parse helpers ---
481
517
  const ATTRIBUTE_SET = new Set(ATTRIBUTE_NAMES);
482
518
  // The words each position of a definition can hold: a bare token is a keyword,
483
- // an attribute or a colour name; `on` takes a colour name; `not` an attribute.
519
+ // an attribute or a color name; `on` takes a color name; `not` an attribute.
484
520
  // The short aliases are left out, since `b` is no help to someone who typed
485
521
  // `bolt` and a one-letter name sits one edit from every other short word.
486
522
  // [LAW:one-source-of-truth] Derived from the lists the parser accepts from; the
@@ -512,8 +548,8 @@ function editDistance(a, b) {
512
548
  * Plausible means within one edit per three letters, at least one, which lets
513
549
  * `rd` reach `red` and keeps `hello` from reaching `yellow`. Every name tied
514
550
  * for nearest is offered: `gold` is one edit from `bold` and from three
515
- * `goldN` colours, and choosing among them would be a guess the reader cannot
516
- * tell from a finding. Case is not an edit, because the colour parser ignores
551
+ * `goldN` colors, and choosing among them would be a guess the reader cannot
552
+ * tell from a finding. Case is not an edit, because the color parser ignores
517
553
  * it: `Cyna` is as near `cyan` as `cyna` is.
518
554
  *
519
555
  * This runs on every render that meets the bad style, since only successful
@@ -535,12 +571,12 @@ function nearMiss(word, words) {
535
571
  return names.length === 0 ? "" : ` (did you mean ${names.join(" or ")}?)`;
536
572
  }
537
573
  /**
538
- * A style token read as a colour. The colour parser's own error is the reason
574
+ * A style token read as a color. The color parser's own error is the reason
539
575
  * the token failed, so it rides along twice: in the message, for a reader with
540
576
  * only the text, and as `cause`, for code that wants the original error.
541
577
  *
542
- * [LAW:single-enforcer] Both colour positions in a definition rewrap here, and
543
- * the near miss is named here rather than by the colour parser: only a
578
+ * [LAW:single-enforcer] Both color positions in a definition rewrap here, and
579
+ * the near miss is named here rather than by the color parser: only a
544
580
  * definition knows whether the token could also have been an attribute.
545
581
  */
546
582
  function parseStyleColor(token, failure, words) {
@@ -619,7 +655,7 @@ function resolveColor(c) {
619
655
  // so it is no background: a style added on top of another keeps that one's,
620
656
  // which is what Textual's `#00000000` means. A transparent foreground is not
621
657
  // the same, it draws its glyph in whatever lies beneath it — the background,
622
- // or `SURFACE_BLACK` when there is none — so it stays a colour.
658
+ // or `SURFACE_BLACK` when there is none — so it stays a color.
623
659
  function resolveBackground(c) {
624
660
  const spec = resolveColor(c);
625
661
  return spec?.value?.alpha === 0 ? undefined : spec;
@@ -636,11 +672,11 @@ export const DEFAULT_STYLES = {
636
672
  italic: false,
637
673
  underline: false,
638
674
  blink: false,
639
- blink2: false,
675
+ rapidBlink: false,
640
676
  reverse: false,
641
677
  conceal: false,
642
678
  strike: false,
643
- underline2: false,
679
+ doubleUnderline: false,
644
680
  frame: false,
645
681
  encircle: false,
646
682
  overline: false,
@@ -650,11 +686,11 @@ export const DEFAULT_STYLES = {
650
686
  italic: new Style({ italic: true }),
651
687
  underline: new Style({ underline: true }),
652
688
  blink: new Style({ blink: true }),
653
- blink2: new Style({ blink2: true }),
689
+ rapidBlink: new Style({ rapidBlink: true }),
654
690
  reverse: new Style({ reverse: true }),
655
691
  conceal: new Style({ conceal: true }),
656
692
  strike: new Style({ strike: true }),
657
- underline2: new Style({ underline2: true }),
693
+ doubleUnderline: new Style({ doubleUnderline: true }),
658
694
  frame: new Style({ frame: true }),
659
695
  encircle: new Style({ encircle: true }),
660
696
  overline: new Style({ overline: true }),
@@ -719,9 +755,9 @@ export const DEFAULT_STYLES = {
719
755
  "syntax.number": Style.parse("cyan"),
720
756
  "syntax.comment": Style.parse("dim italic"),
721
757
  "syntax.line_number": Style.parse("dim"),
722
- // Ink and ground both from the fixed 256-colour greys: the terminal theme's
758
+ // Ink and ground both from the fixed 256-color greys: the terminal theme's
723
759
  // own foreground is dark on a light theme and would vanish on grey27. At 16
724
- // colours `Syntax` draws it without the pair (`groundedStyle`).
760
+ // colors `Syntax` draws it without the pair (`groundedStyle`).
725
761
  "syntax.line_number.highlight": Style.parse("bold grey93 on grey27"),
726
762
  // Markdown
727
763
  "markdown.h1": Style.parse("bold underline"),
@@ -731,7 +767,7 @@ export const DEFAULT_STYLES = {
731
767
  "markdown.h5": Style.parse("italic"),
732
768
  "markdown.h6": Style.parse("dim"),
733
769
  // A fixed cyan, not the theme's: `cyan` is dark enough on some themes to
734
- // sink into grey11. Bold, as Rich's is: at 16 colours a renderable draws it
770
+ // sink into grey11. Bold, as Rich's is: at 16 colors a renderable draws it
735
771
  // without its pair (`groundedStyle`), and bold is what still marks code.
736
772
  "markdown.code": Style.parse("bold dark_turquoise on grey11"),
737
773
  "markdown.em": Style.parse("italic"),
@@ -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
+ }
@@ -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);
@@ -75,8 +85,8 @@ export declare class RichText implements Renderable, Measurable {
75
85
  * any spans covering the leftmost (side="left") or rightmost (side="right")
76
86
  * character.
77
87
  *
78
- * [LAW:locality-or-seam] Used by `Joiner`s to paint the transition between
79
- * adjacent `Strip` items. Joiners only ever need the column adjacent to
88
+ * [LAW:locality-or-seam] Used by `Separator`s to paint the transition between
89
+ * adjacent `Strip` items. Separators only ever need the column adjacent to
80
90
  * them, so the cell type exposes that column rather than constraining its
81
91
  * interior to be uniform. For RichText with uniform styling, both edges
82
92
  * return the same style; for RichText with edge variation, each edge