@opentui/core 0.1.100 → 0.1.102

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.
@@ -6867,7 +6867,8 @@ var DEFAULT_PROTOCOL_CONTEXT = {
6867
6867
  kittyKeyboardEnabled: false,
6868
6868
  privateCapabilityRepliesActive: false,
6869
6869
  pixelResolutionQueryActive: false,
6870
- explicitWidthCprActive: false
6870
+ explicitWidthCprActive: false,
6871
+ startupCursorCprActive: false
6871
6872
  };
6872
6873
  var RXVT_DOLLAR_CSI_RE = /^\x1b\[\d+\$$/;
6873
6874
  var SYSTEM_CLOCK = new SystemClock;
@@ -7056,11 +7057,14 @@ function canStillBeKittySpecial(state) {
7056
7057
  function canStillBeExplicitWidthCpr(state) {
7057
7058
  return state.firstParamValue === 1 && state.semicolons === 1;
7058
7059
  }
7060
+ function canStillBeStartupCursorCpr(state) {
7061
+ return state.semicolons === 1;
7062
+ }
7059
7063
  function canStillBePixelResolution(state) {
7060
7064
  return state.firstParamValue === 4 && state.semicolons === 2;
7061
7065
  }
7062
7066
  function canDeferParametricCsi(state, context) {
7063
- return context.kittyKeyboardEnabled && (canStillBeKittyU(state) || canStillBeKittySpecial(state)) || context.explicitWidthCprActive && canStillBeExplicitWidthCpr(state) || context.pixelResolutionQueryActive && canStillBePixelResolution(state);
7067
+ return context.kittyKeyboardEnabled && (canStillBeKittyU(state) || canStillBeKittySpecial(state)) || context.explicitWidthCprActive && canStillBeExplicitWidthCpr(state) || context.startupCursorCprActive && canStillBeStartupCursorCpr(state) || context.pixelResolutionQueryActive && canStillBePixelResolution(state);
7064
7068
  }
7065
7069
  function canCompleteDeferredParametricCsi(state, byte, context) {
7066
7070
  if (context.kittyKeyboardEnabled) {
@@ -7073,11 +7077,20 @@ function canCompleteDeferredParametricCsi(state, byte, context) {
7073
7077
  if (context.explicitWidthCprActive && state.hasDigit && state.firstParamValue === 1 && state.semicolons === 1 && byte === 82) {
7074
7078
  return true;
7075
7079
  }
7080
+ if (context.startupCursorCprActive && state.hasDigit && state.semicolons === 1 && byte === 82) {
7081
+ return true;
7082
+ }
7076
7083
  if (context.pixelResolutionQueryActive && state.hasDigit && state.firstParamValue === 4 && state.semicolons === 2 && byte === 116) {
7077
7084
  return true;
7078
7085
  }
7079
7086
  return false;
7080
7087
  }
7088
+ function classifyParametricCsiProtocol(state, finalByte) {
7089
+ if (finalByte === 82 && state.semicolons === 1 && state.segments === 1 && state.hasDigit) {
7090
+ return "cpr";
7091
+ }
7092
+ return "csi";
7093
+ }
7081
7094
  function canDeferPrivateReplyCsi(context) {
7082
7095
  return context.privateCapabilityRepliesActive;
7083
7096
  }
@@ -7088,6 +7101,8 @@ function canCompleteDeferredPrivateReplyCsi(state, byte, context) {
7088
7101
  return state.hasDigit && byte === 121;
7089
7102
  if (byte === 99)
7090
7103
  return state.hasDigit || state.semicolons > 0;
7104
+ if (byte === 110)
7105
+ return state.hasDigit;
7091
7106
  return state.hasDigit && byte === 117;
7092
7107
  }
7093
7108
  function concatBytes(left, right) {
@@ -7182,7 +7197,8 @@ class StdinParser {
7182
7197
  kittyKeyboardEnabled: options.protocolContext?.kittyKeyboardEnabled ?? false,
7183
7198
  privateCapabilityRepliesActive: options.protocolContext?.privateCapabilityRepliesActive ?? false,
7184
7199
  pixelResolutionQueryActive: options.protocolContext?.pixelResolutionQueryActive ?? false,
7185
- explicitWidthCprActive: options.protocolContext?.explicitWidthCprActive ?? false
7200
+ explicitWidthCprActive: options.protocolContext?.explicitWidthCprActive ?? false,
7201
+ startupCursorCprActive: options.protocolContext?.startupCursorCprActive ?? false
7186
7202
  };
7187
7203
  }
7188
7204
  get bufferCapacity() {
@@ -7684,7 +7700,8 @@ class StdinParser {
7684
7700
  }
7685
7701
  if (byte >= 64 && byte <= 126) {
7686
7702
  const end = this.cursor + 1;
7687
- this.emitKeyOrResponse("csi", decodeUtf8(bytes.subarray(this.unitStart, end)));
7703
+ const protocol = classifyParametricCsiProtocol(this.state, byte);
7704
+ this.emitKeyOrResponse(protocol, decodeUtf8(bytes.subarray(this.unitStart, end)));
7688
7705
  this.state = { tag: "ground" };
7689
7706
  this.consumePrefix(end);
7690
7707
  continue;
@@ -7789,7 +7806,7 @@ class StdinParser {
7789
7806
  }
7790
7807
  if (byte >= 64 && byte <= 126) {
7791
7808
  const end = this.cursor + 1;
7792
- this.emitKeyOrResponse("csi", decodeUtf8(bytes.subarray(this.unitStart, end)));
7809
+ this.emitOpaqueResponse("csi", bytes.subarray(this.unitStart, end));
7793
7810
  this.state = { tag: "ground" };
7794
7811
  this.consumePrefix(end);
7795
7812
  continue;
@@ -11893,6 +11910,10 @@ function getOpenTUILib(libPath) {
11893
11910
  args: ["ptr", "bool"],
11894
11911
  returns: "void"
11895
11912
  },
11913
+ setClearOnShutdown: {
11914
+ args: ["ptr", "bool"],
11915
+ returns: "void"
11916
+ },
11896
11917
  setBackgroundColor: {
11897
11918
  args: ["ptr", "ptr"],
11898
11919
  returns: "void"
@@ -11901,6 +11922,22 @@ function getOpenTUILib(libPath) {
11901
11922
  args: ["ptr", "u32"],
11902
11923
  returns: "void"
11903
11924
  },
11925
+ resetSplitScrollback: {
11926
+ args: ["ptr", "u32", "u32"],
11927
+ returns: "u32"
11928
+ },
11929
+ syncSplitScrollback: {
11930
+ args: ["ptr", "u32"],
11931
+ returns: "u32"
11932
+ },
11933
+ setPendingSplitFooterTransition: {
11934
+ args: ["ptr", "u8", "u32", "u32", "u32", "u32"],
11935
+ returns: "void"
11936
+ },
11937
+ clearPendingSplitFooterTransition: {
11938
+ args: ["ptr"],
11939
+ returns: "void"
11940
+ },
11904
11941
  updateStats: {
11905
11942
  args: ["ptr", "f64", "u32", "f64"],
11906
11943
  returns: "void"
@@ -11913,6 +11950,14 @@ function getOpenTUILib(libPath) {
11913
11950
  args: ["ptr", "bool"],
11914
11951
  returns: "void"
11915
11952
  },
11953
+ repaintSplitFooter: {
11954
+ args: ["ptr", "u32", "bool"],
11955
+ returns: "u32"
11956
+ },
11957
+ commitSplitFooterSnapshot: {
11958
+ args: ["ptr", "ptr", "u32", "bool", "bool", "u32", "bool", "bool", "bool"],
11959
+ returns: "u32"
11960
+ },
11916
11961
  getNextBuffer: {
11917
11962
  args: ["ptr"],
11918
11963
  returns: "ptr"
@@ -12385,6 +12430,10 @@ function getOpenTUILib(libPath) {
12385
12430
  args: ["ptr", "u8"],
12386
12431
  returns: "void"
12387
12432
  },
12433
+ textBufferViewSetFirstLineOffset: {
12434
+ args: ["ptr", "u32"],
12435
+ returns: "void"
12436
+ },
12388
12437
  textBufferViewSetViewportSize: {
12389
12438
  args: ["ptr", "u32", "u32"],
12390
12439
  returns: "void"
@@ -13122,12 +13171,27 @@ class FFIRenderLib {
13122
13171
  setUseThread(renderer, useThread) {
13123
13172
  this.opentui.symbols.setUseThread(renderer, useThread);
13124
13173
  }
13174
+ setClearOnShutdown(renderer, clear) {
13175
+ this.opentui.symbols.setClearOnShutdown(renderer, clear);
13176
+ }
13125
13177
  setBackgroundColor(renderer, color) {
13126
13178
  this.opentui.symbols.setBackgroundColor(renderer, color.buffer);
13127
13179
  }
13128
13180
  setRenderOffset(renderer, offset) {
13129
13181
  this.opentui.symbols.setRenderOffset(renderer, offset);
13130
13182
  }
13183
+ resetSplitScrollback(renderer, seedRows, pinnedRenderOffset) {
13184
+ return this.opentui.symbols.resetSplitScrollback(renderer, seedRows, pinnedRenderOffset);
13185
+ }
13186
+ syncSplitScrollback(renderer, pinnedRenderOffset) {
13187
+ return this.opentui.symbols.syncSplitScrollback(renderer, pinnedRenderOffset);
13188
+ }
13189
+ setPendingSplitFooterTransition(renderer, mode, sourceTopLine, sourceHeight, targetTopLine, targetHeight) {
13190
+ this.opentui.symbols.setPendingSplitFooterTransition(renderer, mode, sourceTopLine, sourceHeight, targetTopLine, targetHeight);
13191
+ }
13192
+ clearPendingSplitFooterTransition(renderer) {
13193
+ this.opentui.symbols.clearPendingSplitFooterTransition(renderer);
13194
+ }
13131
13195
  updateStats(renderer, time, fps, frameCallbackTime) {
13132
13196
  this.opentui.symbols.updateStats(renderer, time, fps, frameCallbackTime);
13133
13197
  }
@@ -13317,6 +13381,12 @@ class FFIRenderLib {
13317
13381
  render(renderer, force) {
13318
13382
  this.opentui.symbols.render(renderer, force);
13319
13383
  }
13384
+ repaintSplitFooter(renderer, pinnedRenderOffset, force) {
13385
+ return this.opentui.symbols.repaintSplitFooter(renderer, pinnedRenderOffset, force);
13386
+ }
13387
+ commitSplitFooterSnapshot(renderer, snapshot, rowColumns, startOnNewLine, trailingNewline, pinnedRenderOffset, force, beginFrame = true, finalizeFrame = true) {
13388
+ return this.opentui.symbols.commitSplitFooterSnapshot(renderer, snapshot.ptr, rowColumns, startOnNewLine, trailingNewline, pinnedRenderOffset, force, beginFrame, finalizeFrame);
13389
+ }
13320
13390
  createOptimizedBuffer(width, height, widthMethod, respectAlpha = false, id) {
13321
13391
  if (Number.isNaN(width) || Number.isNaN(height)) {
13322
13392
  console.error(new Error(`Invalid dimensions for OptimizedBuffer: ${width}x${height}`).stack);
@@ -13596,6 +13666,9 @@ class FFIRenderLib {
13596
13666
  const modeValue = mode === "none" ? 0 : mode === "char" ? 1 : 2;
13597
13667
  this.opentui.symbols.textBufferViewSetWrapMode(view, modeValue);
13598
13668
  }
13669
+ textBufferViewSetFirstLineOffset(view, offset) {
13670
+ this.opentui.symbols.textBufferViewSetFirstLineOffset(view, offset);
13671
+ }
13599
13672
  textBufferViewSetViewportSize(view, width, height) {
13600
13673
  this.opentui.symbols.textBufferViewSetViewportSize(view, width, height);
13601
13674
  }
@@ -16049,6 +16122,139 @@ function delegate(mapping, vnode) {
16049
16122
  return vnode;
16050
16123
  }
16051
16124
 
16125
+ // src/text-buffer-view.ts
16126
+ class TextBufferView {
16127
+ lib;
16128
+ viewPtr;
16129
+ textBuffer;
16130
+ _destroyed = false;
16131
+ constructor(lib, ptr5, textBuffer) {
16132
+ this.lib = lib;
16133
+ this.viewPtr = ptr5;
16134
+ this.textBuffer = textBuffer;
16135
+ }
16136
+ static create(textBuffer) {
16137
+ const lib = resolveRenderLib();
16138
+ const viewPtr = lib.createTextBufferView(textBuffer.ptr);
16139
+ return new TextBufferView(lib, viewPtr, textBuffer);
16140
+ }
16141
+ guard() {
16142
+ if (this._destroyed)
16143
+ throw new Error("TextBufferView is destroyed");
16144
+ }
16145
+ get ptr() {
16146
+ this.guard();
16147
+ return this.viewPtr;
16148
+ }
16149
+ setSelection(start, end, bgColor, fgColor) {
16150
+ this.guard();
16151
+ this.lib.textBufferViewSetSelection(this.viewPtr, start, end, bgColor || null, fgColor || null);
16152
+ }
16153
+ updateSelection(end, bgColor, fgColor) {
16154
+ this.guard();
16155
+ this.lib.textBufferViewUpdateSelection(this.viewPtr, end, bgColor || null, fgColor || null);
16156
+ }
16157
+ resetSelection() {
16158
+ this.guard();
16159
+ this.lib.textBufferViewResetSelection(this.viewPtr);
16160
+ }
16161
+ getSelection() {
16162
+ this.guard();
16163
+ return this.lib.textBufferViewGetSelection(this.viewPtr);
16164
+ }
16165
+ hasSelection() {
16166
+ this.guard();
16167
+ return this.getSelection() !== null;
16168
+ }
16169
+ setLocalSelection(anchorX, anchorY, focusX, focusY, bgColor, fgColor) {
16170
+ this.guard();
16171
+ return this.lib.textBufferViewSetLocalSelection(this.viewPtr, anchorX, anchorY, focusX, focusY, bgColor || null, fgColor || null);
16172
+ }
16173
+ updateLocalSelection(anchorX, anchorY, focusX, focusY, bgColor, fgColor) {
16174
+ this.guard();
16175
+ return this.lib.textBufferViewUpdateLocalSelection(this.viewPtr, anchorX, anchorY, focusX, focusY, bgColor || null, fgColor || null);
16176
+ }
16177
+ resetLocalSelection() {
16178
+ this.guard();
16179
+ this.lib.textBufferViewResetLocalSelection(this.viewPtr);
16180
+ }
16181
+ setWrapWidth(width) {
16182
+ this.guard();
16183
+ this.lib.textBufferViewSetWrapWidth(this.viewPtr, width ?? 0);
16184
+ }
16185
+ setWrapMode(mode) {
16186
+ this.guard();
16187
+ this.lib.textBufferViewSetWrapMode(this.viewPtr, mode);
16188
+ }
16189
+ setFirstLineOffset(offset) {
16190
+ this.guard();
16191
+ this.lib.textBufferViewSetFirstLineOffset(this.viewPtr, offset);
16192
+ }
16193
+ setViewportSize(width, height) {
16194
+ this.guard();
16195
+ this.lib.textBufferViewSetViewportSize(this.viewPtr, width, height);
16196
+ }
16197
+ setViewport(x, y, width, height) {
16198
+ this.guard();
16199
+ this.lib.textBufferViewSetViewport(this.viewPtr, x, y, width, height);
16200
+ }
16201
+ get lineInfo() {
16202
+ this.guard();
16203
+ return this.lib.textBufferViewGetLineInfo(this.viewPtr);
16204
+ }
16205
+ get logicalLineInfo() {
16206
+ this.guard();
16207
+ return this.lib.textBufferViewGetLogicalLineInfo(this.viewPtr);
16208
+ }
16209
+ getSelectedText() {
16210
+ this.guard();
16211
+ const byteSize = this.textBuffer.byteSize;
16212
+ if (byteSize === 0)
16213
+ return "";
16214
+ const selectedBytes = this.lib.textBufferViewGetSelectedTextBytes(this.viewPtr, byteSize);
16215
+ if (!selectedBytes)
16216
+ return "";
16217
+ return this.lib.decoder.decode(selectedBytes);
16218
+ }
16219
+ getPlainText() {
16220
+ this.guard();
16221
+ const byteSize = this.textBuffer.byteSize;
16222
+ if (byteSize === 0)
16223
+ return "";
16224
+ const plainBytes = this.lib.textBufferViewGetPlainTextBytes(this.viewPtr, byteSize);
16225
+ if (!plainBytes)
16226
+ return "";
16227
+ return this.lib.decoder.decode(plainBytes);
16228
+ }
16229
+ setTabIndicator(indicator) {
16230
+ this.guard();
16231
+ const codePoint = typeof indicator === "string" ? indicator.codePointAt(0) ?? 0 : indicator;
16232
+ this.lib.textBufferViewSetTabIndicator(this.viewPtr, codePoint);
16233
+ }
16234
+ setTabIndicatorColor(color) {
16235
+ this.guard();
16236
+ this.lib.textBufferViewSetTabIndicatorColor(this.viewPtr, color);
16237
+ }
16238
+ setTruncate(truncate) {
16239
+ this.guard();
16240
+ this.lib.textBufferViewSetTruncate(this.viewPtr, truncate);
16241
+ }
16242
+ measureForDimensions(width, height) {
16243
+ this.guard();
16244
+ return this.lib.textBufferViewMeasureForDimensions(this.viewPtr, width, height);
16245
+ }
16246
+ getVirtualLineCount() {
16247
+ this.guard();
16248
+ return this.lib.textBufferViewGetVirtualLineCount(this.viewPtr);
16249
+ }
16250
+ destroy() {
16251
+ if (this._destroyed)
16252
+ return;
16253
+ this._destroyed = true;
16254
+ this.lib.destroyTextBufferView(this.viewPtr);
16255
+ }
16256
+ }
16257
+
16052
16258
  // src/edit-buffer.ts
16053
16259
  import { EventEmitter as EventEmitter6 } from "events";
16054
16260
 
@@ -16553,117 +16759,1552 @@ class EditorView {
16553
16759
  }
16554
16760
  }
16555
16761
 
16556
- // src/console.ts
16557
- import { EventEmitter as EventEmitter8 } from "events";
16558
- import { Console } from "console";
16559
- import fs from "fs";
16560
- import path5 from "path";
16561
- import util2 from "util";
16562
-
16563
- // src/lib/output.capture.ts
16564
- import { Writable } from "stream";
16565
- import { EventEmitter as EventEmitter7 } from "events";
16762
+ // src/syntax-style.ts
16763
+ function convertThemeToStyles(theme) {
16764
+ const flatStyles = {};
16765
+ for (const tokenStyle of theme) {
16766
+ const styleDefinition = {};
16767
+ if (tokenStyle.style.foreground) {
16768
+ styleDefinition.fg = parseColor(tokenStyle.style.foreground);
16769
+ }
16770
+ if (tokenStyle.style.background) {
16771
+ styleDefinition.bg = parseColor(tokenStyle.style.background);
16772
+ }
16773
+ if (tokenStyle.style.bold !== undefined) {
16774
+ styleDefinition.bold = tokenStyle.style.bold;
16775
+ }
16776
+ if (tokenStyle.style.italic !== undefined) {
16777
+ styleDefinition.italic = tokenStyle.style.italic;
16778
+ }
16779
+ if (tokenStyle.style.underline !== undefined) {
16780
+ styleDefinition.underline = tokenStyle.style.underline;
16781
+ }
16782
+ if (tokenStyle.style.dim !== undefined) {
16783
+ styleDefinition.dim = tokenStyle.style.dim;
16784
+ }
16785
+ for (const scope of tokenStyle.scope) {
16786
+ flatStyles[scope] = styleDefinition;
16787
+ }
16788
+ }
16789
+ return flatStyles;
16790
+ }
16566
16791
 
16567
- class Capture extends EventEmitter7 {
16568
- output = [];
16569
- constructor() {
16570
- super();
16792
+ class SyntaxStyle {
16793
+ lib;
16794
+ stylePtr;
16795
+ _destroyed = false;
16796
+ nameCache = new Map;
16797
+ styleDefs = new Map;
16798
+ mergedCache = new Map;
16799
+ constructor(lib, ptr5) {
16800
+ this.lib = lib;
16801
+ this.stylePtr = ptr5;
16571
16802
  }
16572
- get size() {
16573
- return this.output.length;
16803
+ static create() {
16804
+ const lib = resolveRenderLib();
16805
+ const ptr5 = lib.createSyntaxStyle();
16806
+ return new SyntaxStyle(lib, ptr5);
16574
16807
  }
16575
- write(stream, data) {
16576
- this.output.push({ stream, output: data });
16577
- this.emit("write", stream, data);
16808
+ static fromTheme(theme) {
16809
+ const style = SyntaxStyle.create();
16810
+ const flatStyles = convertThemeToStyles(theme);
16811
+ for (const [name, styleDef] of Object.entries(flatStyles)) {
16812
+ style.registerStyle(name, styleDef);
16813
+ }
16814
+ return style;
16578
16815
  }
16579
- claimOutput() {
16580
- const output = this.output.map((o) => o.output).join("");
16581
- this.clear();
16582
- return output;
16816
+ static fromStyles(styles) {
16817
+ const style = SyntaxStyle.create();
16818
+ for (const [name, styleDef] of Object.entries(styles)) {
16819
+ style.registerStyle(name, styleDef);
16820
+ }
16821
+ return style;
16583
16822
  }
16584
- clear() {
16585
- this.output = [];
16823
+ guard() {
16824
+ if (this._destroyed)
16825
+ throw new Error("NativeSyntaxStyle is destroyed");
16586
16826
  }
16587
- }
16588
-
16589
- class CapturedWritableStream extends Writable {
16590
- stream;
16591
- capture;
16592
- isTTY = true;
16593
- columns = process.stdout.columns || 80;
16594
- rows = process.stdout.rows || 24;
16595
- constructor(stream, capture) {
16596
- super();
16597
- this.stream = stream;
16598
- this.capture = capture;
16827
+ registerStyle(name, style) {
16828
+ this.guard();
16829
+ const attributes = createTextAttributes({
16830
+ bold: style.bold,
16831
+ italic: style.italic,
16832
+ underline: style.underline,
16833
+ dim: style.dim
16834
+ });
16835
+ const id = this.lib.syntaxStyleRegister(this.stylePtr, name, style.fg || null, style.bg || null, attributes);
16836
+ this.nameCache.set(name, id);
16837
+ this.styleDefs.set(name, style);
16838
+ return id;
16599
16839
  }
16600
- _write(chunk, encoding, callback) {
16601
- const data = chunk.toString();
16602
- this.capture.write(this.stream, data);
16603
- callback();
16840
+ resolveStyleId(name) {
16841
+ this.guard();
16842
+ const cached = this.nameCache.get(name);
16843
+ if (cached !== undefined)
16844
+ return cached;
16845
+ const id = this.lib.syntaxStyleResolveByName(this.stylePtr, name);
16846
+ if (id !== null) {
16847
+ this.nameCache.set(name, id);
16848
+ }
16849
+ return id;
16604
16850
  }
16605
- getColorDepth() {
16606
- return process.stdout.getColorDepth?.() || 8;
16851
+ getStyleId(name) {
16852
+ this.guard();
16853
+ const id = this.resolveStyleId(name);
16854
+ if (id !== null)
16855
+ return id;
16856
+ if (name.includes(".")) {
16857
+ const baseName = name.split(".")[0];
16858
+ return this.resolveStyleId(baseName);
16859
+ }
16860
+ return null;
16607
16861
  }
16608
- }
16609
-
16610
- // src/lib/keymapping.ts
16611
- var defaultKeyAliases = {
16612
- enter: "return",
16613
- esc: "escape",
16614
- kp0: "0",
16615
- kp1: "1",
16616
- kp2: "2",
16617
- kp3: "3",
16618
- kp4: "4",
16619
- kp5: "5",
16620
- kp6: "6",
16621
- kp7: "7",
16622
- kp8: "8",
16623
- kp9: "9",
16624
- kpdecimal: ".",
16625
- kpdivide: "/",
16626
- kpmultiply: "*",
16627
- kpminus: "-",
16628
- kpplus: "+",
16629
- kpenter: "enter",
16630
- kpequal: "=",
16631
- kpseparator: ",",
16632
- kpleft: "left",
16633
- kpright: "right",
16634
- kpup: "up",
16635
- kpdown: "down",
16636
- kppageup: "pageup",
16637
- kppagedown: "pagedown",
16638
- kphome: "home",
16639
- kpend: "end",
16640
- kpinsert: "insert",
16641
- kpdelete: "delete"
16642
- };
16643
- function mergeKeyAliases(defaults, custom) {
16644
- return { ...defaults, ...custom };
16645
- }
16646
- function mergeKeyBindings(defaults, custom) {
16647
- const map = new Map;
16648
- for (const binding of defaults) {
16649
- const key = getKeyBindingKey(binding);
16650
- map.set(key, binding);
16862
+ get ptr() {
16863
+ this.guard();
16864
+ return this.stylePtr;
16651
16865
  }
16652
- for (const binding of custom) {
16653
- const key = getKeyBindingKey(binding);
16654
- map.set(key, binding);
16866
+ getStyleCount() {
16867
+ this.guard();
16868
+ return this.lib.syntaxStyleGetStyleCount(this.stylePtr);
16655
16869
  }
16656
- return Array.from(map.values());
16657
- }
16658
- function getKeyBindingKey(binding) {
16659
- return `${binding.name}:${binding.ctrl ? 1 : 0}:${binding.shift ? 1 : 0}:${binding.meta ? 1 : 0}:${binding.super ? 1 : 0}`;
16660
- }
16661
- function getBaseCodeKeyName(baseCode) {
16662
- if (baseCode === undefined || baseCode < 32 || baseCode === 127) {
16663
- return;
16870
+ clearNameCache() {
16871
+ this.nameCache.clear();
16664
16872
  }
16665
- try {
16666
- const name = String.fromCodePoint(baseCode);
16873
+ getStyle(name) {
16874
+ this.guard();
16875
+ if (Object.prototype.hasOwnProperty.call(this.styleDefs, name)) {
16876
+ return;
16877
+ }
16878
+ const style = this.styleDefs.get(name);
16879
+ if (style)
16880
+ return style;
16881
+ if (name.includes(".")) {
16882
+ const baseName = name.split(".")[0];
16883
+ if (Object.prototype.hasOwnProperty.call(this.styleDefs, baseName)) {
16884
+ return;
16885
+ }
16886
+ return this.styleDefs.get(baseName);
16887
+ }
16888
+ return;
16889
+ }
16890
+ mergeStyles(...styleNames) {
16891
+ this.guard();
16892
+ const cacheKey = styleNames.join(":");
16893
+ const cached = this.mergedCache.get(cacheKey);
16894
+ if (cached)
16895
+ return cached;
16896
+ const styleDefinition = {};
16897
+ for (const name of styleNames) {
16898
+ const style = this.getStyle(name);
16899
+ if (!style)
16900
+ continue;
16901
+ if (style.fg)
16902
+ styleDefinition.fg = style.fg;
16903
+ if (style.bg)
16904
+ styleDefinition.bg = style.bg;
16905
+ if (style.bold !== undefined)
16906
+ styleDefinition.bold = style.bold;
16907
+ if (style.italic !== undefined)
16908
+ styleDefinition.italic = style.italic;
16909
+ if (style.underline !== undefined)
16910
+ styleDefinition.underline = style.underline;
16911
+ if (style.dim !== undefined)
16912
+ styleDefinition.dim = style.dim;
16913
+ }
16914
+ const attributes = createTextAttributes({
16915
+ bold: styleDefinition.bold,
16916
+ italic: styleDefinition.italic,
16917
+ underline: styleDefinition.underline,
16918
+ dim: styleDefinition.dim
16919
+ });
16920
+ const merged = {
16921
+ fg: styleDefinition.fg,
16922
+ bg: styleDefinition.bg,
16923
+ attributes
16924
+ };
16925
+ this.mergedCache.set(cacheKey, merged);
16926
+ return merged;
16927
+ }
16928
+ clearCache() {
16929
+ this.guard();
16930
+ this.mergedCache.clear();
16931
+ }
16932
+ getCacheSize() {
16933
+ this.guard();
16934
+ return this.mergedCache.size;
16935
+ }
16936
+ getAllStyles() {
16937
+ this.guard();
16938
+ return new Map(this.styleDefs);
16939
+ }
16940
+ getRegisteredNames() {
16941
+ this.guard();
16942
+ return Array.from(this.styleDefs.keys());
16943
+ }
16944
+ destroy() {
16945
+ if (this._destroyed)
16946
+ return;
16947
+ this._destroyed = true;
16948
+ this.nameCache.clear();
16949
+ this.styleDefs.clear();
16950
+ this.mergedCache.clear();
16951
+ this.lib.destroySyntaxStyle(this.stylePtr);
16952
+ }
16953
+ }
16954
+
16955
+ // src/renderables/Box.ts
16956
+ function isGapType(value) {
16957
+ if (value === undefined) {
16958
+ return true;
16959
+ }
16960
+ if (typeof value === "number" && !Number.isNaN(value)) {
16961
+ return true;
16962
+ }
16963
+ return isValidPercentage(value);
16964
+ }
16965
+
16966
+ class BoxRenderable extends Renderable {
16967
+ _backgroundColor;
16968
+ _border;
16969
+ _borderStyle;
16970
+ _borderColor;
16971
+ _focusedBorderColor;
16972
+ _customBorderCharsObj;
16973
+ _customBorderChars;
16974
+ borderSides;
16975
+ shouldFill;
16976
+ _title;
16977
+ _titleAlignment;
16978
+ _bottomTitle;
16979
+ _bottomTitleAlignment;
16980
+ _defaultOptions = {
16981
+ backgroundColor: "transparent",
16982
+ borderStyle: "single",
16983
+ border: false,
16984
+ borderColor: "#FFFFFF",
16985
+ shouldFill: true,
16986
+ titleAlignment: "left",
16987
+ bottomTitleAlignment: "left",
16988
+ focusedBorderColor: "#00AAFF"
16989
+ };
16990
+ constructor(ctx, options) {
16991
+ super(ctx, options);
16992
+ if (options.focusable === true) {
16993
+ this._focusable = true;
16994
+ }
16995
+ this._backgroundColor = parseColor(options.backgroundColor || this._defaultOptions.backgroundColor);
16996
+ this._border = options.border ?? this._defaultOptions.border;
16997
+ if (!options.border && (options.borderStyle || options.borderColor || options.focusedBorderColor || options.customBorderChars)) {
16998
+ this._border = true;
16999
+ }
17000
+ this._borderStyle = parseBorderStyle(options.borderStyle, this._defaultOptions.borderStyle);
17001
+ this._borderColor = parseColor(options.borderColor || this._defaultOptions.borderColor);
17002
+ this._focusedBorderColor = parseColor(options.focusedBorderColor || this._defaultOptions.focusedBorderColor);
17003
+ this._customBorderCharsObj = options.customBorderChars;
17004
+ this._customBorderChars = this._customBorderCharsObj ? borderCharsToArray(this._customBorderCharsObj) : undefined;
17005
+ this.borderSides = getBorderSides(this._border);
17006
+ this.shouldFill = options.shouldFill ?? this._defaultOptions.shouldFill;
17007
+ this._title = options.title;
17008
+ this._titleAlignment = options.titleAlignment || this._defaultOptions.titleAlignment;
17009
+ this._bottomTitle = options.bottomTitle;
17010
+ this._bottomTitleAlignment = options.bottomTitleAlignment || this._defaultOptions.bottomTitleAlignment;
17011
+ this.applyYogaBorders();
17012
+ const hasInitialGapProps = options.gap !== undefined || options.rowGap !== undefined || options.columnGap !== undefined;
17013
+ if (hasInitialGapProps) {
17014
+ this.applyYogaGap(options);
17015
+ }
17016
+ }
17017
+ initializeBorder() {
17018
+ if (this._border === false) {
17019
+ this._border = true;
17020
+ this.borderSides = getBorderSides(this._border);
17021
+ this.applyYogaBorders();
17022
+ }
17023
+ }
17024
+ get customBorderChars() {
17025
+ return this._customBorderCharsObj;
17026
+ }
17027
+ set customBorderChars(value) {
17028
+ this._customBorderCharsObj = value;
17029
+ this._customBorderChars = value ? borderCharsToArray(value) : undefined;
17030
+ this.requestRender();
17031
+ }
17032
+ get backgroundColor() {
17033
+ return this._backgroundColor;
17034
+ }
17035
+ set backgroundColor(value) {
17036
+ const newColor = parseColor(value ?? this._defaultOptions.backgroundColor);
17037
+ if (this._backgroundColor !== newColor) {
17038
+ this._backgroundColor = newColor;
17039
+ this.requestRender();
17040
+ }
17041
+ }
17042
+ get border() {
17043
+ return this._border;
17044
+ }
17045
+ set border(value) {
17046
+ if (this._border !== value) {
17047
+ this._border = value;
17048
+ this.borderSides = getBorderSides(value);
17049
+ this.applyYogaBorders();
17050
+ this.requestRender();
17051
+ }
17052
+ }
17053
+ get borderStyle() {
17054
+ return this._borderStyle;
17055
+ }
17056
+ set borderStyle(value) {
17057
+ const _value = parseBorderStyle(value, this._defaultOptions.borderStyle);
17058
+ if (this._borderStyle !== _value || !this._border) {
17059
+ this._borderStyle = _value;
17060
+ this._customBorderChars = undefined;
17061
+ this.initializeBorder();
17062
+ this.requestRender();
17063
+ }
17064
+ }
17065
+ get borderColor() {
17066
+ return this._borderColor;
17067
+ }
17068
+ set borderColor(value) {
17069
+ const newColor = parseColor(value ?? this._defaultOptions.borderColor);
17070
+ if (this._borderColor !== newColor) {
17071
+ this._borderColor = newColor;
17072
+ this.initializeBorder();
17073
+ this.requestRender();
17074
+ }
17075
+ }
17076
+ get focusedBorderColor() {
17077
+ return this._focusedBorderColor;
17078
+ }
17079
+ set focusedBorderColor(value) {
17080
+ const newColor = parseColor(value ?? this._defaultOptions.focusedBorderColor);
17081
+ if (this._focusedBorderColor !== newColor) {
17082
+ this._focusedBorderColor = newColor;
17083
+ this.initializeBorder();
17084
+ if (this._focused) {
17085
+ this.requestRender();
17086
+ }
17087
+ }
17088
+ }
17089
+ get title() {
17090
+ return this._title;
17091
+ }
17092
+ set title(value) {
17093
+ if (this._title !== value) {
17094
+ this._title = value;
17095
+ this.requestRender();
17096
+ }
17097
+ }
17098
+ get titleAlignment() {
17099
+ return this._titleAlignment;
17100
+ }
17101
+ set titleAlignment(value) {
17102
+ if (this._titleAlignment !== value) {
17103
+ this._titleAlignment = value;
17104
+ this.requestRender();
17105
+ }
17106
+ }
17107
+ get bottomTitle() {
17108
+ return this._bottomTitle;
17109
+ }
17110
+ set bottomTitle(value) {
17111
+ if (this._bottomTitle !== value) {
17112
+ this._bottomTitle = value;
17113
+ this.requestRender();
17114
+ }
17115
+ }
17116
+ get bottomTitleAlignment() {
17117
+ return this._bottomTitleAlignment;
17118
+ }
17119
+ set bottomTitleAlignment(value) {
17120
+ if (this._bottomTitleAlignment !== value) {
17121
+ this._bottomTitleAlignment = value;
17122
+ this.requestRender();
17123
+ }
17124
+ }
17125
+ renderSelf(buffer) {
17126
+ const hasBorder = this.borderSides.top || this.borderSides.right || this.borderSides.bottom || this.borderSides.left;
17127
+ const hasVisibleFill = this.shouldFill && this._backgroundColor.a > 0;
17128
+ if (!hasBorder && !hasVisibleFill) {
17129
+ return;
17130
+ }
17131
+ const hasFocusWithin = this._focusable && (this._focused || this._hasFocusedDescendant);
17132
+ const currentBorderColor = hasFocusWithin ? this._focusedBorderColor : this._borderColor;
17133
+ const screenX = this._screenX;
17134
+ const screenY = this._screenY;
17135
+ buffer.drawBox({
17136
+ x: screenX,
17137
+ y: screenY,
17138
+ width: this.width,
17139
+ height: this.height,
17140
+ borderStyle: this._borderStyle,
17141
+ customBorderChars: this._customBorderChars,
17142
+ border: this._border,
17143
+ borderColor: currentBorderColor,
17144
+ backgroundColor: this._backgroundColor,
17145
+ shouldFill: this.shouldFill,
17146
+ title: this._title,
17147
+ titleAlignment: this._titleAlignment,
17148
+ bottomTitle: this._bottomTitle,
17149
+ bottomTitleAlignment: this._bottomTitleAlignment
17150
+ });
17151
+ }
17152
+ getScissorRect() {
17153
+ const baseRect = super.getScissorRect();
17154
+ if (!this.borderSides.top && !this.borderSides.right && !this.borderSides.bottom && !this.borderSides.left) {
17155
+ return baseRect;
17156
+ }
17157
+ const leftInset = this.borderSides.left ? 1 : 0;
17158
+ const rightInset = this.borderSides.right ? 1 : 0;
17159
+ const topInset = this.borderSides.top ? 1 : 0;
17160
+ const bottomInset = this.borderSides.bottom ? 1 : 0;
17161
+ return {
17162
+ x: baseRect.x + leftInset,
17163
+ y: baseRect.y + topInset,
17164
+ width: Math.max(0, baseRect.width - leftInset - rightInset),
17165
+ height: Math.max(0, baseRect.height - topInset - bottomInset)
17166
+ };
17167
+ }
17168
+ applyYogaBorders() {
17169
+ const node = this.yogaNode;
17170
+ node.setBorder(Edge.Left, this.borderSides.left ? 1 : 0);
17171
+ node.setBorder(Edge.Right, this.borderSides.right ? 1 : 0);
17172
+ node.setBorder(Edge.Top, this.borderSides.top ? 1 : 0);
17173
+ node.setBorder(Edge.Bottom, this.borderSides.bottom ? 1 : 0);
17174
+ this.requestRender();
17175
+ }
17176
+ applyYogaGap(options) {
17177
+ const node = this.yogaNode;
17178
+ if (isGapType(options.gap)) {
17179
+ node.setGap(Gutter.All, options.gap);
17180
+ }
17181
+ if (isGapType(options.rowGap)) {
17182
+ node.setGap(Gutter.Row, options.rowGap);
17183
+ }
17184
+ if (isGapType(options.columnGap)) {
17185
+ node.setGap(Gutter.Column, options.columnGap);
17186
+ }
17187
+ }
17188
+ set gap(gap) {
17189
+ if (isGapType(gap)) {
17190
+ this.yogaNode.setGap(Gutter.All, gap);
17191
+ this.requestRender();
17192
+ }
17193
+ }
17194
+ set rowGap(rowGap) {
17195
+ if (isGapType(rowGap)) {
17196
+ this.yogaNode.setGap(Gutter.Row, rowGap);
17197
+ this.requestRender();
17198
+ }
17199
+ }
17200
+ set columnGap(columnGap) {
17201
+ if (isGapType(columnGap)) {
17202
+ this.yogaNode.setGap(Gutter.Column, columnGap);
17203
+ this.requestRender();
17204
+ }
17205
+ }
17206
+ }
17207
+
17208
+ // src/renderables/TextBufferRenderable.ts
17209
+ class TextBufferRenderable extends Renderable {
17210
+ selectable = true;
17211
+ _defaultFg;
17212
+ _defaultBg;
17213
+ _defaultAttributes;
17214
+ _selectionBg;
17215
+ _selectionFg;
17216
+ _wrapMode = "word";
17217
+ lastLocalSelection = null;
17218
+ _tabIndicator;
17219
+ _tabIndicatorColor;
17220
+ _scrollX = 0;
17221
+ _scrollY = 0;
17222
+ _truncate = false;
17223
+ _firstLineOffset = 0;
17224
+ textBuffer;
17225
+ textBufferView;
17226
+ _textBufferSyntaxStyle;
17227
+ _defaultOptions = {
17228
+ fg: RGBA.fromValues(1, 1, 1, 1),
17229
+ bg: RGBA.fromValues(0, 0, 0, 0),
17230
+ selectionBg: undefined,
17231
+ selectionFg: undefined,
17232
+ selectable: true,
17233
+ attributes: 0,
17234
+ wrapMode: "word",
17235
+ tabIndicator: undefined,
17236
+ tabIndicatorColor: undefined,
17237
+ truncate: false
17238
+ };
17239
+ constructor(ctx, options) {
17240
+ super(ctx, options);
17241
+ this._defaultFg = parseColor(options.fg ?? this._defaultOptions.fg);
17242
+ this._defaultBg = parseColor(options.bg ?? this._defaultOptions.bg);
17243
+ this._defaultAttributes = options.attributes ?? this._defaultOptions.attributes;
17244
+ this._selectionBg = options.selectionBg ? parseColor(options.selectionBg) : this._defaultOptions.selectionBg;
17245
+ this._selectionFg = options.selectionFg ? parseColor(options.selectionFg) : this._defaultOptions.selectionFg;
17246
+ this.selectable = options.selectable ?? this._defaultOptions.selectable;
17247
+ this._wrapMode = options.wrapMode ?? this._defaultOptions.wrapMode;
17248
+ this._tabIndicator = options.tabIndicator ?? this._defaultOptions.tabIndicator;
17249
+ this._tabIndicatorColor = options.tabIndicatorColor ? parseColor(options.tabIndicatorColor) : this._defaultOptions.tabIndicatorColor;
17250
+ this._truncate = options.truncate ?? this._defaultOptions.truncate;
17251
+ this.textBuffer = TextBuffer.create(this._ctx.widthMethod);
17252
+ this.textBufferView = TextBufferView.create(this.textBuffer);
17253
+ this._firstLineOffset = ctx.claimFirstLineOffset?.(this) ?? 0;
17254
+ this._textBufferSyntaxStyle = SyntaxStyle.create();
17255
+ this.textBuffer.setSyntaxStyle(this._textBufferSyntaxStyle);
17256
+ this.textBufferView.setWrapMode(this._wrapMode);
17257
+ this.textBufferView.setFirstLineOffset(this._firstLineOffset);
17258
+ this.setupMeasureFunc();
17259
+ this.textBuffer.setDefaultFg(this._defaultFg);
17260
+ this.textBuffer.setDefaultBg(this._defaultBg);
17261
+ this.textBuffer.setDefaultAttributes(this._defaultAttributes);
17262
+ if (this._tabIndicator !== undefined) {
17263
+ this.textBufferView.setTabIndicator(this._tabIndicator);
17264
+ }
17265
+ if (this._tabIndicatorColor !== undefined) {
17266
+ this.textBufferView.setTabIndicatorColor(this._tabIndicatorColor);
17267
+ }
17268
+ if (this._wrapMode !== "none" && this.width > 0) {
17269
+ this.textBufferView.setWrapWidth(this.width);
17270
+ }
17271
+ if (this.width > 0 && this.height > 0) {
17272
+ this.textBufferView.setViewport(this._scrollX, this._scrollY, this.width, this.height);
17273
+ }
17274
+ this.textBufferView.setTruncate(this._truncate);
17275
+ this.updateTextInfo();
17276
+ }
17277
+ onMouseEvent(event) {
17278
+ if (event.type === "scroll") {
17279
+ this.handleScroll(event);
17280
+ }
17281
+ }
17282
+ handleScroll(event) {
17283
+ if (!event.scroll)
17284
+ return;
17285
+ const { direction, delta } = event.scroll;
17286
+ if (direction === "up") {
17287
+ this.scrollY -= delta;
17288
+ } else if (direction === "down") {
17289
+ this.scrollY += delta;
17290
+ }
17291
+ if (this._wrapMode === "none") {
17292
+ if (direction === "left") {
17293
+ this.scrollX -= delta;
17294
+ } else if (direction === "right") {
17295
+ this.scrollX += delta;
17296
+ }
17297
+ }
17298
+ }
17299
+ get lineInfo() {
17300
+ return this.textBufferView.logicalLineInfo;
17301
+ }
17302
+ get lineCount() {
17303
+ return this.textBuffer.getLineCount();
17304
+ }
17305
+ get virtualLineCount() {
17306
+ return this.textBufferView.getVirtualLineCount();
17307
+ }
17308
+ get scrollY() {
17309
+ return this._scrollY;
17310
+ }
17311
+ set scrollY(value) {
17312
+ const maxScrollY = Math.max(0, this.scrollHeight - this.height);
17313
+ const clamped = Math.max(0, Math.min(value, maxScrollY));
17314
+ if (this._scrollY !== clamped) {
17315
+ this._scrollY = clamped;
17316
+ this.updateViewportOffset();
17317
+ this.requestRender();
17318
+ }
17319
+ }
17320
+ get scrollX() {
17321
+ return this._scrollX;
17322
+ }
17323
+ set scrollX(value) {
17324
+ const maxScrollX = Math.max(0, this.scrollWidth - this.width);
17325
+ const clamped = Math.max(0, Math.min(value, maxScrollX));
17326
+ if (this._scrollX !== clamped) {
17327
+ this._scrollX = clamped;
17328
+ this.updateViewportOffset();
17329
+ this.requestRender();
17330
+ }
17331
+ }
17332
+ get scrollWidth() {
17333
+ return this.lineInfo.lineWidthColsMax;
17334
+ }
17335
+ get scrollHeight() {
17336
+ return this.lineInfo.lineStartCols.length;
17337
+ }
17338
+ get maxScrollY() {
17339
+ return Math.max(0, this.scrollHeight - this.height);
17340
+ }
17341
+ get maxScrollX() {
17342
+ return Math.max(0, this.scrollWidth - this.width);
17343
+ }
17344
+ updateViewportOffset() {
17345
+ if (this.width > 0 && this.height > 0) {
17346
+ this.textBufferView.setViewport(this._scrollX, this._scrollY, this.width, this.height);
17347
+ }
17348
+ }
17349
+ get plainText() {
17350
+ return this.textBuffer.getPlainText();
17351
+ }
17352
+ get textLength() {
17353
+ return this.textBuffer.length;
17354
+ }
17355
+ get fg() {
17356
+ return this._defaultFg;
17357
+ }
17358
+ set fg(value) {
17359
+ const newColor = parseColor(value ?? this._defaultOptions.fg);
17360
+ if (this._defaultFg !== newColor) {
17361
+ this._defaultFg = newColor;
17362
+ this.textBuffer.setDefaultFg(this._defaultFg);
17363
+ this.onFgChanged(newColor);
17364
+ this.requestRender();
17365
+ }
17366
+ }
17367
+ get selectionBg() {
17368
+ return this._selectionBg;
17369
+ }
17370
+ set selectionBg(value) {
17371
+ const newColor = value ? parseColor(value) : this._defaultOptions.selectionBg;
17372
+ if (this._selectionBg !== newColor) {
17373
+ this._selectionBg = newColor;
17374
+ if (this.lastLocalSelection) {
17375
+ this.updateLocalSelection(this.lastLocalSelection);
17376
+ }
17377
+ this.requestRender();
17378
+ }
17379
+ }
17380
+ get selectionFg() {
17381
+ return this._selectionFg;
17382
+ }
17383
+ set selectionFg(value) {
17384
+ const newColor = value ? parseColor(value) : this._defaultOptions.selectionFg;
17385
+ if (this._selectionFg !== newColor) {
17386
+ this._selectionFg = newColor;
17387
+ if (this.lastLocalSelection) {
17388
+ this.updateLocalSelection(this.lastLocalSelection);
17389
+ }
17390
+ this.requestRender();
17391
+ }
17392
+ }
17393
+ get bg() {
17394
+ return this._defaultBg;
17395
+ }
17396
+ set bg(value) {
17397
+ const newColor = parseColor(value ?? this._defaultOptions.bg);
17398
+ if (this._defaultBg !== newColor) {
17399
+ this._defaultBg = newColor;
17400
+ this.textBuffer.setDefaultBg(this._defaultBg);
17401
+ this.onBgChanged(newColor);
17402
+ this.requestRender();
17403
+ }
17404
+ }
17405
+ get attributes() {
17406
+ return this._defaultAttributes;
17407
+ }
17408
+ set attributes(value) {
17409
+ if (this._defaultAttributes !== value) {
17410
+ this._defaultAttributes = value;
17411
+ this.textBuffer.setDefaultAttributes(this._defaultAttributes);
17412
+ this.onAttributesChanged(value);
17413
+ this.requestRender();
17414
+ }
17415
+ }
17416
+ get wrapMode() {
17417
+ return this._wrapMode;
17418
+ }
17419
+ set wrapMode(value) {
17420
+ if (this._wrapMode !== value) {
17421
+ this._wrapMode = value;
17422
+ this.textBufferView.setWrapMode(this._wrapMode);
17423
+ if (value !== "none" && this.width > 0) {
17424
+ this.textBufferView.setWrapWidth(this.width);
17425
+ }
17426
+ this.yogaNode.markDirty();
17427
+ this.requestRender();
17428
+ }
17429
+ }
17430
+ get tabIndicator() {
17431
+ return this._tabIndicator;
17432
+ }
17433
+ set tabIndicator(value) {
17434
+ if (this._tabIndicator !== value) {
17435
+ this._tabIndicator = value;
17436
+ if (value !== undefined) {
17437
+ this.textBufferView.setTabIndicator(value);
17438
+ }
17439
+ this.requestRender();
17440
+ }
17441
+ }
17442
+ get tabIndicatorColor() {
17443
+ return this._tabIndicatorColor;
17444
+ }
17445
+ set tabIndicatorColor(value) {
17446
+ const newColor = value ? parseColor(value) : undefined;
17447
+ if (this._tabIndicatorColor !== newColor) {
17448
+ this._tabIndicatorColor = newColor;
17449
+ if (newColor !== undefined) {
17450
+ this.textBufferView.setTabIndicatorColor(newColor);
17451
+ }
17452
+ this.requestRender();
17453
+ }
17454
+ }
17455
+ get truncate() {
17456
+ return this._truncate;
17457
+ }
17458
+ set truncate(value) {
17459
+ if (this._truncate !== value) {
17460
+ this._truncate = value;
17461
+ this.textBufferView.setTruncate(value);
17462
+ this.requestRender();
17463
+ }
17464
+ }
17465
+ onResize(width, height) {
17466
+ this.textBufferView.setViewport(this._scrollX, this._scrollY, width, height);
17467
+ this.yogaNode.markDirty();
17468
+ this.requestRender();
17469
+ this.emit("line-info-change");
17470
+ }
17471
+ refreshLocalSelection() {
17472
+ if (this.lastLocalSelection) {
17473
+ return this.updateLocalSelection(this.lastLocalSelection);
17474
+ }
17475
+ return false;
17476
+ }
17477
+ updateLocalSelection(localSelection) {
17478
+ if (!localSelection?.isActive) {
17479
+ this.textBufferView.resetLocalSelection();
17480
+ return true;
17481
+ }
17482
+ return this.textBufferView.setLocalSelection(localSelection.anchorX, localSelection.anchorY, localSelection.focusX, localSelection.focusY, this._selectionBg, this._selectionFg);
17483
+ }
17484
+ updateTextInfo() {
17485
+ if (this.lastLocalSelection) {
17486
+ this.updateLocalSelection(this.lastLocalSelection);
17487
+ }
17488
+ this.yogaNode.markDirty();
17489
+ this.requestRender();
17490
+ this.emit("line-info-change");
17491
+ }
17492
+ setupMeasureFunc() {
17493
+ const measureFunc = (width, widthMode, height, heightMode) => {
17494
+ let effectiveWidth;
17495
+ if (widthMode === MeasureMode.Undefined || isNaN(width)) {
17496
+ effectiveWidth = 0;
17497
+ } else {
17498
+ effectiveWidth = width;
17499
+ }
17500
+ const effectiveHeight = isNaN(height) ? 1 : height;
17501
+ const measureResult = this.textBufferView.measureForDimensions(Math.floor(effectiveWidth), Math.floor(effectiveHeight));
17502
+ const measuredWidth = measureResult ? Math.max(1, measureResult.widthColsMax) : 1;
17503
+ const measuredHeight = measureResult ? Math.max(1, measureResult.lineCount) : 1;
17504
+ if (widthMode === MeasureMode.AtMost && this._positionType !== "absolute") {
17505
+ return {
17506
+ width: Math.min(effectiveWidth, measuredWidth),
17507
+ height: Math.min(effectiveHeight, measuredHeight)
17508
+ };
17509
+ }
17510
+ return {
17511
+ width: measuredWidth,
17512
+ height: measuredHeight
17513
+ };
17514
+ };
17515
+ this.yogaNode.setMeasureFunc(measureFunc);
17516
+ }
17517
+ shouldStartSelection(x, y) {
17518
+ if (!this.selectable)
17519
+ return false;
17520
+ const localX = x - this.x;
17521
+ const localY = y - this.y;
17522
+ return localX >= 0 && localX < this.width && localY >= 0 && localY < this.height;
17523
+ }
17524
+ onSelectionChanged(selection2) {
17525
+ const localSelection = convertGlobalToLocalSelection(selection2, this.x, this.y);
17526
+ this.lastLocalSelection = localSelection;
17527
+ let changed;
17528
+ if (!localSelection?.isActive) {
17529
+ this.textBufferView.resetLocalSelection();
17530
+ changed = true;
17531
+ } else if (selection2?.isStart) {
17532
+ changed = this.textBufferView.setLocalSelection(localSelection.anchorX, localSelection.anchorY, localSelection.focusX, localSelection.focusY, this._selectionBg, this._selectionFg);
17533
+ } else {
17534
+ changed = this.textBufferView.updateLocalSelection(localSelection.anchorX, localSelection.anchorY, localSelection.focusX, localSelection.focusY, this._selectionBg, this._selectionFg);
17535
+ }
17536
+ if (changed) {
17537
+ this.requestRender();
17538
+ }
17539
+ return this.hasSelection();
17540
+ }
17541
+ getSelectedText() {
17542
+ return this.textBufferView.getSelectedText();
17543
+ }
17544
+ hasSelection() {
17545
+ return this.textBufferView.hasSelection();
17546
+ }
17547
+ getSelection() {
17548
+ return this.textBufferView.getSelection();
17549
+ }
17550
+ render(buffer, deltaTime) {
17551
+ if (!this.visible)
17552
+ return;
17553
+ const screenX = this._screenX;
17554
+ const screenY = this._screenY;
17555
+ this.markClean();
17556
+ this._ctx.addToHitGrid(screenX, screenY, this.width, this.height, this.num);
17557
+ this.renderSelf(buffer);
17558
+ if (this.buffered && this.frameBuffer) {
17559
+ buffer.drawFrameBuffer(screenX, screenY, this.frameBuffer);
17560
+ }
17561
+ }
17562
+ renderSelf(buffer) {
17563
+ if (this.textBuffer.ptr) {
17564
+ buffer.drawTextBuffer(this.textBufferView, this._screenX, this._screenY);
17565
+ }
17566
+ }
17567
+ destroy() {
17568
+ if (this.isDestroyed)
17569
+ return;
17570
+ this.textBuffer.setSyntaxStyle(null);
17571
+ this._textBufferSyntaxStyle.destroy();
17572
+ this.textBufferView.destroy();
17573
+ this.textBuffer.destroy();
17574
+ super.destroy();
17575
+ }
17576
+ onFgChanged(newColor) {}
17577
+ onBgChanged(newColor) {}
17578
+ onAttributesChanged(newAttributes) {}
17579
+ }
17580
+
17581
+ // src/renderables/Code.ts
17582
+ class CodeRenderable extends TextBufferRenderable {
17583
+ _content;
17584
+ _filetype;
17585
+ _syntaxStyle;
17586
+ _isHighlighting = false;
17587
+ _treeSitterClient;
17588
+ _highlightsDirty = false;
17589
+ _highlightSnapshotId = 0;
17590
+ _conceal;
17591
+ _drawUnstyledText;
17592
+ _shouldRenderTextBuffer = true;
17593
+ _streaming;
17594
+ _hadInitialContent = false;
17595
+ _lastHighlights = [];
17596
+ _onHighlight;
17597
+ _onChunks;
17598
+ _highlightingPromise = Promise.resolve();
17599
+ _contentDefaultOptions = {
17600
+ content: "",
17601
+ conceal: true,
17602
+ drawUnstyledText: true,
17603
+ streaming: false
17604
+ };
17605
+ constructor(ctx, options) {
17606
+ super(ctx, options);
17607
+ this._content = options.content ?? this._contentDefaultOptions.content;
17608
+ this._filetype = options.filetype;
17609
+ this._syntaxStyle = options.syntaxStyle;
17610
+ this._treeSitterClient = options.treeSitterClient ?? getTreeSitterClient();
17611
+ this._conceal = options.conceal ?? this._contentDefaultOptions.conceal;
17612
+ this._drawUnstyledText = options.drawUnstyledText ?? this._contentDefaultOptions.drawUnstyledText;
17613
+ this._streaming = options.streaming ?? this._contentDefaultOptions.streaming;
17614
+ this._onHighlight = options.onHighlight;
17615
+ this._onChunks = options.onChunks;
17616
+ if (this._content.length > 0) {
17617
+ this.textBuffer.setText(this._content);
17618
+ this.updateTextInfo();
17619
+ this._shouldRenderTextBuffer = this._drawUnstyledText || !this._filetype;
17620
+ }
17621
+ this._highlightsDirty = this._content.length > 0;
17622
+ }
17623
+ get content() {
17624
+ return this._content;
17625
+ }
17626
+ set content(value) {
17627
+ if (this._content !== value) {
17628
+ this._content = value;
17629
+ this._highlightsDirty = true;
17630
+ this._highlightSnapshotId++;
17631
+ if (this._streaming && !this._drawUnstyledText && this._filetype) {
17632
+ return;
17633
+ }
17634
+ this.textBuffer.setText(value);
17635
+ this.updateTextInfo();
17636
+ }
17637
+ }
17638
+ get filetype() {
17639
+ return this._filetype;
17640
+ }
17641
+ set filetype(value) {
17642
+ if (this._filetype !== value) {
17643
+ this._filetype = value;
17644
+ this._highlightsDirty = true;
17645
+ }
17646
+ }
17647
+ get syntaxStyle() {
17648
+ return this._syntaxStyle;
17649
+ }
17650
+ set syntaxStyle(value) {
17651
+ if (this._syntaxStyle !== value) {
17652
+ this._syntaxStyle = value;
17653
+ this._highlightsDirty = true;
17654
+ }
17655
+ }
17656
+ get conceal() {
17657
+ return this._conceal;
17658
+ }
17659
+ set conceal(value) {
17660
+ if (this._conceal !== value) {
17661
+ this._conceal = value;
17662
+ this._highlightsDirty = true;
17663
+ }
17664
+ }
17665
+ get drawUnstyledText() {
17666
+ return this._drawUnstyledText;
17667
+ }
17668
+ set drawUnstyledText(value) {
17669
+ if (this._drawUnstyledText !== value) {
17670
+ this._drawUnstyledText = value;
17671
+ this._highlightsDirty = true;
17672
+ }
17673
+ }
17674
+ get streaming() {
17675
+ return this._streaming;
17676
+ }
17677
+ set streaming(value) {
17678
+ if (this._streaming !== value) {
17679
+ this._streaming = value;
17680
+ this._hadInitialContent = false;
17681
+ this._lastHighlights = [];
17682
+ this._highlightsDirty = true;
17683
+ }
17684
+ }
17685
+ get treeSitterClient() {
17686
+ return this._treeSitterClient;
17687
+ }
17688
+ set treeSitterClient(value) {
17689
+ if (this._treeSitterClient !== value) {
17690
+ this._treeSitterClient = value;
17691
+ this._highlightsDirty = true;
17692
+ }
17693
+ }
17694
+ get onHighlight() {
17695
+ return this._onHighlight;
17696
+ }
17697
+ set onHighlight(value) {
17698
+ if (this._onHighlight !== value) {
17699
+ this._onHighlight = value;
17700
+ this._highlightsDirty = true;
17701
+ }
17702
+ }
17703
+ get onChunks() {
17704
+ return this._onChunks;
17705
+ }
17706
+ set onChunks(value) {
17707
+ if (this._onChunks !== value) {
17708
+ this._onChunks = value;
17709
+ this._highlightsDirty = true;
17710
+ }
17711
+ }
17712
+ get isHighlighting() {
17713
+ return this._isHighlighting;
17714
+ }
17715
+ get highlightingDone() {
17716
+ return this._highlightingPromise;
17717
+ }
17718
+ async transformChunks(chunks, context) {
17719
+ if (!this._onChunks)
17720
+ return chunks;
17721
+ const modified = await this._onChunks(chunks, context);
17722
+ return modified ?? chunks;
17723
+ }
17724
+ ensureVisibleTextBeforeHighlight() {
17725
+ if (this.isDestroyed)
17726
+ return;
17727
+ const content = this._content;
17728
+ if (!this._filetype) {
17729
+ this._shouldRenderTextBuffer = true;
17730
+ return;
17731
+ }
17732
+ const isInitialContent = this._streaming && !this._hadInitialContent;
17733
+ const shouldDrawUnstyledNow = this._streaming ? isInitialContent && this._drawUnstyledText : this._drawUnstyledText;
17734
+ if (this._streaming && !isInitialContent) {
17735
+ this._shouldRenderTextBuffer = true;
17736
+ } else if (shouldDrawUnstyledNow) {
17737
+ this.textBuffer.setText(content);
17738
+ this._shouldRenderTextBuffer = true;
17739
+ } else {
17740
+ this._shouldRenderTextBuffer = false;
17741
+ }
17742
+ }
17743
+ async startHighlight() {
17744
+ const content = this._content;
17745
+ const filetype = this._filetype;
17746
+ const snapshotId = ++this._highlightSnapshotId;
17747
+ if (!filetype)
17748
+ return;
17749
+ const isInitialContent = this._streaming && !this._hadInitialContent;
17750
+ if (isInitialContent) {
17751
+ this._hadInitialContent = true;
17752
+ }
17753
+ this._isHighlighting = true;
17754
+ try {
17755
+ const result = await this._treeSitterClient.highlightOnce(content, filetype);
17756
+ if (snapshotId !== this._highlightSnapshotId) {
17757
+ return;
17758
+ }
17759
+ if (this.isDestroyed)
17760
+ return;
17761
+ let highlights = result.highlights ?? [];
17762
+ if (this._onHighlight && highlights.length >= 0) {
17763
+ const context = {
17764
+ content,
17765
+ filetype,
17766
+ syntaxStyle: this._syntaxStyle
17767
+ };
17768
+ const modified = await this._onHighlight(highlights, context);
17769
+ if (modified !== undefined) {
17770
+ highlights = modified;
17771
+ }
17772
+ }
17773
+ if (snapshotId !== this._highlightSnapshotId) {
17774
+ return;
17775
+ }
17776
+ if (this.isDestroyed)
17777
+ return;
17778
+ if (highlights.length > 0) {
17779
+ if (this._streaming) {
17780
+ this._lastHighlights = highlights;
17781
+ }
17782
+ }
17783
+ if (highlights.length > 0 || this._onChunks) {
17784
+ const context = {
17785
+ content,
17786
+ filetype,
17787
+ syntaxStyle: this._syntaxStyle,
17788
+ highlights
17789
+ };
17790
+ let chunks = treeSitterToTextChunks(content, highlights, this._syntaxStyle, {
17791
+ enabled: this._conceal
17792
+ });
17793
+ chunks = await this.transformChunks(chunks, context);
17794
+ if (snapshotId !== this._highlightSnapshotId) {
17795
+ return;
17796
+ }
17797
+ if (this.isDestroyed)
17798
+ return;
17799
+ const styledText = new StyledText(chunks);
17800
+ this.textBuffer.setStyledText(styledText);
17801
+ } else {
17802
+ this.textBuffer.setText(content);
17803
+ }
17804
+ this._shouldRenderTextBuffer = true;
17805
+ this._isHighlighting = false;
17806
+ this._highlightsDirty = false;
17807
+ this.updateTextInfo();
17808
+ this.requestRender();
17809
+ } catch (error) {
17810
+ if (snapshotId !== this._highlightSnapshotId) {
17811
+ return;
17812
+ }
17813
+ console.warn("Code highlighting failed, falling back to plain text:", error);
17814
+ if (this.isDestroyed)
17815
+ return;
17816
+ this.textBuffer.setText(content);
17817
+ this._shouldRenderTextBuffer = true;
17818
+ this._isHighlighting = false;
17819
+ this._highlightsDirty = false;
17820
+ this.updateTextInfo();
17821
+ this.requestRender();
17822
+ }
17823
+ }
17824
+ getLineHighlights(lineIdx) {
17825
+ return this.textBuffer.getLineHighlights(lineIdx);
17826
+ }
17827
+ renderSelf(buffer) {
17828
+ if (this._highlightsDirty) {
17829
+ if (this.isDestroyed)
17830
+ return;
17831
+ if (this._content.length === 0) {
17832
+ this._shouldRenderTextBuffer = false;
17833
+ this._highlightsDirty = false;
17834
+ } else if (!this._filetype) {
17835
+ this._shouldRenderTextBuffer = true;
17836
+ this._highlightsDirty = false;
17837
+ } else {
17838
+ this.ensureVisibleTextBeforeHighlight();
17839
+ this._highlightsDirty = false;
17840
+ this._highlightingPromise = this.startHighlight();
17841
+ }
17842
+ }
17843
+ if (!this._shouldRenderTextBuffer)
17844
+ return;
17845
+ super.renderSelf(buffer);
17846
+ }
17847
+ }
17848
+
17849
+ // src/renderables/TextNode.ts
17850
+ var BrandedTextNodeRenderable = Symbol.for("@opentui/core/TextNodeRenderable");
17851
+ function isTextNodeRenderable(obj) {
17852
+ return !!obj?.[BrandedTextNodeRenderable];
17853
+ }
17854
+ function styledTextToTextNodes(styledText) {
17855
+ return styledText.chunks.map((chunk) => {
17856
+ const node = new TextNodeRenderable({
17857
+ fg: chunk.fg,
17858
+ bg: chunk.bg,
17859
+ attributes: chunk.attributes,
17860
+ link: chunk.link
17861
+ });
17862
+ node.add(chunk.text);
17863
+ return node;
17864
+ });
17865
+ }
17866
+
17867
+ class TextNodeRenderable extends BaseRenderable {
17868
+ [BrandedTextNodeRenderable] = true;
17869
+ _fg;
17870
+ _bg;
17871
+ _attributes;
17872
+ _link;
17873
+ _children = [];
17874
+ parent = null;
17875
+ constructor(options) {
17876
+ super(options);
17877
+ this._fg = options.fg ? parseColor(options.fg) : undefined;
17878
+ this._bg = options.bg ? parseColor(options.bg) : undefined;
17879
+ this._attributes = options.attributes ?? 0;
17880
+ this._link = options.link;
17881
+ }
17882
+ get children() {
17883
+ return this._children;
17884
+ }
17885
+ set children(children) {
17886
+ this._children = children;
17887
+ this.requestRender();
17888
+ }
17889
+ requestRender() {
17890
+ this.markDirty();
17891
+ this.parent?.requestRender();
17892
+ }
17893
+ add(obj, index) {
17894
+ if (typeof obj === "string") {
17895
+ if (index !== undefined) {
17896
+ this._children.splice(index, 0, obj);
17897
+ this.requestRender();
17898
+ return index;
17899
+ }
17900
+ const insertIndex = this._children.length;
17901
+ this._children.push(obj);
17902
+ this.requestRender();
17903
+ return insertIndex;
17904
+ }
17905
+ if (isTextNodeRenderable(obj)) {
17906
+ if (index !== undefined) {
17907
+ this._children.splice(index, 0, obj);
17908
+ obj.parent = this;
17909
+ this.requestRender();
17910
+ return index;
17911
+ }
17912
+ const insertIndex = this._children.length;
17913
+ this._children.push(obj);
17914
+ obj.parent = this;
17915
+ this.requestRender();
17916
+ return insertIndex;
17917
+ }
17918
+ if (isStyledText(obj)) {
17919
+ const textNodes = styledTextToTextNodes(obj);
17920
+ if (index !== undefined) {
17921
+ this._children.splice(index, 0, ...textNodes);
17922
+ textNodes.forEach((node) => node.parent = this);
17923
+ this.requestRender();
17924
+ return index;
17925
+ }
17926
+ const insertIndex = this._children.length;
17927
+ this._children.push(...textNodes);
17928
+ textNodes.forEach((node) => node.parent = this);
17929
+ this.requestRender();
17930
+ return insertIndex;
17931
+ }
17932
+ throw new Error("TextNodeRenderable only accepts strings, TextNodeRenderable instances, or StyledText instances");
17933
+ }
17934
+ replace(obj, index) {
17935
+ this._children[index] = obj;
17936
+ if (typeof obj !== "string") {
17937
+ obj.parent = this;
17938
+ }
17939
+ this.requestRender();
17940
+ }
17941
+ insertBefore(child, anchorNode) {
17942
+ if (!anchorNode || !isTextNodeRenderable(anchorNode)) {
17943
+ throw new Error("Anchor must be a TextNodeRenderable");
17944
+ }
17945
+ const anchorIndex = this._children.indexOf(anchorNode);
17946
+ if (anchorIndex === -1) {
17947
+ throw new Error("Anchor node not found in children");
17948
+ }
17949
+ if (typeof child === "string") {
17950
+ this._children.splice(anchorIndex, 0, child);
17951
+ } else if (isTextNodeRenderable(child)) {
17952
+ this._children.splice(anchorIndex, 0, child);
17953
+ child.parent = this;
17954
+ } else if (child instanceof StyledText) {
17955
+ const textNodes = styledTextToTextNodes(child);
17956
+ this._children.splice(anchorIndex, 0, ...textNodes);
17957
+ textNodes.forEach((node) => node.parent = this);
17958
+ } else {
17959
+ throw new Error("Child must be a string, TextNodeRenderable, or StyledText instance");
17960
+ }
17961
+ this.requestRender();
17962
+ return this;
17963
+ }
17964
+ remove(id) {
17965
+ const childIndex = this.getRenderableIndex(id);
17966
+ if (childIndex === -1) {
17967
+ throw new Error("Child not found in children");
17968
+ }
17969
+ const child = this._children[childIndex];
17970
+ this._children.splice(childIndex, 1);
17971
+ child.parent = null;
17972
+ this.requestRender();
17973
+ return this;
17974
+ }
17975
+ clear() {
17976
+ this._children = [];
17977
+ this.requestRender();
17978
+ }
17979
+ mergeStyles(parentStyle) {
17980
+ return {
17981
+ fg: this._fg ?? parentStyle.fg,
17982
+ bg: this._bg ?? parentStyle.bg,
17983
+ attributes: this._attributes | parentStyle.attributes,
17984
+ link: this._link ?? parentStyle.link
17985
+ };
17986
+ }
17987
+ gatherWithInheritedStyle(parentStyle = {
17988
+ fg: undefined,
17989
+ bg: undefined,
17990
+ attributes: 0
17991
+ }) {
17992
+ const currentStyle = this.mergeStyles(parentStyle);
17993
+ const chunks = [];
17994
+ for (const child of this._children) {
17995
+ if (typeof child === "string") {
17996
+ chunks.push({
17997
+ __isChunk: true,
17998
+ text: child,
17999
+ fg: currentStyle.fg,
18000
+ bg: currentStyle.bg,
18001
+ attributes: currentStyle.attributes,
18002
+ link: currentStyle.link
18003
+ });
18004
+ } else {
18005
+ const childChunks = child.gatherWithInheritedStyle(currentStyle);
18006
+ chunks.push(...childChunks);
18007
+ }
18008
+ }
18009
+ this.markClean();
18010
+ return chunks;
18011
+ }
18012
+ static fromString(text, options = {}) {
18013
+ const node = new TextNodeRenderable(options);
18014
+ node.add(text);
18015
+ return node;
18016
+ }
18017
+ static fromNodes(nodes, options = {}) {
18018
+ const node = new TextNodeRenderable(options);
18019
+ for (const childNode of nodes) {
18020
+ node.add(childNode);
18021
+ }
18022
+ return node;
18023
+ }
18024
+ toChunks(parentStyle = {
18025
+ fg: undefined,
18026
+ bg: undefined,
18027
+ attributes: 0
18028
+ }) {
18029
+ return this.gatherWithInheritedStyle(parentStyle);
18030
+ }
18031
+ getChildren() {
18032
+ return this._children.filter((child) => typeof child !== "string");
18033
+ }
18034
+ getChildrenCount() {
18035
+ return this._children.length;
18036
+ }
18037
+ getRenderable(id) {
18038
+ return this._children.find((child) => typeof child !== "string" && child.id === id);
18039
+ }
18040
+ getRenderableIndex(id) {
18041
+ return this._children.findIndex((child) => isTextNodeRenderable(child) && child.id === id);
18042
+ }
18043
+ get fg() {
18044
+ return this._fg;
18045
+ }
18046
+ set fg(fg2) {
18047
+ if (!fg2) {
18048
+ this._fg = undefined;
18049
+ this.requestRender();
18050
+ return;
18051
+ }
18052
+ this._fg = parseColor(fg2);
18053
+ this.requestRender();
18054
+ }
18055
+ set bg(bg2) {
18056
+ if (!bg2) {
18057
+ this._bg = undefined;
18058
+ this.requestRender();
18059
+ return;
18060
+ }
18061
+ this._bg = parseColor(bg2);
18062
+ this.requestRender();
18063
+ }
18064
+ get bg() {
18065
+ return this._bg;
18066
+ }
18067
+ set attributes(attributes) {
18068
+ this._attributes = attributes;
18069
+ this.requestRender();
18070
+ }
18071
+ get attributes() {
18072
+ return this._attributes;
18073
+ }
18074
+ set link(link2) {
18075
+ this._link = link2;
18076
+ this.requestRender();
18077
+ }
18078
+ get link() {
18079
+ return this._link;
18080
+ }
18081
+ findDescendantById(id) {
18082
+ return;
18083
+ }
18084
+ }
18085
+
18086
+ class RootTextNodeRenderable extends TextNodeRenderable {
18087
+ ctx;
18088
+ textParent;
18089
+ constructor(ctx, options, textParent) {
18090
+ super(options);
18091
+ this.ctx = ctx;
18092
+ this.textParent = textParent;
18093
+ }
18094
+ requestRender() {
18095
+ this.markDirty();
18096
+ this.ctx.requestRender();
18097
+ }
18098
+ }
18099
+
18100
+ // src/renderables/Text.ts
18101
+ class TextRenderable extends TextBufferRenderable {
18102
+ _text;
18103
+ _hasManualStyledText = false;
18104
+ rootTextNode;
18105
+ _contentDefaultOptions = {
18106
+ content: ""
18107
+ };
18108
+ constructor(ctx, options) {
18109
+ super(ctx, options);
18110
+ const content = options.content ?? this._contentDefaultOptions.content;
18111
+ const styledText = typeof content === "string" ? stringToStyledText(content) : content;
18112
+ this._text = styledText;
18113
+ this._hasManualStyledText = options.content !== undefined && content !== "";
18114
+ this.rootTextNode = new RootTextNodeRenderable(ctx, {
18115
+ id: `${this.id}-root`,
18116
+ fg: this._defaultFg,
18117
+ bg: this._defaultBg,
18118
+ attributes: this._defaultAttributes
18119
+ }, this);
18120
+ this.updateTextBuffer(styledText);
18121
+ }
18122
+ updateTextBuffer(styledText) {
18123
+ this.textBuffer.setStyledText(styledText);
18124
+ this.clearChunks(styledText);
18125
+ }
18126
+ clearChunks(styledText) {}
18127
+ get content() {
18128
+ return this._text;
18129
+ }
18130
+ get chunks() {
18131
+ return this._text.chunks;
18132
+ }
18133
+ get textNode() {
18134
+ return this.rootTextNode;
18135
+ }
18136
+ set content(value) {
18137
+ this._hasManualStyledText = true;
18138
+ const styledText = typeof value === "string" ? stringToStyledText(value) : value;
18139
+ if (this._text !== styledText) {
18140
+ this._text = styledText;
18141
+ this.updateTextBuffer(styledText);
18142
+ this.updateTextInfo();
18143
+ }
18144
+ }
18145
+ updateTextFromNodes() {
18146
+ if (this.rootTextNode.isDirty && !this._hasManualStyledText) {
18147
+ const chunks = this.rootTextNode.gatherWithInheritedStyle({
18148
+ fg: this._defaultFg,
18149
+ bg: this._defaultBg,
18150
+ attributes: this._defaultAttributes,
18151
+ link: undefined
18152
+ });
18153
+ this.textBuffer.setStyledText(new StyledText(chunks));
18154
+ this.refreshLocalSelection();
18155
+ this.yogaNode.markDirty();
18156
+ }
18157
+ }
18158
+ add(obj, index) {
18159
+ return this.rootTextNode.add(obj, index);
18160
+ }
18161
+ remove(id) {
18162
+ this.rootTextNode.remove(id);
18163
+ }
18164
+ insertBefore(obj, anchor) {
18165
+ this.rootTextNode.insertBefore(obj, anchor);
18166
+ return this.rootTextNode.children.indexOf(obj);
18167
+ }
18168
+ getTextChildren() {
18169
+ return this.rootTextNode.getChildren();
18170
+ }
18171
+ clear() {
18172
+ this.rootTextNode.clear();
18173
+ const emptyStyledText = stringToStyledText("");
18174
+ this._text = emptyStyledText;
18175
+ this.updateTextBuffer(emptyStyledText);
18176
+ this.updateTextInfo();
18177
+ this.requestRender();
18178
+ }
18179
+ onLifecyclePass = () => {
18180
+ this.updateTextFromNodes();
18181
+ };
18182
+ onFgChanged(newColor) {
18183
+ this.rootTextNode.fg = newColor;
18184
+ }
18185
+ onBgChanged(newColor) {
18186
+ this.rootTextNode.bg = newColor;
18187
+ }
18188
+ onAttributesChanged(newAttributes) {
18189
+ this.rootTextNode.attributes = newAttributes;
18190
+ }
18191
+ destroy() {
18192
+ this.rootTextNode.children.length = 0;
18193
+ super.destroy();
18194
+ }
18195
+ }
18196
+
18197
+ // src/console.ts
18198
+ import { EventEmitter as EventEmitter8 } from "events";
18199
+ import { Console } from "console";
18200
+ import fs from "fs";
18201
+ import path5 from "path";
18202
+ import util2 from "util";
18203
+
18204
+ // src/lib/output.capture.ts
18205
+ import { Writable } from "stream";
18206
+ import { EventEmitter as EventEmitter7 } from "events";
18207
+
18208
+ class Capture extends EventEmitter7 {
18209
+ output = [];
18210
+ constructor() {
18211
+ super();
18212
+ }
18213
+ get size() {
18214
+ return this.output.length;
18215
+ }
18216
+ write(stream, data) {
18217
+ this.output.push({ stream, output: data });
18218
+ this.emit("write", stream, data);
18219
+ }
18220
+ claimOutput() {
18221
+ const output = this.output.map((o) => o.output).join("");
18222
+ this.clear();
18223
+ return output;
18224
+ }
18225
+ clear() {
18226
+ this.output = [];
18227
+ }
18228
+ }
18229
+
18230
+ class CapturedWritableStream extends Writable {
18231
+ stream;
18232
+ capture;
18233
+ isTTY = true;
18234
+ columns = process.stdout.columns || 80;
18235
+ rows = process.stdout.rows || 24;
18236
+ constructor(stream, capture) {
18237
+ super();
18238
+ this.stream = stream;
18239
+ this.capture = capture;
18240
+ }
18241
+ _write(chunk, encoding, callback) {
18242
+ const data = chunk.toString();
18243
+ this.capture.write(this.stream, data);
18244
+ callback();
18245
+ }
18246
+ getColorDepth() {
18247
+ return process.stdout.getColorDepth?.() || 8;
18248
+ }
18249
+ }
18250
+
18251
+ // src/lib/keymapping.ts
18252
+ var defaultKeyAliases = {
18253
+ enter: "return",
18254
+ esc: "escape",
18255
+ kp0: "0",
18256
+ kp1: "1",
18257
+ kp2: "2",
18258
+ kp3: "3",
18259
+ kp4: "4",
18260
+ kp5: "5",
18261
+ kp6: "6",
18262
+ kp7: "7",
18263
+ kp8: "8",
18264
+ kp9: "9",
18265
+ kpdecimal: ".",
18266
+ kpdivide: "/",
18267
+ kpmultiply: "*",
18268
+ kpminus: "-",
18269
+ kpplus: "+",
18270
+ kpenter: "enter",
18271
+ kpequal: "=",
18272
+ kpseparator: ",",
18273
+ kpleft: "left",
18274
+ kpright: "right",
18275
+ kpup: "up",
18276
+ kpdown: "down",
18277
+ kppageup: "pageup",
18278
+ kppagedown: "pagedown",
18279
+ kphome: "home",
18280
+ kpend: "end",
18281
+ kpinsert: "insert",
18282
+ kpdelete: "delete"
18283
+ };
18284
+ function mergeKeyAliases(defaults, custom) {
18285
+ return { ...defaults, ...custom };
18286
+ }
18287
+ function mergeKeyBindings(defaults, custom) {
18288
+ const map = new Map;
18289
+ for (const binding of defaults) {
18290
+ const key = getKeyBindingKey(binding);
18291
+ map.set(key, binding);
18292
+ }
18293
+ for (const binding of custom) {
18294
+ const key = getKeyBindingKey(binding);
18295
+ map.set(key, binding);
18296
+ }
18297
+ return Array.from(map.values());
18298
+ }
18299
+ function getKeyBindingKey(binding) {
18300
+ return `${binding.name}:${binding.ctrl ? 1 : 0}:${binding.shift ? 1 : 0}:${binding.meta ? 1 : 0}:${binding.super ? 1 : 0}`;
18301
+ }
18302
+ function getBaseCodeKeyName(baseCode) {
18303
+ if (baseCode === undefined || baseCode < 32 || baseCode === 127) {
18304
+ return;
18305
+ }
18306
+ try {
18307
+ const name = String.fromCodePoint(baseCode);
16667
18308
  if (name.length === 1 && name >= "A" && name <= "Z") {
16668
18309
  return name.toLowerCase();
16669
18310
  }
@@ -18732,6 +20373,27 @@ function getObjectsInViewport(viewport, objects, direction = "column", padding =
18732
20373
  return visibleChildren;
18733
20374
  }
18734
20375
 
20376
+ // src/lib/render-geometry.ts
20377
+ function calculateRenderGeometry(screenMode, terminalWidth, terminalHeight, footerHeight) {
20378
+ const safeTerminalWidth = Math.max(terminalWidth, 0);
20379
+ const safeTerminalHeight = Math.max(terminalHeight, 0);
20380
+ if (screenMode !== "split-footer") {
20381
+ return {
20382
+ effectiveFooterHeight: 0,
20383
+ renderOffset: 0,
20384
+ renderWidth: safeTerminalWidth,
20385
+ renderHeight: safeTerminalHeight
20386
+ };
20387
+ }
20388
+ const effectiveFooterHeight = Math.min(footerHeight, safeTerminalHeight);
20389
+ return {
20390
+ effectiveFooterHeight,
20391
+ renderOffset: safeTerminalHeight - effectiveFooterHeight,
20392
+ renderWidth: safeTerminalWidth,
20393
+ renderHeight: effectiveFooterHeight
20394
+ };
20395
+ }
20396
+
18735
20397
  // src/lib/terminal-capability-detection.ts
18736
20398
  function isCapabilityResponse(sequence) {
18737
20399
  if (/\x1b\[\?\d+(?:;\d+)*\$y/.test(sequence)) {
@@ -18769,6 +20431,26 @@ function parsePixelResolution(sequence) {
18769
20431
  }
18770
20432
 
18771
20433
  // src/renderer.ts
20434
+ var OSC_THEME_RESPONSE = /\x1b](10|11);(?:(?:rgb:)([0-9a-fA-F]+)\/([0-9a-fA-F]+)\/([0-9a-fA-F]+)|#([0-9a-fA-F]{6}))(?:\x07|\x1b\\)/g;
20435
+ function scaleOscThemeComponent(component) {
20436
+ const value = parseInt(component, 16);
20437
+ const maxValue = (1 << 4 * component.length) - 1;
20438
+ return Math.round(value / maxValue * 255).toString(16).padStart(2, "0");
20439
+ }
20440
+ function oscThemeColorToHex(r, g, b, hex6) {
20441
+ if (hex6) {
20442
+ return `#${hex6.toLowerCase()}`;
20443
+ }
20444
+ if (r && g && b) {
20445
+ return `#${scaleOscThemeComponent(r)}${scaleOscThemeComponent(g)}${scaleOscThemeComponent(b)}`;
20446
+ }
20447
+ return "#000000";
20448
+ }
20449
+ function inferThemeModeFromBackgroundColor(color) {
20450
+ const [r, g, b] = parseColor(color).toInts();
20451
+ const brightness = (r * 299 + g * 587 + b * 114) / 1000;
20452
+ return brightness > 128 ? "light" : "dark";
20453
+ }
18772
20454
  registerEnvVar({
18773
20455
  name: "OTUI_DUMP_CAPTURES",
18774
20456
  description: "Dump captured stdout and console caches when the renderer exit handler runs.",
@@ -18806,6 +20488,9 @@ registerEnvVar({
18806
20488
  default: false
18807
20489
  });
18808
20490
  var DEFAULT_FOOTER_HEIGHT = 12;
20491
+ var MAX_SCROLLBACK_SURFACE_HEIGHT_PASSES = 4;
20492
+ var TRANSPARENT_RGBA = RGBA.fromValues(0, 0, 0, 0);
20493
+ var scrollbackSurfaceCounter = 0;
18809
20494
  function normalizeFooterHeight(footerHeight) {
18810
20495
  if (footerHeight === undefined) {
18811
20496
  return DEFAULT_FOOTER_HEIGHT;
@@ -18832,11 +20517,106 @@ function resolveModes(config) {
18832
20517
  if (externalOutputMode === "capture-stdout" && screenMode !== "split-footer") {
18833
20518
  throw new Error('externalOutputMode "capture-stdout" requires screenMode "split-footer"');
18834
20519
  }
18835
- return {
18836
- screenMode,
18837
- footerHeight,
18838
- externalOutputMode
18839
- };
20520
+ return {
20521
+ screenMode,
20522
+ footerHeight,
20523
+ externalOutputMode
20524
+ };
20525
+ }
20526
+
20527
+ class ExternalOutputQueue {
20528
+ commits = [];
20529
+ get size() {
20530
+ return this.commits.length;
20531
+ }
20532
+ writeSnapshot(commit) {
20533
+ this.commits.push(commit);
20534
+ }
20535
+ claim(limit = Number.POSITIVE_INFINITY) {
20536
+ if (this.commits.length === 0) {
20537
+ return [];
20538
+ }
20539
+ const clampedLimit = Number.isFinite(limit) ? Math.max(1, Math.trunc(limit)) : this.commits.length;
20540
+ if (clampedLimit >= this.commits.length) {
20541
+ const output2 = this.commits;
20542
+ this.commits = [];
20543
+ return output2;
20544
+ }
20545
+ const output = this.commits.slice(0, clampedLimit);
20546
+ this.commits = this.commits.slice(clampedLimit);
20547
+ return output;
20548
+ }
20549
+ clear() {
20550
+ for (const commit of this.commits) {
20551
+ commit.snapshot.destroy();
20552
+ }
20553
+ this.commits = [];
20554
+ }
20555
+ }
20556
+ var CHAR_FLAG_CONTINUATION = 3221225472 >>> 0;
20557
+ var CHAR_FLAG_MASK = 3221225472 >>> 0;
20558
+
20559
+ class ScrollbackSnapshotRenderContext extends EventEmitter9 {
20560
+ width;
20561
+ height;
20562
+ frameId = 0;
20563
+ widthMethod;
20564
+ capabilities = null;
20565
+ hasSelection = false;
20566
+ currentFocusedRenderable = null;
20567
+ keyInput;
20568
+ _internalKeyInput;
20569
+ lifecyclePasses = new Set;
20570
+ constructor(width, height, widthMethod) {
20571
+ super();
20572
+ this.width = width;
20573
+ this.height = height;
20574
+ this.widthMethod = widthMethod;
20575
+ this.keyInput = new KeyHandler;
20576
+ this._internalKeyInput = new InternalKeyHandler;
20577
+ }
20578
+ addToHitGrid(_x, _y, _width, _height, _id) {}
20579
+ pushHitGridScissorRect(_x, _y, _width, _height) {}
20580
+ popHitGridScissorRect() {}
20581
+ clearHitGridScissorRects() {}
20582
+ requestRender() {}
20583
+ setCursorPosition(_x, _y, _visible) {}
20584
+ setCursorStyle(_options) {}
20585
+ setCursorColor(_color) {}
20586
+ setMousePointer(_shape) {}
20587
+ requestLive() {}
20588
+ dropLive() {}
20589
+ getSelection() {
20590
+ return null;
20591
+ }
20592
+ get currentFocusedEditor() {
20593
+ if (!this.currentFocusedRenderable)
20594
+ return null;
20595
+ if (!isEditBufferRenderable(this.currentFocusedRenderable))
20596
+ return null;
20597
+ return this.currentFocusedRenderable;
20598
+ }
20599
+ requestSelectionUpdate() {}
20600
+ focusRenderable(renderable) {
20601
+ this.currentFocusedRenderable = renderable;
20602
+ }
20603
+ blurRenderable(renderable) {
20604
+ if (this.currentFocusedRenderable === renderable) {
20605
+ this.currentFocusedRenderable = null;
20606
+ }
20607
+ }
20608
+ registerLifecyclePass(renderable) {
20609
+ this.lifecyclePasses.add(renderable);
20610
+ }
20611
+ unregisterLifecyclePass(renderable) {
20612
+ this.lifecyclePasses.delete(renderable);
20613
+ }
20614
+ getLifecyclePasses() {
20615
+ return this.lifecyclePasses;
20616
+ }
20617
+ clearSelection() {}
20618
+ startSelection(_renderable, _x, _y) {}
20619
+ updateSelection(_currentRenderable, _x, _y, _options) {}
18840
20620
  }
18841
20621
  var DEFAULT_FORWARDED_ENV_KEYS = [
18842
20622
  "TMUX",
@@ -18958,9 +20738,9 @@ async function createCliRenderer(config = {}) {
18958
20738
  const { screenMode, footerHeight } = resolveModes(config);
18959
20739
  const width = stdout.columns || 80;
18960
20740
  const height = stdout.rows || 24;
18961
- const renderHeight = screenMode === "split-footer" ? footerHeight : height;
20741
+ const geometry = calculateRenderGeometry(screenMode, width, height, footerHeight);
18962
20742
  const ziglib = resolveRenderLib();
18963
- const rendererPtr = ziglib.createRenderer(width, renderHeight, {
20743
+ const rendererPtr = ziglib.createRenderer(geometry.renderWidth, geometry.renderHeight, {
18964
20744
  remote: config.remote ?? false,
18965
20745
  testing: config.testing ?? false
18966
20746
  });
@@ -19077,6 +20857,8 @@ class CliRenderer extends EventEmitter9 {
19077
20857
  animationRequest = new Map;
19078
20858
  resizeTimeoutId = null;
19079
20859
  capabilityTimeoutId = null;
20860
+ splitStartupSeedTimeoutId = null;
20861
+ pendingSplitStartupCursorSeed = false;
19080
20862
  resizeDebounceDelay = 100;
19081
20863
  enableMouseMovement = false;
19082
20864
  _useMouse = true;
@@ -19084,6 +20866,7 @@ class CliRenderer extends EventEmitter9 {
19084
20866
  _screenMode = "alternate-screen";
19085
20867
  _footerHeight = DEFAULT_FOOTER_HEIGHT;
19086
20868
  _externalOutputMode = "passthrough";
20869
+ clearOnShutdown = true;
19087
20870
  _suspendedMouseEnabled = false;
19088
20871
  _previousControlState = "idle" /* IDLE */;
19089
20872
  capturedRenderable;
@@ -19094,15 +20877,15 @@ class CliRenderer extends EventEmitter9 {
19094
20877
  clipboard;
19095
20878
  _splitHeight = 0;
19096
20879
  renderOffset = 0;
20880
+ splitTailColumn = 0;
20881
+ pendingSplitFooterTransition = null;
20882
+ forceFullRepaintRequested = false;
20883
+ maxSplitCommitsPerFrame = 8;
19097
20884
  _terminalWidth = 0;
19098
20885
  _terminalHeight = 0;
19099
20886
  _terminalIsSetup = false;
20887
+ externalOutputQueue = new ExternalOutputQueue;
19100
20888
  realStdoutWrite;
19101
- captureCallback = () => {
19102
- if (this._splitHeight > 0) {
19103
- this.requestRender();
19104
- }
19105
- };
19106
20889
  _useConsole = true;
19107
20890
  sigwinchHandler = (() => {
19108
20891
  const width = this.stdout.columns || 80;
@@ -19126,6 +20909,10 @@ class CliRenderer extends EventEmitter9 {
19126
20909
  _paletteDetectionPromise = null;
19127
20910
  _onDestroy;
19128
20911
  _themeMode = null;
20912
+ _themeModeSource = "none";
20913
+ _themeFallbackPending = true;
20914
+ _themeOscForeground = null;
20915
+ _themeOscBackground = null;
19129
20916
  _terminalFocusState = null;
19130
20917
  sequenceHandlers = [];
19131
20918
  prependedInputHandlers = [];
@@ -19141,8 +20928,15 @@ class CliRenderer extends EventEmitter9 {
19141
20928
  }).bind(this);
19142
20929
  dumpOutputCache(optionalMessage = "") {
19143
20930
  const cachedLogs = this.console.getCachedLogs();
19144
- const capturedOutput = capture.claimOutput();
19145
- if (capturedOutput.length > 0 || cachedLogs.length > 0) {
20931
+ const capturedConsoleOutput = capture.claimOutput();
20932
+ const capturedExternalOutputCommits = this.externalOutputQueue.claim();
20933
+ let capturedExternalOutput = "";
20934
+ for (const commit of capturedExternalOutputCommits) {
20935
+ capturedExternalOutput += `[snapshot ${commit.snapshot.width}x${commit.snapshot.height}]
20936
+ `;
20937
+ commit.snapshot.destroy();
20938
+ }
20939
+ if (capturedConsoleOutput.length > 0 || capturedExternalOutput.length > 0 || cachedLogs.length > 0) {
19146
20940
  this.realStdoutWrite.call(this.stdout, optionalMessage);
19147
20941
  }
19148
20942
  if (cachedLogs.length > 0) {
@@ -19150,11 +20944,18 @@ class CliRenderer extends EventEmitter9 {
19150
20944
  `);
19151
20945
  this.realStdoutWrite.call(this.stdout, cachedLogs);
19152
20946
  }
19153
- if (capturedOutput.length > 0) {
20947
+ if (capturedConsoleOutput.length > 0) {
20948
+ this.realStdoutWrite.call(this.stdout, `
20949
+ Captured console output:
20950
+ `);
20951
+ this.realStdoutWrite.call(this.stdout, capturedConsoleOutput + `
20952
+ `);
20953
+ }
20954
+ if (capturedExternalOutput.length > 0) {
19154
20955
  this.realStdoutWrite.call(this.stdout, `
19155
- Captured output:
20956
+ Captured external output:
19156
20957
  `);
19157
- this.realStdoutWrite.call(this.stdout, capturedOutput + `
20958
+ this.realStdoutWrite.call(this.stdout, capturedExternalOutput + `
19158
20959
  `);
19159
20960
  }
19160
20961
  this.realStdoutWrite.call(this.stdout, ANSI.reset);
@@ -19183,13 +20984,18 @@ Captured output:
19183
20984
  this.lib = lib;
19184
20985
  this._terminalWidth = stdout.columns ?? width;
19185
20986
  this._terminalHeight = stdout.rows ?? height;
19186
- this.width = width;
19187
- this.height = height;
19188
20987
  this._useThread = config.useThread === undefined ? false : config.useThread;
19189
20988
  const { screenMode, footerHeight, externalOutputMode } = resolveModes(config);
20989
+ this._externalOutputMode = externalOutputMode;
20990
+ const initialGeometry = calculateRenderGeometry(screenMode, this._terminalWidth, this._terminalHeight, footerHeight);
20991
+ this.width = initialGeometry.renderWidth;
20992
+ this.height = initialGeometry.renderHeight;
20993
+ this._splitHeight = initialGeometry.effectiveFooterHeight;
20994
+ this.renderOffset = screenMode === "split-footer" ? 0 : initialGeometry.renderOffset;
19190
20995
  this._footerHeight = footerHeight;
19191
- this._screenMode = screenMode;
19192
20996
  this.rendererPtr = rendererPtr;
20997
+ this.clearOnShutdown = config.clearOnShutdown ?? true;
20998
+ this.lib.setClearOnShutdown(this.rendererPtr, this.clearOnShutdown);
19193
20999
  const forwardEnvKeys = config.forwardEnvKeys ?? [...DEFAULT_FORWARDED_ENV_KEYS];
19194
21000
  for (const key of forwardEnvKeys) {
19195
21001
  const value = process.env[key];
@@ -19257,7 +21063,8 @@ Captured output:
19257
21063
  kittyKeyboardEnabled: useKittyForParsing,
19258
21064
  privateCapabilityRepliesActive: false,
19259
21065
  pixelResolutionQueryActive: false,
19260
- explicitWidthCprActive: false
21066
+ explicitWidthCprActive: false,
21067
+ startupCursorCprActive: false
19261
21068
  },
19262
21069
  clock: this.clock
19263
21070
  });
@@ -19267,7 +21074,7 @@ Captured output:
19267
21074
  });
19268
21075
  this.consoleMode = config.consoleMode ?? "console-overlay";
19269
21076
  this.applyScreenMode(screenMode, false, false);
19270
- this.externalOutputMode = externalOutputMode;
21077
+ this.stdout.write = externalOutputMode === "capture-stdout" ? this.interceptStdoutWrite : this.realStdoutWrite;
19271
21078
  this._openConsoleOnError = config.openConsoleOnError ?? true;
19272
21079
  this._onDestroy = config.onDestroy;
19273
21080
  global.requestAnimationFrame = (callback) => {
@@ -19426,163 +21233,795 @@ Captured output:
19426
21233
  return;
19427
21234
  }
19428
21235
  try {
19429
- await this.loop();
21236
+ await this.loop();
21237
+ } finally {
21238
+ this.updateScheduled = false;
21239
+ this.resolveIdleIfNeeded();
21240
+ }
21241
+ }
21242
+ get consoleMode() {
21243
+ return this._useConsole ? "console-overlay" : "disabled";
21244
+ }
21245
+ set consoleMode(mode) {
21246
+ this._useConsole = mode === "console-overlay";
21247
+ if (this._useConsole) {
21248
+ this.console.activate();
21249
+ } else {
21250
+ this.console.deactivate();
21251
+ }
21252
+ }
21253
+ get isRunning() {
21254
+ return this._isRunning;
21255
+ }
21256
+ isIdleNow() {
21257
+ return !this._isRunning && !this.rendering && !this.renderTimeout && !this.updateScheduled && !this.immediateRerenderRequested;
21258
+ }
21259
+ resolveIdleIfNeeded() {
21260
+ if (!this.isIdleNow())
21261
+ return;
21262
+ const resolvers = this.idleResolvers.splice(0);
21263
+ for (const resolve4 of resolvers) {
21264
+ resolve4();
21265
+ }
21266
+ }
21267
+ idle() {
21268
+ if (this._isDestroyed)
21269
+ return Promise.resolve();
21270
+ if (this.isIdleNow())
21271
+ return Promise.resolve();
21272
+ return new Promise((resolve4) => {
21273
+ this.idleResolvers.push(resolve4);
21274
+ });
21275
+ }
21276
+ get resolution() {
21277
+ return this._resolution;
21278
+ }
21279
+ get console() {
21280
+ return this._console;
21281
+ }
21282
+ get keyInput() {
21283
+ return this._keyHandler;
21284
+ }
21285
+ get _internalKeyInput() {
21286
+ return this._keyHandler;
21287
+ }
21288
+ get terminalWidth() {
21289
+ return this._terminalWidth;
21290
+ }
21291
+ get terminalHeight() {
21292
+ return this._terminalHeight;
21293
+ }
21294
+ get useThread() {
21295
+ return this._useThread;
21296
+ }
21297
+ get targetFps() {
21298
+ return this._targetFps;
21299
+ }
21300
+ set targetFps(targetFps) {
21301
+ this._targetFps = targetFps;
21302
+ this.targetFrameTime = 1000 / this._targetFps;
21303
+ }
21304
+ get maxFps() {
21305
+ return this._maxFps;
21306
+ }
21307
+ set maxFps(maxFps) {
21308
+ this._maxFps = maxFps;
21309
+ this.minTargetFrameTime = 1000 / this._maxFps;
21310
+ }
21311
+ get useMouse() {
21312
+ return this._useMouse;
21313
+ }
21314
+ set useMouse(useMouse) {
21315
+ if (this._useMouse === useMouse)
21316
+ return;
21317
+ this._useMouse = useMouse;
21318
+ if (useMouse) {
21319
+ this.enableMouse();
21320
+ } else {
21321
+ this.disableMouse();
21322
+ }
21323
+ }
21324
+ get screenMode() {
21325
+ return this._screenMode;
21326
+ }
21327
+ set screenMode(mode) {
21328
+ if (this.externalOutputMode === "capture-stdout" && mode !== "split-footer") {
21329
+ throw new Error('externalOutputMode "capture-stdout" requires screenMode "split-footer"');
21330
+ }
21331
+ this.applyScreenMode(mode);
21332
+ }
21333
+ get footerHeight() {
21334
+ return this._footerHeight;
21335
+ }
21336
+ set footerHeight(footerHeight) {
21337
+ const normalizedFooterHeight = normalizeFooterHeight(footerHeight);
21338
+ if (normalizedFooterHeight === this._footerHeight) {
21339
+ return;
21340
+ }
21341
+ this._footerHeight = normalizedFooterHeight;
21342
+ if (this.screenMode === "split-footer") {
21343
+ this.applyScreenMode("split-footer");
21344
+ }
21345
+ }
21346
+ get externalOutputMode() {
21347
+ return this._externalOutputMode;
21348
+ }
21349
+ set externalOutputMode(mode) {
21350
+ if (mode === "capture-stdout" && this.screenMode !== "split-footer") {
21351
+ throw new Error('externalOutputMode "capture-stdout" requires screenMode "split-footer"');
21352
+ }
21353
+ const previousMode = this._externalOutputMode;
21354
+ if (previousMode === mode) {
21355
+ return;
21356
+ }
21357
+ if (previousMode === "capture-stdout" && mode === "passthrough" && this._splitHeight > 0) {
21358
+ this.flushPendingSplitOutputBeforeTransition();
21359
+ }
21360
+ this._externalOutputMode = mode;
21361
+ this.stdout.write = mode === "capture-stdout" ? this.interceptStdoutWrite : this.realStdoutWrite;
21362
+ if (this._screenMode === "split-footer" && this._splitHeight > 0 && mode === "capture-stdout") {
21363
+ this.clearPendingSplitFooterTransition();
21364
+ this.resetSplitScrollback(this.getSplitCursorSeedRows());
21365
+ return;
21366
+ }
21367
+ if (this._screenMode === "split-footer" && this._splitHeight > 0 && previousMode === "capture-stdout" && mode === "passthrough") {
21368
+ this.clearPendingSplitFooterTransition();
21369
+ return;
21370
+ }
21371
+ this.syncSplitFooterState();
21372
+ }
21373
+ get liveRequestCount() {
21374
+ return this.liveRequestCounter;
21375
+ }
21376
+ get currentControlState() {
21377
+ return this._controlState;
21378
+ }
21379
+ get capabilities() {
21380
+ return this._capabilities;
21381
+ }
21382
+ get themeMode() {
21383
+ return this._themeMode;
21384
+ }
21385
+ waitForThemeMode(timeoutMs = 1000) {
21386
+ if (!Number.isFinite(timeoutMs) || timeoutMs < 0) {
21387
+ throw new Error("timeoutMs must be a non-negative finite number");
21388
+ }
21389
+ if (this._themeMode !== null || this._isDestroyed || timeoutMs === 0) {
21390
+ return Promise.resolve(this._themeMode);
21391
+ }
21392
+ return new Promise((resolve4) => {
21393
+ let timeoutHandle = null;
21394
+ const cleanup = () => {
21395
+ if (timeoutHandle !== null) {
21396
+ this.clock.clearTimeout(timeoutHandle);
21397
+ timeoutHandle = null;
21398
+ }
21399
+ this.off("theme_mode" /* THEME_MODE */, handleThemeMode);
21400
+ this.off("destroy" /* DESTROY */, handleDestroy);
21401
+ };
21402
+ const finish = () => {
21403
+ cleanup();
21404
+ resolve4(this._themeMode);
21405
+ };
21406
+ const handleThemeMode = () => {
21407
+ finish();
21408
+ };
21409
+ const handleDestroy = () => {
21410
+ finish();
21411
+ };
21412
+ this.on("theme_mode" /* THEME_MODE */, handleThemeMode);
21413
+ this.on("destroy" /* DESTROY */, handleDestroy);
21414
+ timeoutHandle = this.clock.setTimeout(finish, timeoutMs);
21415
+ });
21416
+ }
21417
+ getDebugInputs() {
21418
+ return [...this._debugInputs];
21419
+ }
21420
+ get useKittyKeyboard() {
21421
+ return this.lib.getKittyKeyboardFlags(this.rendererPtr) > 0;
21422
+ }
21423
+ set useKittyKeyboard(use) {
21424
+ const flags = use ? KITTY_FLAG_DISAMBIGUATE | KITTY_FLAG_ALTERNATE_KEYS : 0;
21425
+ this.lib.setKittyKeyboardFlags(this.rendererPtr, flags);
21426
+ }
21427
+ createScrollbackSurface(options = {}) {
21428
+ if (this._screenMode !== "split-footer" || this._externalOutputMode !== "capture-stdout") {
21429
+ throw new Error('createScrollbackSurface requires screenMode "split-footer" and externalOutputMode "capture-stdout"');
21430
+ }
21431
+ const renderer = this;
21432
+ const surfaceId = scrollbackSurfaceCounter++;
21433
+ const startOnNewLine = options.startOnNewLine ?? true;
21434
+ const firstLineOffset = !startOnNewLine && renderer.splitTailColumn > 0 && renderer.splitTailColumn < renderer.width ? renderer.splitTailColumn : 0;
21435
+ const snapshotContext = new ScrollbackSnapshotRenderContext(renderer.width, 1, renderer.widthMethod);
21436
+ let firstLineOffsetOwner = null;
21437
+ const renderContext = Object.create(snapshotContext);
21438
+ Object.defineProperty(renderContext, "claimFirstLineOffset", {
21439
+ value: (renderable) => {
21440
+ if (firstLineOffsetOwner?.isDestroyed) {
21441
+ firstLineOffsetOwner = null;
21442
+ }
21443
+ if (firstLineOffsetOwner) {
21444
+ return 0;
21445
+ }
21446
+ firstLineOffsetOwner = renderable ?? null;
21447
+ return firstLineOffset;
21448
+ },
21449
+ enumerable: true,
21450
+ configurable: true
21451
+ });
21452
+ const internalRoot = new RootRenderable(renderContext);
21453
+ const publicRoot = new BoxRenderable(renderContext, {
21454
+ id: `scrollback-surface-root-${surfaceId}`,
21455
+ position: "absolute",
21456
+ left: 0,
21457
+ top: 0,
21458
+ width: renderer.width,
21459
+ height: "auto",
21460
+ border: false,
21461
+ backgroundColor: "transparent",
21462
+ shouldFill: false,
21463
+ flexDirection: "column"
21464
+ });
21465
+ internalRoot.add(publicRoot);
21466
+ let surfaceWidth = renderer.width;
21467
+ let surfaceHeight = 1;
21468
+ let surfaceWidthMethod = renderer.widthMethod;
21469
+ let surfaceDestroyed = false;
21470
+ let hasRendered = false;
21471
+ let nextCommitStartOnNewLine = startOnNewLine;
21472
+ let backingBuffer = OptimizedBuffer.create(surfaceWidth, surfaceHeight, surfaceWidthMethod, {
21473
+ id: `scrollback-surface-buffer-${surfaceId}`
21474
+ });
21475
+ const destroyListener = () => {
21476
+ destroySurface();
21477
+ };
21478
+ const assertNotDestroyed = () => {
21479
+ if (surfaceDestroyed) {
21480
+ throw new Error("ScrollbackSurface is destroyed");
21481
+ }
21482
+ };
21483
+ const assertRendered = () => {
21484
+ if (!hasRendered) {
21485
+ throw new Error("ScrollbackSurface.commitRows requires render() before commitRows()");
21486
+ }
21487
+ };
21488
+ const assertGeometryStillCurrent = () => {
21489
+ if (renderer.width !== surfaceWidth || renderer.widthMethod !== surfaceWidthMethod) {
21490
+ throw new Error("ScrollbackSurface.commitRows requires render() after renderer geometry changes");
21491
+ }
21492
+ };
21493
+ const assertRowRange = (startRow, endRowExclusive) => {
21494
+ if (!Number.isInteger(startRow) || !Number.isInteger(endRowExclusive)) {
21495
+ throw new Error("ScrollbackSurface.commitRows requires finite integer row bounds");
21496
+ }
21497
+ if (startRow < 0) {
21498
+ throw new Error("ScrollbackSurface.commitRows requires startRow >= 0");
21499
+ }
21500
+ if (endRowExclusive < startRow) {
21501
+ throw new Error("ScrollbackSurface.commitRows requires endRowExclusive >= startRow");
21502
+ }
21503
+ if (endRowExclusive > surfaceHeight) {
21504
+ throw new Error("ScrollbackSurface.commitRows row range exceeds rendered surface height");
21505
+ }
21506
+ };
21507
+ const collectPendingCodeRenderables = (node) => {
21508
+ const pending = [];
21509
+ if (node instanceof CodeRenderable && node.isHighlighting) {
21510
+ pending.push(node);
21511
+ }
21512
+ for (const child of node.getChildren()) {
21513
+ pending.push(...collectPendingCodeRenderables(child));
21514
+ }
21515
+ return pending;
21516
+ };
21517
+ const waitForPendingHighlights = async (pending, timeoutMs) => {
21518
+ await new Promise((resolve4, reject) => {
21519
+ let settled = false;
21520
+ const timeoutHandle = renderer.clock.setTimeout(() => {
21521
+ if (settled) {
21522
+ return;
21523
+ }
21524
+ settled = true;
21525
+ reject(new Error("ScrollbackSurface.settle timed out waiting for CodeRenderable highlighting"));
21526
+ }, timeoutMs);
21527
+ Promise.all(pending.map((renderable) => renderable.highlightingDone)).then(() => {
21528
+ if (settled) {
21529
+ return;
21530
+ }
21531
+ settled = true;
21532
+ renderer.clock.clearTimeout(timeoutHandle);
21533
+ resolve4();
21534
+ }, (error) => {
21535
+ if (settled) {
21536
+ return;
21537
+ }
21538
+ settled = true;
21539
+ renderer.clock.clearTimeout(timeoutHandle);
21540
+ reject(error);
21541
+ });
21542
+ });
21543
+ };
21544
+ const renderSurface = () => {
21545
+ assertNotDestroyed();
21546
+ const width = renderer.width;
21547
+ const widthMethod = renderer.widthMethod;
21548
+ snapshotContext.width = width;
21549
+ snapshotContext.widthMethod = widthMethod;
21550
+ publicRoot.width = width;
21551
+ const renderPass = (height) => {
21552
+ snapshotContext.height = height;
21553
+ internalRoot.resize(width, height);
21554
+ backingBuffer.resize(width, height);
21555
+ backingBuffer.clear(TRANSPARENT_RGBA);
21556
+ snapshotContext.frameId += 1;
21557
+ internalRoot.render(backingBuffer, 0);
21558
+ };
21559
+ let targetHeight = Math.max(1, surfaceHeight);
21560
+ if (surfaceWidthMethod !== widthMethod) {
21561
+ backingBuffer.destroy();
21562
+ backingBuffer = OptimizedBuffer.create(width, targetHeight, widthMethod, {
21563
+ id: `scrollback-surface-buffer-${surfaceId}`
21564
+ });
21565
+ } else {
21566
+ backingBuffer.resize(width, targetHeight);
21567
+ }
21568
+ for (let pass = 0;pass < MAX_SCROLLBACK_SURFACE_HEIGHT_PASSES; pass += 1) {
21569
+ renderPass(targetHeight);
21570
+ const measuredHeight = Math.max(1, publicRoot.height);
21571
+ if (measuredHeight === targetHeight) {
21572
+ surfaceWidth = width;
21573
+ surfaceHeight = measuredHeight;
21574
+ surfaceWidthMethod = widthMethod;
21575
+ hasRendered = true;
21576
+ return;
21577
+ }
21578
+ targetHeight = measuredHeight;
21579
+ }
21580
+ renderPass(targetHeight);
21581
+ surfaceWidth = width;
21582
+ surfaceHeight = targetHeight;
21583
+ surfaceWidthMethod = widthMethod;
21584
+ hasRendered = true;
21585
+ };
21586
+ const settleSurface = async (timeoutMs = 2000) => {
21587
+ assertNotDestroyed();
21588
+ const startedAt = renderer.clock.now();
21589
+ renderSurface();
21590
+ while (true) {
21591
+ assertNotDestroyed();
21592
+ const pending = collectPendingCodeRenderables(publicRoot);
21593
+ if (pending.length === 0) {
21594
+ return;
21595
+ }
21596
+ const remainingMs = timeoutMs - (renderer.clock.now() - startedAt);
21597
+ if (remainingMs <= 0) {
21598
+ throw new Error("ScrollbackSurface.settle timed out waiting for CodeRenderable highlighting");
21599
+ }
21600
+ await waitForPendingHighlights(pending, remainingMs);
21601
+ assertNotDestroyed();
21602
+ renderSurface();
21603
+ }
21604
+ };
21605
+ const commitRows = (startRow, endRowExclusive, commitOptions = {}) => {
21606
+ assertNotDestroyed();
21607
+ assertRendered();
21608
+ assertGeometryStillCurrent();
21609
+ assertRowRange(startRow, endRowExclusive);
21610
+ if (startRow === endRowExclusive) {
21611
+ return;
21612
+ }
21613
+ const rowCount = endRowExclusive - startRow;
21614
+ const commitBuffer = OptimizedBuffer.create(surfaceWidth, rowCount, surfaceWidthMethod, {
21615
+ id: `scrollback-surface-commit-${surfaceId}`
21616
+ });
21617
+ try {
21618
+ commitBuffer.drawFrameBuffer(0, 0, backingBuffer, 0, startRow, surfaceWidth, rowCount);
21619
+ renderer.enqueueRenderedScrollbackCommit({
21620
+ snapshot: commitBuffer,
21621
+ rowColumns: commitOptions.rowColumns,
21622
+ startOnNewLine: nextCommitStartOnNewLine,
21623
+ trailingNewline: commitOptions.trailingNewline ?? false
21624
+ });
21625
+ nextCommitStartOnNewLine = false;
21626
+ } catch (error) {
21627
+ commitBuffer.destroy();
21628
+ throw error;
21629
+ }
21630
+ };
21631
+ const destroySurface = () => {
21632
+ if (surfaceDestroyed) {
21633
+ return;
21634
+ }
21635
+ surfaceDestroyed = true;
21636
+ renderer.off("destroy" /* DESTROY */, destroyListener);
21637
+ let destroyError = null;
21638
+ try {
21639
+ internalRoot.destroyRecursively();
21640
+ } catch (error) {
21641
+ destroyError = error;
21642
+ }
21643
+ try {
21644
+ backingBuffer.destroy();
21645
+ } catch (error) {
21646
+ if (destroyError === null) {
21647
+ destroyError = error;
21648
+ }
21649
+ }
21650
+ renderContext.removeAllListeners();
21651
+ snapshotContext.removeAllListeners();
21652
+ if (destroyError !== null) {
21653
+ throw destroyError;
21654
+ }
21655
+ };
21656
+ renderer.on("destroy" /* DESTROY */, destroyListener);
21657
+ return {
21658
+ get renderContext() {
21659
+ return renderContext;
21660
+ },
21661
+ get root() {
21662
+ return publicRoot;
21663
+ },
21664
+ get width() {
21665
+ return surfaceWidth;
21666
+ },
21667
+ get height() {
21668
+ return surfaceHeight;
21669
+ },
21670
+ get isDestroyed() {
21671
+ return surfaceDestroyed;
21672
+ },
21673
+ render: renderSurface,
21674
+ settle: settleSurface,
21675
+ commitRows,
21676
+ destroy: destroySurface
21677
+ };
21678
+ }
21679
+ writeToScrollback(write) {
21680
+ if (this._screenMode !== "split-footer" || this._externalOutputMode !== "capture-stdout") {
21681
+ throw new Error('writeToScrollback requires screenMode "split-footer" and externalOutputMode "capture-stdout"');
21682
+ }
21683
+ const snapshotContext = new ScrollbackSnapshotRenderContext(this.width, this.height, this.widthMethod);
21684
+ const snapshot = write({
21685
+ width: this.width,
21686
+ widthMethod: this.widthMethod,
21687
+ tailColumn: this.splitTailColumn,
21688
+ renderContext: snapshotContext
21689
+ });
21690
+ if (!snapshot || !snapshot.root) {
21691
+ throw new Error("writeToScrollback must return a snapshot root renderable");
21692
+ }
21693
+ let renderFailed = false;
21694
+ let snapshotRoot = null;
21695
+ let snapshotBuffer = null;
21696
+ try {
21697
+ const rootRenderable = snapshot.root;
21698
+ const snapshotWidth = this.getSnapshotWidth(snapshot.width, rootRenderable.width);
21699
+ const snapshotHeight = this.getSnapshotHeight(snapshot.height, rootRenderable.height);
21700
+ snapshotContext.width = snapshotWidth;
21701
+ snapshotContext.height = snapshotHeight;
21702
+ snapshotContext.widthMethod = this.widthMethod;
21703
+ snapshotRoot = new RootRenderable(snapshotContext);
21704
+ snapshotBuffer = OptimizedBuffer.create(snapshotWidth, snapshotHeight, this.widthMethod, {
21705
+ id: "scrollback-snapshot-commit"
21706
+ });
21707
+ snapshotRoot.add(rootRenderable);
21708
+ snapshotRoot.render(snapshotBuffer, 0);
21709
+ this.enqueueRenderedScrollbackCommit({
21710
+ snapshot: snapshotBuffer,
21711
+ rowColumns: snapshot.rowColumns,
21712
+ startOnNewLine: snapshot.startOnNewLine,
21713
+ trailingNewline: snapshot.trailingNewline
21714
+ });
21715
+ } catch (error) {
21716
+ renderFailed = true;
21717
+ snapshotBuffer?.destroy();
21718
+ throw error;
19430
21719
  } finally {
19431
- this.updateScheduled = false;
19432
- this.resolveIdleIfNeeded();
21720
+ let cleanupError = null;
21721
+ try {
21722
+ if (snapshotRoot) {
21723
+ snapshotRoot.destroyRecursively();
21724
+ } else {
21725
+ snapshot.root.destroyRecursively();
21726
+ }
21727
+ } catch (error) {
21728
+ cleanupError = error;
21729
+ }
21730
+ try {
21731
+ snapshot.teardown?.();
21732
+ } catch (error) {
21733
+ if (cleanupError === null) {
21734
+ cleanupError = error;
21735
+ }
21736
+ }
21737
+ if (!renderFailed && cleanupError) {
21738
+ throw cleanupError;
21739
+ }
19433
21740
  }
19434
21741
  }
19435
- get consoleMode() {
19436
- return this._useConsole ? "console-overlay" : "disabled";
19437
- }
19438
- set consoleMode(mode) {
19439
- this._useConsole = mode === "console-overlay";
19440
- if (this._useConsole) {
19441
- this.console.activate();
19442
- } else {
19443
- this.console.deactivate();
21742
+ getSnapshotWidth(value, fallback) {
21743
+ const rawValue = value ?? fallback;
21744
+ if (!Number.isFinite(rawValue)) {
21745
+ throw new Error("writeToScrollback produced a non-finite width");
19444
21746
  }
21747
+ return Math.min(Math.max(Math.trunc(rawValue), 1), Math.max(this.width, 1));
19445
21748
  }
19446
- get isRunning() {
19447
- return this._isRunning;
21749
+ getSnapshotHeight(value, fallback) {
21750
+ const rawValue = value ?? fallback;
21751
+ if (!Number.isFinite(rawValue)) {
21752
+ throw new Error("writeToScrollback produced a non-finite height");
21753
+ }
21754
+ return Math.max(Math.trunc(rawValue), 1);
19448
21755
  }
19449
- isIdleNow() {
19450
- return !this._isRunning && !this.rendering && !this.renderTimeout && !this.updateScheduled && !this.immediateRerenderRequested;
21756
+ getSnapshotRowWidths(snapshot, rowColumns) {
21757
+ const widths = [];
21758
+ const limit = Math.min(Math.max(Math.trunc(rowColumns), 0), snapshot.width);
21759
+ const chars = snapshot.buffers.char;
21760
+ for (let y = 0;y < snapshot.height; y += 1) {
21761
+ let x = limit;
21762
+ while (x > 0) {
21763
+ const cp = chars[y * snapshot.width + x - 1];
21764
+ if (cp === 0 || (cp & CHAR_FLAG_MASK) === CHAR_FLAG_CONTINUATION) {
21765
+ x -= 1;
21766
+ continue;
21767
+ }
21768
+ break;
21769
+ }
21770
+ widths.push(x);
21771
+ }
21772
+ return widths;
19451
21773
  }
19452
- resolveIdleIfNeeded() {
19453
- if (!this.isIdleNow())
21774
+ publishSplitTailColumns(columns) {
21775
+ if (columns <= 0) {
19454
21776
  return;
19455
- const resolvers = this.idleResolvers.splice(0);
19456
- for (const resolve4 of resolvers) {
19457
- resolve4();
19458
21777
  }
21778
+ const width = Math.max(this.width, 1);
21779
+ let tail = this.splitTailColumn;
21780
+ let remaining = columns;
21781
+ while (remaining > 0) {
21782
+ if (tail >= width) {
21783
+ tail = 0;
21784
+ }
21785
+ const step = Math.min(remaining, width - tail);
21786
+ tail += step;
21787
+ remaining -= step;
21788
+ if (remaining > 0 && tail >= width) {
21789
+ tail = 0;
21790
+ }
21791
+ }
21792
+ this.splitTailColumn = tail;
19459
21793
  }
19460
- idle() {
19461
- if (this._isDestroyed)
19462
- return Promise.resolve();
19463
- if (this.isIdleNow())
19464
- return Promise.resolve();
19465
- return new Promise((resolve4) => {
19466
- this.idleResolvers.push(resolve4);
19467
- });
21794
+ recordSplitCommit(commit) {
21795
+ if (commit.startOnNewLine && this.splitTailColumn > 0) {
21796
+ this.splitTailColumn = 0;
21797
+ }
21798
+ const rowWidths = this.getSnapshotRowWidths(commit.snapshot, commit.rowColumns);
21799
+ for (const [index, rowWidth] of rowWidths.entries()) {
21800
+ this.publishSplitTailColumns(rowWidth);
21801
+ if (index < rowWidths.length - 1 || commit.trailingNewline) {
21802
+ this.splitTailColumn = 0;
21803
+ }
21804
+ }
19468
21805
  }
19469
- get resolution() {
19470
- return this._resolution;
21806
+ enqueueRenderedScrollbackCommit(options) {
21807
+ if (this._screenMode !== "split-footer" || this._externalOutputMode !== "capture-stdout") {
21808
+ throw new Error('scrollback commit requires screenMode "split-footer" and externalOutputMode "capture-stdout"');
21809
+ }
21810
+ const rowColumns = Math.min(Math.max(Math.trunc(options.rowColumns ?? options.snapshot.width), 0), options.snapshot.width);
21811
+ this.enqueueSplitCommit({
21812
+ snapshot: options.snapshot,
21813
+ rowColumns,
21814
+ startOnNewLine: options.startOnNewLine ?? true,
21815
+ trailingNewline: options.trailingNewline ?? true
21816
+ });
21817
+ this.requestRender();
19471
21818
  }
19472
- get console() {
19473
- return this._console;
21819
+ enqueueSplitCommit(commit) {
21820
+ this.recordSplitCommit(commit);
21821
+ this.externalOutputQueue.writeSnapshot(commit);
21822
+ }
21823
+ createStdoutSnapshotCommit(line, trailingNewline) {
21824
+ const snapshotContext = new ScrollbackSnapshotRenderContext(this.width, 1, this.widthMethod);
21825
+ const maxWidth = Math.max(1, this.width);
21826
+ const lineCells = [...line];
21827
+ const rowColumns = Math.min(lineCells.length, maxWidth);
21828
+ const renderedLine = lineCells.slice(0, maxWidth).join("");
21829
+ const snapshotRoot = new RootRenderable(snapshotContext);
21830
+ const snapshotRenderable = new TextRenderable(snapshotContext, {
21831
+ id: "captured-stdout-snapshot",
21832
+ position: "absolute",
21833
+ left: 0,
21834
+ top: 0,
21835
+ width: Math.max(1, rowColumns),
21836
+ height: 1,
21837
+ content: renderedLine
21838
+ });
21839
+ const snapshotBuffer = OptimizedBuffer.create(Math.max(1, rowColumns), 1, this.widthMethod, {
21840
+ id: "captured-stdout-snapshot"
21841
+ });
21842
+ try {
21843
+ snapshotRoot.add(snapshotRenderable);
21844
+ snapshotRoot.render(snapshotBuffer, 0);
21845
+ return {
21846
+ snapshot: snapshotBuffer,
21847
+ rowColumns,
21848
+ startOnNewLine: false,
21849
+ trailingNewline
21850
+ };
21851
+ } catch (error) {
21852
+ snapshotBuffer.destroy();
21853
+ throw error;
21854
+ } finally {
21855
+ snapshotRoot.destroyRecursively();
21856
+ }
19474
21857
  }
19475
- get keyInput() {
19476
- return this._keyHandler;
21858
+ splitStdoutRows(text) {
21859
+ const rows = [];
21860
+ let current = "";
21861
+ for (const char of text) {
21862
+ if (char === "\r") {
21863
+ current = "";
21864
+ continue;
21865
+ }
21866
+ if (char === `
21867
+ `) {
21868
+ rows.push({ line: current, trailingNewline: true });
21869
+ current = "";
21870
+ continue;
21871
+ }
21872
+ current += char;
21873
+ }
21874
+ if (current.length > 0) {
21875
+ rows.push({ line: current, trailingNewline: false });
21876
+ }
21877
+ return rows;
19477
21878
  }
19478
- get _internalKeyInput() {
19479
- return this._keyHandler;
21879
+ createStdoutSnapshotCommits(text) {
21880
+ if (text.length === 0) {
21881
+ return [];
21882
+ }
21883
+ const commits = [];
21884
+ const chunkWidth = Math.max(1, this.width);
21885
+ for (const row of this.splitStdoutRows(text)) {
21886
+ const rowCells = [...row.line];
21887
+ if (rowCells.length === 0) {
21888
+ commits.push(this.createStdoutSnapshotCommit("", row.trailingNewline));
21889
+ continue;
21890
+ }
21891
+ let offset = 0;
21892
+ while (offset < rowCells.length) {
21893
+ const chunk = rowCells.slice(offset, offset + chunkWidth).join("");
21894
+ offset += chunkWidth;
21895
+ const isLastChunk = offset >= rowCells.length;
21896
+ commits.push(this.createStdoutSnapshotCommit(chunk, isLastChunk ? row.trailingNewline : false));
21897
+ }
21898
+ }
21899
+ return commits;
19480
21900
  }
19481
- get terminalWidth() {
19482
- return this._terminalWidth;
21901
+ flushPendingSplitCommits(forceFooterRepaint = false) {
21902
+ const commits = this.externalOutputQueue.claim(this.maxSplitCommitsPerFrame);
21903
+ let hasCommittedOutput = false;
21904
+ const lastCommitIndex = commits.length - 1;
21905
+ for (const [index, commit] of commits.entries()) {
21906
+ const forceCommit = forceFooterRepaint && index === lastCommitIndex;
21907
+ const beginFrame = index === 0;
21908
+ const finalizeFrame = index === lastCommitIndex;
21909
+ try {
21910
+ this.renderOffset = this.lib.commitSplitFooterSnapshot(this.rendererPtr, commit.snapshot, commit.rowColumns, commit.startOnNewLine, commit.trailingNewline, this.getSplitPinnedRenderOffset(), forceCommit, beginFrame, finalizeFrame);
21911
+ hasCommittedOutput = true;
21912
+ } finally {
21913
+ commit.snapshot.destroy();
21914
+ }
21915
+ }
21916
+ if (!hasCommittedOutput) {
21917
+ this.renderOffset = this.lib.repaintSplitFooter(this.rendererPtr, this.getSplitPinnedRenderOffset(), forceFooterRepaint);
21918
+ }
21919
+ this.pendingSplitFooterTransition = null;
21920
+ if (this.externalOutputQueue.size > 0) {
21921
+ this.requestRender();
21922
+ }
19483
21923
  }
19484
- get terminalHeight() {
19485
- return this._terminalHeight;
21924
+ interceptStdoutWrite = (chunk, encoding, callback) => {
21925
+ const resolvedCallback = typeof encoding === "function" ? encoding : callback;
21926
+ const resolvedEncoding = typeof encoding === "string" ? encoding : undefined;
21927
+ const text = typeof chunk === "string" ? chunk : chunk?.toString(resolvedEncoding) ?? "";
21928
+ if (this._externalOutputMode === "capture-stdout" && this._screenMode === "split-footer" && this._splitHeight > 0) {
21929
+ const commits = this.createStdoutSnapshotCommits(text);
21930
+ for (const commit of commits) {
21931
+ this.enqueueSplitCommit(commit);
21932
+ }
21933
+ if (commits.length > 0) {
21934
+ this.requestRender();
21935
+ }
21936
+ }
21937
+ if (typeof resolvedCallback === "function") {
21938
+ process.nextTick(resolvedCallback);
21939
+ }
21940
+ return true;
21941
+ };
21942
+ getSplitPinnedRenderOffset() {
21943
+ return this._screenMode === "split-footer" ? Math.max(this._terminalHeight - this._splitHeight, 0) : 0;
19486
21944
  }
19487
- get useThread() {
19488
- return this._useThread;
21945
+ getSplitCursorSeedRows() {
21946
+ const cursorState = this.lib.getCursorState(this.rendererPtr);
21947
+ const cursorRow = Number.isFinite(cursorState.y) ? Math.max(Math.trunc(cursorState.y), 1) : 1;
21948
+ return Math.min(cursorRow, Math.max(this._terminalHeight, 1));
19489
21949
  }
19490
- get targetFps() {
19491
- return this._targetFps;
21950
+ flushPendingSplitOutputBeforeTransition(forceFooterRepaint = false) {
21951
+ if (this._screenMode !== "split-footer" || this._splitHeight <= 0 || this._externalOutputMode !== "capture-stdout") {
21952
+ return;
21953
+ }
21954
+ if (this.externalOutputQueue.size === 0 && !forceFooterRepaint) {
21955
+ return;
21956
+ }
21957
+ this.flushPendingSplitCommits(forceFooterRepaint);
19492
21958
  }
19493
- set targetFps(targetFps) {
19494
- this._targetFps = targetFps;
19495
- this.targetFrameTime = 1000 / this._targetFps;
21959
+ resetSplitScrollback(seedRows = 0) {
21960
+ this.splitTailColumn = 0;
21961
+ this.renderOffset = this.lib.resetSplitScrollback(this.rendererPtr, seedRows, this.getSplitPinnedRenderOffset());
19496
21962
  }
19497
- get maxFps() {
19498
- return this._maxFps;
21963
+ syncSplitScrollback() {
21964
+ this.renderOffset = this.lib.syncSplitScrollback(this.rendererPtr, this.getSplitPinnedRenderOffset());
19499
21965
  }
19500
- set maxFps(maxFps) {
19501
- this._maxFps = maxFps;
19502
- this.minTargetFrameTime = 1000 / this._maxFps;
21966
+ clearPendingSplitFooterTransition() {
21967
+ if (this.pendingSplitFooterTransition === null) {
21968
+ return;
21969
+ }
21970
+ this.pendingSplitFooterTransition = null;
21971
+ this.lib.clearPendingSplitFooterTransition(this.rendererPtr);
19503
21972
  }
19504
- get useMouse() {
19505
- return this._useMouse;
21973
+ setPendingSplitFooterTransition(transition) {
21974
+ this.pendingSplitFooterTransition = transition;
21975
+ this.lib.setPendingSplitFooterTransition(this.rendererPtr, transition.mode === "viewport-scroll" ? 1 : 2, transition.sourceTopLine, transition.sourceHeight, transition.targetTopLine, transition.targetHeight);
19506
21976
  }
19507
- set useMouse(useMouse) {
19508
- if (this._useMouse === useMouse)
21977
+ syncSplitFooterState() {
21978
+ const splitActive = this._screenMode === "split-footer" && this._splitHeight > 0;
21979
+ if (!splitActive) {
21980
+ this.clearPendingSplitFooterTransition();
21981
+ this.splitTailColumn = 0;
21982
+ this.lib.resetSplitScrollback(this.rendererPtr, 0, 0);
21983
+ this.renderOffset = 0;
21984
+ this.lib.setRenderOffset(this.rendererPtr, this.renderOffset);
19509
21985
  return;
19510
- this._useMouse = useMouse;
19511
- if (useMouse) {
19512
- this.enableMouse();
21986
+ }
21987
+ if (this._externalOutputMode === "capture-stdout") {
21988
+ this.syncSplitScrollback();
19513
21989
  } else {
19514
- this.disableMouse();
21990
+ this.clearPendingSplitFooterTransition();
21991
+ this.splitTailColumn = 0;
21992
+ this.lib.resetSplitScrollback(this.rendererPtr, 0, 0);
21993
+ this.renderOffset = this.getSplitPinnedRenderOffset();
21994
+ this.lib.setRenderOffset(this.rendererPtr, this.renderOffset);
19515
21995
  }
19516
21996
  }
19517
- get screenMode() {
19518
- return this._screenMode;
19519
- }
19520
- set screenMode(mode) {
19521
- if (this.externalOutputMode === "capture-stdout" && mode !== "split-footer") {
19522
- throw new Error('externalOutputMode "capture-stdout" requires screenMode "split-footer"');
21997
+ clearStaleSplitSurfaceRows(previousTopLine, previousHeight, nextTopLine, nextHeight) {
21998
+ if (!this._terminalIsSetup || previousHeight <= 0 || this._terminalHeight <= 0) {
21999
+ return;
19523
22000
  }
19524
- this.applyScreenMode(mode);
19525
- }
19526
- get footerHeight() {
19527
- return this._footerHeight;
19528
- }
19529
- set footerHeight(footerHeight) {
19530
- const normalizedFooterHeight = normalizeFooterHeight(footerHeight);
19531
- if (normalizedFooterHeight === this._footerHeight) {
22001
+ const terminalBottom = this._terminalHeight;
22002
+ const previousStart = Math.max(1, previousTopLine);
22003
+ const previousEnd = Math.min(terminalBottom, previousTopLine + previousHeight - 1);
22004
+ if (previousEnd < previousStart) {
19532
22005
  return;
19533
22006
  }
19534
- this._footerHeight = normalizedFooterHeight;
19535
- if (this.screenMode === "split-footer") {
19536
- this.applyScreenMode("split-footer");
22007
+ const nextStart = Math.max(1, nextTopLine);
22008
+ const nextEnd = Math.min(terminalBottom, nextTopLine + Math.max(nextHeight, 0) - 1);
22009
+ let clear = "";
22010
+ for (let line = previousStart;line <= previousEnd; line += 1) {
22011
+ if (line >= nextStart && line <= nextEnd) {
22012
+ continue;
22013
+ }
22014
+ clear += `${ANSI.moveCursor(line, 1)}\x1B[2K`;
19537
22015
  }
19538
- }
19539
- get externalOutputMode() {
19540
- return this._externalOutputMode;
19541
- }
19542
- set externalOutputMode(mode) {
19543
- if (mode === "capture-stdout" && this.screenMode !== "split-footer") {
19544
- throw new Error('externalOutputMode "capture-stdout" requires screenMode "split-footer"');
22016
+ if (clear.length > 0) {
22017
+ this.writeOut(clear);
19545
22018
  }
19546
- this._externalOutputMode = mode;
19547
- this.stdout.write = mode === "capture-stdout" ? this.interceptStdoutWrite : this.realStdoutWrite;
19548
- }
19549
- get liveRequestCount() {
19550
- return this.liveRequestCounter;
19551
- }
19552
- get currentControlState() {
19553
- return this._controlState;
19554
- }
19555
- get capabilities() {
19556
- return this._capabilities;
19557
- }
19558
- get themeMode() {
19559
- return this._themeMode;
19560
- }
19561
- getDebugInputs() {
19562
- return [...this._debugInputs];
19563
- }
19564
- get useKittyKeyboard() {
19565
- return this.lib.getKittyKeyboardFlags(this.rendererPtr) > 0;
19566
- }
19567
- set useKittyKeyboard(use) {
19568
- const flags = use ? KITTY_FLAG_DISAMBIGUATE | KITTY_FLAG_ALTERNATE_KEYS : 0;
19569
- this.lib.setKittyKeyboardFlags(this.rendererPtr, flags);
19570
22019
  }
19571
- interceptStdoutWrite = (chunk, encoding, callback) => {
19572
- const text = chunk.toString();
19573
- capture.write("stdout", text);
19574
- if (this._splitHeight > 0) {
19575
- this.requestRender();
19576
- }
19577
- if (typeof callback === "function") {
19578
- process.nextTick(callback);
19579
- }
19580
- return true;
19581
- };
19582
22020
  applyScreenMode(screenMode, emitResize = true, requestRender = true) {
19583
22021
  const prevScreenMode = this._screenMode;
19584
22022
  const prevSplitHeight = this._splitHeight;
19585
- const nextSplitHeight = screenMode === "split-footer" ? this._footerHeight : 0;
22023
+ const nextGeometry = calculateRenderGeometry(screenMode, this._terminalWidth, this._terminalHeight, this._footerHeight);
22024
+ const nextSplitHeight = nextGeometry.effectiveFooterHeight;
19586
22025
  if (prevScreenMode === screenMode && prevSplitHeight === nextSplitHeight) {
19587
22026
  return;
19588
22027
  }
@@ -19590,10 +22029,24 @@ Captured output:
19590
22029
  const nextUseAlternateScreen = screenMode === "alternate-screen";
19591
22030
  const terminalScreenModeChanged = this._terminalIsSetup && prevUseAlternateScreen !== nextUseAlternateScreen;
19592
22031
  const leavingSplitFooter = prevSplitHeight > 0 && nextSplitHeight === 0;
22032
+ if (this._terminalIsSetup && prevSplitHeight > 0) {
22033
+ this.flushPendingSplitOutputBeforeTransition();
22034
+ }
22035
+ const previousSurfaceTopLine = this.renderOffset + 1;
22036
+ const previousPinnedRenderOffset = Math.max(this._terminalHeight - prevSplitHeight, 0);
22037
+ const splitWasSettled = prevSplitHeight === 0 || this.renderOffset >= previousPinnedRenderOffset;
22038
+ const shouldUseViewportScrollTransitions = this._externalOutputMode !== "capture-stdout" || splitWasSettled;
22039
+ const shouldDeferSplitFooterResizeTransition = this._terminalIsSetup && prevScreenMode === "split-footer" && screenMode === "split-footer" && this._externalOutputMode === "capture-stdout" && prevSplitHeight > 0 && nextSplitHeight > 0 && !terminalScreenModeChanged;
22040
+ const splitStartupSeedBlocksFirstNativeFrame = this.pendingSplitStartupCursorSeed && this.splitStartupSeedTimeoutId !== null;
22041
+ const splitTransitionSourceTopLine = this.pendingSplitFooterTransition?.sourceTopLine ?? previousSurfaceTopLine;
22042
+ const splitTransitionSourceHeight = this.pendingSplitFooterTransition?.sourceHeight ?? prevSplitHeight;
22043
+ const splitTransitionMode = this.pendingSplitFooterTransition?.mode ?? (splitWasSettled ? "viewport-scroll" : "clear-stale-rows");
19593
22044
  if (this._terminalIsSetup && leavingSplitFooter) {
19594
- this.flushStdoutCache(this._terminalHeight, true);
22045
+ this.clearPendingSplitFooterTransition();
22046
+ this.renderOffset = 0;
22047
+ this.lib.setRenderOffset(this.rendererPtr, 0);
19595
22048
  }
19596
- if (this._terminalIsSetup && !terminalScreenModeChanged) {
22049
+ if (this._terminalIsSetup && !terminalScreenModeChanged && shouldUseViewportScrollTransitions && !shouldDeferSplitFooterResizeTransition) {
19597
22050
  if (prevSplitHeight === 0 && nextSplitHeight > 0) {
19598
22051
  const freedLines = this._terminalHeight - nextSplitHeight;
19599
22052
  const scrollDown = ANSI.scrollDown(freedLines);
@@ -19608,18 +22061,39 @@ Captured output:
19608
22061
  this.writeOut(scrollUp);
19609
22062
  }
19610
22063
  }
19611
- if (prevSplitHeight === 0 && nextSplitHeight > 0) {
19612
- capture.on("write", this.captureCallback);
19613
- } else if (prevSplitHeight > 0 && nextSplitHeight === 0) {
19614
- capture.off("write", this.captureCallback);
19615
- }
19616
22064
  this._screenMode = screenMode;
19617
22065
  this._splitHeight = nextSplitHeight;
19618
- this.renderOffset = nextSplitHeight > 0 ? this._terminalHeight - nextSplitHeight : 0;
19619
- this.width = this._terminalWidth;
19620
- this.height = nextSplitHeight > 0 ? nextSplitHeight : this._terminalHeight;
19621
- this.lib.setRenderOffset(this.rendererPtr, this.renderOffset);
22066
+ this.width = nextGeometry.renderWidth;
22067
+ this.height = nextGeometry.renderHeight;
19622
22068
  this.lib.resizeRenderer(this.rendererPtr, this.width, this.height);
22069
+ if (this._screenMode === "split-footer" && this._externalOutputMode === "capture-stdout") {
22070
+ if (prevScreenMode !== "split-footer") {
22071
+ this.resetSplitScrollback(this.getSplitCursorSeedRows());
22072
+ } else {
22073
+ this.syncSplitScrollback();
22074
+ }
22075
+ if (shouldDeferSplitFooterResizeTransition) {
22076
+ if (splitStartupSeedBlocksFirstNativeFrame) {
22077
+ this.clearPendingSplitFooterTransition();
22078
+ } else {
22079
+ this.setPendingSplitFooterTransition({
22080
+ mode: splitTransitionMode,
22081
+ sourceTopLine: splitTransitionSourceTopLine,
22082
+ sourceHeight: splitTransitionSourceHeight,
22083
+ targetTopLine: this.renderOffset + 1,
22084
+ targetHeight: nextSplitHeight
22085
+ });
22086
+ }
22087
+ this.forceFullRepaintRequested = true;
22088
+ } else if (!shouldUseViewportScrollTransitions && prevSplitHeight > 0 && nextSplitHeight > 0) {
22089
+ this.clearPendingSplitFooterTransition();
22090
+ this.clearStaleSplitSurfaceRows(previousSurfaceTopLine, prevSplitHeight, this.renderOffset + 1, nextSplitHeight);
22091
+ } else {
22092
+ this.clearPendingSplitFooterTransition();
22093
+ }
22094
+ } else {
22095
+ this.syncSplitFooterState();
22096
+ }
19623
22097
  this.nextRenderBuffer = this.lib.getNextBuffer(this.rendererPtr);
19624
22098
  this.currentRenderBuffer = this.lib.getCurrentBuffer(this.rendererPtr);
19625
22099
  this._console.resize(this.width, this.height);
@@ -19639,12 +22113,18 @@ Captured output:
19639
22113
  }
19640
22114
  }
19641
22115
  flushStdoutCache(space, force = false) {
19642
- if (capture.size === 0 && !force)
22116
+ if (this.externalOutputQueue.size === 0 && !force)
19643
22117
  return false;
19644
- const output = capture.claimOutput();
19645
- const rendererStartLine = this._terminalHeight - this._splitHeight;
22118
+ const outputCommits = this.externalOutputQueue.claim();
22119
+ let output = "";
22120
+ for (const commit of outputCommits) {
22121
+ output += `[snapshot ${commit.snapshot.width}x${commit.snapshot.height}]
22122
+ `;
22123
+ commit.snapshot.destroy();
22124
+ }
22125
+ const rendererStartLine = this.renderOffset + 1;
19646
22126
  const flush = ANSI.moveCursorAndClear(rendererStartLine, 1);
19647
- const outputLine = this._terminalHeight - this._splitHeight;
22127
+ const outputLine = this.renderOffset + 1;
19648
22128
  const move = ANSI.moveCursor(outputLine, 1);
19649
22129
  let clear = "";
19650
22130
  if (space > 0) {
@@ -19686,9 +22166,11 @@ Captured output:
19686
22166
  if (this._terminalIsSetup)
19687
22167
  return;
19688
22168
  this._terminalIsSetup = true;
22169
+ const startupCursorCprActive = this._screenMode === "split-footer" && this._externalOutputMode === "capture-stdout";
19689
22170
  this.updateStdinParserProtocolContext({
19690
22171
  privateCapabilityRepliesActive: true,
19691
- explicitWidthCprActive: true
22172
+ explicitWidthCprActive: true,
22173
+ startupCursorCprActive
19692
22174
  });
19693
22175
  this.lib.setupTerminal(this.rendererPtr, this._screenMode === "alternate-screen");
19694
22176
  this._capabilities = this.lib.getTerminalCapabilities(this.rendererPtr);
@@ -19702,15 +22184,40 @@ Captured output:
19702
22184
  }
19703
22185
  this.capabilityTimeoutId = this.clock.setTimeout(() => {
19704
22186
  this.capabilityTimeoutId = null;
22187
+ this.pendingSplitStartupCursorSeed = false;
22188
+ if (this.splitStartupSeedTimeoutId !== null) {
22189
+ this.clock.clearTimeout(this.splitStartupSeedTimeoutId);
22190
+ this.splitStartupSeedTimeoutId = null;
22191
+ }
22192
+ if (this._screenMode === "split-footer" && this._externalOutputMode === "capture-stdout") {
22193
+ this.requestRender();
22194
+ }
19705
22195
  this.removeInputHandler(this.capabilityHandler);
19706
22196
  this.updateStdinParserProtocolContext({
19707
22197
  privateCapabilityRepliesActive: false,
19708
- explicitWidthCprActive: false
22198
+ explicitWidthCprActive: false,
22199
+ startupCursorCprActive: false
19709
22200
  }, true);
19710
22201
  }, 5000);
19711
22202
  if (this._useMouse) {
19712
22203
  this.enableMouse();
19713
22204
  }
22205
+ if (this._screenMode === "split-footer" && this._externalOutputMode === "capture-stdout") {
22206
+ this.pendingSplitStartupCursorSeed = true;
22207
+ if (this.splitStartupSeedTimeoutId !== null) {
22208
+ this.clock.clearTimeout(this.splitStartupSeedTimeoutId);
22209
+ }
22210
+ this.splitStartupSeedTimeoutId = this.clock.setTimeout(() => {
22211
+ this.splitStartupSeedTimeoutId = null;
22212
+ if (!this.pendingSplitStartupCursorSeed) {
22213
+ return;
22214
+ }
22215
+ this.updateStdinParserProtocolContext({ startupCursorCprActive: false });
22216
+ if (this._screenMode === "split-footer" && this._externalOutputMode === "capture-stdout") {
22217
+ this.requestRender();
22218
+ }
22219
+ }, 120);
22220
+ }
19714
22221
  this.queryPixelResolution();
19715
22222
  }
19716
22223
  stdinListener = ((chunk) => {
@@ -19746,14 +22253,32 @@ Captured output:
19746
22253
  this.oscSubscribers.delete(handler);
19747
22254
  };
19748
22255
  }
19749
- capabilityHandler = ((sequence) => {
19750
- if (isCapabilityResponse(sequence)) {
19751
- this.lib.processCapabilityResponse(this.rendererPtr, sequence);
19752
- this._capabilities = this.lib.getTerminalCapabilities(this.rendererPtr);
19753
- this.emit("capabilities" /* CAPABILITIES */, this._capabilities);
19754
- return true;
22256
+ processCapabilitySequence(sequence, hasCursorReport) {
22257
+ const hasStandardCapabilitySignature = isCapabilityResponse(sequence);
22258
+ const shouldProcessAsCapability = hasStandardCapabilitySignature || hasCursorReport && this.capabilityTimeoutId !== null;
22259
+ if (!shouldProcessAsCapability) {
22260
+ return false;
19755
22261
  }
19756
- return false;
22262
+ this.lib.processCapabilityResponse(this.rendererPtr, sequence);
22263
+ this._capabilities = this.lib.getTerminalCapabilities(this.rendererPtr);
22264
+ this.emit("capabilities" /* CAPABILITIES */, this._capabilities);
22265
+ const hadPendingSplitStartupCursorSeed = this.pendingSplitStartupCursorSeed;
22266
+ if (hadPendingSplitStartupCursorSeed && hasCursorReport && this._screenMode === "split-footer" && this._externalOutputMode === "capture-stdout") {
22267
+ this.resetSplitScrollback(this.getSplitCursorSeedRows());
22268
+ this.clearPendingSplitFooterTransition();
22269
+ this.pendingSplitStartupCursorSeed = false;
22270
+ this.updateStdinParserProtocolContext({ startupCursorCprActive: false });
22271
+ if (this.splitStartupSeedTimeoutId !== null) {
22272
+ this.clock.clearTimeout(this.splitStartupSeedTimeoutId);
22273
+ this.splitStartupSeedTimeoutId = null;
22274
+ }
22275
+ this.requestRender();
22276
+ }
22277
+ const consumeStartupCursorReport = hadPendingSplitStartupCursorSeed && hasCursorReport && this.splitStartupSeedTimeoutId !== null;
22278
+ return hasStandardCapabilitySignature || consumeStartupCursorReport;
22279
+ }
22280
+ capabilityHandler = ((sequence) => {
22281
+ return this.processCapabilitySequence(sequence, false);
19757
22282
  }).bind(this);
19758
22283
  focusHandler = ((sequence) => {
19759
22284
  if (sequence === "\x1B[I") {
@@ -19779,21 +22304,50 @@ Captured output:
19779
22304
  }).bind(this);
19780
22305
  themeModeHandler = ((sequence) => {
19781
22306
  if (sequence === "\x1B[?997;1n") {
19782
- if (this._themeMode !== "dark") {
19783
- this._themeMode = "dark";
19784
- this.emit("theme_mode" /* THEME_MODE */, "dark");
19785
- }
22307
+ this.applyThemeMode("dark", "csi");
22308
+ this._themeFallbackPending = false;
19786
22309
  return true;
19787
22310
  }
19788
22311
  if (sequence === "\x1B[?997;2n") {
19789
- if (this._themeMode !== "light") {
19790
- this._themeMode = "light";
19791
- this.emit("theme_mode" /* THEME_MODE */, "light");
22312
+ this.applyThemeMode("light", "csi");
22313
+ this._themeFallbackPending = false;
22314
+ return true;
22315
+ }
22316
+ let handledOscThemeResponse = false;
22317
+ let match;
22318
+ OSC_THEME_RESPONSE.lastIndex = 0;
22319
+ while (match = OSC_THEME_RESPONSE.exec(sequence)) {
22320
+ handledOscThemeResponse = true;
22321
+ const color = oscThemeColorToHex(match[2], match[3], match[4], match[5]);
22322
+ if (match[1] === "10") {
22323
+ this._themeOscForeground = color;
22324
+ } else {
22325
+ this._themeOscBackground = color;
19792
22326
  }
22327
+ }
22328
+ if (!handledOscThemeResponse) {
22329
+ return false;
22330
+ }
22331
+ if (!this._themeFallbackPending) {
19793
22332
  return true;
19794
22333
  }
19795
- return false;
22334
+ if (this._themeOscForeground && this._themeOscBackground) {
22335
+ this.applyThemeMode(inferThemeModeFromBackgroundColor(this._themeOscBackground), "osc");
22336
+ this._themeFallbackPending = false;
22337
+ }
22338
+ return true;
19796
22339
  }).bind(this);
22340
+ applyThemeMode(mode, source) {
22341
+ if (source === "osc" && this._themeModeSource === "csi") {
22342
+ return;
22343
+ }
22344
+ const changed = this._themeMode !== mode;
22345
+ this._themeMode = mode;
22346
+ this._themeModeSource = source;
22347
+ if (changed) {
22348
+ this.emit("theme_mode" /* THEME_MODE */, mode);
22349
+ }
22350
+ }
19797
22351
  dispatchSequenceHandlers(sequence) {
19798
22352
  if (this._debugModeEnabled) {
19799
22353
  this._debugInputs.push({
@@ -19838,6 +22392,9 @@ Captured output:
19838
22392
  subscriber(event.sequence);
19839
22393
  }
19840
22394
  }
22395
+ if (event.protocol === "cpr" && this.processCapabilitySequence(event.sequence, true)) {
22396
+ return;
22397
+ }
19841
22398
  this.dispatchSequenceHandlers(event.sequence);
19842
22399
  return;
19843
22400
  }
@@ -20124,28 +22681,45 @@ Captured output:
20124
22681
  processResize(width, height) {
20125
22682
  if (width === this._terminalWidth && height === this._terminalHeight)
20126
22683
  return;
22684
+ if (this._terminalIsSetup && this._controlState !== "explicit_suspended" /* EXPLICIT_SUSPENDED */) {
22685
+ this.flushPendingSplitOutputBeforeTransition();
22686
+ }
22687
+ const pendingSplitFooterTransition = this.pendingSplitFooterTransition;
22688
+ const previousGeometry = calculateRenderGeometry(this._screenMode, this._terminalWidth, this._terminalHeight, this._footerHeight);
20127
22689
  const prevWidth = this._terminalWidth;
22690
+ const previousTerminalHeight = this._terminalHeight;
22691
+ const visiblePreviousSplitHeight = pendingSplitFooterTransition?.sourceHeight ?? previousGeometry.effectiveFooterHeight;
20128
22692
  this._terminalWidth = width;
20129
22693
  this._terminalHeight = height;
20130
22694
  this.queryPixelResolution();
20131
22695
  this.setCapturedRenderable(undefined);
20132
22696
  this.stdinParser?.resetMouseState();
20133
- if (this._splitHeight > 0) {
20134
- if (width < prevWidth) {
20135
- const start = this._terminalHeight - this._splitHeight * 2;
20136
- const flush = ANSI.moveCursorAndClear(start, 1);
22697
+ const nextGeometry = calculateRenderGeometry(this._screenMode, this._terminalWidth, this._terminalHeight, this._footerHeight);
22698
+ const splitFooterActive = this._screenMode === "split-footer";
22699
+ if (splitFooterActive) {
22700
+ let clearStart = null;
22701
+ if (width < prevWidth && visiblePreviousSplitHeight > 0) {
22702
+ clearStart = Math.max(previousTerminalHeight - visiblePreviousSplitHeight * 2, 1);
22703
+ }
22704
+ if (pendingSplitFooterTransition !== null) {
22705
+ clearStart = clearStart === null ? pendingSplitFooterTransition.sourceTopLine : Math.min(clearStart, pendingSplitFooterTransition.sourceTopLine);
22706
+ }
22707
+ if (clearStart !== null) {
22708
+ const flush = ANSI.moveCursorAndClear(clearStart, 1);
20137
22709
  this.writeOut(flush);
20138
22710
  }
20139
- this.renderOffset = height - this._splitHeight;
20140
- this.width = width;
20141
- this.height = this._splitHeight;
20142
22711
  this.currentRenderBuffer.clear(this.backgroundColor);
20143
- this.lib.setRenderOffset(this.rendererPtr, this.renderOffset);
20144
- } else {
20145
- this.width = width;
20146
- this.height = height;
20147
22712
  }
22713
+ this.clearPendingSplitFooterTransition();
22714
+ this._splitHeight = nextGeometry.effectiveFooterHeight;
22715
+ this.width = nextGeometry.renderWidth;
22716
+ this.height = nextGeometry.renderHeight;
20148
22717
  this.lib.resizeRenderer(this.rendererPtr, this.width, this.height);
22718
+ if (this._screenMode === "split-footer" && this._externalOutputMode === "capture-stdout") {
22719
+ this.syncSplitScrollback();
22720
+ } else {
22721
+ this.syncSplitFooterState();
22722
+ }
20149
22723
  this.nextRenderBuffer = this.lib.getNextBuffer(this.rendererPtr);
20150
22724
  this.currentRenderBuffer = this.lib.getCurrentBuffer(this.rendererPtr);
20151
22725
  this._console.resize(this.width, this.height);
@@ -20293,6 +22867,9 @@ Captured output:
20293
22867
  this._previousControlState = this._controlState;
20294
22868
  this._controlState = "explicit_suspended" /* EXPLICIT_SUSPENDED */;
20295
22869
  this.internalPause();
22870
+ if (this._terminalIsSetup) {
22871
+ this.flushPendingSplitOutputBeforeTransition(true);
22872
+ }
20296
22873
  this._suspendedMouseEnabled = this._useMouse;
20297
22874
  this.disableMouse();
20298
22875
  this.removeExitListeners();
@@ -20300,7 +22877,8 @@ Captured output:
20300
22877
  this.updateStdinParserProtocolContext({
20301
22878
  privateCapabilityRepliesActive: false,
20302
22879
  pixelResolutionQueryActive: false,
20303
- explicitWidthCprActive: false
22880
+ explicitWidthCprActive: false,
22881
+ startupCursorCprActive: false
20304
22882
  });
20305
22883
  this.stdinParser?.reset();
20306
22884
  this.stdin.removeListener("data", this.stdinListener);
@@ -20319,6 +22897,9 @@ Captured output:
20319
22897
  this.stdin.resume();
20320
22898
  this.addExitListeners();
20321
22899
  this.lib.resumeRenderer(this.rendererPtr);
22900
+ if (this._screenMode === "split-footer" && this._splitHeight > 0) {
22901
+ this.syncSplitFooterState();
22902
+ }
20322
22903
  if (this._suspendedMouseEnabled) {
20323
22904
  this.enableMouse();
20324
22905
  }
@@ -20380,7 +22961,6 @@ Captured output:
20380
22961
  process.removeListener("unhandledRejection", this.handleError);
20381
22962
  process.removeListener("warning", this.warningHandler);
20382
22963
  process.removeListener("beforeExit", this.exitHandler);
20383
- capture.removeListener("write", this.captureCallback);
20384
22964
  this.removeExitListeners();
20385
22965
  if (this.resizeTimeoutId !== null) {
20386
22966
  this.clock.clearTimeout(this.resizeTimeoutId);
@@ -20390,6 +22970,10 @@ Captured output:
20390
22970
  this.clock.clearTimeout(this.capabilityTimeoutId);
20391
22971
  this.capabilityTimeoutId = null;
20392
22972
  }
22973
+ if (this.splitStartupSeedTimeoutId !== null) {
22974
+ this.clock.clearTimeout(this.splitStartupSeedTimeoutId);
22975
+ this.splitStartupSeedTimeoutId = null;
22976
+ }
20393
22977
  if (this.memorySnapshotTimer) {
20394
22978
  this.clock.clearInterval(this.memorySnapshotTimer);
20395
22979
  this.memorySnapshotTimer = null;
@@ -20403,7 +22987,8 @@ Captured output:
20403
22987
  this.updateStdinParserProtocolContext({
20404
22988
  privateCapabilityRepliesActive: false,
20405
22989
  pixelResolutionQueryActive: false,
20406
- explicitWidthCprActive: false
22990
+ explicitWidthCprActive: false,
22991
+ startupCursorCprActive: false
20407
22992
  }, true);
20408
22993
  this._useMouse = false;
20409
22994
  this.setCapturedRenderable(undefined);
@@ -20442,6 +23027,16 @@ Captured output:
20442
23027
  this.stdinParser = null;
20443
23028
  this.oscSubscribers.clear();
20444
23029
  this._console.destroy();
23030
+ if (this._splitHeight > 0 && this._terminalIsSetup && this._controlState !== "explicit_suspended" /* EXPLICIT_SUSPENDED */) {
23031
+ this.flushPendingSplitOutputBeforeTransition(true);
23032
+ this.renderOffset = 0;
23033
+ if (this.clearOnShutdown) {
23034
+ this.lib.setRenderOffset(this.rendererPtr, 0);
23035
+ }
23036
+ }
23037
+ this._externalOutputMode = "passthrough";
23038
+ this.stdout.write = this.realStdoutWrite;
23039
+ this.externalOutputQueue.clear();
20445
23040
  this.lib.destroyRenderer(this.rendererPtr);
20446
23041
  rendererTracker.removeRenderer(this);
20447
23042
  if (this._onDestroy) {
@@ -20550,12 +23145,21 @@ Captured output:
20550
23145
  console.error("Rendering called concurrently");
20551
23146
  throw new Error("Rendering called concurrently");
20552
23147
  }
20553
- let force = false;
20554
- if (this._splitHeight > 0) {
20555
- force = this.flushStdoutCache(this._splitHeight);
20556
- }
20557
23148
  this.renderingNative = true;
20558
- this.lib.render(this.rendererPtr, force);
23149
+ if (this.pendingSplitStartupCursorSeed && this.splitStartupSeedTimeoutId !== null && this._splitHeight > 0 && this._externalOutputMode === "capture-stdout") {
23150
+ this.renderingNative = false;
23151
+ return;
23152
+ }
23153
+ if (this._splitHeight > 0 && this._externalOutputMode === "capture-stdout") {
23154
+ const forceSplitRepaint = this.forceFullRepaintRequested;
23155
+ this.forceFullRepaintRequested = false;
23156
+ this.flushPendingSplitCommits(forceSplitRepaint);
23157
+ this.pendingSplitFooterTransition = null;
23158
+ } else {
23159
+ this.forceFullRepaintRequested = false;
23160
+ this.pendingSplitFooterTransition = null;
23161
+ this.lib.render(this.rendererPtr, false);
23162
+ }
20559
23163
  this.renderingNative = false;
20560
23164
  }
20561
23165
  collectStatSample(frameTime) {
@@ -20737,7 +23341,7 @@ Captured output:
20737
23341
  }
20738
23342
  }
20739
23343
 
20740
- export { __toESM, __commonJS, __export, __require, Edge, Gutter, MeasureMode, exports_src, isValidBorderStyle, parseBorderStyle, BorderChars, getBorderFromSides, getBorderSides, borderCharsToArray, BorderCharArrays, KeyEvent, PasteEvent, KeyHandler, InternalKeyHandler, RGBA, hexToRgb, rgbToHex, hsvToRgb, parseColor, fonts, measureText, getCharacterPositions, coordinateToCharacterIndex, renderFontToFrameBuffer, TextAttributes, ATTRIBUTE_BASE_BITS, ATTRIBUTE_BASE_MASK, getBaseAttributes, DebugOverlayCorner, TargetChannel, createTextAttributes, attributesWithLink, getLinkId, visualizeRenderableTree, isStyledText, StyledText, stringToStyledText, black, red, green, yellow, blue, magenta, cyan, white, brightBlack, brightRed, brightGreen, brightYellow, brightBlue, brightMagenta, brightCyan, brightWhite, bgBlack, bgRed, bgGreen, bgYellow, bgBlue, bgMagenta, bgCyan, bgWhite, bold, italic, underline, strikethrough, dim, reverse, blink, fg, bg, link, t, hastToStyledText, SystemClock, nonAlphanumericKeys, parseKeypress, LinearScrollAccel, MacOSScrollAccel, parseAlign, parseAlignItems, parseBoxSizing, parseDimension, parseDirection, parseDisplay, parseEdge, parseFlexDirection, parseGutter, parseJustify, parseLogLevel, parseMeasureMode, parseOverflow, parsePositionType, parseUnit, parseWrap, MouseParser, Selection, convertGlobalToLocalSelection, ASCIIFontSelectionHelper, envRegistry, registerEnvVar, clearEnvCache, generateEnvMarkdown, generateEnvColored, env, StdinParser, treeSitterToTextChunks, treeSitterToStyledText, addDefaultParsers, TreeSitterClient, DataPathsManager, getDataPaths, extensionToFiletype, basenameToFiletype, extToFiletype, pathToFiletype, infoStringToFiletype, main, getTreeSitterClient, ExtmarksController, createExtmarksController, TerminalPalette, createTerminalPalette, decodePasteBytes, stripAnsiSequences, detectLinks, TextBuffer, SpanInfoStruct, LogLevel2 as LogLevel, setRenderLibPath, resolveRenderLib, OptimizedBuffer, h, isVNode, maybeMakeRenderable, wrapWithDelegates, instantiate, delegate, isValidPercentage, LayoutEvents, RenderableEvents, isRenderable, BaseRenderable, Renderable, RootRenderable, EditBuffer, EditorView, ANSI, defaultKeyAliases, mergeKeyAliases, mergeKeyBindings, getKeyBindingAction, buildKeyBindingsMap, capture, ConsolePosition, TerminalConsole, getObjectsInViewport, EditBufferRenderableEvents, isEditBufferRenderable, EditBufferRenderable, buildKittyKeyboardFlags, MouseEvent, MouseButton, createCliRenderer, CliRenderEvents, RendererControlState, CliRenderer };
23344
+ export { __toESM, __commonJS, __export, __require, MeasureMode, exports_src, isValidBorderStyle, parseBorderStyle, BorderChars, getBorderFromSides, getBorderSides, borderCharsToArray, BorderCharArrays, KeyEvent, PasteEvent, KeyHandler, InternalKeyHandler, RGBA, hexToRgb, rgbToHex, hsvToRgb, parseColor, fonts, measureText, getCharacterPositions, coordinateToCharacterIndex, renderFontToFrameBuffer, TextAttributes, ATTRIBUTE_BASE_BITS, ATTRIBUTE_BASE_MASK, getBaseAttributes, DebugOverlayCorner, TargetChannel, createTextAttributes, attributesWithLink, getLinkId, visualizeRenderableTree, isStyledText, StyledText, stringToStyledText, black, red, green, yellow, blue, magenta, cyan, white, brightBlack, brightRed, brightGreen, brightYellow, brightBlue, brightMagenta, brightCyan, brightWhite, bgBlack, bgRed, bgGreen, bgYellow, bgBlue, bgMagenta, bgCyan, bgWhite, bold, italic, underline, strikethrough, dim, reverse, blink, fg, bg, link, t, hastToStyledText, SystemClock, nonAlphanumericKeys, parseKeypress, LinearScrollAccel, MacOSScrollAccel, parseAlign, parseAlignItems, parseBoxSizing, parseDimension, parseDirection, parseDisplay, parseEdge, parseFlexDirection, parseGutter, parseJustify, parseLogLevel, parseMeasureMode, parseOverflow, parsePositionType, parseUnit, parseWrap, MouseParser, Selection, convertGlobalToLocalSelection, ASCIIFontSelectionHelper, envRegistry, registerEnvVar, clearEnvCache, generateEnvMarkdown, generateEnvColored, env, StdinParser, treeSitterToTextChunks, treeSitterToStyledText, addDefaultParsers, TreeSitterClient, DataPathsManager, getDataPaths, extensionToFiletype, basenameToFiletype, extToFiletype, pathToFiletype, infoStringToFiletype, main, getTreeSitterClient, ExtmarksController, createExtmarksController, TerminalPalette, createTerminalPalette, decodePasteBytes, stripAnsiSequences, detectLinks, TextBuffer, SpanInfoStruct, LogLevel2 as LogLevel, setRenderLibPath, resolveRenderLib, OptimizedBuffer, h, isVNode, maybeMakeRenderable, wrapWithDelegates, instantiate, delegate, LayoutEvents, RenderableEvents, isRenderable, BaseRenderable, Renderable, RootRenderable, TextBufferView, EditBuffer, EditorView, convertThemeToStyles, SyntaxStyle, ANSI, BoxRenderable, TextBufferRenderable, CodeRenderable, isTextNodeRenderable, TextNodeRenderable, RootTextNodeRenderable, TextRenderable, defaultKeyAliases, mergeKeyAliases, mergeKeyBindings, getKeyBindingAction, buildKeyBindingsMap, capture, ConsolePosition, TerminalConsole, getObjectsInViewport, EditBufferRenderableEvents, isEditBufferRenderable, EditBufferRenderable, calculateRenderGeometry, buildKittyKeyboardFlags, MouseEvent, MouseButton, createCliRenderer, CliRenderEvents, RendererControlState, CliRenderer };
20741
23345
 
20742
- //# debugId=1B3B44D6B234F20A64756E2164756E21
20743
- //# sourceMappingURL=index-zkcykvp8.js.map
23346
+ //# debugId=F970487203C0DBFC64756E2164756E21
23347
+ //# sourceMappingURL=index-qpcsqve6.js.map