@suds-cli/textinput 0.0.1-alpha.0 → 0.1.0-alpha.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.
- package/dist/index.cjs +665 -0
- package/dist/index.cjs.map +1 -0
- package/dist/{model.d.ts → index.d.cts} +92 -5
- package/dist/index.d.ts +173 -4
- package/dist/index.js +649 -3
- package/dist/index.js.map +1 -1
- package/package.json +31 -18
- package/dist/index.d.ts.map +0 -1
- package/dist/messages.d.ts +0 -26
- package/dist/messages.d.ts.map +0 -1
- package/dist/messages.js +0 -40
- package/dist/messages.js.map +0 -1
- package/dist/model.d.ts.map +0 -1
- package/dist/model.js +0 -613
- package/dist/model.js.map +0 -1
- package/dist/types.d.ts +0 -63
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -33
- package/dist/types.js.map +0 -1
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chapstick = require('@suds-cli/chapstick');
|
|
4
|
+
var cursor = require('@suds-cli/cursor');
|
|
5
|
+
var key = require('@suds-cli/key');
|
|
6
|
+
var tea = require('@suds-cli/tea');
|
|
7
|
+
var runeutil = require('@suds-cli/runeutil');
|
|
8
|
+
var GraphemeSplitter = require('grapheme-splitter');
|
|
9
|
+
var clipboard = require('clipboardy');
|
|
10
|
+
|
|
11
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
|
+
|
|
13
|
+
var GraphemeSplitter__default = /*#__PURE__*/_interopDefault(GraphemeSplitter);
|
|
14
|
+
var clipboard__default = /*#__PURE__*/_interopDefault(clipboard);
|
|
15
|
+
|
|
16
|
+
// src/model.ts
|
|
17
|
+
var PasteMsg = class {
|
|
18
|
+
constructor(text) {
|
|
19
|
+
this.text = text;
|
|
20
|
+
}
|
|
21
|
+
_tag = "textinput/paste";
|
|
22
|
+
};
|
|
23
|
+
var PasteErrorMsg = class {
|
|
24
|
+
constructor(error) {
|
|
25
|
+
this.error = error;
|
|
26
|
+
}
|
|
27
|
+
_tag = "textinput/paste-error";
|
|
28
|
+
};
|
|
29
|
+
function pasteCommand() {
|
|
30
|
+
return async () => {
|
|
31
|
+
try {
|
|
32
|
+
const text = await clipboard__default.default.read();
|
|
33
|
+
return new PasteMsg(text ?? "");
|
|
34
|
+
} catch (error) {
|
|
35
|
+
return new PasteErrorMsg(error);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
var EchoMode = /* @__PURE__ */ ((EchoMode2) => {
|
|
40
|
+
EchoMode2["Normal"] = "normal";
|
|
41
|
+
EchoMode2["Password"] = "password";
|
|
42
|
+
EchoMode2["None"] = "none";
|
|
43
|
+
return EchoMode2;
|
|
44
|
+
})(EchoMode || {});
|
|
45
|
+
var defaultKeyMap = {
|
|
46
|
+
characterForward: key.newBinding({ keys: ["right", "ctrl+f"] }),
|
|
47
|
+
characterBackward: key.newBinding({ keys: ["left", "ctrl+b"] }),
|
|
48
|
+
wordForward: key.newBinding({ keys: ["alt+right", "ctrl+right", "alt+f"] }),
|
|
49
|
+
wordBackward: key.newBinding({ keys: ["alt+left", "ctrl+left", "alt+b"] }),
|
|
50
|
+
deleteWordBackward: key.newBinding({ keys: ["alt+backspace", "ctrl+w"] }),
|
|
51
|
+
deleteWordForward: key.newBinding({ keys: ["alt+delete", "alt+d"] }),
|
|
52
|
+
deleteAfterCursor: key.newBinding({ keys: ["ctrl+k"] }),
|
|
53
|
+
deleteBeforeCursor: key.newBinding({ keys: ["ctrl+u"] }),
|
|
54
|
+
deleteCharacterBackward: key.newBinding({ keys: ["backspace", "ctrl+h"] }),
|
|
55
|
+
deleteCharacterForward: key.newBinding({ keys: ["delete", "ctrl+d"] }),
|
|
56
|
+
lineStart: key.newBinding({ keys: ["home", "ctrl+a"] }),
|
|
57
|
+
lineEnd: key.newBinding({ keys: ["end", "ctrl+e"] }),
|
|
58
|
+
paste: key.newBinding({ keys: ["ctrl+v"] })
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// src/model.ts
|
|
62
|
+
var splitter = new GraphemeSplitter__default.default();
|
|
63
|
+
var sanitizer = runeutil.newSanitizer({ replaceNewLine: " ", replaceTab: " " });
|
|
64
|
+
var TextInputModel = class _TextInputModel {
|
|
65
|
+
value;
|
|
66
|
+
position;
|
|
67
|
+
focused;
|
|
68
|
+
placeholder;
|
|
69
|
+
echoMode;
|
|
70
|
+
echoCharacter;
|
|
71
|
+
charLimit;
|
|
72
|
+
width;
|
|
73
|
+
prompt;
|
|
74
|
+
promptStyle;
|
|
75
|
+
textStyle;
|
|
76
|
+
placeholderStyle;
|
|
77
|
+
cursorStyle;
|
|
78
|
+
validateFn;
|
|
79
|
+
keyMap;
|
|
80
|
+
cursor;
|
|
81
|
+
error;
|
|
82
|
+
constructor(state) {
|
|
83
|
+
this.value = state.value;
|
|
84
|
+
this.position = state.position;
|
|
85
|
+
this.focused = state.focused;
|
|
86
|
+
this.placeholder = state.placeholder;
|
|
87
|
+
this.echoMode = state.echoMode;
|
|
88
|
+
this.echoCharacter = state.echoCharacter;
|
|
89
|
+
this.charLimit = state.charLimit;
|
|
90
|
+
this.width = state.width;
|
|
91
|
+
this.prompt = state.prompt;
|
|
92
|
+
this.promptStyle = state.promptStyle;
|
|
93
|
+
this.textStyle = state.textStyle;
|
|
94
|
+
this.placeholderStyle = state.placeholderStyle;
|
|
95
|
+
this.cursorStyle = state.cursorStyle;
|
|
96
|
+
this.validateFn = state.validateFn;
|
|
97
|
+
this.keyMap = state.keyMap;
|
|
98
|
+
this.cursor = state.cursor;
|
|
99
|
+
this.error = state.error;
|
|
100
|
+
}
|
|
101
|
+
/** Create a new text input model. */
|
|
102
|
+
static new(options = {}) {
|
|
103
|
+
const sanitized = clampToLimit(
|
|
104
|
+
sanitizeValue(options.value ?? ""),
|
|
105
|
+
options.charLimit ?? 0
|
|
106
|
+
);
|
|
107
|
+
const baseTextStyle = options.textStyle ?? new chapstick.Style();
|
|
108
|
+
const cursorMode = options.cursorMode ?? cursor.CursorMode.Blink;
|
|
109
|
+
const cursor$1 = new cursor.CursorModel({
|
|
110
|
+
style: options.cursorStyle ?? new chapstick.Style(),
|
|
111
|
+
textStyle: baseTextStyle,
|
|
112
|
+
mode: cursorMode,
|
|
113
|
+
focused: false,
|
|
114
|
+
char: " "
|
|
115
|
+
});
|
|
116
|
+
const model = new _TextInputModel({
|
|
117
|
+
value: sanitized,
|
|
118
|
+
position: runeCount(sanitized),
|
|
119
|
+
focused: false,
|
|
120
|
+
placeholder: options.placeholder ?? "",
|
|
121
|
+
echoMode: options.echoMode ?? "normal" /* Normal */,
|
|
122
|
+
echoCharacter: options.echoCharacter ?? "\u2022",
|
|
123
|
+
charLimit: options.charLimit ?? 0,
|
|
124
|
+
width: options.width ?? 0,
|
|
125
|
+
prompt: options.prompt ?? "> ",
|
|
126
|
+
promptStyle: options.promptStyle ?? new chapstick.Style(),
|
|
127
|
+
textStyle: baseTextStyle,
|
|
128
|
+
placeholderStyle: options.placeholderStyle ?? new chapstick.Style(),
|
|
129
|
+
cursorStyle: options.cursorStyle ?? new chapstick.Style(),
|
|
130
|
+
validateFn: options.validate,
|
|
131
|
+
keyMap: options.keyMap ?? defaultKeyMap,
|
|
132
|
+
cursor: cursor$1,
|
|
133
|
+
error: null
|
|
134
|
+
});
|
|
135
|
+
return model.#withError(model.#runValidation(sanitized));
|
|
136
|
+
}
|
|
137
|
+
/** Initial command (none needed by default). */
|
|
138
|
+
init() {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
/** Current value. */
|
|
142
|
+
valueOf() {
|
|
143
|
+
return this.value;
|
|
144
|
+
}
|
|
145
|
+
/** Length in graphemes. */
|
|
146
|
+
length() {
|
|
147
|
+
return runeCount(this.value);
|
|
148
|
+
}
|
|
149
|
+
/** True if no content. */
|
|
150
|
+
isEmpty() {
|
|
151
|
+
return this.length() === 0;
|
|
152
|
+
}
|
|
153
|
+
/** Cursor position in graphemes. */
|
|
154
|
+
cursorPosition() {
|
|
155
|
+
return this.position;
|
|
156
|
+
}
|
|
157
|
+
/** Focus the input (shows cursor, enables key handling). */
|
|
158
|
+
focus() {
|
|
159
|
+
if (this.focused) {
|
|
160
|
+
return [this, null];
|
|
161
|
+
}
|
|
162
|
+
const [cursor, cmd] = this.cursor.focus();
|
|
163
|
+
const next = this.#with({ focused: true, cursor });
|
|
164
|
+
return [next, cmd];
|
|
165
|
+
}
|
|
166
|
+
/** Blur the input (hides cursor, ignores key handling). */
|
|
167
|
+
blur() {
|
|
168
|
+
if (!this.focused) {
|
|
169
|
+
return this;
|
|
170
|
+
}
|
|
171
|
+
return this.#with({ focused: false, cursor: this.cursor.blur() });
|
|
172
|
+
}
|
|
173
|
+
/** Move cursor to start. */
|
|
174
|
+
cursorStart() {
|
|
175
|
+
return this.#withPosition(0);
|
|
176
|
+
}
|
|
177
|
+
/** Move cursor to end. */
|
|
178
|
+
cursorEnd() {
|
|
179
|
+
return this.#withPosition(this.length());
|
|
180
|
+
}
|
|
181
|
+
/** Move cursor left by n (default 1). */
|
|
182
|
+
cursorLeft(n = 1) {
|
|
183
|
+
return this.#withPosition(Math.max(0, this.position - Math.max(1, n)));
|
|
184
|
+
}
|
|
185
|
+
/** Move cursor right by n (default 1). */
|
|
186
|
+
cursorRight(n = 1) {
|
|
187
|
+
return this.#withPosition(
|
|
188
|
+
Math.min(this.length(), this.position + Math.max(1, n))
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
/** Move cursor one word to the left. */
|
|
192
|
+
wordLeft() {
|
|
193
|
+
if (this.position === 0 || this.isEmpty()) {
|
|
194
|
+
return this;
|
|
195
|
+
}
|
|
196
|
+
if (this.echoMode !== "normal" /* Normal */) {
|
|
197
|
+
return this.cursorStart();
|
|
198
|
+
}
|
|
199
|
+
const graphemes = splitGraphemes(this.value);
|
|
200
|
+
let i = this.position - 1;
|
|
201
|
+
while (i >= 0) {
|
|
202
|
+
const g = graphemes[i];
|
|
203
|
+
if (g && isWhitespace(g)) {
|
|
204
|
+
i--;
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
while (i >= 0) {
|
|
210
|
+
const g = graphemes[i];
|
|
211
|
+
if (g && !isWhitespace(g)) {
|
|
212
|
+
i--;
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
return this.#withPosition(Math.max(0, i + 1));
|
|
218
|
+
}
|
|
219
|
+
/** Move cursor one word to the right. */
|
|
220
|
+
wordRight() {
|
|
221
|
+
if (this.position >= this.length() || this.isEmpty()) {
|
|
222
|
+
return this;
|
|
223
|
+
}
|
|
224
|
+
if (this.echoMode !== "normal" /* Normal */) {
|
|
225
|
+
return this.cursorEnd();
|
|
226
|
+
}
|
|
227
|
+
const graphemes = splitGraphemes(this.value);
|
|
228
|
+
let i = this.position;
|
|
229
|
+
while (i < graphemes.length) {
|
|
230
|
+
const g = graphemes[i];
|
|
231
|
+
if (g && isWhitespace(g)) {
|
|
232
|
+
i++;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
while (i < graphemes.length) {
|
|
238
|
+
const g = graphemes[i];
|
|
239
|
+
if (g && !isWhitespace(g)) {
|
|
240
|
+
i++;
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
return this.#withPosition(Math.min(this.length(), i));
|
|
246
|
+
}
|
|
247
|
+
/** Insert runes at the cursor position. */
|
|
248
|
+
insertRunes(runes) {
|
|
249
|
+
if (!runes) {
|
|
250
|
+
return this;
|
|
251
|
+
}
|
|
252
|
+
const sanitized = sanitizeValue(runes);
|
|
253
|
+
if (!sanitized) {
|
|
254
|
+
return this;
|
|
255
|
+
}
|
|
256
|
+
const before = sliceGraphemes(this.value, 0, this.position);
|
|
257
|
+
const after = sliceGraphemes(this.value, this.position);
|
|
258
|
+
const insertion = clampGraphemes(
|
|
259
|
+
sanitized,
|
|
260
|
+
this.#remainingCapacity(before + after)
|
|
261
|
+
);
|
|
262
|
+
const nextValue = before + insertion + after;
|
|
263
|
+
const nextPos = this.position + runeCount(insertion);
|
|
264
|
+
return this.#withValue(nextValue, nextPos);
|
|
265
|
+
}
|
|
266
|
+
/** Delete up to n runes to the left of the cursor. */
|
|
267
|
+
deleteLeft(n = 1) {
|
|
268
|
+
if (this.position === 0 || n <= 0) {
|
|
269
|
+
return this;
|
|
270
|
+
}
|
|
271
|
+
const remove = Math.min(n, this.position);
|
|
272
|
+
const before = sliceGraphemes(this.value, 0, this.position - remove);
|
|
273
|
+
const after = sliceGraphemes(this.value, this.position);
|
|
274
|
+
return this.#withValue(before + after, this.position - remove);
|
|
275
|
+
}
|
|
276
|
+
/** Delete up to n runes to the right of the cursor. */
|
|
277
|
+
deleteRight(n = 1) {
|
|
278
|
+
if (this.position >= this.length() || n <= 0) {
|
|
279
|
+
return this;
|
|
280
|
+
}
|
|
281
|
+
const before = sliceGraphemes(this.value, 0, this.position);
|
|
282
|
+
const after = sliceGraphemes(this.value, this.position + Math.max(1, n));
|
|
283
|
+
return this.#withValue(before + after, this.position);
|
|
284
|
+
}
|
|
285
|
+
/** Delete the word to the left of the cursor. */
|
|
286
|
+
deleteWordLeft() {
|
|
287
|
+
if (this.position === 0 || this.isEmpty()) {
|
|
288
|
+
return this;
|
|
289
|
+
}
|
|
290
|
+
if (this.echoMode !== "normal" /* Normal */) {
|
|
291
|
+
return this.deleteToStart();
|
|
292
|
+
}
|
|
293
|
+
const graphemes = splitGraphemes(this.value);
|
|
294
|
+
let start = this.position;
|
|
295
|
+
while (start > 0) {
|
|
296
|
+
const g = graphemes[start - 1];
|
|
297
|
+
if (g && isWhitespace(g)) {
|
|
298
|
+
start--;
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
while (start > 0) {
|
|
304
|
+
const g = graphemes[start - 1];
|
|
305
|
+
if (g && !isWhitespace(g)) {
|
|
306
|
+
start--;
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
const before = graphemes.slice(0, start).join("");
|
|
312
|
+
const after = graphemes.slice(this.position).join("");
|
|
313
|
+
return this.#withValue(before + after, start);
|
|
314
|
+
}
|
|
315
|
+
/** Delete the word to the right of the cursor. */
|
|
316
|
+
deleteWordRight() {
|
|
317
|
+
if (this.position >= this.length() || this.isEmpty()) {
|
|
318
|
+
return this;
|
|
319
|
+
}
|
|
320
|
+
if (this.echoMode !== "normal" /* Normal */) {
|
|
321
|
+
return this.deleteToEnd();
|
|
322
|
+
}
|
|
323
|
+
const graphemes = splitGraphemes(this.value);
|
|
324
|
+
let end = this.position;
|
|
325
|
+
while (end < graphemes.length) {
|
|
326
|
+
const g = graphemes[end];
|
|
327
|
+
if (g && isWhitespace(g)) {
|
|
328
|
+
end++;
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
while (end < graphemes.length) {
|
|
334
|
+
const g = graphemes[end];
|
|
335
|
+
if (g && !isWhitespace(g)) {
|
|
336
|
+
end++;
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
const before = graphemes.slice(0, this.position).join("");
|
|
342
|
+
const after = graphemes.slice(end).join("");
|
|
343
|
+
return this.#withValue(before + after, this.position);
|
|
344
|
+
}
|
|
345
|
+
/** Delete everything before the cursor. */
|
|
346
|
+
deleteToStart() {
|
|
347
|
+
const after = sliceGraphemes(this.value, this.position);
|
|
348
|
+
return this.#withValue(after, 0);
|
|
349
|
+
}
|
|
350
|
+
/** Delete everything after the cursor. */
|
|
351
|
+
deleteToEnd() {
|
|
352
|
+
const before = sliceGraphemes(this.value, 0, this.position);
|
|
353
|
+
return this.#withValue(before, before.length === 0 ? 0 : runeCount(before));
|
|
354
|
+
}
|
|
355
|
+
/** Reset to empty value and cursor at start. */
|
|
356
|
+
reset() {
|
|
357
|
+
return this.#withValue("", 0);
|
|
358
|
+
}
|
|
359
|
+
/** Replace the value and re-run validation. */
|
|
360
|
+
setValue(value) {
|
|
361
|
+
const sanitized = clampToLimit(sanitizeValue(value), this.charLimit);
|
|
362
|
+
return this.#withValue(
|
|
363
|
+
sanitized,
|
|
364
|
+
Math.min(this.position, runeCount(sanitized))
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
/** Run validation and return the error (if any). */
|
|
368
|
+
validate() {
|
|
369
|
+
return this.#runValidation(this.value);
|
|
370
|
+
}
|
|
371
|
+
/** Set a new validation function (re-validates current value). */
|
|
372
|
+
setValidateFunc(fn) {
|
|
373
|
+
const next = this.#with({ validateFn: fn });
|
|
374
|
+
return next.#withError(next.#runValidation(next.value));
|
|
375
|
+
}
|
|
376
|
+
/** Command to paste from the clipboard. */
|
|
377
|
+
paste() {
|
|
378
|
+
return [this, pasteCommand()];
|
|
379
|
+
}
|
|
380
|
+
/** Handle Tea messages (keys, cursor, paste). */
|
|
381
|
+
update(msg) {
|
|
382
|
+
let model = this;
|
|
383
|
+
const cmds = [];
|
|
384
|
+
if (msg instanceof tea.KeyMsg) {
|
|
385
|
+
if (model.focused) {
|
|
386
|
+
const [nextModel, keyCmd] = model.#handleKey(msg);
|
|
387
|
+
model = nextModel;
|
|
388
|
+
if (keyCmd) {
|
|
389
|
+
cmds.push(keyCmd);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
} else if (msg instanceof PasteMsg) {
|
|
393
|
+
model = model.insertRunes(msg.text);
|
|
394
|
+
} else if (msg instanceof PasteErrorMsg) {
|
|
395
|
+
model = model.#withError(asError(msg.error));
|
|
396
|
+
}
|
|
397
|
+
const [cursor, cursorCmd] = model.cursor.update(msg);
|
|
398
|
+
model = model.#with({ cursor });
|
|
399
|
+
if (cursorCmd) {
|
|
400
|
+
cmds.push(cursorCmd);
|
|
401
|
+
}
|
|
402
|
+
return [model, tea.batch(...cmds)];
|
|
403
|
+
}
|
|
404
|
+
/** Render the input with prompt, text, and cursor. */
|
|
405
|
+
view() {
|
|
406
|
+
const promptRendered = this.promptStyle.inline(true).render(this.prompt);
|
|
407
|
+
const availableWidth = this.width > 0 ? Math.max(0, this.width - chapstick.width(promptRendered)) : Number.POSITIVE_INFINITY;
|
|
408
|
+
if (this.isEmpty() && this.placeholder) {
|
|
409
|
+
const placeholder = this.#placeholderView(availableWidth);
|
|
410
|
+
return promptRendered + placeholder;
|
|
411
|
+
}
|
|
412
|
+
const rendered = this.#valueView(availableWidth);
|
|
413
|
+
return promptRendered + rendered;
|
|
414
|
+
}
|
|
415
|
+
#handleKey(msg) {
|
|
416
|
+
let next = this;
|
|
417
|
+
let cmd = null;
|
|
418
|
+
const keyMap = this.keyMap;
|
|
419
|
+
switch (true) {
|
|
420
|
+
case key.matches(msg, keyMap.deleteWordBackward):
|
|
421
|
+
next = next.deleteWordLeft();
|
|
422
|
+
break;
|
|
423
|
+
case key.matches(msg, keyMap.deleteCharacterBackward):
|
|
424
|
+
next = next.deleteLeft();
|
|
425
|
+
break;
|
|
426
|
+
case key.matches(msg, keyMap.wordBackward):
|
|
427
|
+
next = next.wordLeft();
|
|
428
|
+
break;
|
|
429
|
+
case key.matches(msg, keyMap.characterBackward):
|
|
430
|
+
next = next.cursorLeft();
|
|
431
|
+
break;
|
|
432
|
+
case key.matches(msg, keyMap.wordForward):
|
|
433
|
+
next = next.wordRight();
|
|
434
|
+
break;
|
|
435
|
+
case key.matches(msg, keyMap.characterForward):
|
|
436
|
+
next = next.cursorRight();
|
|
437
|
+
break;
|
|
438
|
+
case key.matches(msg, keyMap.lineStart):
|
|
439
|
+
next = next.cursorStart();
|
|
440
|
+
break;
|
|
441
|
+
case key.matches(msg, keyMap.deleteCharacterForward):
|
|
442
|
+
next = next.deleteRight();
|
|
443
|
+
break;
|
|
444
|
+
case key.matches(msg, keyMap.lineEnd):
|
|
445
|
+
next = next.cursorEnd();
|
|
446
|
+
break;
|
|
447
|
+
case key.matches(msg, keyMap.deleteAfterCursor):
|
|
448
|
+
next = next.deleteToEnd();
|
|
449
|
+
break;
|
|
450
|
+
case key.matches(msg, keyMap.deleteBeforeCursor):
|
|
451
|
+
next = next.deleteToStart();
|
|
452
|
+
break;
|
|
453
|
+
case key.matches(msg, keyMap.paste):
|
|
454
|
+
cmd = pasteCommand();
|
|
455
|
+
break;
|
|
456
|
+
case key.matches(msg, keyMap.deleteWordForward):
|
|
457
|
+
next = next.deleteWordRight();
|
|
458
|
+
break;
|
|
459
|
+
default:
|
|
460
|
+
if ((msg.key.type === tea.KeyType.Runes || msg.key.type === tea.KeyType.Space) && !msg.key.alt) {
|
|
461
|
+
next = next.insertRunes(msg.key.runes);
|
|
462
|
+
}
|
|
463
|
+
break;
|
|
464
|
+
}
|
|
465
|
+
return [next, cmd];
|
|
466
|
+
}
|
|
467
|
+
#placeholderView(availableWidth) {
|
|
468
|
+
const graphemes = splitGraphemes(this.placeholder);
|
|
469
|
+
const cursorChar = graphemes[0] ?? " ";
|
|
470
|
+
const rest = graphemes.slice(1).join("");
|
|
471
|
+
const budget = Number.isFinite(availableWidth) ? Math.max(0, availableWidth - chapstick.width(cursorChar)) : void 0;
|
|
472
|
+
const visibleRest = budget === void 0 ? rest : chapstick.clampWidth(rest, budget);
|
|
473
|
+
const cursorView = this.cursor.withChar(cursorChar || " ").view();
|
|
474
|
+
const styledRest = this.placeholderStyle.inline(true).render(visibleRest);
|
|
475
|
+
const usedWidth = chapstick.width(cursorChar + visibleRest);
|
|
476
|
+
const padding = Number.isFinite(availableWidth) && availableWidth > 0 ? Math.max(0, availableWidth - usedWidth) : 0;
|
|
477
|
+
return cursorView + styledRest + " ".repeat(padding);
|
|
478
|
+
}
|
|
479
|
+
#valueView(availableWidth) {
|
|
480
|
+
const graphemes = splitGraphemes(this.value);
|
|
481
|
+
const displayGraphemes = graphemes.map((g) => this.#echoTransform(g));
|
|
482
|
+
const widths = displayGraphemes.map((g) => chapstick.width(g));
|
|
483
|
+
const [start, end] = visibleRange(widths, this.position, availableWidth);
|
|
484
|
+
const slice = displayGraphemes.slice(start, end);
|
|
485
|
+
const cursorOffset = clamp(this.position - start, 0, slice.length);
|
|
486
|
+
const before = slice.slice(0, cursorOffset).join("");
|
|
487
|
+
const after = cursorOffset < slice.length ? slice.slice(cursorOffset + 1).join("") : "";
|
|
488
|
+
const cursorChar = cursorOffset < slice.length ? slice[cursorOffset] ?? " " : " ";
|
|
489
|
+
const beforeStyled = this.textStyle.inline(true).render(before);
|
|
490
|
+
const afterStyled = this.textStyle.inline(true).render(after);
|
|
491
|
+
const cursorView = this.cursor.withChar(cursorChar || " ").view();
|
|
492
|
+
const usedWidth = chapstick.width(before) + chapstick.width(cursorChar || " ") + chapstick.width(after);
|
|
493
|
+
const padding = Number.isFinite(availableWidth) && availableWidth > 0 ? Math.max(0, availableWidth - usedWidth) : 0;
|
|
494
|
+
return beforeStyled + cursorView + afterStyled + " ".repeat(padding);
|
|
495
|
+
}
|
|
496
|
+
#echoTransform(s) {
|
|
497
|
+
switch (this.echoMode) {
|
|
498
|
+
case "password" /* Password */:
|
|
499
|
+
return this.echoCharacter.repeat(Math.max(0, runeCount(s)));
|
|
500
|
+
case "none" /* None */:
|
|
501
|
+
return "";
|
|
502
|
+
case "normal" /* Normal */:
|
|
503
|
+
default:
|
|
504
|
+
return s;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
#withPosition(position) {
|
|
508
|
+
const clamped = clamp(position, 0, this.length());
|
|
509
|
+
if (clamped === this.position) {
|
|
510
|
+
return this;
|
|
511
|
+
}
|
|
512
|
+
return this.#with({ position: clamped });
|
|
513
|
+
}
|
|
514
|
+
#withValue(value, position) {
|
|
515
|
+
const clamped = clampToLimit(value, this.charLimit);
|
|
516
|
+
const pos = clamp(position ?? this.position, 0, runeCount(clamped));
|
|
517
|
+
const next = this.#with({ value: clamped, position: pos });
|
|
518
|
+
return next.#withError(next.#runValidation(clamped));
|
|
519
|
+
}
|
|
520
|
+
#withError(error) {
|
|
521
|
+
if (error === this.error) {
|
|
522
|
+
return this;
|
|
523
|
+
}
|
|
524
|
+
return this.#with({ error });
|
|
525
|
+
}
|
|
526
|
+
#with(patch) {
|
|
527
|
+
return new _TextInputModel({
|
|
528
|
+
value: this.value,
|
|
529
|
+
position: this.position,
|
|
530
|
+
focused: this.focused,
|
|
531
|
+
placeholder: this.placeholder,
|
|
532
|
+
echoMode: this.echoMode,
|
|
533
|
+
echoCharacter: this.echoCharacter,
|
|
534
|
+
charLimit: this.charLimit,
|
|
535
|
+
width: this.width,
|
|
536
|
+
prompt: this.prompt,
|
|
537
|
+
promptStyle: this.promptStyle,
|
|
538
|
+
textStyle: this.textStyle,
|
|
539
|
+
placeholderStyle: this.placeholderStyle,
|
|
540
|
+
cursorStyle: this.cursorStyle,
|
|
541
|
+
validateFn: this.validateFn,
|
|
542
|
+
keyMap: this.keyMap,
|
|
543
|
+
cursor: this.cursor,
|
|
544
|
+
error: this.error,
|
|
545
|
+
...patch
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
#runValidation(value) {
|
|
549
|
+
return this.validateFn ? this.validateFn(value) : null;
|
|
550
|
+
}
|
|
551
|
+
#remainingCapacity(current) {
|
|
552
|
+
if (this.charLimit <= 0) {
|
|
553
|
+
return Number.POSITIVE_INFINITY;
|
|
554
|
+
}
|
|
555
|
+
const remaining = this.charLimit - runeCount(current);
|
|
556
|
+
return Math.max(0, remaining);
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
function sanitizeValue(value) {
|
|
560
|
+
return sanitizer.sanitize(value ?? "");
|
|
561
|
+
}
|
|
562
|
+
function clampToLimit(value, charLimit) {
|
|
563
|
+
if (charLimit <= 0) {
|
|
564
|
+
return value;
|
|
565
|
+
}
|
|
566
|
+
return clampGraphemes(value, charLimit);
|
|
567
|
+
}
|
|
568
|
+
function clampGraphemes(value, limit) {
|
|
569
|
+
if (limit <= 0) {
|
|
570
|
+
return "";
|
|
571
|
+
}
|
|
572
|
+
const graphemes = splitGraphemes(value);
|
|
573
|
+
if (graphemes.length <= limit) {
|
|
574
|
+
return graphemes.join("");
|
|
575
|
+
}
|
|
576
|
+
return graphemes.slice(0, limit).join("");
|
|
577
|
+
}
|
|
578
|
+
function splitGraphemes(value) {
|
|
579
|
+
return splitter.splitGraphemes(value ?? "");
|
|
580
|
+
}
|
|
581
|
+
function sliceGraphemes(value, start, end) {
|
|
582
|
+
const graphemes = splitGraphemes(value);
|
|
583
|
+
return graphemes.slice(start, end).join("");
|
|
584
|
+
}
|
|
585
|
+
function runeCount(value) {
|
|
586
|
+
return splitter.countGraphemes(value ?? "");
|
|
587
|
+
}
|
|
588
|
+
function clamp(value, min, max) {
|
|
589
|
+
const lower = Math.min(min, max);
|
|
590
|
+
const upper = Math.max(min, max);
|
|
591
|
+
return Math.min(upper, Math.max(lower, value));
|
|
592
|
+
}
|
|
593
|
+
function isWhitespace(grapheme) {
|
|
594
|
+
return /^\s+$/.test(grapheme);
|
|
595
|
+
}
|
|
596
|
+
function visibleRange(widths, position, maxWidth) {
|
|
597
|
+
const len = widths.length;
|
|
598
|
+
if (!Number.isFinite(maxWidth) || maxWidth <= 0) {
|
|
599
|
+
return [0, len];
|
|
600
|
+
}
|
|
601
|
+
if (len === 0) {
|
|
602
|
+
return [0, 0];
|
|
603
|
+
}
|
|
604
|
+
const widthAt = (i) => i >= 0 && i < len ? widths[i] ?? 0 : 0;
|
|
605
|
+
const budget = maxWidth;
|
|
606
|
+
let start = Math.min(position, len);
|
|
607
|
+
let end = start;
|
|
608
|
+
let current = 0;
|
|
609
|
+
if (position < len) {
|
|
610
|
+
current = widthAt(position);
|
|
611
|
+
start = position;
|
|
612
|
+
end = position + 1;
|
|
613
|
+
} else {
|
|
614
|
+
current = 1;
|
|
615
|
+
}
|
|
616
|
+
while (start > 0) {
|
|
617
|
+
const w = widthAt(start - 1);
|
|
618
|
+
if (current > 0 && current + w > budget) {
|
|
619
|
+
break;
|
|
620
|
+
}
|
|
621
|
+
start--;
|
|
622
|
+
current += w;
|
|
623
|
+
if (current >= budget) {
|
|
624
|
+
break;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
while (end < len) {
|
|
628
|
+
const w = widthAt(end);
|
|
629
|
+
if (current > 0 && current + w > budget) {
|
|
630
|
+
break;
|
|
631
|
+
}
|
|
632
|
+
end++;
|
|
633
|
+
current += w;
|
|
634
|
+
if (current >= budget) {
|
|
635
|
+
break;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
while (current > budget && start < end) {
|
|
639
|
+
current -= widthAt(start);
|
|
640
|
+
start++;
|
|
641
|
+
}
|
|
642
|
+
return [start, end];
|
|
643
|
+
}
|
|
644
|
+
function asError(err) {
|
|
645
|
+
if (err instanceof Error) {
|
|
646
|
+
return err;
|
|
647
|
+
}
|
|
648
|
+
if (typeof err === "string") return new Error(err);
|
|
649
|
+
if (typeof err === "number" || typeof err === "boolean")
|
|
650
|
+
return new Error(String(err));
|
|
651
|
+
return new Error("clipboard error");
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
Object.defineProperty(exports, "CursorMode", {
|
|
655
|
+
enumerable: true,
|
|
656
|
+
get: function () { return cursor.CursorMode; }
|
|
657
|
+
});
|
|
658
|
+
exports.EchoMode = EchoMode;
|
|
659
|
+
exports.PasteErrorMsg = PasteErrorMsg;
|
|
660
|
+
exports.PasteMsg = PasteMsg;
|
|
661
|
+
exports.TextInputModel = TextInputModel;
|
|
662
|
+
exports.defaultKeyMap = defaultKeyMap;
|
|
663
|
+
exports.pasteCommand = pasteCommand;
|
|
664
|
+
//# sourceMappingURL=index.cjs.map
|
|
665
|
+
//# sourceMappingURL=index.cjs.map
|