@dui-toolkit/plugin-tui 0.1.1-next.1 → 0.1.1-next.3

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 (2) hide show
  1. package/dist/index.mjs +41 -20
  2. package/package.json +2 -2
package/dist/index.mjs CHANGED
@@ -307,28 +307,30 @@ var SelectList = class extends BaseWidget {
307
307
  }
308
308
  switch (key) {
309
309
  case "ArrowUp":
310
- if (this.data.selectedIndex > 0) {
310
+ if (items.length > 0 && this.data.selectedIndex > 0) {
311
311
  this.data.selectedIndex--;
312
312
  if (this.data.selectedIndex < this.data.scrollOffset) this.data.scrollOffset = this.data.selectedIndex;
313
313
  }
314
- this.data.onSelect?.(items[this.data.selectedIndex], this.data.selectedIndex);
314
+ if (items[this.data.selectedIndex]) this.data.onSelect?.(items[this.data.selectedIndex], this.data.selectedIndex);
315
315
  return true;
316
316
  case "ArrowDown":
317
- if (this.data.selectedIndex < items.length - 1) {
317
+ if (items.length > 0 && this.data.selectedIndex < items.length - 1) {
318
318
  this.data.selectedIndex++;
319
319
  if (this.data.selectedIndex >= this.data.scrollOffset + maxVisible) this.data.scrollOffset = this.data.selectedIndex - maxVisible + 1;
320
320
  }
321
- this.data.onSelect?.(items[this.data.selectedIndex], this.data.selectedIndex);
321
+ if (items[this.data.selectedIndex]) this.data.onSelect?.(items[this.data.selectedIndex], this.data.selectedIndex);
322
322
  return true;
323
323
  case "Home":
324
324
  this.data.selectedIndex = 0;
325
325
  this.data.scrollOffset = 0;
326
- this.data.onSelect?.(items[0], 0);
326
+ if (items[0]) this.data.onSelect?.(items[0], 0);
327
327
  return true;
328
328
  case "End":
329
- this.data.selectedIndex = items.length - 1;
330
- if (items.length > maxVisible) this.data.scrollOffset = items.length - maxVisible;
331
- this.data.onSelect?.(items[this.data.selectedIndex], this.data.selectedIndex);
329
+ if (items.length > 0) {
330
+ this.data.selectedIndex = items.length - 1;
331
+ if (items.length > maxVisible) this.data.scrollOffset = items.length - maxVisible;
332
+ this.data.onSelect?.(items[this.data.selectedIndex], this.data.selectedIndex);
333
+ }
332
334
  return true;
333
335
  case "Enter": {
334
336
  const sel = items[this.data.selectedIndex];
@@ -368,7 +370,28 @@ var SelectList = class extends BaseWidget {
368
370
  * corrupts terminal state) or wide glyphs (CJK / emoji). These helpers stay
369
371
  * cell-aware and never split an escape sequence.
370
372
  */
371
- const CSI_RE = /\x1b\[[0-9;:<=>?]*[ -/]*[@-~]/;
373
+ const CSI_RE = /\x1b\[[0-9;:<=>?]*[ -\/]*[@-~]/;
374
+ /**
375
+ * Skip past a single escape sequence starting at `rest[0]`.
376
+ * Returns the length of the escape sequence, or 0 if `rest` does not start
377
+ * with a recognised escape.
378
+ */
379
+ function escapeLength(rest) {
380
+ if (!rest.startsWith("\x1B")) return 0;
381
+ const csi = CSI_RE.exec(rest);
382
+ if (csi && csi[0].length > 0) return csi[0].length;
383
+ if (rest.length >= 2) {
384
+ if (rest[1] === "]") {
385
+ for (let i = 2; i < rest.length; i++) {
386
+ if (rest[i] === "\x07") return i + 1;
387
+ if (rest[i] === "\x1B" && rest[i + 1] === "\\") return i + 2;
388
+ }
389
+ return rest.length;
390
+ }
391
+ return 2;
392
+ }
393
+ return 1;
394
+ }
372
395
  /**
373
396
  * Truncate an ANSI-wrapped string to `maxCells` terminal cells.
374
397
  *
@@ -384,22 +407,20 @@ function truncateAnsi(text, maxCells) {
384
407
  const target = Math.max(0, maxCells - 1);
385
408
  let out = "";
386
409
  let used = 0;
387
- let rest = text;
388
- while (rest.length > 0 && used < target) {
389
- if (rest.startsWith("\x1B")) {
390
- const m = CSI_RE.exec(rest);
391
- if (m && m[0].length > 0) {
392
- out += m[0];
393
- rest = rest.slice(m[0].length);
394
- continue;
395
- }
410
+ let pos = 0;
411
+ while (pos < text.length && used < target) {
412
+ const escLen = escapeLength(text.slice(pos));
413
+ if (escLen > 0) {
414
+ out += text.slice(pos, pos + escLen);
415
+ pos += escLen;
416
+ continue;
396
417
  }
397
- const ch = rest.charAt(0);
418
+ const ch = text.charAt(pos);
398
419
  const w = visibleLength(ch);
399
420
  if (used + w > target) break;
400
421
  out += ch;
401
422
  used += w;
402
- rest = rest.slice(1);
423
+ pos++;
403
424
  }
404
425
  return out + "…";
405
426
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dui-toolkit/plugin-tui",
3
- "version": "0.1.1-next.1",
3
+ "version": "0.1.1-next.3",
4
4
  "description": "TUI widget toolkit plugin for @bdocs/dui — text inputs, select lists, modals, status bars, and more.",
5
5
  "main": "dist/index.mjs",
6
6
  "types": "dist/index.d.mts",
@@ -28,7 +28,7 @@
28
28
  "license": "MIT",
29
29
  "type": "module",
30
30
  "dependencies": {
31
- "@bdocs/dui": "0.7.0-next.3"
31
+ "@bdocs/dui": "0.7.0-next.5"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/node": "^22.0.0",