numux 2.17.0 → 2.17.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/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.1" "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.1",
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",
@@ -604,9 +939,6 @@ var require_package = __commonJS((exports, module) => {
604
939
  "@commitlint/config-conventional": "^20.4.2",
605
940
  "@types/bun": "^1.3.9",
606
941
  "marked-man": "^2.1.0"
607
- },
608
- patchedDependencies: {
609
- "ghostty-opentui@1.4.7": "patches/ghostty-opentui@1.4.7.patch"
610
942
  }
611
943
  };
612
944
  });
@@ -3285,6 +3617,7 @@ function resolveTimestampFormat(timestamps) {
3285
3617
  }
3286
3618
 
3287
3619
  // node_modules/ghostty-opentui/src/terminal-buffer.ts
3620
+ var import_wcwidth = __toESM(require_wcwidth(), 1);
3288
3621
  import {
3289
3622
  TextBufferRenderable,
3290
3623
  StyledText,
@@ -3293,6 +3626,22 @@ import {
3293
3626
  import { ptyToJson, PersistentTerminal, hasPersistentTerminalSupport, StyleFlags } from "ghostty-opentui";
3294
3627
  var DEFAULT_FG = RGBA.fromHex("#d4d4d4");
3295
3628
  var DEFAULT_BG = RGBA.fromHex("#1e1e1e");
3629
+ function getChunkCellWidth(chunk) {
3630
+ return "cellWidth" in chunk && typeof chunk.cellWidth === "number" ? chunk.cellWidth : import_wcwidth.default(chunk.text);
3631
+ }
3632
+ function cellColToStringIndex(text, cellCol) {
3633
+ if (cellCol <= 0)
3634
+ return 0;
3635
+ let col = 0;
3636
+ let strIdx = 0;
3637
+ for (const ch of text) {
3638
+ if (col >= cellCol)
3639
+ break;
3640
+ col += import_wcwidth.default(ch);
3641
+ strIdx += ch.length;
3642
+ }
3643
+ return strIdx;
3644
+ }
3296
3645
  var TextAttributes = {
3297
3646
  BOLD: 1 << 0,
3298
3647
  DIM: 1 << 1,
@@ -3303,8 +3652,11 @@ var TextAttributes = {
3303
3652
  HIDDEN: 1 << 6,
3304
3653
  STRIKETHROUGH: 1 << 7
3305
3654
  };
3655
+ function getLineStarts(lineInfo) {
3656
+ return lineInfo.lineStarts ?? lineInfo.lineStartCols ?? [];
3657
+ }
3306
3658
  function convertSpanToChunk(span) {
3307
- const { text, fg, bg, flags } = span;
3659
+ const { text, fg, bg, flags, width } = span;
3308
3660
  let fgColor = fg ? RGBA.fromHex(fg) : DEFAULT_FG;
3309
3661
  let bgColor = bg ? RGBA.fromHex(bg) : undefined;
3310
3662
  if (flags & StyleFlags.INVERSE) {
@@ -3323,7 +3675,7 @@ function convertSpanToChunk(span) {
3323
3675
  attributes |= TextAttributes.STRIKETHROUGH;
3324
3676
  if (flags & StyleFlags.FAINT)
3325
3677
  attributes |= TextAttributes.DIM;
3326
- return { __isChunk: true, text, fg: fgColor, bg: bgColor, attributes };
3678
+ return { __isChunk: true, text, fg: fgColor, bg: bgColor, attributes, cellWidth: width };
3327
3679
  }
3328
3680
  function applyHighlightsToLine(chunks, highlights) {
3329
3681
  if (highlights.length === 0)
@@ -3331,63 +3683,56 @@ function applyHighlightsToLine(chunks, highlights) {
3331
3683
  const result = [];
3332
3684
  let col = 0;
3333
3685
  for (const chunk of chunks) {
3686
+ const w = getChunkCellWidth(chunk);
3334
3687
  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) {
3688
+ const chunkEnd = col + w;
3689
+ const overlapping = highlights.filter((hl) => hl.start < chunkEnd && hl.end > chunkStart).sort((a, b) => a.start - b.start);
3690
+ if (overlapping.length === 0) {
3338
3691
  result.push(chunk);
3339
3692
  col = chunkEnd;
3340
3693
  continue;
3341
3694
  }
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
- });
3695
+ let cellPos = 0;
3696
+ for (const hl of overlapping) {
3697
+ const hlStartLocal = Math.max(0, hl.start - chunkStart);
3698
+ const hlEndLocal = Math.min(w, hl.end - chunkStart);
3699
+ if (cellPos < hlStartLocal) {
3700
+ const startStr = cellColToStringIndex(chunk.text, cellPos);
3701
+ const endStr = cellColToStringIndex(chunk.text, hlStartLocal);
3702
+ result.push({ ...chunk, text: chunk.text.slice(startStr, endStr), cellWidth: hlStartLocal - cellPos });
3356
3703
  }
3357
- if (hlStartInChunk < hlEndInChunk) {
3358
- const highlightedText = text.slice(hlStartInChunk, hlEndInChunk);
3359
- const displayText = hl.replaceWithX ? "x".repeat(highlightedText.length) : highlightedText;
3704
+ if (hlStartLocal < hlEndLocal) {
3705
+ const startStr = cellColToStringIndex(chunk.text, hlStartLocal);
3706
+ const endStr = cellColToStringIndex(chunk.text, hlEndLocal);
3707
+ const hlText = chunk.text.slice(startStr, endStr);
3708
+ const cellWidth = hlEndLocal - hlStartLocal;
3360
3709
  result.push({
3361
- __isChunk: true,
3362
- text: displayText,
3363
- fg: chunk.fg,
3710
+ ...chunk,
3711
+ text: hl.replaceWithX ? "x".repeat(cellWidth) : hlText,
3364
3712
  bg: RGBA.fromHex(hl.backgroundColor),
3365
- attributes: chunk.attributes
3713
+ cellWidth
3366
3714
  });
3367
3715
  }
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
- });
3716
+ cellPos = hlEndLocal;
3717
+ }
3718
+ if (cellPos < w) {
3719
+ const startStr = cellColToStringIndex(chunk.text, cellPos);
3720
+ result.push({ ...chunk, text: chunk.text.slice(startStr), cellWidth: w - cellPos });
3378
3721
  }
3379
3722
  col = chunkEnd;
3380
3723
  }
3381
3724
  return result;
3382
3725
  }
3383
3726
  function makeCursorChunk(char, style, original) {
3727
+ const cellWidth = Math.max(1, import_wcwidth.default(char));
3384
3728
  if (style === "block") {
3385
3729
  return {
3386
3730
  __isChunk: true,
3387
3731
  text: char,
3388
3732
  fg: original?.bg || RGBA.fromHex("#1e1e1e"),
3389
3733
  bg: original?.fg || DEFAULT_FG,
3390
- attributes: original?.attributes ?? 0
3734
+ attributes: original?.attributes ?? 0,
3735
+ cellWidth
3391
3736
  };
3392
3737
  }
3393
3738
  return {
@@ -3395,30 +3740,35 @@ function makeCursorChunk(char, style, original) {
3395
3740
  text: char,
3396
3741
  fg: original?.fg || DEFAULT_FG,
3397
3742
  bg: original?.bg,
3398
- attributes: (original?.attributes ?? 0) | TextAttributes.UNDERLINE
3743
+ attributes: (original?.attributes ?? 0) | TextAttributes.UNDERLINE,
3744
+ cellWidth
3399
3745
  };
3400
3746
  }
3401
3747
  function applyCursorToLine(chunks, cursorX, cursorStyle) {
3402
- const totalLen = chunks.reduce((sum, c) => sum + c.text.length, 0);
3748
+ const totalLen = chunks.reduce((sum, chunk) => sum + getChunkCellWidth(chunk), 0);
3403
3749
  if (cursorX >= totalLen) {
3404
3750
  const gap = cursorX - totalLen;
3405
3751
  if (gap > 0) {
3406
- return [...chunks, { __isChunk: true, text: " ".repeat(gap), attributes: 0 }, makeCursorChunk(" ", cursorStyle)];
3752
+ return [...chunks, { __isChunk: true, text: " ".repeat(gap), attributes: 0, cellWidth: gap }, makeCursorChunk(" ", cursorStyle)];
3407
3753
  }
3408
3754
  return [...chunks, makeCursorChunk(" ", cursorStyle)];
3409
3755
  }
3410
3756
  const result = [];
3411
3757
  let col = 0;
3412
3758
  for (const chunk of chunks) {
3413
- const chunkEnd = col + chunk.text.length;
3759
+ const w = getChunkCellWidth(chunk);
3760
+ const chunkEnd = col + w;
3414
3761
  if (cursorX >= col && cursorX < chunkEnd) {
3415
- const pos = cursorX - col;
3416
- if (pos > 0) {
3417
- result.push({ ...chunk, text: chunk.text.slice(0, pos) });
3762
+ const localCol = cursorX - col;
3763
+ const strIdx = cellColToStringIndex(chunk.text, localCol);
3764
+ const cursorChar = String.fromCodePoint(chunk.text.codePointAt(strIdx));
3765
+ const strEnd = strIdx + cursorChar.length;
3766
+ if (strIdx > 0) {
3767
+ result.push({ ...chunk, text: chunk.text.slice(0, strIdx) });
3418
3768
  }
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) });
3769
+ result.push(makeCursorChunk(cursorChar, cursorStyle, chunk));
3770
+ if (strEnd < chunk.text.length) {
3771
+ result.push({ ...chunk, text: chunk.text.slice(strEnd) });
3422
3772
  }
3423
3773
  } else {
3424
3774
  result.push(chunk);
@@ -3473,7 +3823,13 @@ class GhosttyTerminalRenderable extends TextBufferRenderable {
3473
3823
  _ansiDirty = false;
3474
3824
  _lineCount = 0;
3475
3825
  _showCursor = false;
3476
- _cursorStyle = "block";
3826
+ _cursorStyle = undefined;
3827
+ _renderCursor = {
3828
+ x: 0,
3829
+ y: 0,
3830
+ visible: false,
3831
+ style: "default"
3832
+ };
3477
3833
  _persistent = false;
3478
3834
  _persistentTerminal = null;
3479
3835
  constructor(ctx, options) {
@@ -3490,7 +3846,10 @@ class GhosttyTerminalRenderable extends TextBufferRenderable {
3490
3846
  this._highlights = options.highlights;
3491
3847
  this._persistent = options.persistent ?? false;
3492
3848
  this._showCursor = options.showCursor ?? false;
3493
- this._cursorStyle = options.cursorStyle ?? "block";
3849
+ this._cursorStyle = options.cursorStyle;
3850
+ if (options.focusable) {
3851
+ this._focusable = true;
3852
+ }
3494
3853
  if (this._persistent && hasPersistentTerminalSupport()) {
3495
3854
  this._persistentTerminal = new PersistentTerminal({
3496
3855
  cols: this._cols,
@@ -3634,6 +3993,35 @@ class GhosttyTerminalRenderable extends TextBufferRenderable {
3634
3993
  }
3635
3994
  super.destroy();
3636
3995
  }
3996
+ onRemove() {
3997
+ if (this._focused || !this._focusable) {
3998
+ this.hideTerminalCursor();
3999
+ }
4000
+ }
4001
+ hideTerminalCursor() {
4002
+ this.ctx.setCursorPosition(0, 0, false);
4003
+ }
4004
+ renderTerminalCursor() {
4005
+ if (!this._renderCursor.visible || this._focusable && !this._focused) {
4006
+ this.hideTerminalCursor();
4007
+ return;
4008
+ }
4009
+ const style = this._cursorStyle ?? this._renderCursor.style;
4010
+ this.ctx.setCursorStyle({
4011
+ style,
4012
+ blinking: false
4013
+ });
4014
+ this.ctx.setCursorPosition(this.x + this._renderCursor.x + 1, this.y + this._renderCursor.y + 1, true);
4015
+ }
4016
+ focus() {
4017
+ super.focus();
4018
+ this.requestRender();
4019
+ }
4020
+ blur() {
4021
+ super.blur();
4022
+ this.hideTerminalCursor();
4023
+ this.requestRender();
4024
+ }
3637
4025
  renderSelf(buffer) {
3638
4026
  if (this._ansiDirty) {
3639
4027
  let data;
@@ -3657,26 +4045,29 @@ class GhosttyTerminalRenderable extends TextBufferRenderable {
3657
4045
  data.lines.pop();
3658
4046
  }
3659
4047
  }
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);
4048
+ this.textBuffer.setStyledText(terminalDataToStyledText(data, this._highlights));
3667
4049
  this.updateTextInfo();
3668
- const lineInfo = this.textBufferView.logicalLineInfo;
3669
- if (lineInfo) {
3670
- this._lineCount = lineInfo.lineStartCols?.length ?? lineInfo.lineStarts?.length ?? this._lineCount;
4050
+ if (this._showCursor) {
4051
+ const cursorY = Math.max(0, data.totalLines - data.rows + data.cursor[1] - data.offset);
4052
+ this._renderCursor.x = data.cursor[0];
4053
+ this._renderCursor.y = cursorY;
4054
+ this._renderCursor.visible = data.cursorVisible && cursorY < data.lines.length;
4055
+ const ts = data.cursorStyle;
4056
+ this._renderCursor.style = ts === "default" ? "default" : ts === "bar" ? "line" : ts === "underline" ? "underline" : "block";
4057
+ } else {
4058
+ this._renderCursor.visible = false;
3671
4059
  }
4060
+ const lineInfo = this.textBufferView.logicalLineInfo;
4061
+ this._lineCount = getLineStarts(lineInfo).length;
3672
4062
  this._ansiDirty = false;
3673
4063
  }
3674
4064
  super.renderSelf(buffer);
4065
+ this.renderTerminalCursor();
3675
4066
  }
3676
4067
  getScrollPositionForLine(lineNumber) {
3677
4068
  const clampedLine = Math.max(0, Math.min(lineNumber, this._lineCount - 1));
3678
4069
  const lineInfo = this.textBufferView.logicalLineInfo;
3679
- const lineStarts = lineInfo?.lineStartCols ?? lineInfo?.lineStarts;
4070
+ const lineStarts = getLineStarts(lineInfo);
3680
4071
  let lineYOffset = clampedLine;
3681
4072
  if (lineStarts && lineStarts.length > clampedLine) {
3682
4073
  lineYOffset = lineStarts[clampedLine];
@@ -3684,13 +4075,6 @@ class GhosttyTerminalRenderable extends TextBufferRenderable {
3684
4075
  return this.y + lineYOffset;
3685
4076
  }
3686
4077
  }
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
4078
 
3695
4079
  // src/ui/tailing-terminal.ts
3696
4080
  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.1",
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",
@@ -54,8 +54,5 @@
54
54
  "@commitlint/config-conventional": "^20.4.2",
55
55
  "@types/bun": "^1.3.9",
56
56
  "marked-man": "^2.1.0"
57
- },
58
- "patchedDependencies": {
59
- "ghostty-opentui@1.4.7": "patches/ghostty-opentui@1.4.7.patch"
60
57
  }
61
58
  }