@promptctl/rich-js 0.18.0 → 0.18.1

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