@promptctl/rich-js 0.16.0 → 0.18.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 (206) hide show
  1. package/README.md +14 -27
  2. package/package.json +7 -3
  3. package/dist/core/box.d.ts +0 -144
  4. package/dist/core/box.js +0 -391
  5. package/dist/core/cells.d.ts +0 -148
  6. package/dist/core/cells.js +0 -266
  7. package/dist/core/color.d.ts +0 -229
  8. package/dist/core/color.js +0 -992
  9. package/dist/core/console.d.ts +0 -142
  10. package/dist/core/console.js +0 -477
  11. package/dist/core/emoji.d.ts +0 -24
  12. package/dist/core/emoji.js +0 -391
  13. package/dist/core/export-html.d.ts +0 -24
  14. package/dist/core/export-html.js +0 -106
  15. package/dist/core/export-lines.d.ts +0 -121
  16. package/dist/core/export-lines.js +0 -141
  17. package/dist/core/highlighter.d.ts +0 -33
  18. package/dist/core/highlighter.js +0 -124
  19. package/dist/core/json.d.ts +0 -27
  20. package/dist/core/json.js +0 -53
  21. package/dist/core/markup.d.ts +0 -97
  22. package/dist/core/markup.js +0 -619
  23. package/dist/core/measure.d.ts +0 -27
  24. package/dist/core/measure.js +0 -61
  25. package/dist/core/oklch.d.ts +0 -161
  26. package/dist/core/oklch.js +0 -312
  27. package/dist/core/osc8.d.ts +0 -68
  28. package/dist/core/osc8.js +0 -90
  29. package/dist/core/pretty.d.ts +0 -141
  30. package/dist/core/pretty.js +0 -472
  31. package/dist/core/protocol.d.ts +0 -137
  32. package/dist/core/protocol.js +0 -101
  33. package/dist/core/render.d.ts +0 -69
  34. package/dist/core/render.js +0 -140
  35. package/dist/core/segment.d.ts +0 -142
  36. package/dist/core/segment.js +0 -433
  37. package/dist/core/spinnerData.d.ts +0 -10
  38. package/dist/core/spinnerData.js +0 -115
  39. package/dist/core/strip.d.ts +0 -139
  40. package/dist/core/strip.js +0 -302
  41. package/dist/core/style.d.ts +0 -149
  42. package/dist/core/style.js +0 -741
  43. package/dist/core/subscription.d.ts +0 -12
  44. package/dist/core/subscription.js +0 -12
  45. package/dist/core/text.d.ts +0 -255
  46. package/dist/core/text.js +0 -990
  47. package/dist/core/wrap.d.ts +0 -41
  48. package/dist/core/wrap.js +0 -127
  49. package/dist/host/host-stream.d.ts +0 -30
  50. package/dist/host/host-stream.js +0 -35
  51. package/dist/host/index.d.ts +0 -3
  52. package/dist/host/index.js +0 -2
  53. package/dist/host/terminal-host.d.ts +0 -143
  54. package/dist/host/terminal-host.js +0 -132
  55. package/dist/index.d.ts +0 -87
  56. package/dist/index.js +0 -104
  57. package/dist/node/prompt.d.ts +0 -21
  58. package/dist/node/prompt.js +0 -30
  59. package/dist/node/save.d.ts +0 -17
  60. package/dist/node/save.js +0 -21
  61. package/dist/node/terminal-host.d.ts +0 -84
  62. package/dist/node/terminal-host.js +0 -160
  63. package/dist/node/traceback.d.ts +0 -27
  64. package/dist/node/traceback.js +0 -112
  65. package/dist/renderables/align.d.ts +0 -16
  66. package/dist/renderables/align.js +0 -46
  67. package/dist/renderables/columns.d.ts +0 -61
  68. package/dist/renderables/columns.js +0 -168
  69. package/dist/renderables/constrain.d.ts +0 -15
  70. package/dist/renderables/constrain.js +0 -37
  71. package/dist/renderables/flexStrip.d.ts +0 -41
  72. package/dist/renderables/flexStrip.js +0 -200
  73. package/dist/renderables/group.d.ts +0 -11
  74. package/dist/renderables/group.js +0 -15
  75. package/dist/renderables/layout.d.ts +0 -92
  76. package/dist/renderables/layout.js +0 -315
  77. package/dist/renderables/live.d.ts +0 -47
  78. package/dist/renderables/live.js +0 -131
  79. package/dist/renderables/markdown.d.ts +0 -24
  80. package/dist/renderables/markdown.js +0 -182
  81. package/dist/renderables/padding.d.ts +0 -38
  82. package/dist/renderables/padding.js +0 -140
  83. package/dist/renderables/panel.d.ts +0 -116
  84. package/dist/renderables/panel.js +0 -322
  85. package/dist/renderables/progress.d.ts +0 -90
  86. package/dist/renderables/progress.js +0 -241
  87. package/dist/renderables/progressBar.d.ts +0 -31
  88. package/dist/renderables/progressBar.js +0 -52
  89. package/dist/renderables/prompt.d.ts +0 -49
  90. package/dist/renderables/prompt.js +0 -125
  91. package/dist/renderables/rule.d.ts +0 -24
  92. package/dist/renderables/rule.js +0 -90
  93. package/dist/renderables/spinner.d.ts +0 -29
  94. package/dist/renderables/spinner.js +0 -64
  95. package/dist/renderables/status.d.ts +0 -23
  96. package/dist/renderables/status.js +0 -60
  97. package/dist/renderables/syntax.d.ts +0 -35
  98. package/dist/renderables/syntax.js +0 -114
  99. package/dist/renderables/table.d.ts +0 -182
  100. package/dist/renderables/table.js +0 -697
  101. package/dist/renderables/traceback.d.ts +0 -31
  102. package/dist/renderables/traceback.js +0 -109
  103. package/dist/renderables/tree.d.ts +0 -54
  104. package/dist/renderables/tree.js +0 -159
  105. package/dist/template-bindings/color-funcs.d.ts +0 -87
  106. package/dist/template-bindings/color-funcs.js +0 -219
  107. package/dist/template-bindings/helpers.d.ts +0 -24
  108. package/dist/template-bindings/helpers.js +0 -48
  109. package/dist/template-bindings/index.d.ts +0 -107
  110. package/dist/template-bindings/index.js +0 -144
  111. package/dist/template-bindings/palette-funcs.d.ts +0 -94
  112. package/dist/template-bindings/palette-funcs.js +0 -151
  113. package/dist/template-bindings/style-funcs.d.ts +0 -28
  114. package/dist/template-bindings/style-funcs.js +0 -151
  115. package/dist/themes/buildPalette.d.ts +0 -36
  116. package/dist/themes/buildPalette.js +0 -56
  117. package/dist/themes/colorMath.d.ts +0 -82
  118. package/dist/themes/colorMath.js +0 -254
  119. package/dist/themes/colorRef.d.ts +0 -51
  120. package/dist/themes/colorRef.js +0 -96
  121. package/dist/themes/data/atom-one-dark.d.ts +0 -3
  122. package/dist/themes/data/atom-one-dark.js +0 -158
  123. package/dist/themes/data/atom-one-light.d.ts +0 -3
  124. package/dist/themes/data/atom-one-light.js +0 -158
  125. package/dist/themes/data/catppuccin-frappe.d.ts +0 -3
  126. package/dist/themes/data/catppuccin-frappe.js +0 -158
  127. package/dist/themes/data/catppuccin-latte.d.ts +0 -3
  128. package/dist/themes/data/catppuccin-latte.js +0 -158
  129. package/dist/themes/data/catppuccin-macchiato.d.ts +0 -3
  130. package/dist/themes/data/catppuccin-macchiato.js +0 -158
  131. package/dist/themes/data/catppuccin-mocha.d.ts +0 -3
  132. package/dist/themes/data/catppuccin-mocha.js +0 -158
  133. package/dist/themes/data/cyberpunk.d.ts +0 -17
  134. package/dist/themes/data/cyberpunk.js +0 -191
  135. package/dist/themes/data/default.d.ts +0 -3
  136. package/dist/themes/data/default.js +0 -158
  137. package/dist/themes/data/dracula.d.ts +0 -3
  138. package/dist/themes/data/dracula.js +0 -158
  139. package/dist/themes/data/flexoki.d.ts +0 -3
  140. package/dist/themes/data/flexoki.js +0 -158
  141. package/dist/themes/data/gruvbox.d.ts +0 -3
  142. package/dist/themes/data/gruvbox.js +0 -158
  143. package/dist/themes/data/index.d.ts +0 -35
  144. package/dist/themes/data/index.js +0 -64
  145. package/dist/themes/data/monokai.d.ts +0 -3
  146. package/dist/themes/data/monokai.js +0 -158
  147. package/dist/themes/data/nord.d.ts +0 -3
  148. package/dist/themes/data/nord.js +0 -158
  149. package/dist/themes/data/rose-pine-dawn.d.ts +0 -3
  150. package/dist/themes/data/rose-pine-dawn.js +0 -158
  151. package/dist/themes/data/rose-pine-moon.d.ts +0 -3
  152. package/dist/themes/data/rose-pine-moon.js +0 -158
  153. package/dist/themes/data/rose-pine.d.ts +0 -3
  154. package/dist/themes/data/rose-pine.js +0 -158
  155. package/dist/themes/data/solarized-dark.d.ts +0 -3
  156. package/dist/themes/data/solarized-dark.js +0 -158
  157. package/dist/themes/data/solarized-light.d.ts +0 -3
  158. package/dist/themes/data/solarized-light.js +0 -158
  159. package/dist/themes/data/svg-export.d.ts +0 -3
  160. package/dist/themes/data/svg-export.js +0 -158
  161. package/dist/themes/data/textual-ansi.d.ts +0 -3
  162. package/dist/themes/data/textual-ansi.js +0 -158
  163. package/dist/themes/data/textual-dark.d.ts +0 -3
  164. package/dist/themes/data/textual-dark.js +0 -158
  165. package/dist/themes/data/textual-light.d.ts +0 -3
  166. package/dist/themes/data/textual-light.js +0 -158
  167. package/dist/themes/data/tokyo-night.d.ts +0 -3
  168. package/dist/themes/data/tokyo-night.js +0 -158
  169. package/dist/themes/data/types.d.ts +0 -15
  170. package/dist/themes/data/types.js +0 -1
  171. package/dist/themes/palette.d.ts +0 -21
  172. package/dist/themes/palette.js +0 -28
  173. package/dist/themes/ramp.d.ts +0 -83
  174. package/dist/themes/ramp.js +0 -122
  175. package/dist/themes/registry.d.ts +0 -43
  176. package/dist/themes/registry.js +0 -82
  177. package/dist/themes/terminalThemes.d.ts +0 -54
  178. package/dist/themes/terminalThemes.js +0 -68
  179. package/dist/themes/transpose.d.ts +0 -87
  180. package/dist/themes/transpose.js +0 -138
  181. package/dist/widgets/button.d.ts +0 -49
  182. package/dist/widgets/button.js +0 -206
  183. package/dist/widgets/checkbox.d.ts +0 -41
  184. package/dist/widgets/checkbox.js +0 -155
  185. package/dist/widgets/dropdown.d.ts +0 -67
  186. package/dist/widgets/dropdown.js +0 -453
  187. package/dist/widgets/event-router.d.ts +0 -78
  188. package/dist/widgets/event-router.js +0 -622
  189. package/dist/widgets/focus-manager.d.ts +0 -26
  190. package/dist/widgets/focus-manager.js +0 -193
  191. package/dist/widgets/index.d.ts +0 -22
  192. package/dist/widgets/index.js +0 -15
  193. package/dist/widgets/screen.d.ts +0 -98
  194. package/dist/widgets/screen.js +0 -542
  195. package/dist/widgets/slider.d.ts +0 -57
  196. package/dist/widgets/slider.js +0 -270
  197. package/dist/widgets/static-item.d.ts +0 -40
  198. package/dist/widgets/static-item.js +0 -60
  199. package/dist/widgets/text-input.d.ts +0 -274
  200. package/dist/widgets/text-input.js +0 -1162
  201. package/dist/widgets/toggle.d.ts +0 -44
  202. package/dist/widgets/toggle.js +0 -179
  203. package/dist/widgets/types.d.ts +0 -108
  204. package/dist/widgets/types.js +0 -31
  205. package/dist/widgets/widget-base.d.ts +0 -42
  206. package/dist/widgets/widget-base.js +0 -173
@@ -1,148 +0,0 @@
1
- /**
2
- * Terminal cell width calculation.
3
- * Handles ASCII, CJK (double-width), and emoji characters.
4
- */
5
- /**
6
- * Returns the terminal cell width of a string.
7
- */
8
- export declare function cellLen(text: string): number;
9
- declare const _cellCol: unique symbol;
10
- declare const _codeUnit: unique symbol;
11
- declare const _codePoint: unique symbol;
12
- /** Terminal cell-column offset. Never interchangeable with a code-unit index. */
13
- export type CellCol = number & {
14
- readonly [_cellCol]: true;
15
- };
16
- /** JS string code-unit index. Never interchangeable with a cell-column offset. */
17
- export type CodeUnit = number & {
18
- readonly [_codeUnit]: true;
19
- };
20
- /**
21
- * A `CodeUnit` index that is additionally guaranteed to fall on a Unicode
22
- * code-point boundary (i.e. never inside a surrogate pair). Assignable to
23
- * `CodeUnit`; the reverse assignment is forbidden.
24
- */
25
- export type CodePoint = CodeUnit & {
26
- readonly [_codePoint]: true;
27
- };
28
- /** Brand a raw number as a CellCol. Use only at trust boundaries. */
29
- export declare function asCellCol(n: number): CellCol;
30
- /** Brand a raw number as a CodeUnit. Use only at trust boundaries. */
31
- export declare function asCodeUnit(n: number): CodeUnit;
32
- /** Brand a raw number as a CodePoint. Use only when the value is known to be
33
- * on a Unicode code-point boundary. */
34
- export declare function asCodePoint(n: number): CodePoint;
35
- /**
36
- * [LAW:parse-dont-validate] A caller's number as a count of cells — the checked
37
- * counterpart to `asCellCol`'s unchecked brand, and the one place the rule "a
38
- * cell count is a non-negative integer" is written.
39
- *
40
- * [LAW:one-source-of-truth] Every renderable is handed `RenderOptions.maxWidth`
41
- * as a plain `number`, so every renderable used to answer this for itself and
42
- * they disagreed: at NaN, `Table` collapsed to one cell of garbage, `Columns`
43
- * and `Layout` threw `Invalid array length`, and `RichText` ignored the request
44
- * and emitted its full natural width. Parse at the entry to `render`/`measure`
45
- * and nothing downstream re-asks.
46
- *
47
- * The comparison is the point, not `Math.max(0, n)`: NaN fails every comparison
48
- * and so floors here, where `Math.max` would return it and let it poison every
49
- * bounds check downstream. Integral because a cell is not divisible — a
50
- * fractional budget reaches a largest-remainder division, which grants a whole
51
- * cell against a fractional residue and hands out more width than it was given.
52
- *
53
- * `Infinity` is outside what the comparison floors, so it passes through
54
- * unchanged, and no clamp belongs here: the finite answer to an unbounded offer
55
- * is the renderable's own natural width, which this function cannot see from
56
- * the number alone. `withBoundedWidth` in `protocol.ts` is the second half of
57
- * the parse and the only place that value is resolved.
58
- */
59
- export declare function cellCount(n: number): CellCol;
60
- /**
61
- * Pads or crops a string to exactly `totalWidth` terminal cells.
62
- * Invariant: cellLen(setCellSize(text, n)) === n (unless n is 0)
63
- */
64
- export declare function setCellSize(text: string, totalWidth: CellCol): string;
65
- /**
66
- * Splits text at a cell position. Returns [left, right].
67
- * When the position falls mid-wide-character, the left side is padded
68
- * to reach exactly `position` cells. The wide char remains in the right side.
69
- */
70
- export declare function splitText(text: string, position: CellCol): [string, string];
71
- /**
72
- * Wraps text into lines of at most `maxWidth` cells, preserving every code
73
- * point: `chopCells(t, w).join("") === t`. Unlike `splitText` this never pads,
74
- * so a line ending before a wide glyph is narrower than `maxWidth` rather than
75
- * padded out to it.
76
- *
77
- * A line overflows exactly where the budget cannot be met: a glyph wider than
78
- * `maxWidth` is force-taken whole rather than dropped, and a `maxWidth` of zero
79
- * or less is no budget at all, so the text comes back as one line.
80
- */
81
- export declare function chopCells(text: string, maxWidth: CellCol): string[];
82
- /**
83
- * Returns the largest prefix of `text` whose cell width fits within `cap` cells.
84
- * No padding — the returned string may be narrower than `cap` when the next
85
- * character is wide and would overshoot. Never wider than `cap` cells.
86
- *
87
- * When the first character already exceeds `cap` cells, returns "" (the caller
88
- * must decide whether to force-take the character or skip it).
89
- */
90
- export declare function cellFit(text: string, cap: CellCol): string;
91
- /**
92
- * Returns the largest code-unit end offset starting from `startCU` whose
93
- * prefix (from `startCU`) has cell width ≤ `cap`. Iterates from the given
94
- * offset without slicing the tail, avoiding O(N²) allocation when called
95
- * repeatedly across a long string.
96
- *
97
- * [LAW:types-are-the-program] returns CodePoint because for...of always
98
- * stops on a code-point boundary.
99
- */
100
- export declare function cellFitFrom(text: string, startCU: CodePoint, cap: CellCol): CodePoint;
101
- /**
102
- * Like `cellFitFrom`, but for any `startCU` inside `text` the result is
103
- * strictly past it: a glyph too wide for `cap` is force-taken whole and
104
- * overflows `cap` by its own width. So a `while (i < text.length)` loop driven
105
- * by this terminates by construction, and the re-brand is honest — both
106
- * operands land on code-point boundaries, so their max does too.
107
- *
108
- * [LAW:single-enforcer] `cellFit` documents that its caller must choose between
109
- * force-taking and skipping; this is that choice, made once. It was made twice
110
- * before and the copies drifted — the textarea's soft wrap force-took,
111
- * `chopCells` did neither and hung.
112
- */
113
- export declare function cellStepFrom(text: string, startCU: CodePoint, cap: CellCol): CodePoint;
114
- /**
115
- * Returns the largest code-unit offset into `content` whose prefix has
116
- * cell width ≤ `cellCol`. When `cellCol` falls mid-wide-character the
117
- * function stops before that character (never advances into it).
118
- * Clamps to `content.length` if `cellCol` exceeds the string's total
119
- * cell width.
120
- *
121
- * This is the inverse of `cellLen(content.slice(0, codeUnit))` — given a
122
- * visual column, return the corresponding string index.
123
- *
124
- * Returns `CodePoint` because `for...of` iteration always stops on a
125
- * code-point boundary.
126
- */
127
- export declare function cellColToCodeUnitOffset(content: string, cellCol: CellCol): CodePoint;
128
- /**
129
- * Advance one full Unicode code point from `cu`, returning the code-unit
130
- * offset of the start of the *next* code point. Returns `s.length` when
131
- * already at or past the end.
132
- *
133
- * Handles surrogate pairs: when the code point at `cu` is a supplementary
134
- * character (U+10000…U+10FFFF) it occupies 2 UTF-16 code units, so the
135
- * returned offset advances by 2.
136
- */
137
- export declare function nextCodePoint(s: string, cu: CodeUnit): CodePoint;
138
- /**
139
- * Step back one full Unicode code point from `cu`, returning the code-unit
140
- * offset of the start of the *previous* code point. Returns 0 when already
141
- * at the start.
142
- *
143
- * Handles surrogate pairs: when the code unit at `cu - 1` is a low surrogate
144
- * AND the code unit at `cu - 2` is a high surrogate, steps back 2 code units.
145
- * Unpaired surrogates are treated as 1-CU characters.
146
- */
147
- export declare function prevCodePoint(s: string, cu: CodeUnit): CodePoint;
148
- export {};
@@ -1,266 +0,0 @@
1
- /**
2
- * Terminal cell width calculation.
3
- * Handles ASCII, CJK (double-width), and emoji characters.
4
- */
5
- import stringWidth from "string-width";
6
- // [LAW:one-source-of-truth] string-width is the single authority for cell width
7
- const cellLenCache = new Map();
8
- const CACHE_MAX = 4096;
9
- /**
10
- * Returns the terminal cell width of a string.
11
- */
12
- export function cellLen(text) {
13
- if (text.length === 0)
14
- return 0;
15
- // Fast path for pure ASCII
16
- if (text.length <= 64) {
17
- const cached = cellLenCache.get(text);
18
- if (cached !== undefined)
19
- return cached;
20
- }
21
- const width = stringWidth(text);
22
- if (text.length <= 64) {
23
- if (cellLenCache.size >= CACHE_MAX)
24
- cellLenCache.clear();
25
- cellLenCache.set(text, width);
26
- }
27
- return width;
28
- }
29
- /** Brand a raw number as a CellCol. Use only at trust boundaries. */
30
- export function asCellCol(n) { return n; }
31
- /** Brand a raw number as a CodeUnit. Use only at trust boundaries. */
32
- export function asCodeUnit(n) { return n; }
33
- /** Brand a raw number as a CodePoint. Use only when the value is known to be
34
- * on a Unicode code-point boundary. */
35
- export function asCodePoint(n) { return n; }
36
- /**
37
- * [LAW:parse-dont-validate] A caller's number as a count of cells — the checked
38
- * counterpart to `asCellCol`'s unchecked brand, and the one place the rule "a
39
- * cell count is a non-negative integer" is written.
40
- *
41
- * [LAW:one-source-of-truth] Every renderable is handed `RenderOptions.maxWidth`
42
- * as a plain `number`, so every renderable used to answer this for itself and
43
- * they disagreed: at NaN, `Table` collapsed to one cell of garbage, `Columns`
44
- * and `Layout` threw `Invalid array length`, and `RichText` ignored the request
45
- * and emitted its full natural width. Parse at the entry to `render`/`measure`
46
- * and nothing downstream re-asks.
47
- *
48
- * The comparison is the point, not `Math.max(0, n)`: NaN fails every comparison
49
- * and so floors here, where `Math.max` would return it and let it poison every
50
- * bounds check downstream. Integral because a cell is not divisible — a
51
- * fractional budget reaches a largest-remainder division, which grants a whole
52
- * cell against a fractional residue and hands out more width than it was given.
53
- *
54
- * `Infinity` is outside what the comparison floors, so it passes through
55
- * unchanged, and no clamp belongs here: the finite answer to an unbounded offer
56
- * is the renderable's own natural width, which this function cannot see from
57
- * the number alone. `withBoundedWidth` in `protocol.ts` is the second half of
58
- * the parse and the only place that value is resolved.
59
- */
60
- export function cellCount(n) {
61
- return (n > 0 ? Math.floor(n) : 0);
62
- }
63
- // ── Cell-aware string utilities ──────────────────────────────────────────────
64
- /**
65
- * Pads or crops a string to exactly `totalWidth` terminal cells.
66
- * Invariant: cellLen(setCellSize(text, n)) === n (unless n is 0)
67
- */
68
- export function setCellSize(text, totalWidth) {
69
- if (totalWidth === 0)
70
- return "";
71
- const currentWidth = cellLen(text);
72
- if (currentWidth === totalWidth)
73
- return text;
74
- if (currentWidth < totalWidth) {
75
- return text + " ".repeat(totalWidth - currentWidth);
76
- }
77
- return cropToWidth(text, totalWidth);
78
- }
79
- /**
80
- * Splits text at a cell position. Returns [left, right].
81
- * When the position falls mid-wide-character, the left side is padded
82
- * to reach exactly `position` cells. The wide char remains in the right side.
83
- */
84
- export function splitText(text, position) {
85
- if (position <= 0)
86
- return ["", text];
87
- const totalWidth = cellLen(text);
88
- if (position >= totalWidth)
89
- return [text, ""];
90
- // Walk characters tracking cell width and source char index
91
- let width = 0;
92
- let charIndex = 0;
93
- for (const char of text) {
94
- const charWidth = cellLen(char);
95
- if (width + charWidth > position)
96
- break;
97
- width += charWidth;
98
- charIndex += char.length;
99
- }
100
- const left = text.slice(0, charIndex);
101
- const right = text.slice(charIndex);
102
- // If we stopped short of position (mid-wide-char), pad left with spaces
103
- if (width < position) {
104
- return [left + " ".repeat(position - width), right];
105
- }
106
- return [left, right];
107
- }
108
- /**
109
- * Wraps text into lines of at most `maxWidth` cells, preserving every code
110
- * point: `chopCells(t, w).join("") === t`. Unlike `splitText` this never pads,
111
- * so a line ending before a wide glyph is narrower than `maxWidth` rather than
112
- * padded out to it.
113
- *
114
- * A line overflows exactly where the budget cannot be met: a glyph wider than
115
- * `maxWidth` is force-taken whole rather than dropped, and a `maxWidth` of zero
116
- * or less is no budget at all, so the text comes back as one line.
117
- */
118
- export function chopCells(text, maxWidth) {
119
- if (maxWidth <= 0 || text.length === 0)
120
- return [text];
121
- const lines = [];
122
- let start = asCodePoint(0);
123
- while (start < text.length) {
124
- const end = cellStepFrom(text, start, maxWidth);
125
- lines.push(text.slice(start, end));
126
- start = end;
127
- }
128
- return lines;
129
- }
130
- /**
131
- * Returns the largest prefix of `text` whose cell width fits within `cap` cells.
132
- * No padding — the returned string may be narrower than `cap` when the next
133
- * character is wide and would overshoot. Never wider than `cap` cells.
134
- *
135
- * When the first character already exceeds `cap` cells, returns "" (the caller
136
- * must decide whether to force-take the character or skip it).
137
- */
138
- export function cellFit(text, cap) {
139
- let w = 0;
140
- let i = 0;
141
- for (const ch of text) {
142
- const cw = cellLen(ch);
143
- if (w + cw > cap)
144
- break;
145
- w += cw;
146
- i += ch.length;
147
- }
148
- return text.slice(0, i);
149
- }
150
- /**
151
- * Returns the largest code-unit end offset starting from `startCU` whose
152
- * prefix (from `startCU`) has cell width ≤ `cap`. Iterates from the given
153
- * offset without slicing the tail, avoiding O(N²) allocation when called
154
- * repeatedly across a long string.
155
- *
156
- * [LAW:types-are-the-program] returns CodePoint because for...of always
157
- * stops on a code-point boundary.
158
- */
159
- export function cellFitFrom(text, startCU, cap) {
160
- let w = 0;
161
- let i = startCU;
162
- while (i < text.length) {
163
- const cp = text.codePointAt(i);
164
- const ch = String.fromCodePoint(cp);
165
- const cw = cellLen(ch);
166
- if (w + cw > cap)
167
- break;
168
- w += cw;
169
- i = asCodePoint(i + ch.length);
170
- }
171
- return i;
172
- }
173
- /**
174
- * Like `cellFitFrom`, but for any `startCU` inside `text` the result is
175
- * strictly past it: a glyph too wide for `cap` is force-taken whole and
176
- * overflows `cap` by its own width. So a `while (i < text.length)` loop driven
177
- * by this terminates by construction, and the re-brand is honest — both
178
- * operands land on code-point boundaries, so their max does too.
179
- *
180
- * [LAW:single-enforcer] `cellFit` documents that its caller must choose between
181
- * force-taking and skipping; this is that choice, made once. It was made twice
182
- * before and the copies drifted — the textarea's soft wrap force-took,
183
- * `chopCells` did neither and hung.
184
- */
185
- export function cellStepFrom(text, startCU, cap) {
186
- return asCodePoint(Math.max(cellFitFrom(text, startCU, cap), nextCodePoint(text, startCU)));
187
- }
188
- /**
189
- * Returns the largest code-unit offset into `content` whose prefix has
190
- * cell width ≤ `cellCol`. When `cellCol` falls mid-wide-character the
191
- * function stops before that character (never advances into it).
192
- * Clamps to `content.length` if `cellCol` exceeds the string's total
193
- * cell width.
194
- *
195
- * This is the inverse of `cellLen(content.slice(0, codeUnit))` — given a
196
- * visual column, return the corresponding string index.
197
- *
198
- * Returns `CodePoint` because `for...of` iteration always stops on a
199
- * code-point boundary.
200
- */
201
- export function cellColToCodeUnitOffset(content, cellCol) {
202
- let w = 0;
203
- let i = 0;
204
- for (const ch of content) {
205
- if (w >= cellCol)
206
- break;
207
- const cw = cellLen(ch);
208
- if (w + cw > cellCol)
209
- break;
210
- w += cw;
211
- i += ch.length;
212
- }
213
- return asCodePoint(i);
214
- }
215
- /**
216
- * Advance one full Unicode code point from `cu`, returning the code-unit
217
- * offset of the start of the *next* code point. Returns `s.length` when
218
- * already at or past the end.
219
- *
220
- * Handles surrogate pairs: when the code point at `cu` is a supplementary
221
- * character (U+10000…U+10FFFF) it occupies 2 UTF-16 code units, so the
222
- * returned offset advances by 2.
223
- */
224
- export function nextCodePoint(s, cu) {
225
- if (cu >= s.length)
226
- return asCodePoint(s.length);
227
- const cp = s.codePointAt(cu);
228
- return asCodePoint(cu + (cp > 0xFFFF ? 2 : 1));
229
- }
230
- /**
231
- * Step back one full Unicode code point from `cu`, returning the code-unit
232
- * offset of the start of the *previous* code point. Returns 0 when already
233
- * at the start.
234
- *
235
- * Handles surrogate pairs: when the code unit at `cu - 1` is a low surrogate
236
- * AND the code unit at `cu - 2` is a high surrogate, steps back 2 code units.
237
- * Unpaired surrogates are treated as 1-CU characters.
238
- */
239
- export function prevCodePoint(s, cu) {
240
- if (cu <= 0)
241
- return asCodePoint(0);
242
- const low = s.charCodeAt(cu - 1);
243
- if (low >= 0xDC00 && low <= 0xDFFF && cu >= 2) {
244
- const high = s.charCodeAt(cu - 2);
245
- if (high >= 0xD800 && high <= 0xDBFF)
246
- return asCodePoint(cu - 2);
247
- }
248
- return asCodePoint(cu - 1);
249
- }
250
- // --- internal ---
251
- function cropToWidth(text, targetWidth) {
252
- let width = 0;
253
- let i = 0;
254
- // Use the string's code point iterator to handle surrogate pairs
255
- for (const char of text) {
256
- const charWidth = cellLen(char);
257
- if (width + charWidth > targetWidth)
258
- break;
259
- width += charWidth;
260
- i += char.length;
261
- }
262
- const cropped = text.slice(0, i);
263
- // Pad if we couldn't hit the exact width (wide char boundary)
264
- const diff = targetWidth - width;
265
- return diff > 0 ? cropped + " ".repeat(diff) : cropped;
266
- }
@@ -1,229 +0,0 @@
1
- /**
2
- * Terminal color representation, parsing, downgrading, and ANSI code generation.
3
- */
4
- /**
5
- * Immutable RGBA color value. Alpha defaults to 1 (fully opaque).
6
- *
7
- * [LAW:single-enforcer] The constructor is the sole validation site for
8
- * channel/alpha invariants. Float-arithmetic callers (HSL roundtrips, blends,
9
- * lerps) are responsible for `Math.round` + clamp before construction; the
10
- * constructor throws rather than silently masking out-of-range input.
11
- */
12
- export declare class ColorRgba {
13
- readonly red: number;
14
- readonly green: number;
15
- readonly blue: number;
16
- readonly alpha: number;
17
- constructor(red: number, green: number, blue: number, alpha?: number);
18
- /** 6-char `#RRGGBB` when fully opaque, 8-char `#RRGGBBAA` otherwise. */
19
- get hex(): string;
20
- /** `rgb(r,g,b)` when fully opaque, `rgba(r,g,b,a)` otherwise. */
21
- get rgb(): string;
22
- /** [r/255, g/255, b/255, alpha] — alpha is already 0..1, no division. */
23
- get normalized(): [number, number, number, number];
24
- /**
25
- * Composite this color over an opaque background. Returns a fully opaque
26
- * ColorRgba (alpha=1). Per-channel linear interpolation by alpha.
27
- *
28
- * [LAW:dataflow-not-control-flow] alpha=1 short-circuits to `this`, so
29
- * callers can invoke this unconditionally; the data decides whether work
30
- * happens.
31
- */
32
- compositeOver(bg: ColorRgba): ColorRgba;
33
- }
34
- /**
35
- * The surface a terminal draws a translucent colour over. A terminal cannot
36
- * know what lies under its cells, so the SGR writer (`Style.toSgrCodes`), the
37
- * strip's seam test and the contrast choosers (`themes/colorMath`) all
38
- * composite over this one colour. [LAW:one-source-of-truth] One constant, so
39
- * the colour text is chosen against is the colour the writer draws.
40
- */
41
- export declare const SURFACE_BLACK: ColorRgba;
42
- /**
43
- * WCAG 2.x relative luminance (0..1) of an opaque color. The single
44
- * luminance function in the codebase — `contrastFor`, `contrastRatio`, and
45
- * any caller that needs to reason about readability all funnel through it.
46
- * [LAW:one-source-of-truth]
47
- */
48
- export declare function relativeLuminance(c: ColorRgba): number;
49
- /**
50
- * WCAG 2.x contrast ratio between two colors, in [1, 21]. Symmetric — the
51
- * order of arguments does not matter. 4.5 is the AA threshold for normal
52
- * text, 3.0 for large text.
53
- *
54
- * Assumes opaque inputs: alpha is ignored, since the displayed contrast of a
55
- * translucent color depends on what it composites over. For a translucent
56
- * foreground, flatten it first (or use `ensureContrast`, which does).
57
- */
58
- export declare function contrastRatio(a: ColorRgba, b: ColorRgba): number;
59
- export declare class ColorTable {
60
- private readonly colors;
61
- private readonly firstIndex;
62
- private readonly matchCache;
63
- private readonly readableCache;
64
- constructor(colors: ColorRgba[], firstIndex?: number);
65
- get(index: number): ColorRgba;
66
- private luminanceCache;
67
- /** Each entry's relative luminance, computed once per table. */
68
- private luminances;
69
- /** How many entries the table holds (terminal indices `firstIndex`…). */
70
- get size(): number;
71
- /**
72
- * Finds the nearest table index to the given color (Euclidean RGB distance, cached).
73
- * Alpha is part of the cache key so two values that differ only in alpha
74
- * don't collide, but is not used in the distance metric.
75
- */
76
- match(value: ColorRgba): number;
77
- /**
78
- * The nearest entry to `value` (the distance `match` uses) among those that
79
- * clear `minRatio` against `on`; when none does, the entry with the most
80
- * contrast against `on`. Text downgraded on its own background: `match`
81
- * moves text and background independently, and two independent roundings
82
- * can meet in the middle, so a pair that read at 4.5:1 in truecolor can
83
- * draw at 2:1. [LAW:dataflow-not-control-flow] One scan scores every entry;
84
- * the ratio decides which one wins.
85
- */
86
- matchReadable(value: ColorRgba, on: ColorRgba, minRatio: number): number;
87
- /**
88
- * The nearest entry to `value` (the distance `match` uses) among those
89
- * `accept` takes, given each entry's colour and terminal index; `undefined`
90
- * when it takes none. Entries are offered nearest-first (ties to the lower
91
- * index) and the first taken wins, so an expensive predicate runs only as
92
- * far out as the answer. Uncached: the predicate is the caller's, and a
93
- * closure has no key.
94
- */
95
- matchWhere(value: ColorRgba, accept: (entry: ColorRgba, index: number) => boolean): number | undefined;
96
- }
97
- export declare enum ColorDepth {
98
- DEFAULT = 0,
99
- STANDARD = 1,
100
- EIGHT_BIT = 2,
101
- TRUECOLOR = 3,
102
- WINDOWS = 4
103
- }
104
- export interface DetectColorOptions {
105
- /** Environment to probe. Defaults to `process.env`. */
106
- env?: NodeJS.ProcessEnv;
107
- /** Whether output is going to a real terminal. Defaults to `process.stdout?.isTTY`. */
108
- isTTY?: boolean;
109
- }
110
- /**
111
- * Detect the terminal's color capability from env + TTY state.
112
- *
113
- * Probes (in priority order): NO_COLOR (per https://no-color.org —
114
- * any non-empty value disables color), FORCE_COLOR (numeric/boolean override),
115
- * TTY presence, TERM=dumb/unknown, COLORTERM, known terminal/TERM_PROGRAM
116
- * names, and TERM regex patterns. Returns `null` for "no color".
117
- *
118
- * [LAW:dataflow-not-control-flow] Same probes run every call; variability is
119
- * in the env values, not in which checks execute.
120
- */
121
- export declare function detectColorSystem(options?: DetectColorOptions): ColorDepth | null;
122
- /**
123
- * Resolve a string spec into a `ColorDepth` (or `null` for no color).
124
- *
125
- * `"auto"` triggers env-based detection; all other recognized specs are
126
- * direct table lookups against `STRING_TO_DEPTH`. Throws on unknown specs
127
- * — silent fallback would mask user typos.
128
- *
129
- * [LAW:single-enforcer] All string→ColorDepth resolution flows through here.
130
- */
131
- export declare function resolveColorSystem(spec: string, options?: DetectColorOptions): ColorDepth | null;
132
- export declare class ColorParseError extends Error {
133
- constructor(message: string);
134
- }
135
- export declare class ColorSpec {
136
- readonly name: string;
137
- readonly type: ColorDepth;
138
- readonly number: number | undefined;
139
- readonly value: ColorRgba | undefined;
140
- private downgradeCache;
141
- constructor(name: string, type: ColorDepth, number?: number, value?: ColorRgba);
142
- get isDefault(): boolean;
143
- /**
144
- * The colour every terminal draws this spec as, where that is fixed — before
145
- * any alpha is flattened, so a translucent value is drawn composited over
146
- * its ground (`flattenAlpha`) and should be measured that way: a
147
- * truecolor value, or a 256-colour cube or grey-ramp entry (xterm fixes
148
- * indices 16–255, and `fromAnsi` types only those as EIGHT_BIT). ANSI 0–15
149
- * and the default colour are the terminal theme's own, so they have none.
150
- */
151
- get fixedValue(): ColorRgba | undefined;
152
- get isSystemDefined(): boolean;
153
- /**
154
- * Generates SGR parameter strings for this color.
155
- */
156
- getAnsiCodes(foreground?: boolean): string[];
157
- /**
158
- * Composite this color's alpha (if any) against an opaque background,
159
- * returning a fully-opaque ColorSpec. Non-truecolor specs (palette
160
- * indices, default) have no alpha and pass through unchanged.
161
- *
162
- * [LAW:single-enforcer] Sole alpha-flattening site for the render
163
- * pipeline. Run *before* downgrade — otherwise palette matching on the
164
- * still-translucent RGB silently drops alpha (the "flatten then lose
165
- * alpha" path the unification ticket forbids).
166
- *
167
- * [LAW:dataflow-not-control-flow] `ColorRgba.compositeOver` is a no-op
168
- * when alpha=1, so callers invoke this unconditionally; the data
169
- * (alpha value) decides whether work happens.
170
- */
171
- flattenAlpha(bg: ColorRgba): ColorSpec;
172
- /**
173
- * Downgrade to a lower-fidelity color depth. Cached.
174
- */
175
- downgrade(targetSystem: ColorDepth): ColorSpec;
176
- /**
177
- * Resolves to an actual RGB(A) value for any color type.
178
- */
179
- getTruecolor(theme?: TerminalTheme, foreground?: boolean): ColorRgba;
180
- static default(): ColorSpec;
181
- static fromAnsi(n: number): ColorSpec;
182
- static fromRgba(value: ColorRgba): ColorSpec;
183
- static fromRgb(r: number, g: number, b: number): ColorSpec;
184
- /**
185
- * Parse a color string. Cached — identical strings return the same instance.
186
- */
187
- static parse(colorString: string): ColorSpec;
188
- private performDowngrade;
189
- }
190
- export declare function parseRgbHex(hex: string): ColorRgba;
191
- export declare function parseRgbaHex(hex: string): ColorRgba;
192
- export declare function blendRgb(color1: ColorRgba, color2: ColorRgba, crossFade?: number): ColorRgba;
193
- /**
194
- * A terminal theme — surface/foreground baseline, the ANSI 16/256 LUT, and a
195
- * semantic palette.
196
- *
197
- * **`ansiColors` is the canonical ANSI table, not a per-theme override.** All
198
- * preset themes in this codebase use `STANDARD_TABLE` for `ansiColors`. Theme
199
- * identity lives entirely in `palette` (primary, accent, text-*, surface, ...);
200
- * `ColorSpec.parse("red")` is intentionally consistent across themes — same
201
- * design as Textual itself, which models theming at the truecolor/semantic
202
- * layer rather than overloading the ANSI 16. Custom callers building bespoke
203
- * themes that *do* want to retint ANSI 16 are free to pass their own
204
- * `ColorTable`; that path is supported but not how the built-in presets work.
205
- */
206
- export declare class TerminalTheme {
207
- readonly backgroundColor: ColorRgba;
208
- readonly foregroundColor: ColorRgba;
209
- readonly ansiColors: ColorTable;
210
- readonly palette: import("../themes/palette.js").Palette;
211
- constructor(backgroundColor: ColorRgba, foregroundColor: ColorRgba, ansiColors: ColorTable, palette: import("../themes/palette.js").Palette);
212
- }
213
- export declare const STANDARD_TABLE: ColorTable;
214
- export declare const EIGHT_BIT_TABLE: ColorTable;
215
- /**
216
- * What a downgrade to 256 colours may choose: the cube and the grey ramp,
217
- * indices 16–255. Indices 0–15 are the terminal's own ANSI colours, which
218
- * every theme redefines, so their RGB is unknown and a match against them is
219
- * a guess; Python Rich never picks them either.
220
- */
221
- export declare const EIGHT_BIT_DOWNGRADE_TABLE: ColorTable;
222
- export declare const WINDOWS_TABLE: ColorTable;
223
- export declare const ANSI_COLOR_NAMES: Record<string, number>;
224
- /**
225
- * Every word `ColorSpec.parse` accepts as a name rather than a `#hex`,
226
- * `rgb()` or `color(N)` form: `default` and the named palette entries, which
227
- * are exactly the two name checks `parseSingle` makes.
228
- */
229
- export declare const COLOR_NAMES: readonly string[];