numux 2.17.0 → 2.17.2

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/man/numux.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH "NUMUX" "1" "May 2026" "2.17.0" "numux manual"
1
+ .TH "NUMUX" "1" "June 2026" "2.17.2" "numux manual"
2
2
  .SH "NAME"
3
3
  \fBnumux\fR
4
4
  .P
package/dist/numux.js CHANGED
@@ -504,6 +504,341 @@ Search mode (after pressing \`F\`):
504
504
  };
505
505
  });
506
506
 
507
+ // node_modules/clone/clone.js
508
+ var require_clone = __commonJS((exports, module) => {
509
+ var clone = function() {
510
+ function clone2(parent, circular, depth, prototype) {
511
+ var filter;
512
+ if (typeof circular === "object") {
513
+ depth = circular.depth;
514
+ prototype = circular.prototype;
515
+ filter = circular.filter;
516
+ circular = circular.circular;
517
+ }
518
+ var allParents = [];
519
+ var allChildren = [];
520
+ var useBuffer = typeof Buffer != "undefined";
521
+ if (typeof circular == "undefined")
522
+ circular = true;
523
+ if (typeof depth == "undefined")
524
+ depth = Infinity;
525
+ function _clone(parent2, depth2) {
526
+ if (parent2 === null)
527
+ return null;
528
+ if (depth2 == 0)
529
+ return parent2;
530
+ var child;
531
+ var proto;
532
+ if (typeof parent2 != "object") {
533
+ return parent2;
534
+ }
535
+ if (clone2.__isArray(parent2)) {
536
+ child = [];
537
+ } else if (clone2.__isRegExp(parent2)) {
538
+ child = new RegExp(parent2.source, __getRegExpFlags(parent2));
539
+ if (parent2.lastIndex)
540
+ child.lastIndex = parent2.lastIndex;
541
+ } else if (clone2.__isDate(parent2)) {
542
+ child = new Date(parent2.getTime());
543
+ } else if (useBuffer && Buffer.isBuffer(parent2)) {
544
+ if (Buffer.allocUnsafe) {
545
+ child = Buffer.allocUnsafe(parent2.length);
546
+ } else {
547
+ child = new Buffer(parent2.length);
548
+ }
549
+ parent2.copy(child);
550
+ return child;
551
+ } else {
552
+ if (typeof prototype == "undefined") {
553
+ proto = Object.getPrototypeOf(parent2);
554
+ child = Object.create(proto);
555
+ } else {
556
+ child = Object.create(prototype);
557
+ proto = prototype;
558
+ }
559
+ }
560
+ if (circular) {
561
+ var index = allParents.indexOf(parent2);
562
+ if (index != -1) {
563
+ return allChildren[index];
564
+ }
565
+ allParents.push(parent2);
566
+ allChildren.push(child);
567
+ }
568
+ for (var i in parent2) {
569
+ var attrs;
570
+ if (proto) {
571
+ attrs = Object.getOwnPropertyDescriptor(proto, i);
572
+ }
573
+ if (attrs && attrs.set == null) {
574
+ continue;
575
+ }
576
+ child[i] = _clone(parent2[i], depth2 - 1);
577
+ }
578
+ return child;
579
+ }
580
+ return _clone(parent, depth);
581
+ }
582
+ clone2.clonePrototype = function clonePrototype(parent) {
583
+ if (parent === null)
584
+ return null;
585
+ var c = function() {};
586
+ c.prototype = parent;
587
+ return new c;
588
+ };
589
+ function __objToStr(o) {
590
+ return Object.prototype.toString.call(o);
591
+ }
592
+ clone2.__objToStr = __objToStr;
593
+ function __isDate(o) {
594
+ return typeof o === "object" && __objToStr(o) === "[object Date]";
595
+ }
596
+ clone2.__isDate = __isDate;
597
+ function __isArray(o) {
598
+ return typeof o === "object" && __objToStr(o) === "[object Array]";
599
+ }
600
+ clone2.__isArray = __isArray;
601
+ function __isRegExp(o) {
602
+ return typeof o === "object" && __objToStr(o) === "[object RegExp]";
603
+ }
604
+ clone2.__isRegExp = __isRegExp;
605
+ function __getRegExpFlags(re) {
606
+ var flags = "";
607
+ if (re.global)
608
+ flags += "g";
609
+ if (re.ignoreCase)
610
+ flags += "i";
611
+ if (re.multiline)
612
+ flags += "m";
613
+ return flags;
614
+ }
615
+ clone2.__getRegExpFlags = __getRegExpFlags;
616
+ return clone2;
617
+ }();
618
+ if (typeof module === "object" && module.exports) {
619
+ module.exports = clone;
620
+ }
621
+ });
622
+
623
+ // node_modules/defaults/index.js
624
+ var require_defaults = __commonJS((exports, module) => {
625
+ var clone = require_clone();
626
+ module.exports = function(options, defaults) {
627
+ options = options || {};
628
+ Object.keys(defaults).forEach(function(key) {
629
+ if (typeof options[key] === "undefined") {
630
+ options[key] = clone(defaults[key]);
631
+ }
632
+ });
633
+ return options;
634
+ };
635
+ });
636
+
637
+ // node_modules/wcwidth/combining.js
638
+ var require_combining = __commonJS((exports, module) => {
639
+ module.exports = [
640
+ [768, 879],
641
+ [1155, 1158],
642
+ [1160, 1161],
643
+ [1425, 1469],
644
+ [1471, 1471],
645
+ [1473, 1474],
646
+ [1476, 1477],
647
+ [1479, 1479],
648
+ [1536, 1539],
649
+ [1552, 1557],
650
+ [1611, 1630],
651
+ [1648, 1648],
652
+ [1750, 1764],
653
+ [1767, 1768],
654
+ [1770, 1773],
655
+ [1807, 1807],
656
+ [1809, 1809],
657
+ [1840, 1866],
658
+ [1958, 1968],
659
+ [2027, 2035],
660
+ [2305, 2306],
661
+ [2364, 2364],
662
+ [2369, 2376],
663
+ [2381, 2381],
664
+ [2385, 2388],
665
+ [2402, 2403],
666
+ [2433, 2433],
667
+ [2492, 2492],
668
+ [2497, 2500],
669
+ [2509, 2509],
670
+ [2530, 2531],
671
+ [2561, 2562],
672
+ [2620, 2620],
673
+ [2625, 2626],
674
+ [2631, 2632],
675
+ [2635, 2637],
676
+ [2672, 2673],
677
+ [2689, 2690],
678
+ [2748, 2748],
679
+ [2753, 2757],
680
+ [2759, 2760],
681
+ [2765, 2765],
682
+ [2786, 2787],
683
+ [2817, 2817],
684
+ [2876, 2876],
685
+ [2879, 2879],
686
+ [2881, 2883],
687
+ [2893, 2893],
688
+ [2902, 2902],
689
+ [2946, 2946],
690
+ [3008, 3008],
691
+ [3021, 3021],
692
+ [3134, 3136],
693
+ [3142, 3144],
694
+ [3146, 3149],
695
+ [3157, 3158],
696
+ [3260, 3260],
697
+ [3263, 3263],
698
+ [3270, 3270],
699
+ [3276, 3277],
700
+ [3298, 3299],
701
+ [3393, 3395],
702
+ [3405, 3405],
703
+ [3530, 3530],
704
+ [3538, 3540],
705
+ [3542, 3542],
706
+ [3633, 3633],
707
+ [3636, 3642],
708
+ [3655, 3662],
709
+ [3761, 3761],
710
+ [3764, 3769],
711
+ [3771, 3772],
712
+ [3784, 3789],
713
+ [3864, 3865],
714
+ [3893, 3893],
715
+ [3895, 3895],
716
+ [3897, 3897],
717
+ [3953, 3966],
718
+ [3968, 3972],
719
+ [3974, 3975],
720
+ [3984, 3991],
721
+ [3993, 4028],
722
+ [4038, 4038],
723
+ [4141, 4144],
724
+ [4146, 4146],
725
+ [4150, 4151],
726
+ [4153, 4153],
727
+ [4184, 4185],
728
+ [4448, 4607],
729
+ [4959, 4959],
730
+ [5906, 5908],
731
+ [5938, 5940],
732
+ [5970, 5971],
733
+ [6002, 6003],
734
+ [6068, 6069],
735
+ [6071, 6077],
736
+ [6086, 6086],
737
+ [6089, 6099],
738
+ [6109, 6109],
739
+ [6155, 6157],
740
+ [6313, 6313],
741
+ [6432, 6434],
742
+ [6439, 6440],
743
+ [6450, 6450],
744
+ [6457, 6459],
745
+ [6679, 6680],
746
+ [6912, 6915],
747
+ [6964, 6964],
748
+ [6966, 6970],
749
+ [6972, 6972],
750
+ [6978, 6978],
751
+ [7019, 7027],
752
+ [7616, 7626],
753
+ [7678, 7679],
754
+ [8203, 8207],
755
+ [8234, 8238],
756
+ [8288, 8291],
757
+ [8298, 8303],
758
+ [8400, 8431],
759
+ [12330, 12335],
760
+ [12441, 12442],
761
+ [43014, 43014],
762
+ [43019, 43019],
763
+ [43045, 43046],
764
+ [64286, 64286],
765
+ [65024, 65039],
766
+ [65056, 65059],
767
+ [65279, 65279],
768
+ [65529, 65531],
769
+ [68097, 68099],
770
+ [68101, 68102],
771
+ [68108, 68111],
772
+ [68152, 68154],
773
+ [68159, 68159],
774
+ [119143, 119145],
775
+ [119155, 119170],
776
+ [119173, 119179],
777
+ [119210, 119213],
778
+ [119362, 119364],
779
+ [917505, 917505],
780
+ [917536, 917631],
781
+ [917760, 917999]
782
+ ];
783
+ });
784
+
785
+ // node_modules/wcwidth/index.js
786
+ var require_wcwidth = __commonJS((exports, module) => {
787
+ var defaults = require_defaults();
788
+ var combining = require_combining();
789
+ var DEFAULTS = {
790
+ nul: 0,
791
+ control: 0
792
+ };
793
+ module.exports = function wcwidth2(str) {
794
+ return wcswidth(str, DEFAULTS);
795
+ };
796
+ module.exports.config = function(opts) {
797
+ opts = defaults(opts || {}, DEFAULTS);
798
+ return function wcwidth2(str) {
799
+ return wcswidth(str, opts);
800
+ };
801
+ };
802
+ function wcswidth(str, opts) {
803
+ if (typeof str !== "string")
804
+ return wcwidth(str, opts);
805
+ var s = 0;
806
+ for (var i = 0;i < str.length; i++) {
807
+ var n = wcwidth(str.charCodeAt(i), opts);
808
+ if (n < 0)
809
+ return -1;
810
+ s += n;
811
+ }
812
+ return s;
813
+ }
814
+ function wcwidth(ucs, opts) {
815
+ if (ucs === 0)
816
+ return opts.nul;
817
+ if (ucs < 32 || ucs >= 127 && ucs < 160)
818
+ return opts.control;
819
+ if (bisearch(ucs))
820
+ return 0;
821
+ return 1 + (ucs >= 4352 && (ucs <= 4447 || ucs == 9001 || ucs == 9002 || ucs >= 11904 && ucs <= 42191 && ucs != 12351 || ucs >= 44032 && ucs <= 55203 || ucs >= 63744 && ucs <= 64255 || ucs >= 65040 && ucs <= 65049 || ucs >= 65072 && ucs <= 65135 || ucs >= 65280 && ucs <= 65376 || ucs >= 65504 && ucs <= 65510 || ucs >= 131072 && ucs <= 196605 || ucs >= 196608 && ucs <= 262141));
822
+ }
823
+ function bisearch(ucs) {
824
+ var min = 0;
825
+ var max = combining.length - 1;
826
+ var mid;
827
+ if (ucs < combining[0][0] || ucs > combining[max][1])
828
+ return false;
829
+ while (max >= min) {
830
+ mid = Math.floor((min + max) / 2);
831
+ if (ucs > combining[mid][1])
832
+ min = mid + 1;
833
+ else if (ucs < combining[mid][0])
834
+ max = mid - 1;
835
+ else
836
+ return true;
837
+ }
838
+ return false;
839
+ }
840
+ });
841
+
507
842
  // src/help.ts
508
843
  var exports_help = {};
509
844
  __export(exports_help, {
@@ -550,7 +885,7 @@ var init_help = __esm(() => {
550
885
  var require_package = __commonJS((exports, module) => {
551
886
  module.exports = {
552
887
  name: "numux",
553
- version: "2.17.0",
888
+ version: "2.17.2",
554
889
  description: "Terminal multiplexer with dependency orchestration",
555
890
  type: "module",
556
891
  license: "MIT",
@@ -595,8 +930,8 @@ var require_package = __commonJS((exports, module) => {
595
930
  "dist/"
596
931
  ],
597
932
  dependencies: {
598
- "@opentui/core": "^0.1.88",
599
- "ghostty-opentui": "^1.4.7"
933
+ "@opentui/core": "^0.4.1",
934
+ "ghostty-opentui": "^1.5.0"
600
935
  },
601
936
  devDependencies: {
602
937
  "@biomejs/biome": "^2.4.4",
@@ -606,7 +941,7 @@ var require_package = __commonJS((exports, module) => {
606
941
  "marked-man": "^2.1.0"
607
942
  },
608
943
  patchedDependencies: {
609
- "ghostty-opentui@1.4.7": "patches/ghostty-opentui@1.4.7.patch"
944
+ "@opentui/core@0.4.1": "patches/@opentui%2Fcore@0.4.1.patch"
610
945
  }
611
946
  };
612
947
  });
@@ -3285,6 +3620,7 @@ function resolveTimestampFormat(timestamps) {
3285
3620
  }
3286
3621
 
3287
3622
  // node_modules/ghostty-opentui/src/terminal-buffer.ts
3623
+ var import_wcwidth = __toESM(require_wcwidth(), 1);
3288
3624
  import {
3289
3625
  TextBufferRenderable,
3290
3626
  StyledText,
@@ -3293,6 +3629,22 @@ import {
3293
3629
  import { ptyToJson, PersistentTerminal, hasPersistentTerminalSupport, StyleFlags } from "ghostty-opentui";
3294
3630
  var DEFAULT_FG = RGBA.fromHex("#d4d4d4");
3295
3631
  var DEFAULT_BG = RGBA.fromHex("#1e1e1e");
3632
+ function getChunkCellWidth(chunk) {
3633
+ return "cellWidth" in chunk && typeof chunk.cellWidth === "number" ? chunk.cellWidth : import_wcwidth.default(chunk.text);
3634
+ }
3635
+ function cellColToStringIndex(text, cellCol) {
3636
+ if (cellCol <= 0)
3637
+ return 0;
3638
+ let col = 0;
3639
+ let strIdx = 0;
3640
+ for (const ch of text) {
3641
+ if (col >= cellCol)
3642
+ break;
3643
+ col += import_wcwidth.default(ch);
3644
+ strIdx += ch.length;
3645
+ }
3646
+ return strIdx;
3647
+ }
3296
3648
  var TextAttributes = {
3297
3649
  BOLD: 1 << 0,
3298
3650
  DIM: 1 << 1,
@@ -3303,8 +3655,11 @@ var TextAttributes = {
3303
3655
  HIDDEN: 1 << 6,
3304
3656
  STRIKETHROUGH: 1 << 7
3305
3657
  };
3658
+ function getLineStarts(lineInfo) {
3659
+ return lineInfo.lineStarts ?? lineInfo.lineStartCols ?? [];
3660
+ }
3306
3661
  function convertSpanToChunk(span) {
3307
- const { text, fg, bg, flags } = span;
3662
+ const { text, fg, bg, flags, width } = span;
3308
3663
  let fgColor = fg ? RGBA.fromHex(fg) : DEFAULT_FG;
3309
3664
  let bgColor = bg ? RGBA.fromHex(bg) : undefined;
3310
3665
  if (flags & StyleFlags.INVERSE) {
@@ -3323,7 +3678,7 @@ function convertSpanToChunk(span) {
3323
3678
  attributes |= TextAttributes.STRIKETHROUGH;
3324
3679
  if (flags & StyleFlags.FAINT)
3325
3680
  attributes |= TextAttributes.DIM;
3326
- return { __isChunk: true, text, fg: fgColor, bg: bgColor, attributes };
3681
+ return { __isChunk: true, text, fg: fgColor, bg: bgColor, attributes, cellWidth: width };
3327
3682
  }
3328
3683
  function applyHighlightsToLine(chunks, highlights) {
3329
3684
  if (highlights.length === 0)
@@ -3331,63 +3686,56 @@ function applyHighlightsToLine(chunks, highlights) {
3331
3686
  const result = [];
3332
3687
  let col = 0;
3333
3688
  for (const chunk of chunks) {
3689
+ const w = getChunkCellWidth(chunk);
3334
3690
  const chunkStart = col;
3335
- const chunkEnd = col + chunk.text.length;
3336
- const overlappingHighlights = highlights.filter((hl) => hl.start < chunkEnd && hl.end > chunkStart);
3337
- if (overlappingHighlights.length === 0) {
3691
+ const chunkEnd = col + w;
3692
+ const overlapping = highlights.filter((hl) => hl.start < chunkEnd && hl.end > chunkStart).sort((a, b) => a.start - b.start);
3693
+ if (overlapping.length === 0) {
3338
3694
  result.push(chunk);
3339
3695
  col = chunkEnd;
3340
3696
  continue;
3341
3697
  }
3342
- let pos = 0;
3343
- const text = chunk.text;
3344
- const sortedHighlights = [...overlappingHighlights].sort((a, b) => a.start - b.start);
3345
- for (const hl of sortedHighlights) {
3346
- const hlStartInChunk = Math.max(0, hl.start - chunkStart);
3347
- const hlEndInChunk = Math.min(text.length, hl.end - chunkStart);
3348
- if (pos < hlStartInChunk) {
3349
- result.push({
3350
- __isChunk: true,
3351
- text: text.slice(pos, hlStartInChunk),
3352
- fg: chunk.fg,
3353
- bg: chunk.bg,
3354
- attributes: chunk.attributes
3355
- });
3698
+ let cellPos = 0;
3699
+ for (const hl of overlapping) {
3700
+ const hlStartLocal = Math.max(0, hl.start - chunkStart);
3701
+ const hlEndLocal = Math.min(w, hl.end - chunkStart);
3702
+ if (cellPos < hlStartLocal) {
3703
+ const startStr = cellColToStringIndex(chunk.text, cellPos);
3704
+ const endStr = cellColToStringIndex(chunk.text, hlStartLocal);
3705
+ result.push({ ...chunk, text: chunk.text.slice(startStr, endStr), cellWidth: hlStartLocal - cellPos });
3356
3706
  }
3357
- if (hlStartInChunk < hlEndInChunk) {
3358
- const highlightedText = text.slice(hlStartInChunk, hlEndInChunk);
3359
- const displayText = hl.replaceWithX ? "x".repeat(highlightedText.length) : highlightedText;
3707
+ if (hlStartLocal < hlEndLocal) {
3708
+ const startStr = cellColToStringIndex(chunk.text, hlStartLocal);
3709
+ const endStr = cellColToStringIndex(chunk.text, hlEndLocal);
3710
+ const hlText = chunk.text.slice(startStr, endStr);
3711
+ const cellWidth = hlEndLocal - hlStartLocal;
3360
3712
  result.push({
3361
- __isChunk: true,
3362
- text: displayText,
3363
- fg: chunk.fg,
3713
+ ...chunk,
3714
+ text: hl.replaceWithX ? "x".repeat(cellWidth) : hlText,
3364
3715
  bg: RGBA.fromHex(hl.backgroundColor),
3365
- attributes: chunk.attributes
3716
+ cellWidth
3366
3717
  });
3367
3718
  }
3368
- pos = hlEndInChunk;
3369
- }
3370
- if (pos < text.length) {
3371
- result.push({
3372
- __isChunk: true,
3373
- text: text.slice(pos),
3374
- fg: chunk.fg,
3375
- bg: chunk.bg,
3376
- attributes: chunk.attributes
3377
- });
3719
+ cellPos = hlEndLocal;
3720
+ }
3721
+ if (cellPos < w) {
3722
+ const startStr = cellColToStringIndex(chunk.text, cellPos);
3723
+ result.push({ ...chunk, text: chunk.text.slice(startStr), cellWidth: w - cellPos });
3378
3724
  }
3379
3725
  col = chunkEnd;
3380
3726
  }
3381
3727
  return result;
3382
3728
  }
3383
3729
  function makeCursorChunk(char, style, original) {
3730
+ const cellWidth = Math.max(1, import_wcwidth.default(char));
3384
3731
  if (style === "block") {
3385
3732
  return {
3386
3733
  __isChunk: true,
3387
3734
  text: char,
3388
3735
  fg: original?.bg || RGBA.fromHex("#1e1e1e"),
3389
3736
  bg: original?.fg || DEFAULT_FG,
3390
- attributes: original?.attributes ?? 0
3737
+ attributes: original?.attributes ?? 0,
3738
+ cellWidth
3391
3739
  };
3392
3740
  }
3393
3741
  return {
@@ -3395,30 +3743,35 @@ function makeCursorChunk(char, style, original) {
3395
3743
  text: char,
3396
3744
  fg: original?.fg || DEFAULT_FG,
3397
3745
  bg: original?.bg,
3398
- attributes: (original?.attributes ?? 0) | TextAttributes.UNDERLINE
3746
+ attributes: (original?.attributes ?? 0) | TextAttributes.UNDERLINE,
3747
+ cellWidth
3399
3748
  };
3400
3749
  }
3401
3750
  function applyCursorToLine(chunks, cursorX, cursorStyle) {
3402
- const totalLen = chunks.reduce((sum, c) => sum + c.text.length, 0);
3751
+ const totalLen = chunks.reduce((sum, chunk) => sum + getChunkCellWidth(chunk), 0);
3403
3752
  if (cursorX >= totalLen) {
3404
3753
  const gap = cursorX - totalLen;
3405
3754
  if (gap > 0) {
3406
- return [...chunks, { __isChunk: true, text: " ".repeat(gap), attributes: 0 }, makeCursorChunk(" ", cursorStyle)];
3755
+ return [...chunks, { __isChunk: true, text: " ".repeat(gap), attributes: 0, cellWidth: gap }, makeCursorChunk(" ", cursorStyle)];
3407
3756
  }
3408
3757
  return [...chunks, makeCursorChunk(" ", cursorStyle)];
3409
3758
  }
3410
3759
  const result = [];
3411
3760
  let col = 0;
3412
3761
  for (const chunk of chunks) {
3413
- const chunkEnd = col + chunk.text.length;
3762
+ const w = getChunkCellWidth(chunk);
3763
+ const chunkEnd = col + w;
3414
3764
  if (cursorX >= col && cursorX < chunkEnd) {
3415
- const pos = cursorX - col;
3416
- if (pos > 0) {
3417
- result.push({ ...chunk, text: chunk.text.slice(0, pos) });
3765
+ const localCol = cursorX - col;
3766
+ const strIdx = cellColToStringIndex(chunk.text, localCol);
3767
+ const cursorChar = String.fromCodePoint(chunk.text.codePointAt(strIdx));
3768
+ const strEnd = strIdx + cursorChar.length;
3769
+ if (strIdx > 0) {
3770
+ result.push({ ...chunk, text: chunk.text.slice(0, strIdx) });
3418
3771
  }
3419
- result.push(makeCursorChunk(chunk.text[pos], cursorStyle, chunk));
3420
- if (pos + 1 < chunk.text.length) {
3421
- result.push({ ...chunk, text: chunk.text.slice(pos + 1) });
3772
+ result.push(makeCursorChunk(cursorChar, cursorStyle, chunk));
3773
+ if (strEnd < chunk.text.length) {
3774
+ result.push({ ...chunk, text: chunk.text.slice(strEnd) });
3422
3775
  }
3423
3776
  } else {
3424
3777
  result.push(chunk);
@@ -3473,7 +3826,13 @@ class GhosttyTerminalRenderable extends TextBufferRenderable {
3473
3826
  _ansiDirty = false;
3474
3827
  _lineCount = 0;
3475
3828
  _showCursor = false;
3476
- _cursorStyle = "block";
3829
+ _cursorStyle = undefined;
3830
+ _renderCursor = {
3831
+ x: 0,
3832
+ y: 0,
3833
+ visible: false,
3834
+ style: "default"
3835
+ };
3477
3836
  _persistent = false;
3478
3837
  _persistentTerminal = null;
3479
3838
  constructor(ctx, options) {
@@ -3490,7 +3849,10 @@ class GhosttyTerminalRenderable extends TextBufferRenderable {
3490
3849
  this._highlights = options.highlights;
3491
3850
  this._persistent = options.persistent ?? false;
3492
3851
  this._showCursor = options.showCursor ?? false;
3493
- this._cursorStyle = options.cursorStyle ?? "block";
3852
+ this._cursorStyle = options.cursorStyle;
3853
+ if (options.focusable) {
3854
+ this._focusable = true;
3855
+ }
3494
3856
  if (this._persistent && hasPersistentTerminalSupport()) {
3495
3857
  this._persistentTerminal = new PersistentTerminal({
3496
3858
  cols: this._cols,
@@ -3634,6 +3996,35 @@ class GhosttyTerminalRenderable extends TextBufferRenderable {
3634
3996
  }
3635
3997
  super.destroy();
3636
3998
  }
3999
+ onRemove() {
4000
+ if (this._focused || !this._focusable) {
4001
+ this.hideTerminalCursor();
4002
+ }
4003
+ }
4004
+ hideTerminalCursor() {
4005
+ this.ctx.setCursorPosition(0, 0, false);
4006
+ }
4007
+ renderTerminalCursor() {
4008
+ if (!this._renderCursor.visible || this._focusable && !this._focused) {
4009
+ this.hideTerminalCursor();
4010
+ return;
4011
+ }
4012
+ const style = this._cursorStyle ?? this._renderCursor.style;
4013
+ this.ctx.setCursorStyle({
4014
+ style,
4015
+ blinking: false
4016
+ });
4017
+ this.ctx.setCursorPosition(this.x + this._renderCursor.x + 1, this.y + this._renderCursor.y + 1, true);
4018
+ }
4019
+ focus() {
4020
+ super.focus();
4021
+ this.requestRender();
4022
+ }
4023
+ blur() {
4024
+ super.blur();
4025
+ this.hideTerminalCursor();
4026
+ this.requestRender();
4027
+ }
3637
4028
  renderSelf(buffer) {
3638
4029
  if (this._ansiDirty) {
3639
4030
  let data;
@@ -3657,26 +4048,29 @@ class GhosttyTerminalRenderable extends TextBufferRenderable {
3657
4048
  data.lines.pop();
3658
4049
  }
3659
4050
  }
3660
- const cursor = this._showCursor ? {
3661
- x: data.cursor[0],
3662
- y: Math.max(0, data.totalLines - data.rows + data.cursor[1] - data.offset),
3663
- style: this._cursorStyle
3664
- } : undefined;
3665
- const styledText = terminalDataToStyledText(data, this._highlights, cursor);
3666
- this.textBuffer.setStyledText(styledText);
4051
+ this.textBuffer.setStyledText(terminalDataToStyledText(data, this._highlights));
3667
4052
  this.updateTextInfo();
3668
- const lineInfo = this.textBufferView.logicalLineInfo;
3669
- if (lineInfo) {
3670
- this._lineCount = lineInfo.lineStartCols?.length ?? lineInfo.lineStarts?.length ?? this._lineCount;
4053
+ if (this._showCursor) {
4054
+ const cursorY = Math.max(0, data.totalLines - data.rows + data.cursor[1] - data.offset);
4055
+ this._renderCursor.x = data.cursor[0];
4056
+ this._renderCursor.y = cursorY;
4057
+ this._renderCursor.visible = data.cursorVisible && cursorY < data.lines.length;
4058
+ const ts = data.cursorStyle;
4059
+ this._renderCursor.style = ts === "default" ? "default" : ts === "bar" ? "line" : ts === "underline" ? "underline" : "block";
4060
+ } else {
4061
+ this._renderCursor.visible = false;
3671
4062
  }
4063
+ const lineInfo = this.textBufferView.logicalLineInfo;
4064
+ this._lineCount = getLineStarts(lineInfo).length;
3672
4065
  this._ansiDirty = false;
3673
4066
  }
3674
4067
  super.renderSelf(buffer);
4068
+ this.renderTerminalCursor();
3675
4069
  }
3676
4070
  getScrollPositionForLine(lineNumber) {
3677
4071
  const clampedLine = Math.max(0, Math.min(lineNumber, this._lineCount - 1));
3678
4072
  const lineInfo = this.textBufferView.logicalLineInfo;
3679
- const lineStarts = lineInfo?.lineStartCols ?? lineInfo?.lineStarts;
4073
+ const lineStarts = getLineStarts(lineInfo);
3680
4074
  let lineYOffset = clampedLine;
3681
4075
  if (lineStarts && lineStarts.length > clampedLine) {
3682
4076
  lineYOffset = lineStarts[clampedLine];
@@ -3684,13 +4078,6 @@ class GhosttyTerminalRenderable extends TextBufferRenderable {
3684
4078
  return this.y + lineYOffset;
3685
4079
  }
3686
4080
  }
3687
- var EMPTY_LINE_INFO = { lineStarts: [], lineStartCols: [], lineWidthColsMax: 0, lineSources: [] };
3688
- Object.defineProperty(GhosttyTerminalRenderable.prototype, "lineInfo", {
3689
- get() {
3690
- return this.textBufferView?.logicalLineInfo ?? EMPTY_LINE_INFO;
3691
- },
3692
- configurable: true
3693
- });
3694
4081
 
3695
4082
  // src/ui/tailing-terminal.ts
3696
4083
  class TailingTerminal extends GhosttyTerminalRenderable {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "numux",
3
- "version": "2.17.0",
3
+ "version": "2.17.2",
4
4
  "description": "Terminal multiplexer with dependency orchestration",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -45,8 +45,8 @@
45
45
  "dist/"
46
46
  ],
47
47
  "dependencies": {
48
- "@opentui/core": "^0.1.88",
49
- "ghostty-opentui": "^1.4.7"
48
+ "@opentui/core": "^0.4.1",
49
+ "ghostty-opentui": "^1.5.0"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@biomejs/biome": "^2.4.4",
@@ -56,6 +56,6 @@
56
56
  "marked-man": "^2.1.0"
57
57
  },
58
58
  "patchedDependencies": {
59
- "ghostty-opentui@1.4.7": "patches/ghostty-opentui@1.4.7.patch"
59
+ "@opentui/core@0.4.1": "patches/@opentui%2Fcore@0.4.1.patch"
60
60
  }
61
61
  }