@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,1162 +0,0 @@
1
- /**
2
- * TextInput widget — editable text field with cursor.
3
- *
4
- * Two modes:
5
- * - single-line (default): Enter submits; newlines in value are rendered
6
- * as a literal `↵` glyph if they ever appear.
7
- * - multi-line (`multiline: true`): Enter inserts a `\n`; `value` may
8
- * contain logical line breaks; Up/Down navigate
9
- * between logical lines preserving column intent.
10
- *
11
- * [LAW:dataflow-not-control-flow] cursor and value are observable data; the
12
- * keymap dispatches to small `@action` mutators that all flow through the
13
- * same value/cursor update path. There is no per-key bespoke branch in the
14
- * render loop or the change-emit path.
15
- *
16
- * [LAW:one-source-of-truth] The cursor's logical line — and therefore "what
17
- * Home/End/Up/Down mean" — is derived from `value` and `cursorPosition` via
18
- * `_lineStart()` / `_lineEnd()`. Multi-line behavior is the same code path
19
- * as single-line; with no `\n` in `value`, line bounds collapse to value
20
- * bounds, so single-line semantics are recovered without a special case.
21
- *
22
- * [LAW:types-are-the-program] Three integer spaces coexist: `CellCol` (terminal
23
- * cell columns), `CodeUnit` (JS UTF-16 indices), and `CodePoint` (code-unit
24
- * offsets that are additionally on a Unicode code-point boundary — a subtype of
25
- * `CodeUnit`). `cursorPosition` is `CodePoint`; it is never inside a surrogate
26
- * pair by construction. Visual positions are `CellCol`. The three are never
27
- * interchangeable — the type system enforces this at every crossing point.
28
- *
29
- * Keymap (readline / emacs compatible):
30
- *
31
- * ─── motion ───
32
- * left | Ctrl+B char left
33
- * right | Ctrl+F char right
34
- * up | Ctrl+P line up (preserves preferred column)
35
- * down | Ctrl+N line down (preserves preferred column)
36
- * home | Ctrl+A line start
37
- * end | Ctrl+E line end
38
- * Ctrl+Home document start
39
- * Ctrl+End document end
40
- * Ctrl+Left | Alt+Left | Alt+B word left
41
- * Ctrl+Right | Alt+Right | Alt+F word right
42
- *
43
- * ─── editing ───
44
- * backspace | Ctrl+H delete char back
45
- * delete | Ctrl+D delete char forward
46
- * Ctrl+W | Alt+Backspace delete word back (whitespace-bounded; readline parity)
47
- * Alt+D delete word forward
48
- * Ctrl+U kill to line start (stores in kill buffer)
49
- * Ctrl+K kill to line end (stores in kill buffer)
50
- * Ctrl+Y yank kill buffer at cursor
51
- * Ctrl+T transpose chars (swap pre-cursor/at-cursor, advance)
52
- * enter submit (single-line) or insert `\n` (multiline)
53
- *
54
- * Motion and editing primitives are also exposed as public methods
55
- * (`moveCharLeft`, `killLineForward`, etc.) so a host can bind custom keys
56
- * or invoke them programmatically.
57
- */
58
- var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
59
- var useValue = arguments.length > 2;
60
- for (var i = 0; i < initializers.length; i++) {
61
- value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
62
- }
63
- return useValue ? value : void 0;
64
- };
65
- var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
66
- function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
67
- var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
68
- var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
69
- var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
70
- var _, done = false;
71
- for (var i = decorators.length - 1; i >= 0; i--) {
72
- var context = {};
73
- for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
74
- for (var p in contextIn.access) context.access[p] = contextIn.access[p];
75
- context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
76
- var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
77
- if (kind === "accessor") {
78
- if (result === void 0) continue;
79
- if (result === null || typeof result !== "object") throw new TypeError("Object expected");
80
- if (_ = accept(result.get)) descriptor.get = _;
81
- if (_ = accept(result.set)) descriptor.set = _;
82
- if (_ = accept(result.init)) initializers.unshift(_);
83
- }
84
- else if (_ = accept(result)) {
85
- if (kind === "field") initializers.unshift(_);
86
- else descriptor[key] = _;
87
- }
88
- }
89
- if (target) Object.defineProperty(target, contextIn.name, descriptor);
90
- done = true;
91
- };
92
- import { observable, action, observableRef } from "mobx";
93
- import { Segment } from "../core/segment.js";
94
- import { Style } from "../core/style.js";
95
- import { ColorSpec } from "../core/color.js";
96
- import { cellLen, setCellSize, splitText, cellStepFrom, cellColToCodeUnitOffset, asCellCol, asCodePoint, nextCodePoint, prevCodePoint, } from "../core/cells.js";
97
- import { DEFAULT_TERMINAL_THEME } from "../themes/terminalThemes.js";
98
- import { WidgetBase } from "./widget-base.js";
99
- /**
100
- * Character-greedy soft wrap. Breaks at any character once `firstWidth`
101
- * (or `continuationWidth` for continuation rows) cells are exhausted. The
102
- * textarea default when a consumer says "wrap, I don't care how" — no syntax
103
- * awareness, just fits the line to the width.
104
- *
105
- * Wide characters (CJK, emoji) are treated as atomic: a char that would
106
- * straddle the budget is moved to the next row rather than split, and one too
107
- * wide for the whole budget is force-taken — see `cellStepFrom`.
108
- */
109
- export const charGreedyWrap = (line, { firstWidth, continuationWidth }) => {
110
- if (line.length === 0)
111
- return [{ content: "", start: asCodePoint(0) }];
112
- const rows = [];
113
- let pos = asCodePoint(0);
114
- let isFirst = true;
115
- while (pos < line.length) {
116
- const cap = isFirst ? firstWidth : continuationWidth;
117
- if (cap <= 0)
118
- break;
119
- const end = cellStepFrom(line, pos, cap);
120
- rows.push({ content: line.slice(pos, end), start: pos });
121
- pos = end;
122
- isFirst = false;
123
- }
124
- return rows;
125
- };
126
- const MIN_CONTENT_WIDTH = 8;
127
- const NEWLINE_GLYPH = "↵";
128
- // Word-character regex used by Alt+B/F and Ctrl+Left/Right. Matches the
129
- // readline default (alphanumeric + underscore), which is what users typing
130
- // shell commands or code identifiers expect.
131
- const WORD_CHAR_RE = /[A-Za-z0-9_]/;
132
- const WHITESPACE_RE = /\s/;
133
- function isWordChar(c) {
134
- return c !== undefined && WORD_CHAR_RE.test(c);
135
- }
136
- function isWhitespace(c) {
137
- return c !== undefined && WHITESPACE_RE.test(c);
138
- }
139
- let TextInput = (() => {
140
- let _classSuper = WidgetBase;
141
- let _instanceExtraInitializers = [];
142
- let _value_decorators;
143
- let _value_initializers = [];
144
- let _value_extraInitializers = [];
145
- let _cursorPosition_decorators;
146
- let _cursorPosition_initializers = [];
147
- let _cursorPosition_extraInitializers = [];
148
- let _placeholder_decorators;
149
- let _placeholder_initializers = [];
150
- let _placeholder_extraInitializers = [];
151
- let _indicatorStyleOverride_decorators;
152
- let _indicatorStyleOverride_initializers = [];
153
- let _indicatorStyleOverride_extraInitializers = [];
154
- let _cursorStyleOverride_decorators;
155
- let _cursorStyleOverride_initializers = [];
156
- let _cursorStyleOverride_extraInitializers = [];
157
- let _contentStyleOverride_decorators;
158
- let _contentStyleOverride_initializers = [];
159
- let _contentStyleOverride_extraInitializers = [];
160
- let _handleKey_decorators;
161
- let _handleMouse_decorators;
162
- let _setHovered_decorators;
163
- let _moveCharLeft_decorators;
164
- let _moveCharRight_decorators;
165
- let _moveLineUp_decorators;
166
- let _moveLineDown_decorators;
167
- let _moveLineStart_decorators;
168
- let _moveLineEnd_decorators;
169
- let _moveDocStart_decorators;
170
- let _moveDocEnd_decorators;
171
- let _moveWordLeft_decorators;
172
- let _moveWordRight_decorators;
173
- let _deleteCharBack_decorators;
174
- let _deleteCharForward_decorators;
175
- let _deleteWordBack_decorators;
176
- let _deleteWordForward_decorators;
177
- let _killLineBack_decorators;
178
- let _killLineForward_decorators;
179
- let _yank_decorators;
180
- let _transposeChars_decorators;
181
- let __insertText_decorators;
182
- return class TextInput extends _classSuper {
183
- static {
184
- const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
185
- _value_decorators = [observable];
186
- _cursorPosition_decorators = [observable];
187
- _placeholder_decorators = [observableRef];
188
- _indicatorStyleOverride_decorators = [observableRef];
189
- _cursorStyleOverride_decorators = [observableRef];
190
- _contentStyleOverride_decorators = [observableRef];
191
- _handleKey_decorators = [action];
192
- _handleMouse_decorators = [action];
193
- _setHovered_decorators = [action];
194
- _moveCharLeft_decorators = [action];
195
- _moveCharRight_decorators = [action];
196
- _moveLineUp_decorators = [action];
197
- _moveLineDown_decorators = [action];
198
- _moveLineStart_decorators = [action];
199
- _moveLineEnd_decorators = [action];
200
- _moveDocStart_decorators = [action];
201
- _moveDocEnd_decorators = [action];
202
- _moveWordLeft_decorators = [action];
203
- _moveWordRight_decorators = [action];
204
- _deleteCharBack_decorators = [action];
205
- _deleteCharForward_decorators = [action];
206
- _deleteWordBack_decorators = [action];
207
- _deleteWordForward_decorators = [action];
208
- _killLineBack_decorators = [action];
209
- _killLineForward_decorators = [action];
210
- _yank_decorators = [action];
211
- _transposeChars_decorators = [action];
212
- __insertText_decorators = [action];
213
- __esDecorate(this, null, _value_decorators, { kind: "accessor", name: "value", static: false, private: false, access: { has: obj => "value" in obj, get: obj => obj.value, set: (obj, value) => { obj.value = value; } }, metadata: _metadata }, _value_initializers, _value_extraInitializers);
214
- __esDecorate(this, null, _cursorPosition_decorators, { kind: "accessor", name: "cursorPosition", static: false, private: false, access: { has: obj => "cursorPosition" in obj, get: obj => obj.cursorPosition, set: (obj, value) => { obj.cursorPosition = value; } }, metadata: _metadata }, _cursorPosition_initializers, _cursorPosition_extraInitializers);
215
- __esDecorate(this, null, _placeholder_decorators, { kind: "accessor", name: "placeholder", static: false, private: false, access: { has: obj => "placeholder" in obj, get: obj => obj.placeholder, set: (obj, value) => { obj.placeholder = value; } }, metadata: _metadata }, _placeholder_initializers, _placeholder_extraInitializers);
216
- __esDecorate(this, null, _indicatorStyleOverride_decorators, { kind: "accessor", name: "indicatorStyleOverride", static: false, private: false, access: { has: obj => "indicatorStyleOverride" in obj, get: obj => obj.indicatorStyleOverride, set: (obj, value) => { obj.indicatorStyleOverride = value; } }, metadata: _metadata }, _indicatorStyleOverride_initializers, _indicatorStyleOverride_extraInitializers);
217
- __esDecorate(this, null, _cursorStyleOverride_decorators, { kind: "accessor", name: "cursorStyleOverride", static: false, private: false, access: { has: obj => "cursorStyleOverride" in obj, get: obj => obj.cursorStyleOverride, set: (obj, value) => { obj.cursorStyleOverride = value; } }, metadata: _metadata }, _cursorStyleOverride_initializers, _cursorStyleOverride_extraInitializers);
218
- __esDecorate(this, null, _contentStyleOverride_decorators, { kind: "accessor", name: "contentStyleOverride", static: false, private: false, access: { has: obj => "contentStyleOverride" in obj, get: obj => obj.contentStyleOverride, set: (obj, value) => { obj.contentStyleOverride = value; } }, metadata: _metadata }, _contentStyleOverride_initializers, _contentStyleOverride_extraInitializers);
219
- __esDecorate(this, null, _handleKey_decorators, { kind: "method", name: "handleKey", static: false, private: false, access: { has: obj => "handleKey" in obj, get: obj => obj.handleKey }, metadata: _metadata }, null, _instanceExtraInitializers);
220
- __esDecorate(this, null, _handleMouse_decorators, { kind: "method", name: "handleMouse", static: false, private: false, access: { has: obj => "handleMouse" in obj, get: obj => obj.handleMouse }, metadata: _metadata }, null, _instanceExtraInitializers);
221
- __esDecorate(this, null, _setHovered_decorators, { kind: "method", name: "setHovered", static: false, private: false, access: { has: obj => "setHovered" in obj, get: obj => obj.setHovered }, metadata: _metadata }, null, _instanceExtraInitializers);
222
- __esDecorate(this, null, _moveCharLeft_decorators, { kind: "method", name: "moveCharLeft", static: false, private: false, access: { has: obj => "moveCharLeft" in obj, get: obj => obj.moveCharLeft }, metadata: _metadata }, null, _instanceExtraInitializers);
223
- __esDecorate(this, null, _moveCharRight_decorators, { kind: "method", name: "moveCharRight", static: false, private: false, access: { has: obj => "moveCharRight" in obj, get: obj => obj.moveCharRight }, metadata: _metadata }, null, _instanceExtraInitializers);
224
- __esDecorate(this, null, _moveLineUp_decorators, { kind: "method", name: "moveLineUp", static: false, private: false, access: { has: obj => "moveLineUp" in obj, get: obj => obj.moveLineUp }, metadata: _metadata }, null, _instanceExtraInitializers);
225
- __esDecorate(this, null, _moveLineDown_decorators, { kind: "method", name: "moveLineDown", static: false, private: false, access: { has: obj => "moveLineDown" in obj, get: obj => obj.moveLineDown }, metadata: _metadata }, null, _instanceExtraInitializers);
226
- __esDecorate(this, null, _moveLineStart_decorators, { kind: "method", name: "moveLineStart", static: false, private: false, access: { has: obj => "moveLineStart" in obj, get: obj => obj.moveLineStart }, metadata: _metadata }, null, _instanceExtraInitializers);
227
- __esDecorate(this, null, _moveLineEnd_decorators, { kind: "method", name: "moveLineEnd", static: false, private: false, access: { has: obj => "moveLineEnd" in obj, get: obj => obj.moveLineEnd }, metadata: _metadata }, null, _instanceExtraInitializers);
228
- __esDecorate(this, null, _moveDocStart_decorators, { kind: "method", name: "moveDocStart", static: false, private: false, access: { has: obj => "moveDocStart" in obj, get: obj => obj.moveDocStart }, metadata: _metadata }, null, _instanceExtraInitializers);
229
- __esDecorate(this, null, _moveDocEnd_decorators, { kind: "method", name: "moveDocEnd", static: false, private: false, access: { has: obj => "moveDocEnd" in obj, get: obj => obj.moveDocEnd }, metadata: _metadata }, null, _instanceExtraInitializers);
230
- __esDecorate(this, null, _moveWordLeft_decorators, { kind: "method", name: "moveWordLeft", static: false, private: false, access: { has: obj => "moveWordLeft" in obj, get: obj => obj.moveWordLeft }, metadata: _metadata }, null, _instanceExtraInitializers);
231
- __esDecorate(this, null, _moveWordRight_decorators, { kind: "method", name: "moveWordRight", static: false, private: false, access: { has: obj => "moveWordRight" in obj, get: obj => obj.moveWordRight }, metadata: _metadata }, null, _instanceExtraInitializers);
232
- __esDecorate(this, null, _deleteCharBack_decorators, { kind: "method", name: "deleteCharBack", static: false, private: false, access: { has: obj => "deleteCharBack" in obj, get: obj => obj.deleteCharBack }, metadata: _metadata }, null, _instanceExtraInitializers);
233
- __esDecorate(this, null, _deleteCharForward_decorators, { kind: "method", name: "deleteCharForward", static: false, private: false, access: { has: obj => "deleteCharForward" in obj, get: obj => obj.deleteCharForward }, metadata: _metadata }, null, _instanceExtraInitializers);
234
- __esDecorate(this, null, _deleteWordBack_decorators, { kind: "method", name: "deleteWordBack", static: false, private: false, access: { has: obj => "deleteWordBack" in obj, get: obj => obj.deleteWordBack }, metadata: _metadata }, null, _instanceExtraInitializers);
235
- __esDecorate(this, null, _deleteWordForward_decorators, { kind: "method", name: "deleteWordForward", static: false, private: false, access: { has: obj => "deleteWordForward" in obj, get: obj => obj.deleteWordForward }, metadata: _metadata }, null, _instanceExtraInitializers);
236
- __esDecorate(this, null, _killLineBack_decorators, { kind: "method", name: "killLineBack", static: false, private: false, access: { has: obj => "killLineBack" in obj, get: obj => obj.killLineBack }, metadata: _metadata }, null, _instanceExtraInitializers);
237
- __esDecorate(this, null, _killLineForward_decorators, { kind: "method", name: "killLineForward", static: false, private: false, access: { has: obj => "killLineForward" in obj, get: obj => obj.killLineForward }, metadata: _metadata }, null, _instanceExtraInitializers);
238
- __esDecorate(this, null, _yank_decorators, { kind: "method", name: "yank", static: false, private: false, access: { has: obj => "yank" in obj, get: obj => obj.yank }, metadata: _metadata }, null, _instanceExtraInitializers);
239
- __esDecorate(this, null, _transposeChars_decorators, { kind: "method", name: "transposeChars", static: false, private: false, access: { has: obj => "transposeChars" in obj, get: obj => obj.transposeChars }, metadata: _metadata }, null, _instanceExtraInitializers);
240
- __esDecorate(this, null, __insertText_decorators, { kind: "method", name: "_insertText", static: false, private: false, access: { has: obj => "_insertText" in obj, get: obj => obj._insertText }, metadata: _metadata }, null, _instanceExtraInitializers);
241
- if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
242
- }
243
- id = __runInitializers(this, _instanceExtraInitializers);
244
- focusable = true;
245
- #value_accessor_storage = __runInitializers(this, _value_initializers, void 0);
246
- get value() { return this.#value_accessor_storage; }
247
- set value(value) { this.#value_accessor_storage = value; }
248
- #cursorPosition_accessor_storage = (__runInitializers(this, _value_extraInitializers), __runInitializers(this, _cursorPosition_initializers, void 0));
249
- get cursorPosition() { return this.#cursorPosition_accessor_storage; }
250
- set cursorPosition(value) { this.#cursorPosition_accessor_storage = value; }
251
- #placeholder_accessor_storage = (__runInitializers(this, _cursorPosition_extraInitializers), __runInitializers(this, _placeholder_initializers, void 0));
252
- get placeholder() { return this.#placeholder_accessor_storage; }
253
- set placeholder(value) { this.#placeholder_accessor_storage = value; }
254
- _theme = __runInitializers(this, _placeholder_extraInitializers);
255
- _maxLength;
256
- _password;
257
- _multiline;
258
- _wrap;
259
- _continuationMarker;
260
- _markerWidth;
261
- _maxRows;
262
- _minRows;
263
- _scrollIndicator;
264
- #indicatorStyleOverride_accessor_storage = __runInitializers(this, _indicatorStyleOverride_initializers, void 0);
265
- get indicatorStyleOverride() { return this.#indicatorStyleOverride_accessor_storage; }
266
- set indicatorStyleOverride(value) { this.#indicatorStyleOverride_accessor_storage = value; }
267
- #cursorStyleOverride_accessor_storage = (__runInitializers(this, _indicatorStyleOverride_extraInitializers), __runInitializers(this, _cursorStyleOverride_initializers, void 0));
268
- get cursorStyleOverride() { return this.#cursorStyleOverride_accessor_storage; }
269
- set cursorStyleOverride(value) { this.#cursorStyleOverride_accessor_storage = value; }
270
- #contentStyleOverride_accessor_storage = (__runInitializers(this, _cursorStyleOverride_extraInitializers), __runInitializers(this, _contentStyleOverride_initializers, void 0));
271
- get contentStyleOverride() { return this.#contentStyleOverride_accessor_storage; }
272
- set contentStyleOverride(value) { this.#contentStyleOverride_accessor_storage = value; }
273
- /**
274
- * Last computed visual-row decomposition. Cached at the end of `render()`
275
- * so vertical motion (Up/Down) can step row-by-row without re-running the
276
- * wrap strategy. Null before the first render — vertical motion falls
277
- * back to logical-line motion in that case.
278
- *
279
- * [LAW:dataflow-not-control-flow] One source of truth for "what does Up
280
- * mean right now": the row table the renderer just produced. No parallel
281
- * "where would the cursor go" math; both the renderer and the keymap
282
- * read from the same array.
283
- */
284
- _visualRows = (__runInitializers(this, _contentStyleOverride_extraInitializers), null);
285
- // [LAW:types-are-the-program] `_preferredColumn` is a cell column (not a
286
- // code-unit index) — vertical motion navigates the visual surface, not the
287
- // string's byte layout. Reset to null on every non-vertical motion or edit
288
- // so the next Up/Down captures a fresh intent. Without this, Down through a
289
- // short line then back Up would land at the short-line column instead of the
290
- // original.
291
- _preferredColumn = null;
292
- // Single-slot kill buffer used by Ctrl+U / Ctrl+K → Ctrl+Y. Not a full
293
- // kill ring (no Alt+Y rotation) — sufficient for the common cut/paste
294
- // pattern that's the readline 90% case.
295
- _killBuffer = "";
296
- // [LAW:one-source-of-truth] Top visual row of the scroll viewport.
297
- // Persisted across renders so the cursor can move *within* the viewport
298
- // without dragging it along — the viewport scrolls only when the cursor
299
- // would leave it. Without this, recomputing scroll from cursor position
300
- // alone pins the cursor to the viewport's bottom edge whenever
301
- // `cursorRow >= maxRows`, making every subsequent Up/Down scroll in
302
- // lockstep with the cursor.
303
- _scrollStart = 0;
304
- // [LAW:one-source-of-truth] Viewport scroll offset in cells for the
305
- // single-line renderer. Captured by _renderSingleLine and read by
306
- // handleMouse so the hit-test uses the same visual origin the user sees.
307
- _singleLineViewportStart = asCellCol(0);
308
- multiline;
309
- constructor(options = {}) {
310
- super();
311
- this.id = options.id ?? `text-input-${Math.random().toString(36).slice(2, 8)}`;
312
- this.value = options.value ?? "";
313
- this.placeholder = options.placeholder ?? "";
314
- // [LAW:dataflow-not-control-flow] Initial cursor position is data, not
315
- // a render-time branch. Single-line inputs follow the pre-filled-form
316
- // convention (cursor at end — typing continues the value). Multiline
317
- // inputs follow the textarea convention (cursor at start — pre-loaded
318
- // content is read top-to-bottom, and the viewport's "scroll into view"
319
- // logic does nothing because row 0 is already inside the initial window).
320
- this.cursorPosition = (options.multiline ?? false) ? asCodePoint(0) : asCodePoint(this.value.length);
321
- this.disabled = options.disabled ?? false;
322
- this._theme = options.theme ?? DEFAULT_TERMINAL_THEME;
323
- this._maxLength = options.maxLength;
324
- this._password = options.password ?? false;
325
- this._multiline = options.multiline ?? false;
326
- this._wrap = options.wrap;
327
- this._continuationMarker = options.continuationMarker ?? "↳ ";
328
- this._markerWidth = asCellCol(cellLen(this._continuationMarker));
329
- this._maxRows = options.maxRows;
330
- this._minRows = options.minRows;
331
- this._scrollIndicator = options.scrollIndicator ?? "arrows";
332
- this.indicatorStyleOverride = options.indicatorStyle;
333
- this.cursorStyleOverride = options.cursorStyle;
334
- this.contentStyleOverride = options.contentStyle;
335
- this.multiline = this._multiline;
336
- }
337
- setTheme(theme) { this._theme = theme; }
338
- // --- Event handlers ---
339
- handleKey(event) {
340
- if (this.disabled)
341
- return;
342
- // ─── editing keys (modifier-conscious) ───
343
- if (event.key === "backspace") {
344
- if (event.meta || event.ctrl)
345
- this.deleteWordBack();
346
- else
347
- this.deleteCharBack();
348
- event.stop();
349
- return;
350
- }
351
- if (event.key === "delete") {
352
- this.deleteCharForward();
353
- event.stop();
354
- return;
355
- }
356
- if (event.key === "enter") {
357
- if (this._multiline && !event.ctrl)
358
- this._insertText("\n");
359
- else
360
- this.emitSubmit();
361
- event.stop();
362
- return;
363
- }
364
- // Escape is intentionally ignored — text-input does not stop it so
365
- // higher-level UI (dialogs, modals) can react to it.
366
- if (event.key === "escape")
367
- return;
368
- // ─── unmodified motion ───
369
- if (!event.ctrl && !event.meta) {
370
- switch (event.key) {
371
- case "left":
372
- this.moveCharLeft();
373
- event.stop();
374
- return;
375
- case "right":
376
- this.moveCharRight();
377
- event.stop();
378
- return;
379
- case "up":
380
- this.moveLineUp();
381
- event.stop();
382
- return;
383
- case "down":
384
- this.moveLineDown();
385
- event.stop();
386
- return;
387
- case "home":
388
- this.moveLineStart();
389
- event.stop();
390
- return;
391
- case "end":
392
- this.moveLineEnd();
393
- event.stop();
394
- return;
395
- }
396
- }
397
- // ─── Ctrl-modified motion + readline editing ───
398
- if (event.ctrl && !event.meta) {
399
- switch (event.key) {
400
- case "left":
401
- this.moveWordLeft();
402
- event.stop();
403
- return;
404
- case "right":
405
- this.moveWordRight();
406
- event.stop();
407
- return;
408
- case "home":
409
- this.moveDocStart();
410
- event.stop();
411
- return;
412
- case "end":
413
- this.moveDocEnd();
414
- event.stop();
415
- return;
416
- case "a":
417
- this.moveLineStart();
418
- event.stop();
419
- return;
420
- case "e":
421
- this.moveLineEnd();
422
- event.stop();
423
- return;
424
- case "b":
425
- this.moveCharLeft();
426
- event.stop();
427
- return;
428
- case "f":
429
- this.moveCharRight();
430
- event.stop();
431
- return;
432
- case "p":
433
- this.moveLineUp();
434
- event.stop();
435
- return;
436
- case "n":
437
- this.moveLineDown();
438
- event.stop();
439
- return;
440
- case "d":
441
- this.deleteCharForward();
442
- event.stop();
443
- return;
444
- case "h":
445
- this.deleteCharBack();
446
- event.stop();
447
- return;
448
- case "w":
449
- this.deleteWordBack();
450
- event.stop();
451
- return;
452
- case "u":
453
- this.killLineBack();
454
- event.stop();
455
- return;
456
- case "k":
457
- this.killLineForward();
458
- event.stop();
459
- return;
460
- case "y":
461
- this.yank();
462
- event.stop();
463
- return;
464
- case "t":
465
- this.transposeChars();
466
- event.stop();
467
- return;
468
- }
469
- }
470
- // ─── Alt-modified (meta) motion + editing ───
471
- if (event.meta && !event.ctrl) {
472
- switch (event.key) {
473
- case "left":
474
- this.moveWordLeft();
475
- event.stop();
476
- return;
477
- case "right":
478
- this.moveWordRight();
479
- event.stop();
480
- return;
481
- case "b":
482
- this.moveWordLeft();
483
- event.stop();
484
- return;
485
- case "f":
486
- this.moveWordRight();
487
- event.stop();
488
- return;
489
- case "d":
490
- this.deleteWordForward();
491
- event.stop();
492
- return;
493
- }
494
- }
495
- // ─── printable insertion ───
496
- // A single-char `event.character` with no command modifiers is text to
497
- // insert. Ctrl/meta-modified keys produce empty `character` from the
498
- // router (see event-router.ts) so they never reach this branch.
499
- if (event.character.length === 1 &&
500
- !event.ctrl &&
501
- !event.meta &&
502
- event.character >= " " &&
503
- event.character !== "\x7f") {
504
- this._insertText(event.character);
505
- event.stop();
506
- }
507
- }
508
- handleMouse(event) {
509
- if (this.disabled)
510
- return;
511
- if (event.type !== "mouse_down")
512
- return;
513
- const b = this.bounds;
514
- if (!b)
515
- return;
516
- if (this._multiline) {
517
- // Multiline has no brackets; rows may have a leading continuation marker.
518
- // Use event.y to pick the visual row, then project event.x into its content.
519
- const rows = this._visualRows;
520
- if (!rows || rows.length === 0)
521
- return;
522
- const relY = event.y - b.y;
523
- const rawRowIdx = this._scrollStart + relY;
524
- // Clicks on minRows-padded empty rows (beyond the last real row) snap to end.
525
- if (rawRowIdx >= rows.length) {
526
- this.cursorPosition = asCodePoint(this.value.length);
527
- this._preferredColumn = null;
528
- return;
529
- }
530
- const rowIdx = Math.max(0, rawRowIdx);
531
- const row = rows[rowIdx];
532
- // Continuation rows start after the marker; non-continuation rows start at column 0.
533
- const contentXOff = asCellCol(row.isContinuation ? this._markerWidth : 0);
534
- const relX = asCellCol(Math.max(0, event.x - b.x - contentXOff));
535
- const pos = cellColToCodeUnitOffset(row.content, relX);
536
- const absPos = row.valueStart + pos;
537
- const chu = absPos < this.value.length ? this.value.charCodeAt(absPos) : 0;
538
- const prevChu = absPos > 0 ? this.value.charCodeAt(absPos - 1) : 0;
539
- this.cursorPosition = asCodePoint(chu >= 0xDC00 && chu <= 0xDFFF && prevChu >= 0xD800 && prevChu <= 0xDBFF
540
- ? absPos + 1
541
- : absPos);
542
- }
543
- else {
544
- // [LAW:types-are-the-program] relX is a cell-column offset into the full
545
- // display string. Add _singleLineViewportStart (the scroll offset captured
546
- // by _renderSingleLine) so a click on a scrolled viewport maps to the
547
- // correct code-unit in the underlying value.
548
- const relX = asCellCol(Math.max(0, event.x - b.x - 1) + this._singleLineViewportStart);
549
- const displayForHitTest = this._password
550
- ? "•".repeat(this.value.length)
551
- : this.value.indexOf("\n") >= 0
552
- ? this.value.replace(/\n/g, NEWLINE_GLYPH)
553
- : this.value;
554
- const pos = cellColToCodeUnitOffset(displayForHitTest, relX);
555
- // Snap forward only when pos lands on a valid surrogate pair, never on a
556
- // lone low surrogate (malformed string). Verify the preceding code unit is
557
- // the high-surrogate half before advancing.
558
- const chu = pos < this.value.length ? this.value.charCodeAt(pos) : 0;
559
- const prevChu = pos > 0 ? this.value.charCodeAt(pos - 1) : 0;
560
- this.cursorPosition = asCodePoint(chu >= 0xDC00 && chu <= 0xDFFF && prevChu >= 0xD800 && prevChu <= 0xDBFF
561
- ? pos + 1
562
- : pos);
563
- }
564
- this._preferredColumn = null;
565
- }
566
- // --- Hover mutator (router fast-path) ---
567
- setHovered(value) { this.hovered = value; }
568
- // ─── Public motion primitives ───────────────────────────────────────────
569
- moveCharLeft() {
570
- this.cursorPosition = prevCodePoint(this.value, this.cursorPosition);
571
- this._preferredColumn = null;
572
- }
573
- moveCharRight() {
574
- this.cursorPosition = nextCodePoint(this.value, this.cursorPosition);
575
- this._preferredColumn = null;
576
- }
577
- moveLineUp() {
578
- // Prefer the on-screen visual decomposition when it exists — wraps,
579
- // marker offsets, and scroll alignment are all already accounted for.
580
- // Fall back to logical-line motion only when no render has occurred yet
581
- // (e.g. cursor motion before mount).
582
- if (this._visualRows !== null && this._visualRows.length > 1) {
583
- const rowIdx = this._cursorVisualRow();
584
- if (rowIdx === 0)
585
- return;
586
- const col = this._preferredColumn ?? this._cursorVisualCol();
587
- const target = this._visualRows[rowIdx - 1];
588
- this.cursorPosition = asCodePoint(target.valueStart + this._clampColForRow(col, rowIdx - 1));
589
- this._preferredColumn = col;
590
- return;
591
- }
592
- const lineStart = this._lineStart();
593
- if (lineStart === 0)
594
- return;
595
- // Fallback: no render cache yet; use cell-space arithmetic for wide-char correctness.
596
- const col = this._preferredColumn ?? asCellCol(cellLen(this.value.slice(lineStart, this.cursorPosition)));
597
- const prevLineEnd = lineStart - 1;
598
- let prevLineStart = prevLineEnd;
599
- while (prevLineStart > 0 && this.value[prevLineStart - 1] !== "\n")
600
- prevLineStart--;
601
- const prevLineContent = this.value.slice(prevLineStart, prevLineEnd);
602
- this.cursorPosition = asCodePoint(prevLineStart + cellColToCodeUnitOffset(prevLineContent, asCellCol(Math.min(col, cellLen(prevLineContent)))));
603
- this._preferredColumn = col;
604
- }
605
- moveLineDown() {
606
- if (this._visualRows !== null && this._visualRows.length > 1) {
607
- const rowIdx = this._cursorVisualRow();
608
- if (rowIdx === this._visualRows.length - 1)
609
- return;
610
- const col = this._preferredColumn ?? this._cursorVisualCol();
611
- this.cursorPosition = asCodePoint(this._visualRows[rowIdx + 1].valueStart + this._clampColForRow(col, rowIdx + 1));
612
- this._preferredColumn = col;
613
- return;
614
- }
615
- const lineEnd = this._lineEnd();
616
- if (lineEnd === this.value.length)
617
- return;
618
- const lineStart = this._lineStart();
619
- // Fallback: no render cache yet; use cell-space arithmetic for wide-char correctness.
620
- const col = this._preferredColumn ?? asCellCol(cellLen(this.value.slice(lineStart, this.cursorPosition)));
621
- const nextLineStart = lineEnd + 1;
622
- let nextLineEnd = nextLineStart;
623
- while (nextLineEnd < this.value.length && this.value[nextLineEnd] !== "\n")
624
- nextLineEnd++;
625
- const nextLineContent = this.value.slice(nextLineStart, nextLineEnd);
626
- this.cursorPosition = asCodePoint(nextLineStart + cellColToCodeUnitOffset(nextLineContent, asCellCol(Math.min(col, cellLen(nextLineContent)))));
627
- this._preferredColumn = col;
628
- }
629
- // Clamp `col` (a cell column) to the target visual row's content, returning
630
- // the code-unit offset within that row. When the row IS followed by a
631
- // continuation of the same logical line, we clamp to the last valid
632
- // code-point boundary strictly inside the row (which may be more than
633
- // one cell before the visual end when the last code point is wide) so the
634
- // cursor stays strictly inside the target row — landing at the boundary
635
- // would cause `_cursorVisualRow` to resolve to the *later* row, making
636
- // every subsequent Up/Down stick at the boundary.
637
- //
638
- // For rows NOT followed by a continuation (last wrap row, or any unwrapped
639
- // logical line), the end-of-line position is valid — there is no adjacent
640
- // continuation row to collide with.
641
- _clampColForRow(col, targetIdx) {
642
- const rows = this._visualRows;
643
- const target = rows[targetIdx];
644
- const nextIsContinuation = targetIdx + 1 < rows.length && rows[targetIdx + 1].isContinuation;
645
- const contentCellWidth = asCellCol(cellLen(target.content));
646
- const capCells = nextIsContinuation
647
- ? asCellCol(Math.max(0, contentCellWidth - 1))
648
- : contentCellWidth;
649
- return cellColToCodeUnitOffset(target.content, asCellCol(Math.min(col, capCells)));
650
- }
651
- // Locate which cached visual row the cursor sits on. Returns the row index
652
- // for use in `moveLineUp`/`moveLineDown`; caller must guard `_visualRows`.
653
- _cursorVisualRow() {
654
- const rows = this._visualRows;
655
- let idx = 0;
656
- for (let i = rows.length - 1; i >= 0; i--) {
657
- if (rows[i].valueStart <= this.cursorPosition) {
658
- idx = i;
659
- break;
660
- }
661
- }
662
- return idx;
663
- }
664
- // Returns the cursor's cell column within its current visual row.
665
- _cursorVisualCol() {
666
- const rows = this._visualRows;
667
- const idx = this._cursorVisualRow();
668
- const row = rows[idx];
669
- return asCellCol(cellLen(row.content.slice(0, this.cursorPosition - row.valueStart)));
670
- }
671
- moveLineStart() {
672
- this.cursorPosition = this._lineStart();
673
- this._preferredColumn = null;
674
- }
675
- moveLineEnd() {
676
- this.cursorPosition = this._lineEnd();
677
- this._preferredColumn = null;
678
- }
679
- moveDocStart() {
680
- this.cursorPosition = asCodePoint(0);
681
- this._preferredColumn = null;
682
- }
683
- moveDocEnd() {
684
- this.cursorPosition = asCodePoint(this.value.length);
685
- this._preferredColumn = null;
686
- }
687
- moveWordLeft() {
688
- let p = this.cursorPosition;
689
- while (p > 0) {
690
- const prev = prevCodePoint(this.value, p);
691
- if (isWordChar(this.value.slice(prev, p)))
692
- break;
693
- p = prev;
694
- }
695
- while (p > 0) {
696
- const prev = prevCodePoint(this.value, p);
697
- if (!isWordChar(this.value.slice(prev, p)))
698
- break;
699
- p = prev;
700
- }
701
- this.cursorPosition = p;
702
- this._preferredColumn = null;
703
- }
704
- moveWordRight() {
705
- let p = this.cursorPosition;
706
- while (p < this.value.length) {
707
- const next = nextCodePoint(this.value, p);
708
- if (isWordChar(this.value.slice(p, next)))
709
- break;
710
- p = next;
711
- }
712
- while (p < this.value.length) {
713
- const next = nextCodePoint(this.value, p);
714
- if (!isWordChar(this.value.slice(p, next)))
715
- break;
716
- p = next;
717
- }
718
- this.cursorPosition = p;
719
- this._preferredColumn = null;
720
- }
721
- // ─── Public editing primitives ──────────────────────────────────────────
722
- deleteCharBack() {
723
- if (this.cursorPosition === 0)
724
- return;
725
- const newPos = prevCodePoint(this.value, this.cursorPosition);
726
- this.value = this.value.slice(0, newPos) + this.value.slice(this.cursorPosition);
727
- this.cursorPosition = newPos;
728
- this._preferredColumn = null;
729
- this.emitChange();
730
- }
731
- deleteCharForward() {
732
- if (this.cursorPosition >= this.value.length)
733
- return;
734
- const nextPos = nextCodePoint(this.value, this.cursorPosition);
735
- this.value = this.value.slice(0, this.cursorPosition) + this.value.slice(nextPos);
736
- this._preferredColumn = null;
737
- this.emitChange();
738
- }
739
- deleteWordBack() {
740
- // Readline `unix-word-rubout` semantics: delete back to nearest whitespace,
741
- // skipping trailing whitespace first so successive Ctrl+W at "foo bar |"
742
- // → "foo |" → "|" rather than getting stuck on the trailing space.
743
- let p = this.cursorPosition;
744
- while (p > 0) {
745
- const prev = prevCodePoint(this.value, p);
746
- if (!isWhitespace(this.value.slice(prev, p)))
747
- break;
748
- p = prev;
749
- }
750
- while (p > 0) {
751
- const prev = prevCodePoint(this.value, p);
752
- if (isWhitespace(this.value.slice(prev, p)))
753
- break;
754
- p = prev;
755
- }
756
- if (p === this.cursorPosition)
757
- return;
758
- this._killBuffer = this.value.slice(p, this.cursorPosition);
759
- this.value = this.value.slice(0, p) + this.value.slice(this.cursorPosition);
760
- this.cursorPosition = p;
761
- this._preferredColumn = null;
762
- this.emitChange();
763
- }
764
- deleteWordForward() {
765
- let p = this.cursorPosition;
766
- while (p < this.value.length) {
767
- const next = nextCodePoint(this.value, p);
768
- if (isWordChar(this.value.slice(p, next)))
769
- break;
770
- p = next;
771
- }
772
- while (p < this.value.length) {
773
- const next = nextCodePoint(this.value, p);
774
- if (!isWordChar(this.value.slice(p, next)))
775
- break;
776
- p = next;
777
- }
778
- if (p === this.cursorPosition)
779
- return;
780
- this._killBuffer = this.value.slice(this.cursorPosition, p);
781
- this.value = this.value.slice(0, this.cursorPosition) + this.value.slice(p);
782
- this._preferredColumn = null;
783
- this.emitChange();
784
- }
785
- killLineBack() {
786
- const start = this._lineStart();
787
- if (start === this.cursorPosition)
788
- return;
789
- this._killBuffer = this.value.slice(start, this.cursorPosition);
790
- this.value = this.value.slice(0, start) + this.value.slice(this.cursorPosition);
791
- this.cursorPosition = start;
792
- this._preferredColumn = null;
793
- this.emitChange();
794
- }
795
- killLineForward() {
796
- const end = this._lineEnd();
797
- if (end > this.cursorPosition) {
798
- this._killBuffer = this.value.slice(this.cursorPosition, end);
799
- this.value = this.value.slice(0, this.cursorPosition) + this.value.slice(end);
800
- this._preferredColumn = null;
801
- this.emitChange();
802
- return;
803
- }
804
- // Already at line end → kill the trailing `\n` (join next line).
805
- if (this.cursorPosition < this.value.length) {
806
- this._killBuffer = "\n";
807
- this.value = this.value.slice(0, this.cursorPosition) + this.value.slice(this.cursorPosition + 1);
808
- this._preferredColumn = null;
809
- this.emitChange();
810
- }
811
- }
812
- yank() {
813
- if (this._killBuffer.length === 0)
814
- return;
815
- this._insertText(this._killBuffer);
816
- }
817
- transposeChars() {
818
- // Readline `transpose-chars`: swap the char before the cursor with the
819
- // char at the cursor and advance. At end-of-value, swap the trailing
820
- // two chars without advancing. At position 0, no-op.
821
- const len = this.value.length;
822
- if (len < 2 || this.cursorPosition === 0)
823
- return;
824
- const p = this.cursorPosition;
825
- if (p === len) {
826
- // End-of-value: swap the trailing two code points; cursor stays.
827
- const cp2 = prevCodePoint(this.value, p);
828
- const cp1 = prevCodePoint(this.value, cp2);
829
- this.value = this.value.slice(0, cp1) + this.value.slice(cp2) + this.value.slice(cp1, cp2);
830
- this._preferredColumn = null;
831
- this.emitChange();
832
- return;
833
- }
834
- // Normal case: find full code-point substrings for the char before and at cursor.
835
- const cpBefore = prevCodePoint(this.value, p);
836
- const cpAfter = nextCodePoint(this.value, p);
837
- const charBefore = this.value.slice(cpBefore, p);
838
- const charAt = this.value.slice(p, cpAfter);
839
- this.value = this.value.slice(0, cpBefore) + charAt + charBefore + this.value.slice(cpAfter);
840
- this.cursorPosition = asCodePoint(cpAfter);
841
- this._preferredColumn = null;
842
- this.emitChange();
843
- }
844
- // ─── Internal helpers ───────────────────────────────────────────────────
845
- _lineStart() {
846
- let p = this.cursorPosition;
847
- while (p > 0 && this.value[p - 1] !== "\n")
848
- p--;
849
- return asCodePoint(p);
850
- }
851
- _lineEnd() {
852
- let p = this.cursorPosition;
853
- while (p < this.value.length && this.value[p] !== "\n")
854
- p++;
855
- return asCodePoint(p);
856
- }
857
- _insertText(text) {
858
- let toInsert = text;
859
- if (this._maxLength !== undefined) {
860
- const room = this._maxLength - this.value.length;
861
- if (room <= 0)
862
- return;
863
- if (toInsert.length > room)
864
- toInsert = toInsert.slice(0, room);
865
- }
866
- this.value =
867
- this.value.slice(0, this.cursorPosition) +
868
- toInsert +
869
- this.value.slice(this.cursorPosition);
870
- this.cursorPosition = asCodePoint(this.cursorPosition + toInsert.length);
871
- this._preferredColumn = null;
872
- this.emitChange();
873
- }
874
- // --- Rendering ---
875
- render(options) {
876
- if (this._multiline)
877
- return this._renderMultiline(options);
878
- return this._renderSingleLine(options);
879
- }
880
- _renderSingleLine(options) {
881
- const showPlaceholder = this.focused && this.value.length === 0 && this.placeholder.length > 0;
882
- // [LAW:dataflow-not-control-flow] Single rawDisplay value derives from
883
- // mode (password / placeholder / plain). NEWLINE_GLYPH substitution maps
884
- // every code unit in value 1:1 to rawDisplay so cursorPosition (a code-
885
- // unit index) is a valid index into rawDisplay regardless of mode.
886
- const rawDisplay = showPlaceholder
887
- ? this.placeholder
888
- : this._password
889
- ? "•".repeat(this.value.length)
890
- : this.value.indexOf("\n") >= 0
891
- ? this.value.replace(/\n/g, NEWLINE_GLYPH)
892
- : this.value;
893
- // Measure in cell space — cursorPosition is a code-unit index; convert to
894
- // cell column for all viewport arithmetic.
895
- const rawCellWidth = asCellCol(cellLen(rawDisplay));
896
- const cursorCellCol = asCellCol(cellLen(rawDisplay.slice(0, this.cursorPosition)));
897
- const maxAvailable = asCellCol(Math.max(MIN_CONTENT_WIDTH, options.maxWidth - 2));
898
- const desiredWidth = asCellCol(Math.max(MIN_CONTENT_WIDTH, rawCellWidth, cursorCellCol + 1));
899
- const contentWidth = asCellCol(Math.min(maxAvailable, desiredWidth));
900
- // Slide viewport (in cells) to keep cursor visible when content overflows.
901
- const startCell = asCellCol(Math.max(0, Math.min(rawCellWidth - contentWidth, cursorCellCol - contentWidth + 1)));
902
- const [, afterStart] = splitText(rawDisplay, startCell);
903
- // splitText snaps backward when startCell falls mid-wide-char, so afterStart
904
- // may start 1 cell earlier than requested. actualStartCell is the real offset.
905
- const actualStartCell = asCellCol(rawCellWidth - cellLen(afterStart));
906
- this._singleLineViewportStart = actualStartCell;
907
- const [visible] = splitText(afterStart, contentWidth);
908
- const display = setCellSize(visible, contentWidth);
909
- const cursorDisplayCellCol = asCellCol(cursorCellCol - actualStartCell);
910
- const bracketStyle = this.disabled
911
- ? new Style({ color: "#666666", bgcolor: "#333333", dim: true })
912
- : new Style({ color: this.resolvePalette("foreground") });
913
- const contentStyle = this.disabled
914
- ? new Style({ color: "#666666", bgcolor: "#333333", dim: true })
915
- : showPlaceholder
916
- ? new Style({ color: this.resolvePalette("foreground"), dim: true })
917
- : this.contentStyleOverride ?? new Style({ color: this.resolvePalette("foreground") });
918
- const cursorStyle = this.cursorStyleOverride ?? new Style({
919
- color: this.resolvePalette("on-primary"),
920
- bgcolor: this.resolvePalette("primary"),
921
- });
922
- const segments = [new Segment("[", bracketStyle)];
923
- if (this.focused && !this.disabled && cursorDisplayCellCol >= 0 && cursorDisplayCellCol < contentWidth) {
924
- const [before, rest] = splitText(display, cursorDisplayCellCol);
925
- let firstCh = "";
926
- for (const ch of rest) {
927
- firstCh = ch;
928
- break;
929
- }
930
- const at = firstCh || " ";
931
- const after = rest.slice(firstCh.length);
932
- if (before.length > 0)
933
- segments.push(new Segment(before, contentStyle));
934
- segments.push(new Segment(at, cursorStyle));
935
- if (after.length > 0)
936
- segments.push(new Segment(after, contentStyle));
937
- }
938
- else {
939
- segments.push(new Segment(display, contentStyle));
940
- }
941
- segments.push(new Segment("]", bracketStyle));
942
- return segments;
943
- }
944
- _renderMultiline(options) {
945
- // [LAW:dataflow-not-control-flow] Decompose `value` into visual rows
946
- // exactly once per render, cache the result, then drive both display
947
- // *and* cursor projection from the same array. Vertical motion reads
948
- // from the cache so Up/Down step through whatever the user actually
949
- // sees, including soft-wrap continuations.
950
- const visualRows = this._computeVisualRows(options.maxWidth);
951
- this._visualRows = visualRows;
952
- // Scroll window: keep `_scrollStart` (the viewport's top row) stable
953
- // across renders, adjusting only when the cursor would leave the
954
- // viewport. This is the textarea-conventional "viewport follows cursor
955
- // when it has to" behavior — cursor moves freely *within* the visible
956
- // rows; the window scrolls only at the edges. When `maxRows` is unset,
957
- // render every row.
958
- const total = visualRows.length;
959
- const cursorRow = this._cursorVisualRow();
960
- let scrollStart = 0;
961
- let visibleCount = total;
962
- if (this._maxRows !== undefined && total > this._maxRows) {
963
- const maxStart = total - this._maxRows;
964
- if (cursorRow < this._scrollStart) {
965
- this._scrollStart = cursorRow;
966
- }
967
- else if (cursorRow >= this._scrollStart + this._maxRows) {
968
- this._scrollStart = cursorRow - this._maxRows + 1;
969
- }
970
- this._scrollStart = Math.max(0, Math.min(maxStart, this._scrollStart));
971
- scrollStart = this._scrollStart;
972
- visibleCount = this._maxRows;
973
- }
974
- else {
975
- this._scrollStart = 0;
976
- }
977
- // Pad with empty rows when the value is shorter than `minRows`.
978
- let padRows = 0;
979
- if (this._minRows !== undefined && total < this._minRows && this._maxRows === undefined) {
980
- padRows = this._minRows - total;
981
- }
982
- const contentStyle = this.disabled
983
- ? new Style({ color: "#666666", bgcolor: "#333333", dim: true })
984
- : this.contentStyleOverride ?? new Style({ color: this.resolvePalette("foreground") });
985
- const markerStyle = new Style({ color: this.resolvePalette("foreground"), dim: true });
986
- const cursorStyle = this.cursorStyleOverride ?? new Style({
987
- color: this.resolvePalette("on-primary"),
988
- bgcolor: this.resolvePalette("primary"),
989
- });
990
- // Scroll-direction arrows appear in the rightmost cell of the first/last
991
- // visible row, *only* when scroll is actually possible in that direction.
992
- // _emitRowContent reserves the indicator's cells so content never
993
- // collides with the arrow. Only emitted in `"arrows"` mode; other modes
994
- // leave the content area untouched and rely on the host to surface scroll
995
- // state externally (e.g. Panel border accessory).
996
- const scrollable = this._maxRows !== undefined && total > this._maxRows;
997
- const arrowsMode = this._scrollIndicator === "arrows";
998
- const canScrollUp = arrowsMode && scrollable && this._scrollStart > 0;
999
- const canScrollDown = arrowsMode && scrollable && this._scrollStart + this._maxRows < total;
1000
- const indicatorStyle = this.indicatorStyleOverride ?? new Style({ color: this.resolvePalette("primary") });
1001
- const segments = [];
1002
- const showCursor = this.focused && !this.disabled;
1003
- for (let i = 0; i < visibleCount; i++) {
1004
- const rowIdx = scrollStart + i;
1005
- const row = visualRows[rowIdx];
1006
- if (i > 0)
1007
- segments.push(new Segment("\n"));
1008
- if (row.isContinuation) {
1009
- segments.push(new Segment(this._continuationMarker, markerStyle));
1010
- }
1011
- let indicator;
1012
- if (i === 0 && canScrollUp) {
1013
- indicator = { ch: "▲", style: indicatorStyle };
1014
- }
1015
- else if (i === visibleCount - 1 && canScrollDown) {
1016
- indicator = { ch: "▼", style: indicatorStyle };
1017
- }
1018
- // When a continuation marker was emitted, the row's printable width is
1019
- // reduced by the marker width — the indicator column is still at
1020
- // `options.maxWidth - 1` in the panel's view, which is
1021
- // `options.maxWidth - markerWidth - 1` columns past the marker.
1022
- const rowPrintWidth = row.isContinuation
1023
- ? asCellCol(Math.max(0, options.maxWidth - this._markerWidth))
1024
- : asCellCol(options.maxWidth);
1025
- this._emitRowContent(segments, row, rowIdx === cursorRow && showCursor, contentStyle, cursorStyle, indicator, rowPrintWidth);
1026
- }
1027
- // Trailing empty rows for minRows padding (no cursor, no marker).
1028
- // Emit a whitespace segment of the full content width so Segment.splitLines
1029
- // does not drop the row — an empty segment before a newline would produce
1030
- // an invisible row that the renderer discards.
1031
- for (let i = 0; i < padRows; i++) {
1032
- segments.push(new Segment("\n"));
1033
- segments.push(new Segment(setCellSize("", asCellCol(options.maxWidth)), contentStyle));
1034
- }
1035
- return segments;
1036
- }
1037
- _emitRowContent(out, row, cursorOnRow, contentStyle, cursorStyle, indicator, rowPrintWidth) {
1038
- const content = row.content;
1039
- // cursorCol is a code-unit offset into row.content; both cursorPosition
1040
- // and valueStart are CodePoint, so their difference lands on a code-point
1041
- // boundary within content and is safe for string slicing.
1042
- const cursorCol = cursorOnRow ? this.cursorPosition - row.valueStart : -1;
1043
- // Fast path: no indicator on this row. Let the wrapper pad to width.
1044
- if (indicator === undefined) {
1045
- if (!cursorOnRow) {
1046
- if (content.length > 0)
1047
- out.push(new Segment(content, contentStyle));
1048
- return;
1049
- }
1050
- const before = content.slice(0, cursorCol);
1051
- const nextCp = nextCodePoint(content, asCodePoint(cursorCol));
1052
- const at = content.slice(cursorCol, nextCp) || " ";
1053
- const after = content.slice(nextCp);
1054
- if (before.length > 0)
1055
- out.push(new Segment(before, contentStyle));
1056
- out.push(new Segment(at, cursorStyle));
1057
- if (after.length > 0)
1058
- out.push(new Segment(after, contentStyle));
1059
- return;
1060
- }
1061
- // Indicator path: reserve the indicator's cells at the end of the row so
1062
- // wide characters never collide with it. Content fills [0, contentCellWidth)
1063
- // cells; the indicator occupies the remaining cells.
1064
- const indicatorWidth = asCellCol(cellLen(indicator.ch));
1065
- const contentCellWidth = asCellCol(Math.max(0, rowPrintWidth - indicatorWidth));
1066
- // Clip row content to the non-indicator region and pad to exact cell width.
1067
- const [visibleContent] = splitText(content, contentCellWidth);
1068
- const paddedContent = setCellSize(visibleContent, contentCellWidth);
1069
- // Map the code-unit cursor offset to a cell column within this row.
1070
- const cursorCellColInRow = cursorOnRow && cursorCol >= 0
1071
- ? cellLen(content.slice(0, cursorCol))
1072
- : -1;
1073
- if (cursorCellColInRow >= 0 && cursorCellColInRow < contentCellWidth) {
1074
- const [before, rest] = splitText(paddedContent, asCellCol(cursorCellColInRow));
1075
- let firstCh = "";
1076
- for (const ch of rest) {
1077
- firstCh = ch;
1078
- break;
1079
- }
1080
- const at = firstCh || " ";
1081
- const after = rest.slice(firstCh.length);
1082
- if (before.length > 0)
1083
- out.push(new Segment(before, contentStyle));
1084
- out.push(new Segment(at, cursorStyle));
1085
- if (after.length > 0)
1086
- out.push(new Segment(after, contentStyle));
1087
- }
1088
- else {
1089
- if (paddedContent.length > 0)
1090
- out.push(new Segment(paddedContent, contentStyle));
1091
- }
1092
- out.push(new Segment(indicator.ch, indicator.style));
1093
- }
1094
- _computeVisualRows(maxWidth) {
1095
- const firstWidth = asCellCol(Math.max(1, maxWidth));
1096
- const continuationWidth = asCellCol(Math.max(1, firstWidth - this._markerWidth));
1097
- const rows = [];
1098
- const lines = this.value.split("\n");
1099
- let pos = 0;
1100
- for (let li = 0; li < lines.length; li++) {
1101
- const line = lines[li];
1102
- if (this._wrap !== undefined) {
1103
- const wrapRows = this._wrap(line, { firstWidth, continuationWidth });
1104
- if (wrapRows.length === 0) {
1105
- rows.push({ content: "", valueStart: asCodePoint(pos), isContinuation: false });
1106
- }
1107
- else {
1108
- for (let ri = 0; ri < wrapRows.length; ri++) {
1109
- const wr = wrapRows[ri];
1110
- rows.push({
1111
- content: wr.content,
1112
- valueStart: asCodePoint(pos + wr.start),
1113
- isContinuation: ri > 0,
1114
- });
1115
- }
1116
- }
1117
- }
1118
- else {
1119
- rows.push({ content: line, valueStart: asCodePoint(pos), isContinuation: false });
1120
- }
1121
- pos += line.length + 1; // +1 for the \n separator
1122
- }
1123
- return rows;
1124
- }
1125
- measure(_options) {
1126
- const minimum = MIN_CONTENT_WIDTH + 2;
1127
- const maximum = Math.max(minimum, Math.max(cellLen(this.value), cellLen(this.placeholder)) + 2);
1128
- return { minimum, maximum };
1129
- }
1130
- /**
1131
- * `[X/Y]` scroll-position string when there's actually something to
1132
- * scroll, else `undefined`. X is the cursor's 1-indexed visual row; Y is
1133
- * the total visual row count. Reads cached state populated by the most
1134
- * recent `render()` — call from a `Panel.bottomRightAccessory` thunk so
1135
- * it evaluates after content has been rendered for the current frame.
1136
- *
1137
- * Returns `undefined` when:
1138
- * - `scrollIndicator` is not `"indices"`, or
1139
- * - `maxRows` is unset, or total visual rows ≤ maxRows (nothing to
1140
- * scroll), or
1141
- * - no render has happened yet (cache is empty).
1142
- */
1143
- get scrollIndicatorText() {
1144
- if (this._scrollIndicator !== "indices")
1145
- return undefined;
1146
- if (this._maxRows === undefined)
1147
- return undefined;
1148
- const rows = this._visualRows;
1149
- if (rows === null || rows.length <= this._maxRows)
1150
- return undefined;
1151
- const cursorRow1 = this._cursorVisualRow() + 1;
1152
- return `[${cursorRow1}/${rows.length}]`;
1153
- }
1154
- // --- Palette resolution ---
1155
- resolvePalette(key) {
1156
- const rgba = this._theme.palette.get(key);
1157
- // [LAW:no-defensive-null-guards] palette must contain all keys.
1158
- return ColorSpec.fromRgba(rgba);
1159
- }
1160
- };
1161
- })();
1162
- export { TextInput };