@fairyhunter13/opentui-core 0.1.129 → 0.1.131

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
  }
@@ -16060,6 +16133,139 @@ function delegate(mapping, vnode) {
16060
16133
  return vnode;
16061
16134
  }
16062
16135
 
16136
+ // src/text-buffer-view.ts
16137
+ class TextBufferView {
16138
+ lib;
16139
+ viewPtr;
16140
+ textBuffer;
16141
+ _destroyed = false;
16142
+ constructor(lib, ptr5, textBuffer) {
16143
+ this.lib = lib;
16144
+ this.viewPtr = ptr5;
16145
+ this.textBuffer = textBuffer;
16146
+ }
16147
+ static create(textBuffer) {
16148
+ const lib = resolveRenderLib();
16149
+ const viewPtr = lib.createTextBufferView(textBuffer.ptr);
16150
+ return new TextBufferView(lib, viewPtr, textBuffer);
16151
+ }
16152
+ guard() {
16153
+ if (this._destroyed)
16154
+ throw new Error("TextBufferView is destroyed");
16155
+ }
16156
+ get ptr() {
16157
+ this.guard();
16158
+ return this.viewPtr;
16159
+ }
16160
+ setSelection(start, end, bgColor, fgColor) {
16161
+ this.guard();
16162
+ this.lib.textBufferViewSetSelection(this.viewPtr, start, end, bgColor || null, fgColor || null);
16163
+ }
16164
+ updateSelection(end, bgColor, fgColor) {
16165
+ this.guard();
16166
+ this.lib.textBufferViewUpdateSelection(this.viewPtr, end, bgColor || null, fgColor || null);
16167
+ }
16168
+ resetSelection() {
16169
+ this.guard();
16170
+ this.lib.textBufferViewResetSelection(this.viewPtr);
16171
+ }
16172
+ getSelection() {
16173
+ this.guard();
16174
+ return this.lib.textBufferViewGetSelection(this.viewPtr);
16175
+ }
16176
+ hasSelection() {
16177
+ this.guard();
16178
+ return this.getSelection() !== null;
16179
+ }
16180
+ setLocalSelection(anchorX, anchorY, focusX, focusY, bgColor, fgColor) {
16181
+ this.guard();
16182
+ return this.lib.textBufferViewSetLocalSelection(this.viewPtr, anchorX, anchorY, focusX, focusY, bgColor || null, fgColor || null);
16183
+ }
16184
+ updateLocalSelection(anchorX, anchorY, focusX, focusY, bgColor, fgColor) {
16185
+ this.guard();
16186
+ return this.lib.textBufferViewUpdateLocalSelection(this.viewPtr, anchorX, anchorY, focusX, focusY, bgColor || null, fgColor || null);
16187
+ }
16188
+ resetLocalSelection() {
16189
+ this.guard();
16190
+ this.lib.textBufferViewResetLocalSelection(this.viewPtr);
16191
+ }
16192
+ setWrapWidth(width) {
16193
+ this.guard();
16194
+ this.lib.textBufferViewSetWrapWidth(this.viewPtr, width ?? 0);
16195
+ }
16196
+ setWrapMode(mode) {
16197
+ this.guard();
16198
+ this.lib.textBufferViewSetWrapMode(this.viewPtr, mode);
16199
+ }
16200
+ setFirstLineOffset(offset) {
16201
+ this.guard();
16202
+ this.lib.textBufferViewSetFirstLineOffset(this.viewPtr, offset);
16203
+ }
16204
+ setViewportSize(width, height) {
16205
+ this.guard();
16206
+ this.lib.textBufferViewSetViewportSize(this.viewPtr, width, height);
16207
+ }
16208
+ setViewport(x, y, width, height) {
16209
+ this.guard();
16210
+ this.lib.textBufferViewSetViewport(this.viewPtr, x, y, width, height);
16211
+ }
16212
+ get lineInfo() {
16213
+ this.guard();
16214
+ return this.lib.textBufferViewGetLineInfo(this.viewPtr);
16215
+ }
16216
+ get logicalLineInfo() {
16217
+ this.guard();
16218
+ return this.lib.textBufferViewGetLogicalLineInfo(this.viewPtr);
16219
+ }
16220
+ getSelectedText() {
16221
+ this.guard();
16222
+ const byteSize = this.textBuffer.byteSize;
16223
+ if (byteSize === 0)
16224
+ return "";
16225
+ const selectedBytes = this.lib.textBufferViewGetSelectedTextBytes(this.viewPtr, byteSize);
16226
+ if (!selectedBytes)
16227
+ return "";
16228
+ return this.lib.decoder.decode(selectedBytes);
16229
+ }
16230
+ getPlainText() {
16231
+ this.guard();
16232
+ const byteSize = this.textBuffer.byteSize;
16233
+ if (byteSize === 0)
16234
+ return "";
16235
+ const plainBytes = this.lib.textBufferViewGetPlainTextBytes(this.viewPtr, byteSize);
16236
+ if (!plainBytes)
16237
+ return "";
16238
+ return this.lib.decoder.decode(plainBytes);
16239
+ }
16240
+ setTabIndicator(indicator) {
16241
+ this.guard();
16242
+ const codePoint = typeof indicator === "string" ? indicator.codePointAt(0) ?? 0 : indicator;
16243
+ this.lib.textBufferViewSetTabIndicator(this.viewPtr, codePoint);
16244
+ }
16245
+ setTabIndicatorColor(color) {
16246
+ this.guard();
16247
+ this.lib.textBufferViewSetTabIndicatorColor(this.viewPtr, color);
16248
+ }
16249
+ setTruncate(truncate) {
16250
+ this.guard();
16251
+ this.lib.textBufferViewSetTruncate(this.viewPtr, truncate);
16252
+ }
16253
+ measureForDimensions(width, height) {
16254
+ this.guard();
16255
+ return this.lib.textBufferViewMeasureForDimensions(this.viewPtr, width, height);
16256
+ }
16257
+ getVirtualLineCount() {
16258
+ this.guard();
16259
+ return this.lib.textBufferViewGetVirtualLineCount(this.viewPtr);
16260
+ }
16261
+ destroy() {
16262
+ if (this._destroyed)
16263
+ return;
16264
+ this._destroyed = true;
16265
+ this.lib.destroyTextBufferView(this.viewPtr);
16266
+ }
16267
+ }
16268
+
16063
16269
  // src/edit-buffer.ts
16064
16270
  import { EventEmitter as EventEmitter6 } from "events";
16065
16271
 
@@ -16564,113 +16770,1542 @@ class EditorView {
16564
16770
  }
16565
16771
  }
16566
16772
 
16567
- // src/console.ts
16568
- import { EventEmitter as EventEmitter8 } from "events";
16569
- import { Console } from "console";
16570
- import fs from "fs";
16571
- import path5 from "path";
16572
- import util2 from "util";
16573
-
16574
- // src/lib/output.capture.ts
16575
- import { Writable } from "stream";
16576
- import { EventEmitter as EventEmitter7 } from "events";
16773
+ // src/syntax-style.ts
16774
+ function convertThemeToStyles(theme) {
16775
+ const flatStyles = {};
16776
+ for (const tokenStyle of theme) {
16777
+ const styleDefinition = {};
16778
+ if (tokenStyle.style.foreground) {
16779
+ styleDefinition.fg = parseColor(tokenStyle.style.foreground);
16780
+ }
16781
+ if (tokenStyle.style.background) {
16782
+ styleDefinition.bg = parseColor(tokenStyle.style.background);
16783
+ }
16784
+ if (tokenStyle.style.bold !== undefined) {
16785
+ styleDefinition.bold = tokenStyle.style.bold;
16786
+ }
16787
+ if (tokenStyle.style.italic !== undefined) {
16788
+ styleDefinition.italic = tokenStyle.style.italic;
16789
+ }
16790
+ if (tokenStyle.style.underline !== undefined) {
16791
+ styleDefinition.underline = tokenStyle.style.underline;
16792
+ }
16793
+ if (tokenStyle.style.dim !== undefined) {
16794
+ styleDefinition.dim = tokenStyle.style.dim;
16795
+ }
16796
+ for (const scope of tokenStyle.scope) {
16797
+ flatStyles[scope] = styleDefinition;
16798
+ }
16799
+ }
16800
+ return flatStyles;
16801
+ }
16577
16802
 
16578
- class Capture extends EventEmitter7 {
16579
- output = [];
16580
- constructor() {
16581
- super();
16803
+ class SyntaxStyle {
16804
+ lib;
16805
+ stylePtr;
16806
+ _destroyed = false;
16807
+ nameCache = new Map;
16808
+ styleDefs = new Map;
16809
+ mergedCache = new Map;
16810
+ constructor(lib, ptr5) {
16811
+ this.lib = lib;
16812
+ this.stylePtr = ptr5;
16582
16813
  }
16583
- get size() {
16584
- return this.output.length;
16814
+ static create() {
16815
+ const lib = resolveRenderLib();
16816
+ const ptr5 = lib.createSyntaxStyle();
16817
+ return new SyntaxStyle(lib, ptr5);
16585
16818
  }
16586
- write(stream, data) {
16587
- this.output.push({ stream, output: data });
16588
- this.emit("write", stream, data);
16819
+ static fromTheme(theme) {
16820
+ const style = SyntaxStyle.create();
16821
+ const flatStyles = convertThemeToStyles(theme);
16822
+ for (const [name, styleDef] of Object.entries(flatStyles)) {
16823
+ style.registerStyle(name, styleDef);
16824
+ }
16825
+ return style;
16589
16826
  }
16590
- claimOutput() {
16591
- const output = this.output.map((o) => o.output).join("");
16592
- this.clear();
16593
- return output;
16827
+ static fromStyles(styles) {
16828
+ const style = SyntaxStyle.create();
16829
+ for (const [name, styleDef] of Object.entries(styles)) {
16830
+ style.registerStyle(name, styleDef);
16831
+ }
16832
+ return style;
16594
16833
  }
16595
- clear() {
16596
- this.output = [];
16834
+ guard() {
16835
+ if (this._destroyed)
16836
+ throw new Error("NativeSyntaxStyle is destroyed");
16597
16837
  }
16598
- }
16599
-
16600
- class CapturedWritableStream extends Writable {
16601
- stream;
16602
- capture;
16603
- isTTY = true;
16604
- columns = process.stdout.columns || 80;
16605
- rows = process.stdout.rows || 24;
16606
- constructor(stream, capture) {
16607
- super();
16608
- this.stream = stream;
16609
- this.capture = capture;
16838
+ registerStyle(name, style) {
16839
+ this.guard();
16840
+ const attributes = createTextAttributes({
16841
+ bold: style.bold,
16842
+ italic: style.italic,
16843
+ underline: style.underline,
16844
+ dim: style.dim
16845
+ });
16846
+ const id = this.lib.syntaxStyleRegister(this.stylePtr, name, style.fg || null, style.bg || null, attributes);
16847
+ this.nameCache.set(name, id);
16848
+ this.styleDefs.set(name, style);
16849
+ return id;
16610
16850
  }
16611
- _write(chunk, encoding, callback) {
16612
- const data = chunk.toString();
16613
- this.capture.write(this.stream, data);
16614
- callback();
16851
+ resolveStyleId(name) {
16852
+ this.guard();
16853
+ const cached = this.nameCache.get(name);
16854
+ if (cached !== undefined)
16855
+ return cached;
16856
+ const id = this.lib.syntaxStyleResolveByName(this.stylePtr, name);
16857
+ if (id !== null) {
16858
+ this.nameCache.set(name, id);
16859
+ }
16860
+ return id;
16615
16861
  }
16616
- getColorDepth() {
16617
- return process.stdout.getColorDepth?.() || 8;
16862
+ getStyleId(name) {
16863
+ this.guard();
16864
+ const id = this.resolveStyleId(name);
16865
+ if (id !== null)
16866
+ return id;
16867
+ if (name.includes(".")) {
16868
+ const baseName = name.split(".")[0];
16869
+ return this.resolveStyleId(baseName);
16870
+ }
16871
+ return null;
16618
16872
  }
16619
- }
16620
-
16621
- // src/lib/keymapping.ts
16622
- var defaultKeyAliases = {
16623
- enter: "return",
16624
- esc: "escape",
16625
- kp0: "0",
16626
- kp1: "1",
16627
- kp2: "2",
16628
- kp3: "3",
16629
- kp4: "4",
16630
- kp5: "5",
16631
- kp6: "6",
16632
- kp7: "7",
16633
- kp8: "8",
16634
- kp9: "9",
16635
- kpdecimal: ".",
16636
- kpdivide: "/",
16637
- kpmultiply: "*",
16638
- kpminus: "-",
16639
- kpplus: "+",
16640
- kpenter: "enter",
16641
- kpequal: "=",
16642
- kpseparator: ",",
16643
- kpleft: "left",
16644
- kpright: "right",
16645
- kpup: "up",
16646
- kpdown: "down",
16647
- kppageup: "pageup",
16648
- kppagedown: "pagedown",
16649
- kphome: "home",
16650
- kpend: "end",
16651
- kpinsert: "insert",
16652
- kpdelete: "delete"
16653
- };
16654
- function mergeKeyAliases(defaults, custom) {
16655
- return { ...defaults, ...custom };
16656
- }
16657
- function mergeKeyBindings(defaults, custom) {
16658
- const map = new Map;
16659
- for (const binding of defaults) {
16660
- const key = getKeyBindingKey(binding);
16661
- map.set(key, binding);
16873
+ get ptr() {
16874
+ this.guard();
16875
+ return this.stylePtr;
16662
16876
  }
16663
- for (const binding of custom) {
16664
- const key = getKeyBindingKey(binding);
16665
- map.set(key, binding);
16877
+ getStyleCount() {
16878
+ this.guard();
16879
+ return this.lib.syntaxStyleGetStyleCount(this.stylePtr);
16666
16880
  }
16667
- return Array.from(map.values());
16668
- }
16669
- function getKeyBindingKey(binding) {
16670
- return `${binding.name}:${binding.ctrl ? 1 : 0}:${binding.shift ? 1 : 0}:${binding.meta ? 1 : 0}:${binding.super ? 1 : 0}`;
16671
- }
16672
- function getBaseCodeKeyName(baseCode) {
16673
- if (baseCode === undefined || baseCode < 32 || baseCode === 127) {
16881
+ clearNameCache() {
16882
+ this.nameCache.clear();
16883
+ }
16884
+ getStyle(name) {
16885
+ this.guard();
16886
+ const style = this.styleDefs.get(name);
16887
+ if (style)
16888
+ return style;
16889
+ if (name.includes(".")) {
16890
+ const baseName = name.split(".")[0];
16891
+ return this.styleDefs.get(baseName);
16892
+ }
16893
+ return;
16894
+ }
16895
+ mergeStyles(...styleNames) {
16896
+ this.guard();
16897
+ const cacheKey = styleNames.join(":");
16898
+ const cached = this.mergedCache.get(cacheKey);
16899
+ if (cached)
16900
+ return cached;
16901
+ const styleDefinition = {};
16902
+ for (const name of styleNames) {
16903
+ const style = this.getStyle(name);
16904
+ if (!style)
16905
+ continue;
16906
+ if (style.fg)
16907
+ styleDefinition.fg = style.fg;
16908
+ if (style.bg)
16909
+ styleDefinition.bg = style.bg;
16910
+ if (style.bold !== undefined)
16911
+ styleDefinition.bold = style.bold;
16912
+ if (style.italic !== undefined)
16913
+ styleDefinition.italic = style.italic;
16914
+ if (style.underline !== undefined)
16915
+ styleDefinition.underline = style.underline;
16916
+ if (style.dim !== undefined)
16917
+ styleDefinition.dim = style.dim;
16918
+ }
16919
+ const attributes = createTextAttributes({
16920
+ bold: styleDefinition.bold,
16921
+ italic: styleDefinition.italic,
16922
+ underline: styleDefinition.underline,
16923
+ dim: styleDefinition.dim
16924
+ });
16925
+ const merged = {
16926
+ fg: styleDefinition.fg,
16927
+ bg: styleDefinition.bg,
16928
+ attributes
16929
+ };
16930
+ this.mergedCache.set(cacheKey, merged);
16931
+ return merged;
16932
+ }
16933
+ clearCache() {
16934
+ this.guard();
16935
+ this.mergedCache.clear();
16936
+ }
16937
+ getCacheSize() {
16938
+ this.guard();
16939
+ return this.mergedCache.size;
16940
+ }
16941
+ getAllStyles() {
16942
+ this.guard();
16943
+ return new Map(this.styleDefs);
16944
+ }
16945
+ getRegisteredNames() {
16946
+ this.guard();
16947
+ return Array.from(this.styleDefs.keys());
16948
+ }
16949
+ destroy() {
16950
+ if (this._destroyed)
16951
+ return;
16952
+ this._destroyed = true;
16953
+ this.nameCache.clear();
16954
+ this.styleDefs.clear();
16955
+ this.mergedCache.clear();
16956
+ this.lib.destroySyntaxStyle(this.stylePtr);
16957
+ }
16958
+ }
16959
+
16960
+ // src/renderables/Box.ts
16961
+ function isGapType(value) {
16962
+ if (value === undefined) {
16963
+ return true;
16964
+ }
16965
+ if (typeof value === "number" && !Number.isNaN(value)) {
16966
+ return true;
16967
+ }
16968
+ return isValidPercentage(value);
16969
+ }
16970
+
16971
+ class BoxRenderable extends Renderable {
16972
+ _backgroundColor;
16973
+ _border;
16974
+ _borderStyle;
16975
+ _borderColor;
16976
+ _focusedBorderColor;
16977
+ _customBorderCharsObj;
16978
+ _customBorderChars;
16979
+ borderSides;
16980
+ shouldFill;
16981
+ _title;
16982
+ _titleAlignment;
16983
+ _bottomTitle;
16984
+ _bottomTitleAlignment;
16985
+ _defaultOptions = {
16986
+ backgroundColor: "transparent",
16987
+ borderStyle: "single",
16988
+ border: false,
16989
+ borderColor: "#FFFFFF",
16990
+ shouldFill: true,
16991
+ titleAlignment: "left",
16992
+ bottomTitleAlignment: "left",
16993
+ focusedBorderColor: "#00AAFF"
16994
+ };
16995
+ constructor(ctx, options) {
16996
+ super(ctx, options);
16997
+ if (options.focusable === true) {
16998
+ this._focusable = true;
16999
+ }
17000
+ this._backgroundColor = parseColor(options.backgroundColor || this._defaultOptions.backgroundColor);
17001
+ this._border = options.border ?? this._defaultOptions.border;
17002
+ if (!options.border && (options.borderStyle || options.borderColor || options.focusedBorderColor || options.customBorderChars)) {
17003
+ this._border = true;
17004
+ }
17005
+ this._borderStyle = parseBorderStyle(options.borderStyle, this._defaultOptions.borderStyle);
17006
+ this._borderColor = parseColor(options.borderColor || this._defaultOptions.borderColor);
17007
+ this._focusedBorderColor = parseColor(options.focusedBorderColor || this._defaultOptions.focusedBorderColor);
17008
+ this._customBorderCharsObj = options.customBorderChars;
17009
+ this._customBorderChars = this._customBorderCharsObj ? borderCharsToArray(this._customBorderCharsObj) : undefined;
17010
+ this.borderSides = getBorderSides(this._border);
17011
+ this.shouldFill = options.shouldFill ?? this._defaultOptions.shouldFill;
17012
+ this._title = options.title;
17013
+ this._titleAlignment = options.titleAlignment || this._defaultOptions.titleAlignment;
17014
+ this._bottomTitle = options.bottomTitle;
17015
+ this._bottomTitleAlignment = options.bottomTitleAlignment || this._defaultOptions.bottomTitleAlignment;
17016
+ this.applyYogaBorders();
17017
+ const hasInitialGapProps = options.gap !== undefined || options.rowGap !== undefined || options.columnGap !== undefined;
17018
+ if (hasInitialGapProps) {
17019
+ this.applyYogaGap(options);
17020
+ }
17021
+ }
17022
+ initializeBorder() {
17023
+ if (this._border === false) {
17024
+ this._border = true;
17025
+ this.borderSides = getBorderSides(this._border);
17026
+ this.applyYogaBorders();
17027
+ }
17028
+ }
17029
+ get customBorderChars() {
17030
+ return this._customBorderCharsObj;
17031
+ }
17032
+ set customBorderChars(value) {
17033
+ this._customBorderCharsObj = value;
17034
+ this._customBorderChars = value ? borderCharsToArray(value) : undefined;
17035
+ this.requestRender();
17036
+ }
17037
+ get backgroundColor() {
17038
+ return this._backgroundColor;
17039
+ }
17040
+ set backgroundColor(value) {
17041
+ const newColor = parseColor(value ?? this._defaultOptions.backgroundColor);
17042
+ if (this._backgroundColor !== newColor) {
17043
+ this._backgroundColor = newColor;
17044
+ this.requestRender();
17045
+ }
17046
+ }
17047
+ get border() {
17048
+ return this._border;
17049
+ }
17050
+ set border(value) {
17051
+ if (this._border !== value) {
17052
+ this._border = value;
17053
+ this.borderSides = getBorderSides(value);
17054
+ this.applyYogaBorders();
17055
+ this.requestRender();
17056
+ }
17057
+ }
17058
+ get borderStyle() {
17059
+ return this._borderStyle;
17060
+ }
17061
+ set borderStyle(value) {
17062
+ const _value = parseBorderStyle(value, this._defaultOptions.borderStyle);
17063
+ if (this._borderStyle !== _value || !this._border) {
17064
+ this._borderStyle = _value;
17065
+ this._customBorderChars = undefined;
17066
+ this.initializeBorder();
17067
+ this.requestRender();
17068
+ }
17069
+ }
17070
+ get borderColor() {
17071
+ return this._borderColor;
17072
+ }
17073
+ set borderColor(value) {
17074
+ const newColor = parseColor(value ?? this._defaultOptions.borderColor);
17075
+ if (this._borderColor !== newColor) {
17076
+ this._borderColor = newColor;
17077
+ this.initializeBorder();
17078
+ this.requestRender();
17079
+ }
17080
+ }
17081
+ get focusedBorderColor() {
17082
+ return this._focusedBorderColor;
17083
+ }
17084
+ set focusedBorderColor(value) {
17085
+ const newColor = parseColor(value ?? this._defaultOptions.focusedBorderColor);
17086
+ if (this._focusedBorderColor !== newColor) {
17087
+ this._focusedBorderColor = newColor;
17088
+ this.initializeBorder();
17089
+ if (this._focused) {
17090
+ this.requestRender();
17091
+ }
17092
+ }
17093
+ }
17094
+ get title() {
17095
+ return this._title;
17096
+ }
17097
+ set title(value) {
17098
+ if (this._title !== value) {
17099
+ this._title = value;
17100
+ this.requestRender();
17101
+ }
17102
+ }
17103
+ get titleAlignment() {
17104
+ return this._titleAlignment;
17105
+ }
17106
+ set titleAlignment(value) {
17107
+ if (this._titleAlignment !== value) {
17108
+ this._titleAlignment = value;
17109
+ this.requestRender();
17110
+ }
17111
+ }
17112
+ get bottomTitle() {
17113
+ return this._bottomTitle;
17114
+ }
17115
+ set bottomTitle(value) {
17116
+ if (this._bottomTitle !== value) {
17117
+ this._bottomTitle = value;
17118
+ this.requestRender();
17119
+ }
17120
+ }
17121
+ get bottomTitleAlignment() {
17122
+ return this._bottomTitleAlignment;
17123
+ }
17124
+ set bottomTitleAlignment(value) {
17125
+ if (this._bottomTitleAlignment !== value) {
17126
+ this._bottomTitleAlignment = value;
17127
+ this.requestRender();
17128
+ }
17129
+ }
17130
+ renderSelf(buffer) {
17131
+ const hasBorder = this.borderSides.top || this.borderSides.right || this.borderSides.bottom || this.borderSides.left;
17132
+ const hasVisibleFill = this.shouldFill && this._backgroundColor.a > 0;
17133
+ if (!hasBorder && !hasVisibleFill) {
17134
+ return;
17135
+ }
17136
+ const hasFocusWithin = this._focusable && (this._focused || this._hasFocusedDescendant);
17137
+ const currentBorderColor = hasFocusWithin ? this._focusedBorderColor : this._borderColor;
17138
+ const screenX = this._screenX;
17139
+ const screenY = this._screenY;
17140
+ buffer.drawBox({
17141
+ x: screenX,
17142
+ y: screenY,
17143
+ width: this.width,
17144
+ height: this.height,
17145
+ borderStyle: this._borderStyle,
17146
+ customBorderChars: this._customBorderChars,
17147
+ border: this._border,
17148
+ borderColor: currentBorderColor,
17149
+ backgroundColor: this._backgroundColor,
17150
+ shouldFill: this.shouldFill,
17151
+ title: this._title,
17152
+ titleAlignment: this._titleAlignment,
17153
+ bottomTitle: this._bottomTitle,
17154
+ bottomTitleAlignment: this._bottomTitleAlignment
17155
+ });
17156
+ }
17157
+ getScissorRect() {
17158
+ const baseRect = super.getScissorRect();
17159
+ if (!this.borderSides.top && !this.borderSides.right && !this.borderSides.bottom && !this.borderSides.left) {
17160
+ return baseRect;
17161
+ }
17162
+ const leftInset = this.borderSides.left ? 1 : 0;
17163
+ const rightInset = this.borderSides.right ? 1 : 0;
17164
+ const topInset = this.borderSides.top ? 1 : 0;
17165
+ const bottomInset = this.borderSides.bottom ? 1 : 0;
17166
+ return {
17167
+ x: baseRect.x + leftInset,
17168
+ y: baseRect.y + topInset,
17169
+ width: Math.max(0, baseRect.width - leftInset - rightInset),
17170
+ height: Math.max(0, baseRect.height - topInset - bottomInset)
17171
+ };
17172
+ }
17173
+ applyYogaBorders() {
17174
+ const node = this.yogaNode;
17175
+ node.setBorder(Edge.Left, this.borderSides.left ? 1 : 0);
17176
+ node.setBorder(Edge.Right, this.borderSides.right ? 1 : 0);
17177
+ node.setBorder(Edge.Top, this.borderSides.top ? 1 : 0);
17178
+ node.setBorder(Edge.Bottom, this.borderSides.bottom ? 1 : 0);
17179
+ this.requestRender();
17180
+ }
17181
+ applyYogaGap(options) {
17182
+ const node = this.yogaNode;
17183
+ if (isGapType(options.gap)) {
17184
+ node.setGap(Gutter.All, options.gap);
17185
+ }
17186
+ if (isGapType(options.rowGap)) {
17187
+ node.setGap(Gutter.Row, options.rowGap);
17188
+ }
17189
+ if (isGapType(options.columnGap)) {
17190
+ node.setGap(Gutter.Column, options.columnGap);
17191
+ }
17192
+ }
17193
+ set gap(gap) {
17194
+ if (isGapType(gap)) {
17195
+ this.yogaNode.setGap(Gutter.All, gap);
17196
+ this.requestRender();
17197
+ }
17198
+ }
17199
+ set rowGap(rowGap) {
17200
+ if (isGapType(rowGap)) {
17201
+ this.yogaNode.setGap(Gutter.Row, rowGap);
17202
+ this.requestRender();
17203
+ }
17204
+ }
17205
+ set columnGap(columnGap) {
17206
+ if (isGapType(columnGap)) {
17207
+ this.yogaNode.setGap(Gutter.Column, columnGap);
17208
+ this.requestRender();
17209
+ }
17210
+ }
17211
+ }
17212
+
17213
+ // src/renderables/TextBufferRenderable.ts
17214
+ class TextBufferRenderable extends Renderable {
17215
+ selectable = true;
17216
+ _defaultFg;
17217
+ _defaultBg;
17218
+ _defaultAttributes;
17219
+ _selectionBg;
17220
+ _selectionFg;
17221
+ _wrapMode = "word";
17222
+ lastLocalSelection = null;
17223
+ _tabIndicator;
17224
+ _tabIndicatorColor;
17225
+ _scrollX = 0;
17226
+ _scrollY = 0;
17227
+ _truncate = false;
17228
+ _firstLineOffset = 0;
17229
+ textBuffer;
17230
+ textBufferView;
17231
+ _textBufferSyntaxStyle;
17232
+ _defaultOptions = {
17233
+ fg: RGBA.fromValues(1, 1, 1, 1),
17234
+ bg: RGBA.fromValues(0, 0, 0, 0),
17235
+ selectionBg: undefined,
17236
+ selectionFg: undefined,
17237
+ selectable: true,
17238
+ attributes: 0,
17239
+ wrapMode: "word",
17240
+ tabIndicator: undefined,
17241
+ tabIndicatorColor: undefined,
17242
+ truncate: false
17243
+ };
17244
+ constructor(ctx, options) {
17245
+ super(ctx, options);
17246
+ this._defaultFg = parseColor(options.fg ?? this._defaultOptions.fg);
17247
+ this._defaultBg = parseColor(options.bg ?? this._defaultOptions.bg);
17248
+ this._defaultAttributes = options.attributes ?? this._defaultOptions.attributes;
17249
+ this._selectionBg = options.selectionBg ? parseColor(options.selectionBg) : this._defaultOptions.selectionBg;
17250
+ this._selectionFg = options.selectionFg ? parseColor(options.selectionFg) : this._defaultOptions.selectionFg;
17251
+ this.selectable = options.selectable ?? this._defaultOptions.selectable;
17252
+ this._wrapMode = options.wrapMode ?? this._defaultOptions.wrapMode;
17253
+ this._tabIndicator = options.tabIndicator ?? this._defaultOptions.tabIndicator;
17254
+ this._tabIndicatorColor = options.tabIndicatorColor ? parseColor(options.tabIndicatorColor) : this._defaultOptions.tabIndicatorColor;
17255
+ this._truncate = options.truncate ?? this._defaultOptions.truncate;
17256
+ this.textBuffer = TextBuffer.create(this._ctx.widthMethod);
17257
+ this.textBufferView = TextBufferView.create(this.textBuffer);
17258
+ this._firstLineOffset = ctx.claimFirstLineOffset?.(this) ?? 0;
17259
+ this._textBufferSyntaxStyle = SyntaxStyle.create();
17260
+ this.textBuffer.setSyntaxStyle(this._textBufferSyntaxStyle);
17261
+ this.textBufferView.setWrapMode(this._wrapMode);
17262
+ this.textBufferView.setFirstLineOffset(this._firstLineOffset);
17263
+ this.setupMeasureFunc();
17264
+ this.textBuffer.setDefaultFg(this._defaultFg);
17265
+ this.textBuffer.setDefaultBg(this._defaultBg);
17266
+ this.textBuffer.setDefaultAttributes(this._defaultAttributes);
17267
+ if (this._tabIndicator !== undefined) {
17268
+ this.textBufferView.setTabIndicator(this._tabIndicator);
17269
+ }
17270
+ if (this._tabIndicatorColor !== undefined) {
17271
+ this.textBufferView.setTabIndicatorColor(this._tabIndicatorColor);
17272
+ }
17273
+ if (this._wrapMode !== "none" && this.width > 0) {
17274
+ this.textBufferView.setWrapWidth(this.width);
17275
+ }
17276
+ if (this.width > 0 && this.height > 0) {
17277
+ this.textBufferView.setViewport(this._scrollX, this._scrollY, this.width, this.height);
17278
+ }
17279
+ this.textBufferView.setTruncate(this._truncate);
17280
+ this.updateTextInfo();
17281
+ }
17282
+ onMouseEvent(event) {
17283
+ if (event.type === "scroll") {
17284
+ this.handleScroll(event);
17285
+ }
17286
+ }
17287
+ handleScroll(event) {
17288
+ if (!event.scroll)
17289
+ return;
17290
+ const { direction, delta } = event.scroll;
17291
+ if (direction === "up") {
17292
+ this.scrollY -= delta;
17293
+ } else if (direction === "down") {
17294
+ this.scrollY += delta;
17295
+ }
17296
+ if (this._wrapMode === "none") {
17297
+ if (direction === "left") {
17298
+ this.scrollX -= delta;
17299
+ } else if (direction === "right") {
17300
+ this.scrollX += delta;
17301
+ }
17302
+ }
17303
+ }
17304
+ get lineInfo() {
17305
+ return this.textBufferView.logicalLineInfo;
17306
+ }
17307
+ get lineCount() {
17308
+ return this.textBuffer.getLineCount();
17309
+ }
17310
+ get virtualLineCount() {
17311
+ return this.textBufferView.getVirtualLineCount();
17312
+ }
17313
+ get scrollY() {
17314
+ return this._scrollY;
17315
+ }
17316
+ set scrollY(value) {
17317
+ const maxScrollY = Math.max(0, this.scrollHeight - this.height);
17318
+ const clamped = Math.max(0, Math.min(value, maxScrollY));
17319
+ if (this._scrollY !== clamped) {
17320
+ this._scrollY = clamped;
17321
+ this.updateViewportOffset();
17322
+ this.requestRender();
17323
+ }
17324
+ }
17325
+ get scrollX() {
17326
+ return this._scrollX;
17327
+ }
17328
+ set scrollX(value) {
17329
+ const maxScrollX = Math.max(0, this.scrollWidth - this.width);
17330
+ const clamped = Math.max(0, Math.min(value, maxScrollX));
17331
+ if (this._scrollX !== clamped) {
17332
+ this._scrollX = clamped;
17333
+ this.updateViewportOffset();
17334
+ this.requestRender();
17335
+ }
17336
+ }
17337
+ get scrollWidth() {
17338
+ return this.lineInfo.lineWidthColsMax;
17339
+ }
17340
+ get scrollHeight() {
17341
+ return this.lineInfo.lineStartCols.length;
17342
+ }
17343
+ get maxScrollY() {
17344
+ return Math.max(0, this.scrollHeight - this.height);
17345
+ }
17346
+ get maxScrollX() {
17347
+ return Math.max(0, this.scrollWidth - this.width);
17348
+ }
17349
+ updateViewportOffset() {
17350
+ if (this.width > 0 && this.height > 0) {
17351
+ this.textBufferView.setViewport(this._scrollX, this._scrollY, this.width, this.height);
17352
+ }
17353
+ }
17354
+ get plainText() {
17355
+ return this.textBuffer.getPlainText();
17356
+ }
17357
+ get textLength() {
17358
+ return this.textBuffer.length;
17359
+ }
17360
+ get fg() {
17361
+ return this._defaultFg;
17362
+ }
17363
+ set fg(value) {
17364
+ const newColor = parseColor(value ?? this._defaultOptions.fg);
17365
+ if (this._defaultFg !== newColor) {
17366
+ this._defaultFg = newColor;
17367
+ this.textBuffer.setDefaultFg(this._defaultFg);
17368
+ this.onFgChanged(newColor);
17369
+ this.requestRender();
17370
+ }
17371
+ }
17372
+ get selectionBg() {
17373
+ return this._selectionBg;
17374
+ }
17375
+ set selectionBg(value) {
17376
+ const newColor = value ? parseColor(value) : this._defaultOptions.selectionBg;
17377
+ if (this._selectionBg !== newColor) {
17378
+ this._selectionBg = newColor;
17379
+ if (this.lastLocalSelection) {
17380
+ this.updateLocalSelection(this.lastLocalSelection);
17381
+ }
17382
+ this.requestRender();
17383
+ }
17384
+ }
17385
+ get selectionFg() {
17386
+ return this._selectionFg;
17387
+ }
17388
+ set selectionFg(value) {
17389
+ const newColor = value ? parseColor(value) : this._defaultOptions.selectionFg;
17390
+ if (this._selectionFg !== newColor) {
17391
+ this._selectionFg = newColor;
17392
+ if (this.lastLocalSelection) {
17393
+ this.updateLocalSelection(this.lastLocalSelection);
17394
+ }
17395
+ this.requestRender();
17396
+ }
17397
+ }
17398
+ get bg() {
17399
+ return this._defaultBg;
17400
+ }
17401
+ set bg(value) {
17402
+ const newColor = parseColor(value ?? this._defaultOptions.bg);
17403
+ if (this._defaultBg !== newColor) {
17404
+ this._defaultBg = newColor;
17405
+ this.textBuffer.setDefaultBg(this._defaultBg);
17406
+ this.onBgChanged(newColor);
17407
+ this.requestRender();
17408
+ }
17409
+ }
17410
+ get attributes() {
17411
+ return this._defaultAttributes;
17412
+ }
17413
+ set attributes(value) {
17414
+ if (this._defaultAttributes !== value) {
17415
+ this._defaultAttributes = value;
17416
+ this.textBuffer.setDefaultAttributes(this._defaultAttributes);
17417
+ this.onAttributesChanged(value);
17418
+ this.requestRender();
17419
+ }
17420
+ }
17421
+ get wrapMode() {
17422
+ return this._wrapMode;
17423
+ }
17424
+ set wrapMode(value) {
17425
+ if (this._wrapMode !== value) {
17426
+ this._wrapMode = value;
17427
+ this.textBufferView.setWrapMode(this._wrapMode);
17428
+ if (value !== "none" && this.width > 0) {
17429
+ this.textBufferView.setWrapWidth(this.width);
17430
+ }
17431
+ this.yogaNode.markDirty();
17432
+ this.requestRender();
17433
+ }
17434
+ }
17435
+ get tabIndicator() {
17436
+ return this._tabIndicator;
17437
+ }
17438
+ set tabIndicator(value) {
17439
+ if (this._tabIndicator !== value) {
17440
+ this._tabIndicator = value;
17441
+ if (value !== undefined) {
17442
+ this.textBufferView.setTabIndicator(value);
17443
+ }
17444
+ this.requestRender();
17445
+ }
17446
+ }
17447
+ get tabIndicatorColor() {
17448
+ return this._tabIndicatorColor;
17449
+ }
17450
+ set tabIndicatorColor(value) {
17451
+ const newColor = value ? parseColor(value) : undefined;
17452
+ if (this._tabIndicatorColor !== newColor) {
17453
+ this._tabIndicatorColor = newColor;
17454
+ if (newColor !== undefined) {
17455
+ this.textBufferView.setTabIndicatorColor(newColor);
17456
+ }
17457
+ this.requestRender();
17458
+ }
17459
+ }
17460
+ get truncate() {
17461
+ return this._truncate;
17462
+ }
17463
+ set truncate(value) {
17464
+ if (this._truncate !== value) {
17465
+ this._truncate = value;
17466
+ this.textBufferView.setTruncate(value);
17467
+ this.requestRender();
17468
+ }
17469
+ }
17470
+ onResize(width, height) {
17471
+ this.textBufferView.setViewport(this._scrollX, this._scrollY, width, height);
17472
+ this.yogaNode.markDirty();
17473
+ this.requestRender();
17474
+ this.emit("line-info-change");
17475
+ }
17476
+ refreshLocalSelection() {
17477
+ if (this.lastLocalSelection) {
17478
+ return this.updateLocalSelection(this.lastLocalSelection);
17479
+ }
17480
+ return false;
17481
+ }
17482
+ updateLocalSelection(localSelection) {
17483
+ if (!localSelection?.isActive) {
17484
+ this.textBufferView.resetLocalSelection();
17485
+ return true;
17486
+ }
17487
+ return this.textBufferView.setLocalSelection(localSelection.anchorX, localSelection.anchorY, localSelection.focusX, localSelection.focusY, this._selectionBg, this._selectionFg);
17488
+ }
17489
+ updateTextInfo() {
17490
+ if (this.lastLocalSelection) {
17491
+ this.updateLocalSelection(this.lastLocalSelection);
17492
+ }
17493
+ this.yogaNode.markDirty();
17494
+ this.requestRender();
17495
+ this.emit("line-info-change");
17496
+ }
17497
+ setupMeasureFunc() {
17498
+ const measureFunc = (width, widthMode, height, heightMode) => {
17499
+ let effectiveWidth;
17500
+ if (widthMode === MeasureMode.Undefined || isNaN(width)) {
17501
+ effectiveWidth = 0;
17502
+ } else {
17503
+ effectiveWidth = width;
17504
+ }
17505
+ const effectiveHeight = isNaN(height) ? 1 : height;
17506
+ const measureResult = this.textBufferView.measureForDimensions(Math.floor(effectiveWidth), Math.floor(effectiveHeight));
17507
+ const measuredWidth = measureResult ? Math.max(1, measureResult.widthColsMax) : 1;
17508
+ const measuredHeight = measureResult ? Math.max(1, measureResult.lineCount) : 1;
17509
+ if (widthMode === MeasureMode.AtMost && this._positionType !== "absolute") {
17510
+ return {
17511
+ width: Math.min(effectiveWidth, measuredWidth),
17512
+ height: Math.min(effectiveHeight, measuredHeight)
17513
+ };
17514
+ }
17515
+ return {
17516
+ width: measuredWidth,
17517
+ height: measuredHeight
17518
+ };
17519
+ };
17520
+ this.yogaNode.setMeasureFunc(measureFunc);
17521
+ }
17522
+ shouldStartSelection(x, y) {
17523
+ if (!this.selectable)
17524
+ return false;
17525
+ const localX = x - this.x;
17526
+ const localY = y - this.y;
17527
+ return localX >= 0 && localX < this.width && localY >= 0 && localY < this.height;
17528
+ }
17529
+ onSelectionChanged(selection2) {
17530
+ const localSelection = convertGlobalToLocalSelection(selection2, this.x, this.y);
17531
+ this.lastLocalSelection = localSelection;
17532
+ let changed;
17533
+ if (!localSelection?.isActive) {
17534
+ this.textBufferView.resetLocalSelection();
17535
+ changed = true;
17536
+ } else if (selection2?.isStart) {
17537
+ changed = this.textBufferView.setLocalSelection(localSelection.anchorX, localSelection.anchorY, localSelection.focusX, localSelection.focusY, this._selectionBg, this._selectionFg);
17538
+ } else {
17539
+ changed = this.textBufferView.updateLocalSelection(localSelection.anchorX, localSelection.anchorY, localSelection.focusX, localSelection.focusY, this._selectionBg, this._selectionFg);
17540
+ }
17541
+ if (changed) {
17542
+ this.requestRender();
17543
+ }
17544
+ return this.hasSelection();
17545
+ }
17546
+ getSelectedText() {
17547
+ return this.textBufferView.getSelectedText();
17548
+ }
17549
+ hasSelection() {
17550
+ return this.textBufferView.hasSelection();
17551
+ }
17552
+ getSelection() {
17553
+ return this.textBufferView.getSelection();
17554
+ }
17555
+ render(buffer, deltaTime) {
17556
+ if (!this.visible)
17557
+ return;
17558
+ const screenX = this._screenX;
17559
+ const screenY = this._screenY;
17560
+ this.markClean();
17561
+ this._ctx.addToHitGrid(screenX, screenY, this.width, this.height, this.num);
17562
+ this.renderSelf(buffer);
17563
+ if (this.buffered && this.frameBuffer) {
17564
+ buffer.drawFrameBuffer(screenX, screenY, this.frameBuffer);
17565
+ }
17566
+ }
17567
+ renderSelf(buffer) {
17568
+ if (this.textBuffer.ptr) {
17569
+ buffer.drawTextBuffer(this.textBufferView, this._screenX, this._screenY);
17570
+ }
17571
+ }
17572
+ destroy() {
17573
+ if (this.isDestroyed)
17574
+ return;
17575
+ this.textBuffer.setSyntaxStyle(null);
17576
+ this._textBufferSyntaxStyle.destroy();
17577
+ this.textBufferView.destroy();
17578
+ this.textBuffer.destroy();
17579
+ super.destroy();
17580
+ }
17581
+ onFgChanged(newColor) {}
17582
+ onBgChanged(newColor) {}
17583
+ onAttributesChanged(newAttributes) {}
17584
+ }
17585
+
17586
+ // src/renderables/Code.ts
17587
+ class CodeRenderable extends TextBufferRenderable {
17588
+ _content;
17589
+ _filetype;
17590
+ _syntaxStyle;
17591
+ _isHighlighting = false;
17592
+ _treeSitterClient;
17593
+ _highlightsDirty = false;
17594
+ _highlightSnapshotId = 0;
17595
+ _conceal;
17596
+ _drawUnstyledText;
17597
+ _shouldRenderTextBuffer = true;
17598
+ _streaming;
17599
+ _hadInitialContent = false;
17600
+ _lastHighlights = [];
17601
+ _onHighlight;
17602
+ _onChunks;
17603
+ _highlightingPromise = Promise.resolve();
17604
+ _contentDefaultOptions = {
17605
+ content: "",
17606
+ conceal: true,
17607
+ drawUnstyledText: true,
17608
+ streaming: false
17609
+ };
17610
+ constructor(ctx, options) {
17611
+ super(ctx, options);
17612
+ this._content = options.content ?? this._contentDefaultOptions.content;
17613
+ this._filetype = options.filetype;
17614
+ this._syntaxStyle = options.syntaxStyle;
17615
+ this._treeSitterClient = options.treeSitterClient ?? getTreeSitterClient();
17616
+ this._conceal = options.conceal ?? this._contentDefaultOptions.conceal;
17617
+ this._drawUnstyledText = options.drawUnstyledText ?? this._contentDefaultOptions.drawUnstyledText;
17618
+ this._streaming = options.streaming ?? this._contentDefaultOptions.streaming;
17619
+ this._onHighlight = options.onHighlight;
17620
+ this._onChunks = options.onChunks;
17621
+ if (this._content.length > 0) {
17622
+ this.textBuffer.setText(this._content);
17623
+ this.updateTextInfo();
17624
+ this._shouldRenderTextBuffer = this._drawUnstyledText || !this._filetype;
17625
+ }
17626
+ this._highlightsDirty = this._content.length > 0;
17627
+ }
17628
+ get content() {
17629
+ return this._content;
17630
+ }
17631
+ set content(value) {
17632
+ if (this._content !== value) {
17633
+ this._content = value;
17634
+ this._highlightsDirty = true;
17635
+ this._highlightSnapshotId++;
17636
+ if (this._streaming && !this._drawUnstyledText && this._filetype) {
17637
+ return;
17638
+ }
17639
+ this.textBuffer.setText(value);
17640
+ this.updateTextInfo();
17641
+ }
17642
+ }
17643
+ get filetype() {
17644
+ return this._filetype;
17645
+ }
17646
+ set filetype(value) {
17647
+ if (this._filetype !== value) {
17648
+ this._filetype = value;
17649
+ this._highlightsDirty = true;
17650
+ }
17651
+ }
17652
+ get syntaxStyle() {
17653
+ return this._syntaxStyle;
17654
+ }
17655
+ set syntaxStyle(value) {
17656
+ if (this._syntaxStyle !== value) {
17657
+ this._syntaxStyle = value;
17658
+ this._highlightsDirty = true;
17659
+ }
17660
+ }
17661
+ get conceal() {
17662
+ return this._conceal;
17663
+ }
17664
+ set conceal(value) {
17665
+ if (this._conceal !== value) {
17666
+ this._conceal = value;
17667
+ this._highlightsDirty = true;
17668
+ }
17669
+ }
17670
+ get drawUnstyledText() {
17671
+ return this._drawUnstyledText;
17672
+ }
17673
+ set drawUnstyledText(value) {
17674
+ if (this._drawUnstyledText !== value) {
17675
+ this._drawUnstyledText = value;
17676
+ this._highlightsDirty = true;
17677
+ }
17678
+ }
17679
+ get streaming() {
17680
+ return this._streaming;
17681
+ }
17682
+ set streaming(value) {
17683
+ if (this._streaming !== value) {
17684
+ this._streaming = value;
17685
+ this._hadInitialContent = false;
17686
+ this._lastHighlights = [];
17687
+ this._highlightsDirty = true;
17688
+ }
17689
+ }
17690
+ get treeSitterClient() {
17691
+ return this._treeSitterClient;
17692
+ }
17693
+ set treeSitterClient(value) {
17694
+ if (this._treeSitterClient !== value) {
17695
+ this._treeSitterClient = value;
17696
+ this._highlightsDirty = true;
17697
+ }
17698
+ }
17699
+ get onHighlight() {
17700
+ return this._onHighlight;
17701
+ }
17702
+ set onHighlight(value) {
17703
+ if (this._onHighlight !== value) {
17704
+ this._onHighlight = value;
17705
+ this._highlightsDirty = true;
17706
+ }
17707
+ }
17708
+ get onChunks() {
17709
+ return this._onChunks;
17710
+ }
17711
+ set onChunks(value) {
17712
+ if (this._onChunks !== value) {
17713
+ this._onChunks = value;
17714
+ this._highlightsDirty = true;
17715
+ }
17716
+ }
17717
+ get isHighlighting() {
17718
+ return this._isHighlighting;
17719
+ }
17720
+ get highlightingDone() {
17721
+ return this._highlightingPromise;
17722
+ }
17723
+ async transformChunks(chunks, context) {
17724
+ if (!this._onChunks)
17725
+ return chunks;
17726
+ const modified = await this._onChunks(chunks, context);
17727
+ return modified ?? chunks;
17728
+ }
17729
+ ensureVisibleTextBeforeHighlight() {
17730
+ if (this.isDestroyed)
17731
+ return;
17732
+ const content = this._content;
17733
+ if (!this._filetype) {
17734
+ this._shouldRenderTextBuffer = true;
17735
+ return;
17736
+ }
17737
+ const isInitialContent = this._streaming && !this._hadInitialContent;
17738
+ const shouldDrawUnstyledNow = this._streaming ? isInitialContent && this._drawUnstyledText : this._drawUnstyledText;
17739
+ if (this._streaming && !isInitialContent) {
17740
+ this._shouldRenderTextBuffer = true;
17741
+ } else if (shouldDrawUnstyledNow) {
17742
+ this.textBuffer.setText(content);
17743
+ this._shouldRenderTextBuffer = true;
17744
+ } else {
17745
+ this._shouldRenderTextBuffer = false;
17746
+ }
17747
+ }
17748
+ async startHighlight() {
17749
+ const content = this._content;
17750
+ const filetype = this._filetype;
17751
+ const snapshotId = ++this._highlightSnapshotId;
17752
+ if (!filetype)
17753
+ return;
17754
+ const isInitialContent = this._streaming && !this._hadInitialContent;
17755
+ if (isInitialContent) {
17756
+ this._hadInitialContent = true;
17757
+ }
17758
+ this._isHighlighting = true;
17759
+ try {
17760
+ const result = await this._treeSitterClient.highlightOnce(content, filetype);
17761
+ if (snapshotId !== this._highlightSnapshotId) {
17762
+ return;
17763
+ }
17764
+ if (this.isDestroyed)
17765
+ return;
17766
+ let highlights = result.highlights ?? [];
17767
+ if (this._onHighlight && highlights.length >= 0) {
17768
+ const context = {
17769
+ content,
17770
+ filetype,
17771
+ syntaxStyle: this._syntaxStyle
17772
+ };
17773
+ const modified = await this._onHighlight(highlights, context);
17774
+ if (modified !== undefined) {
17775
+ highlights = modified;
17776
+ }
17777
+ }
17778
+ if (snapshotId !== this._highlightSnapshotId) {
17779
+ return;
17780
+ }
17781
+ if (this.isDestroyed)
17782
+ return;
17783
+ if (highlights.length > 0) {
17784
+ if (this._streaming) {
17785
+ this._lastHighlights = highlights;
17786
+ }
17787
+ }
17788
+ if (highlights.length > 0 || this._onChunks) {
17789
+ const context = {
17790
+ content,
17791
+ filetype,
17792
+ syntaxStyle: this._syntaxStyle,
17793
+ highlights
17794
+ };
17795
+ let chunks = treeSitterToTextChunks(content, highlights, this._syntaxStyle, {
17796
+ enabled: this._conceal
17797
+ });
17798
+ chunks = await this.transformChunks(chunks, context);
17799
+ if (snapshotId !== this._highlightSnapshotId) {
17800
+ return;
17801
+ }
17802
+ if (this.isDestroyed)
17803
+ return;
17804
+ const styledText = new StyledText(chunks);
17805
+ this.textBuffer.setStyledText(styledText);
17806
+ } else {
17807
+ this.textBuffer.setText(content);
17808
+ }
17809
+ this._shouldRenderTextBuffer = true;
17810
+ this._isHighlighting = false;
17811
+ this._highlightsDirty = false;
17812
+ this.updateTextInfo();
17813
+ this.requestRender();
17814
+ } catch (error) {
17815
+ if (snapshotId !== this._highlightSnapshotId) {
17816
+ return;
17817
+ }
17818
+ console.warn("Code highlighting failed, falling back to plain text:", error);
17819
+ if (this.isDestroyed)
17820
+ return;
17821
+ this.textBuffer.setText(content);
17822
+ this._shouldRenderTextBuffer = true;
17823
+ this._isHighlighting = false;
17824
+ this._highlightsDirty = false;
17825
+ this.updateTextInfo();
17826
+ this.requestRender();
17827
+ }
17828
+ }
17829
+ getLineHighlights(lineIdx) {
17830
+ return this.textBuffer.getLineHighlights(lineIdx);
17831
+ }
17832
+ renderSelf(buffer) {
17833
+ if (this._highlightsDirty) {
17834
+ if (this.isDestroyed)
17835
+ return;
17836
+ if (this._content.length === 0) {
17837
+ this._shouldRenderTextBuffer = false;
17838
+ this._highlightsDirty = false;
17839
+ } else if (!this._filetype) {
17840
+ this._shouldRenderTextBuffer = true;
17841
+ this._highlightsDirty = false;
17842
+ } else {
17843
+ this.ensureVisibleTextBeforeHighlight();
17844
+ this._highlightsDirty = false;
17845
+ this._highlightingPromise = this.startHighlight();
17846
+ }
17847
+ }
17848
+ if (!this._shouldRenderTextBuffer)
17849
+ return;
17850
+ super.renderSelf(buffer);
17851
+ }
17852
+ }
17853
+
17854
+ // src/renderables/TextNode.ts
17855
+ var BrandedTextNodeRenderable = Symbol.for("@opentui/core/TextNodeRenderable");
17856
+ function isTextNodeRenderable(obj) {
17857
+ return !!obj?.[BrandedTextNodeRenderable];
17858
+ }
17859
+ function styledTextToTextNodes(styledText) {
17860
+ return styledText.chunks.map((chunk) => {
17861
+ const node = new TextNodeRenderable({
17862
+ fg: chunk.fg,
17863
+ bg: chunk.bg,
17864
+ attributes: chunk.attributes,
17865
+ link: chunk.link
17866
+ });
17867
+ node.add(chunk.text);
17868
+ return node;
17869
+ });
17870
+ }
17871
+
17872
+ class TextNodeRenderable extends BaseRenderable {
17873
+ [BrandedTextNodeRenderable] = true;
17874
+ _fg;
17875
+ _bg;
17876
+ _attributes;
17877
+ _link;
17878
+ _children = [];
17879
+ parent = null;
17880
+ constructor(options) {
17881
+ super(options);
17882
+ this._fg = options.fg ? parseColor(options.fg) : undefined;
17883
+ this._bg = options.bg ? parseColor(options.bg) : undefined;
17884
+ this._attributes = options.attributes ?? 0;
17885
+ this._link = options.link;
17886
+ }
17887
+ get children() {
17888
+ return this._children;
17889
+ }
17890
+ set children(children) {
17891
+ this._children = children;
17892
+ this.requestRender();
17893
+ }
17894
+ requestRender() {
17895
+ this.markDirty();
17896
+ this.parent?.requestRender();
17897
+ }
17898
+ add(obj, index) {
17899
+ if (typeof obj === "string") {
17900
+ if (index !== undefined) {
17901
+ this._children.splice(index, 0, obj);
17902
+ this.requestRender();
17903
+ return index;
17904
+ }
17905
+ const insertIndex = this._children.length;
17906
+ this._children.push(obj);
17907
+ this.requestRender();
17908
+ return insertIndex;
17909
+ }
17910
+ if (isTextNodeRenderable(obj)) {
17911
+ if (index !== undefined) {
17912
+ this._children.splice(index, 0, obj);
17913
+ obj.parent = this;
17914
+ this.requestRender();
17915
+ return index;
17916
+ }
17917
+ const insertIndex = this._children.length;
17918
+ this._children.push(obj);
17919
+ obj.parent = this;
17920
+ this.requestRender();
17921
+ return insertIndex;
17922
+ }
17923
+ if (isStyledText(obj)) {
17924
+ const textNodes = styledTextToTextNodes(obj);
17925
+ if (index !== undefined) {
17926
+ this._children.splice(index, 0, ...textNodes);
17927
+ textNodes.forEach((node) => node.parent = this);
17928
+ this.requestRender();
17929
+ return index;
17930
+ }
17931
+ const insertIndex = this._children.length;
17932
+ this._children.push(...textNodes);
17933
+ textNodes.forEach((node) => node.parent = this);
17934
+ this.requestRender();
17935
+ return insertIndex;
17936
+ }
17937
+ throw new Error("TextNodeRenderable only accepts strings, TextNodeRenderable instances, or StyledText instances");
17938
+ }
17939
+ replace(obj, index) {
17940
+ this._children[index] = obj;
17941
+ if (typeof obj !== "string") {
17942
+ obj.parent = this;
17943
+ }
17944
+ this.requestRender();
17945
+ }
17946
+ insertBefore(child, anchorNode) {
17947
+ if (!anchorNode || !isTextNodeRenderable(anchorNode)) {
17948
+ throw new Error("Anchor must be a TextNodeRenderable");
17949
+ }
17950
+ const anchorIndex = this._children.indexOf(anchorNode);
17951
+ if (anchorIndex === -1) {
17952
+ throw new Error("Anchor node not found in children");
17953
+ }
17954
+ if (typeof child === "string") {
17955
+ this._children.splice(anchorIndex, 0, child);
17956
+ } else if (isTextNodeRenderable(child)) {
17957
+ this._children.splice(anchorIndex, 0, child);
17958
+ child.parent = this;
17959
+ } else if (child instanceof StyledText) {
17960
+ const textNodes = styledTextToTextNodes(child);
17961
+ this._children.splice(anchorIndex, 0, ...textNodes);
17962
+ textNodes.forEach((node) => node.parent = this);
17963
+ } else {
17964
+ throw new Error("Child must be a string, TextNodeRenderable, or StyledText instance");
17965
+ }
17966
+ this.requestRender();
17967
+ return this;
17968
+ }
17969
+ remove(id) {
17970
+ const childIndex = this.getRenderableIndex(id);
17971
+ if (childIndex === -1) {
17972
+ throw new Error("Child not found in children");
17973
+ }
17974
+ const child = this._children[childIndex];
17975
+ this._children.splice(childIndex, 1);
17976
+ child.parent = null;
17977
+ this.requestRender();
17978
+ return this;
17979
+ }
17980
+ clear() {
17981
+ this._children = [];
17982
+ this.requestRender();
17983
+ }
17984
+ mergeStyles(parentStyle) {
17985
+ return {
17986
+ fg: this._fg ?? parentStyle.fg,
17987
+ bg: this._bg ?? parentStyle.bg,
17988
+ attributes: this._attributes | parentStyle.attributes,
17989
+ link: this._link ?? parentStyle.link
17990
+ };
17991
+ }
17992
+ gatherWithInheritedStyle(parentStyle = {
17993
+ fg: undefined,
17994
+ bg: undefined,
17995
+ attributes: 0
17996
+ }) {
17997
+ const currentStyle = this.mergeStyles(parentStyle);
17998
+ const chunks = [];
17999
+ for (const child of this._children) {
18000
+ if (typeof child === "string") {
18001
+ chunks.push({
18002
+ __isChunk: true,
18003
+ text: child,
18004
+ fg: currentStyle.fg,
18005
+ bg: currentStyle.bg,
18006
+ attributes: currentStyle.attributes,
18007
+ link: currentStyle.link
18008
+ });
18009
+ } else {
18010
+ const childChunks = child.gatherWithInheritedStyle(currentStyle);
18011
+ chunks.push(...childChunks);
18012
+ }
18013
+ }
18014
+ this.markClean();
18015
+ return chunks;
18016
+ }
18017
+ static fromString(text, options = {}) {
18018
+ const node = new TextNodeRenderable(options);
18019
+ node.add(text);
18020
+ return node;
18021
+ }
18022
+ static fromNodes(nodes, options = {}) {
18023
+ const node = new TextNodeRenderable(options);
18024
+ for (const childNode of nodes) {
18025
+ node.add(childNode);
18026
+ }
18027
+ return node;
18028
+ }
18029
+ toChunks(parentStyle = {
18030
+ fg: undefined,
18031
+ bg: undefined,
18032
+ attributes: 0
18033
+ }) {
18034
+ return this.gatherWithInheritedStyle(parentStyle);
18035
+ }
18036
+ getChildren() {
18037
+ return this._children.filter((child) => typeof child !== "string");
18038
+ }
18039
+ getChildrenCount() {
18040
+ return this._children.length;
18041
+ }
18042
+ getRenderable(id) {
18043
+ return this._children.find((child) => typeof child !== "string" && child.id === id);
18044
+ }
18045
+ getRenderableIndex(id) {
18046
+ return this._children.findIndex((child) => isTextNodeRenderable(child) && child.id === id);
18047
+ }
18048
+ get fg() {
18049
+ return this._fg;
18050
+ }
18051
+ set fg(fg2) {
18052
+ if (!fg2) {
18053
+ this._fg = undefined;
18054
+ this.requestRender();
18055
+ return;
18056
+ }
18057
+ this._fg = parseColor(fg2);
18058
+ this.requestRender();
18059
+ }
18060
+ set bg(bg2) {
18061
+ if (!bg2) {
18062
+ this._bg = undefined;
18063
+ this.requestRender();
18064
+ return;
18065
+ }
18066
+ this._bg = parseColor(bg2);
18067
+ this.requestRender();
18068
+ }
18069
+ get bg() {
18070
+ return this._bg;
18071
+ }
18072
+ set attributes(attributes) {
18073
+ this._attributes = attributes;
18074
+ this.requestRender();
18075
+ }
18076
+ get attributes() {
18077
+ return this._attributes;
18078
+ }
18079
+ set link(link2) {
18080
+ this._link = link2;
18081
+ this.requestRender();
18082
+ }
18083
+ get link() {
18084
+ return this._link;
18085
+ }
18086
+ findDescendantById(id) {
18087
+ return;
18088
+ }
18089
+ }
18090
+
18091
+ class RootTextNodeRenderable extends TextNodeRenderable {
18092
+ ctx;
18093
+ textParent;
18094
+ constructor(ctx, options, textParent) {
18095
+ super(options);
18096
+ this.ctx = ctx;
18097
+ this.textParent = textParent;
18098
+ }
18099
+ requestRender() {
18100
+ this.markDirty();
18101
+ this.ctx.requestRender();
18102
+ }
18103
+ }
18104
+
18105
+ // src/renderables/Text.ts
18106
+ class TextRenderable extends TextBufferRenderable {
18107
+ _text;
18108
+ _hasManualStyledText = false;
18109
+ rootTextNode;
18110
+ _contentDefaultOptions = {
18111
+ content: ""
18112
+ };
18113
+ constructor(ctx, options) {
18114
+ super(ctx, options);
18115
+ const content = options.content ?? this._contentDefaultOptions.content;
18116
+ const styledText = typeof content === "string" ? stringToStyledText(content) : content;
18117
+ this._text = styledText;
18118
+ this._hasManualStyledText = options.content !== undefined && content !== "";
18119
+ this.rootTextNode = new RootTextNodeRenderable(ctx, {
18120
+ id: `${this.id}-root`,
18121
+ fg: this._defaultFg,
18122
+ bg: this._defaultBg,
18123
+ attributes: this._defaultAttributes
18124
+ }, this);
18125
+ this.updateTextBuffer(styledText);
18126
+ }
18127
+ updateTextBuffer(styledText) {
18128
+ this.textBuffer.setStyledText(styledText);
18129
+ this.clearChunks(styledText);
18130
+ }
18131
+ clearChunks(styledText) {}
18132
+ get content() {
18133
+ return this._text;
18134
+ }
18135
+ get chunks() {
18136
+ return this._text.chunks;
18137
+ }
18138
+ get textNode() {
18139
+ return this.rootTextNode;
18140
+ }
18141
+ set content(value) {
18142
+ this._hasManualStyledText = true;
18143
+ const styledText = typeof value === "string" ? stringToStyledText(value) : value;
18144
+ if (this._text !== styledText) {
18145
+ this._text = styledText;
18146
+ this.updateTextBuffer(styledText);
18147
+ this.updateTextInfo();
18148
+ }
18149
+ }
18150
+ updateTextFromNodes() {
18151
+ if (this.rootTextNode.isDirty && !this._hasManualStyledText) {
18152
+ const chunks = this.rootTextNode.gatherWithInheritedStyle({
18153
+ fg: this._defaultFg,
18154
+ bg: this._defaultBg,
18155
+ attributes: this._defaultAttributes,
18156
+ link: undefined
18157
+ });
18158
+ this.textBuffer.setStyledText(new StyledText(chunks));
18159
+ this.refreshLocalSelection();
18160
+ this.yogaNode.markDirty();
18161
+ }
18162
+ }
18163
+ add(obj, index) {
18164
+ return this.rootTextNode.add(obj, index);
18165
+ }
18166
+ remove(id) {
18167
+ this.rootTextNode.remove(id);
18168
+ }
18169
+ insertBefore(obj, anchor) {
18170
+ this.rootTextNode.insertBefore(obj, anchor);
18171
+ return this.rootTextNode.children.indexOf(obj);
18172
+ }
18173
+ getTextChildren() {
18174
+ return this.rootTextNode.getChildren();
18175
+ }
18176
+ clear() {
18177
+ this.rootTextNode.clear();
18178
+ const emptyStyledText = stringToStyledText("");
18179
+ this._text = emptyStyledText;
18180
+ this.updateTextBuffer(emptyStyledText);
18181
+ this.updateTextInfo();
18182
+ this.requestRender();
18183
+ }
18184
+ onLifecyclePass = () => {
18185
+ this.updateTextFromNodes();
18186
+ };
18187
+ onFgChanged(newColor) {
18188
+ this.rootTextNode.fg = newColor;
18189
+ }
18190
+ onBgChanged(newColor) {
18191
+ this.rootTextNode.bg = newColor;
18192
+ }
18193
+ onAttributesChanged(newAttributes) {
18194
+ this.rootTextNode.attributes = newAttributes;
18195
+ }
18196
+ destroy() {
18197
+ this.rootTextNode.children.length = 0;
18198
+ super.destroy();
18199
+ }
18200
+ }
18201
+
18202
+ // src/console.ts
18203
+ import { EventEmitter as EventEmitter8 } from "events";
18204
+ import { Console } from "console";
18205
+ import fs from "fs";
18206
+ import path5 from "path";
18207
+ import util2 from "util";
18208
+
18209
+ // src/lib/output.capture.ts
18210
+ import { Writable } from "stream";
18211
+ import { EventEmitter as EventEmitter7 } from "events";
18212
+
18213
+ class Capture extends EventEmitter7 {
18214
+ output = [];
18215
+ constructor() {
18216
+ super();
18217
+ }
18218
+ get size() {
18219
+ return this.output.length;
18220
+ }
18221
+ write(stream, data) {
18222
+ this.output.push({ stream, output: data });
18223
+ this.emit("write", stream, data);
18224
+ }
18225
+ claimOutput() {
18226
+ const output = this.output.map((o) => o.output).join("");
18227
+ this.clear();
18228
+ return output;
18229
+ }
18230
+ clear() {
18231
+ this.output = [];
18232
+ }
18233
+ }
18234
+
18235
+ class CapturedWritableStream extends Writable {
18236
+ stream;
18237
+ capture;
18238
+ isTTY = true;
18239
+ columns = process.stdout.columns || 80;
18240
+ rows = process.stdout.rows || 24;
18241
+ constructor(stream, capture) {
18242
+ super();
18243
+ this.stream = stream;
18244
+ this.capture = capture;
18245
+ }
18246
+ _write(chunk, encoding, callback) {
18247
+ const data = chunk.toString();
18248
+ this.capture.write(this.stream, data);
18249
+ callback();
18250
+ }
18251
+ getColorDepth() {
18252
+ return process.stdout.getColorDepth?.() || 8;
18253
+ }
18254
+ }
18255
+
18256
+ // src/lib/keymapping.ts
18257
+ var defaultKeyAliases = {
18258
+ enter: "return",
18259
+ esc: "escape",
18260
+ kp0: "0",
18261
+ kp1: "1",
18262
+ kp2: "2",
18263
+ kp3: "3",
18264
+ kp4: "4",
18265
+ kp5: "5",
18266
+ kp6: "6",
18267
+ kp7: "7",
18268
+ kp8: "8",
18269
+ kp9: "9",
18270
+ kpdecimal: ".",
18271
+ kpdivide: "/",
18272
+ kpmultiply: "*",
18273
+ kpminus: "-",
18274
+ kpplus: "+",
18275
+ kpenter: "enter",
18276
+ kpequal: "=",
18277
+ kpseparator: ",",
18278
+ kpleft: "left",
18279
+ kpright: "right",
18280
+ kpup: "up",
18281
+ kpdown: "down",
18282
+ kppageup: "pageup",
18283
+ kppagedown: "pagedown",
18284
+ kphome: "home",
18285
+ kpend: "end",
18286
+ kpinsert: "insert",
18287
+ kpdelete: "delete"
18288
+ };
18289
+ function mergeKeyAliases(defaults, custom) {
18290
+ return { ...defaults, ...custom };
18291
+ }
18292
+ function mergeKeyBindings(defaults, custom) {
18293
+ const map = new Map;
18294
+ for (const binding of defaults) {
18295
+ const key = getKeyBindingKey(binding);
18296
+ map.set(key, binding);
18297
+ }
18298
+ for (const binding of custom) {
18299
+ const key = getKeyBindingKey(binding);
18300
+ map.set(key, binding);
18301
+ }
18302
+ return Array.from(map.values());
18303
+ }
18304
+ function getKeyBindingKey(binding) {
18305
+ return `${binding.name}:${binding.ctrl ? 1 : 0}:${binding.shift ? 1 : 0}:${binding.meta ? 1 : 0}:${binding.super ? 1 : 0}`;
18306
+ }
18307
+ function getBaseCodeKeyName(baseCode) {
18308
+ if (baseCode === undefined || baseCode < 32 || baseCode === 127) {
16674
18309
  return;
16675
18310
  }
16676
18311
  try {
@@ -18743,6 +20378,27 @@ function getObjectsInViewport(viewport, objects, direction = "column", padding =
18743
20378
  return visibleChildren;
18744
20379
  }
18745
20380
 
20381
+ // src/lib/render-geometry.ts
20382
+ function calculateRenderGeometry(screenMode, terminalWidth, terminalHeight, footerHeight) {
20383
+ const safeTerminalWidth = Math.max(terminalWidth, 0);
20384
+ const safeTerminalHeight = Math.max(terminalHeight, 0);
20385
+ if (screenMode !== "split-footer") {
20386
+ return {
20387
+ effectiveFooterHeight: 0,
20388
+ renderOffset: 0,
20389
+ renderWidth: safeTerminalWidth,
20390
+ renderHeight: safeTerminalHeight
20391
+ };
20392
+ }
20393
+ const effectiveFooterHeight = Math.min(footerHeight, safeTerminalHeight);
20394
+ return {
20395
+ effectiveFooterHeight,
20396
+ renderOffset: safeTerminalHeight - effectiveFooterHeight,
20397
+ renderWidth: safeTerminalWidth,
20398
+ renderHeight: effectiveFooterHeight
20399
+ };
20400
+ }
20401
+
18746
20402
  // src/lib/terminal-capability-detection.ts
18747
20403
  function isCapabilityResponse(sequence) {
18748
20404
  if (/\x1b\[\?\d+(?:;\d+)*\$y/.test(sequence)) {
@@ -18780,6 +20436,26 @@ function parsePixelResolution(sequence) {
18780
20436
  }
18781
20437
 
18782
20438
  // src/renderer.ts
20439
+ 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;
20440
+ function scaleOscThemeComponent(component) {
20441
+ const value = parseInt(component, 16);
20442
+ const maxValue = (1 << 4 * component.length) - 1;
20443
+ return Math.round(value / maxValue * 255).toString(16).padStart(2, "0");
20444
+ }
20445
+ function oscThemeColorToHex(r, g, b, hex6) {
20446
+ if (hex6) {
20447
+ return `#${hex6.toLowerCase()}`;
20448
+ }
20449
+ if (r && g && b) {
20450
+ return `#${scaleOscThemeComponent(r)}${scaleOscThemeComponent(g)}${scaleOscThemeComponent(b)}`;
20451
+ }
20452
+ return "#000000";
20453
+ }
20454
+ function inferThemeModeFromBackgroundColor(color) {
20455
+ const [r, g, b] = parseColor(color).toInts();
20456
+ const brightness = (r * 299 + g * 587 + b * 114) / 1000;
20457
+ return brightness > 128 ? "light" : "dark";
20458
+ }
18783
20459
  registerEnvVar({
18784
20460
  name: "OTUI_DUMP_CAPTURES",
18785
20461
  description: "Dump captured stdout and console caches when the renderer exit handler runs.",
@@ -18817,6 +20493,9 @@ registerEnvVar({
18817
20493
  default: false
18818
20494
  });
18819
20495
  var DEFAULT_FOOTER_HEIGHT = 12;
20496
+ var MAX_SCROLLBACK_SURFACE_HEIGHT_PASSES = 4;
20497
+ var TRANSPARENT_RGBA = RGBA.fromValues(0, 0, 0, 0);
20498
+ var scrollbackSurfaceCounter = 0;
18820
20499
  function normalizeFooterHeight(footerHeight) {
18821
20500
  if (footerHeight === undefined) {
18822
20501
  return DEFAULT_FOOTER_HEIGHT;
@@ -18843,11 +20522,106 @@ function resolveModes(config) {
18843
20522
  if (externalOutputMode === "capture-stdout" && screenMode !== "split-footer") {
18844
20523
  throw new Error('externalOutputMode "capture-stdout" requires screenMode "split-footer"');
18845
20524
  }
18846
- return {
18847
- screenMode,
18848
- footerHeight,
18849
- externalOutputMode
18850
- };
20525
+ return {
20526
+ screenMode,
20527
+ footerHeight,
20528
+ externalOutputMode
20529
+ };
20530
+ }
20531
+
20532
+ class ExternalOutputQueue {
20533
+ commits = [];
20534
+ get size() {
20535
+ return this.commits.length;
20536
+ }
20537
+ writeSnapshot(commit) {
20538
+ this.commits.push(commit);
20539
+ }
20540
+ claim(limit = Number.POSITIVE_INFINITY) {
20541
+ if (this.commits.length === 0) {
20542
+ return [];
20543
+ }
20544
+ const clampedLimit = Number.isFinite(limit) ? Math.max(1, Math.trunc(limit)) : this.commits.length;
20545
+ if (clampedLimit >= this.commits.length) {
20546
+ const output2 = this.commits;
20547
+ this.commits = [];
20548
+ return output2;
20549
+ }
20550
+ const output = this.commits.slice(0, clampedLimit);
20551
+ this.commits = this.commits.slice(clampedLimit);
20552
+ return output;
20553
+ }
20554
+ clear() {
20555
+ for (const commit of this.commits) {
20556
+ commit.snapshot.destroy();
20557
+ }
20558
+ this.commits = [];
20559
+ }
20560
+ }
20561
+ var CHAR_FLAG_CONTINUATION = 3221225472 >>> 0;
20562
+ var CHAR_FLAG_MASK = 3221225472 >>> 0;
20563
+
20564
+ class ScrollbackSnapshotRenderContext extends EventEmitter9 {
20565
+ width;
20566
+ height;
20567
+ frameId = 0;
20568
+ widthMethod;
20569
+ capabilities = null;
20570
+ hasSelection = false;
20571
+ currentFocusedRenderable = null;
20572
+ keyInput;
20573
+ _internalKeyInput;
20574
+ lifecyclePasses = new Set;
20575
+ constructor(width, height, widthMethod) {
20576
+ super();
20577
+ this.width = width;
20578
+ this.height = height;
20579
+ this.widthMethod = widthMethod;
20580
+ this.keyInput = new KeyHandler;
20581
+ this._internalKeyInput = new InternalKeyHandler;
20582
+ }
20583
+ addToHitGrid(_x, _y, _width, _height, _id) {}
20584
+ pushHitGridScissorRect(_x, _y, _width, _height) {}
20585
+ popHitGridScissorRect() {}
20586
+ clearHitGridScissorRects() {}
20587
+ requestRender() {}
20588
+ setCursorPosition(_x, _y, _visible) {}
20589
+ setCursorStyle(_options) {}
20590
+ setCursorColor(_color) {}
20591
+ setMousePointer(_shape) {}
20592
+ requestLive() {}
20593
+ dropLive() {}
20594
+ getSelection() {
20595
+ return null;
20596
+ }
20597
+ get currentFocusedEditor() {
20598
+ if (!this.currentFocusedRenderable)
20599
+ return null;
20600
+ if (!isEditBufferRenderable(this.currentFocusedRenderable))
20601
+ return null;
20602
+ return this.currentFocusedRenderable;
20603
+ }
20604
+ requestSelectionUpdate() {}
20605
+ focusRenderable(renderable) {
20606
+ this.currentFocusedRenderable = renderable;
20607
+ }
20608
+ blurRenderable(renderable) {
20609
+ if (this.currentFocusedRenderable === renderable) {
20610
+ this.currentFocusedRenderable = null;
20611
+ }
20612
+ }
20613
+ registerLifecyclePass(renderable) {
20614
+ this.lifecyclePasses.add(renderable);
20615
+ }
20616
+ unregisterLifecyclePass(renderable) {
20617
+ this.lifecyclePasses.delete(renderable);
20618
+ }
20619
+ getLifecyclePasses() {
20620
+ return this.lifecyclePasses;
20621
+ }
20622
+ clearSelection() {}
20623
+ startSelection(_renderable, _x, _y) {}
20624
+ updateSelection(_currentRenderable, _x, _y, _options) {}
18851
20625
  }
18852
20626
  var DEFAULT_FORWARDED_ENV_KEYS = [
18853
20627
  "TMUX",
@@ -18969,9 +20743,9 @@ async function createCliRenderer(config = {}) {
18969
20743
  const { screenMode, footerHeight } = resolveModes(config);
18970
20744
  const width = stdout.columns || 80;
18971
20745
  const height = stdout.rows || 24;
18972
- const renderHeight = screenMode === "split-footer" ? footerHeight : height;
20746
+ const geometry = calculateRenderGeometry(screenMode, width, height, footerHeight);
18973
20747
  const ziglib = resolveRenderLib();
18974
- const rendererPtr = ziglib.createRenderer(width, renderHeight, {
20748
+ const rendererPtr = ziglib.createRenderer(geometry.renderWidth, geometry.renderHeight, {
18975
20749
  remote: config.remote ?? false,
18976
20750
  testing: config.testing ?? false
18977
20751
  });
@@ -19088,6 +20862,8 @@ class CliRenderer extends EventEmitter9 {
19088
20862
  animationRequest = new Map;
19089
20863
  resizeTimeoutId = null;
19090
20864
  capabilityTimeoutId = null;
20865
+ splitStartupSeedTimeoutId = null;
20866
+ pendingSplitStartupCursorSeed = false;
19091
20867
  resizeDebounceDelay = 100;
19092
20868
  enableMouseMovement = false;
19093
20869
  _useMouse = true;
@@ -19095,6 +20871,7 @@ class CliRenderer extends EventEmitter9 {
19095
20871
  _screenMode = "alternate-screen";
19096
20872
  _footerHeight = DEFAULT_FOOTER_HEIGHT;
19097
20873
  _externalOutputMode = "passthrough";
20874
+ clearOnShutdown = true;
19098
20875
  _suspendedMouseEnabled = false;
19099
20876
  _previousControlState = "idle" /* IDLE */;
19100
20877
  capturedRenderable;
@@ -19105,15 +20882,15 @@ class CliRenderer extends EventEmitter9 {
19105
20882
  clipboard;
19106
20883
  _splitHeight = 0;
19107
20884
  renderOffset = 0;
20885
+ splitTailColumn = 0;
20886
+ pendingSplitFooterTransition = null;
20887
+ forceFullRepaintRequested = false;
20888
+ maxSplitCommitsPerFrame = 8;
19108
20889
  _terminalWidth = 0;
19109
20890
  _terminalHeight = 0;
19110
20891
  _terminalIsSetup = false;
20892
+ externalOutputQueue = new ExternalOutputQueue;
19111
20893
  realStdoutWrite;
19112
- captureCallback = () => {
19113
- if (this._splitHeight > 0) {
19114
- this.requestRender();
19115
- }
19116
- };
19117
20894
  _useConsole = true;
19118
20895
  sigwinchHandler = (() => {
19119
20896
  const width = this.stdout.columns || 80;
@@ -19137,6 +20914,10 @@ class CliRenderer extends EventEmitter9 {
19137
20914
  _paletteDetectionPromise = null;
19138
20915
  _onDestroy;
19139
20916
  _themeMode = null;
20917
+ _themeModeSource = "none";
20918
+ _themeFallbackPending = true;
20919
+ _themeOscForeground = null;
20920
+ _themeOscBackground = null;
19140
20921
  _terminalFocusState = null;
19141
20922
  sequenceHandlers = [];
19142
20923
  prependedInputHandlers = [];
@@ -19152,8 +20933,15 @@ class CliRenderer extends EventEmitter9 {
19152
20933
  }).bind(this);
19153
20934
  dumpOutputCache(optionalMessage = "") {
19154
20935
  const cachedLogs = this.console.getCachedLogs();
19155
- const capturedOutput = capture.claimOutput();
19156
- if (capturedOutput.length > 0 || cachedLogs.length > 0) {
20936
+ const capturedConsoleOutput = capture.claimOutput();
20937
+ const capturedExternalOutputCommits = this.externalOutputQueue.claim();
20938
+ let capturedExternalOutput = "";
20939
+ for (const commit of capturedExternalOutputCommits) {
20940
+ capturedExternalOutput += `[snapshot ${commit.snapshot.width}x${commit.snapshot.height}]
20941
+ `;
20942
+ commit.snapshot.destroy();
20943
+ }
20944
+ if (capturedConsoleOutput.length > 0 || capturedExternalOutput.length > 0 || cachedLogs.length > 0) {
19157
20945
  this.realStdoutWrite.call(this.stdout, optionalMessage);
19158
20946
  }
19159
20947
  if (cachedLogs.length > 0) {
@@ -19161,11 +20949,18 @@ class CliRenderer extends EventEmitter9 {
19161
20949
  `);
19162
20950
  this.realStdoutWrite.call(this.stdout, cachedLogs);
19163
20951
  }
19164
- if (capturedOutput.length > 0) {
20952
+ if (capturedConsoleOutput.length > 0) {
20953
+ this.realStdoutWrite.call(this.stdout, `
20954
+ Captured console output:
20955
+ `);
20956
+ this.realStdoutWrite.call(this.stdout, capturedConsoleOutput + `
20957
+ `);
20958
+ }
20959
+ if (capturedExternalOutput.length > 0) {
19165
20960
  this.realStdoutWrite.call(this.stdout, `
19166
- Captured output:
20961
+ Captured external output:
19167
20962
  `);
19168
- this.realStdoutWrite.call(this.stdout, capturedOutput + `
20963
+ this.realStdoutWrite.call(this.stdout, capturedExternalOutput + `
19169
20964
  `);
19170
20965
  }
19171
20966
  this.realStdoutWrite.call(this.stdout, ANSI.reset);
@@ -19194,13 +20989,18 @@ Captured output:
19194
20989
  this.lib = lib;
19195
20990
  this._terminalWidth = stdout.columns ?? width;
19196
20991
  this._terminalHeight = stdout.rows ?? height;
19197
- this.width = width;
19198
- this.height = height;
19199
20992
  this._useThread = config.useThread === undefined ? false : config.useThread;
19200
20993
  const { screenMode, footerHeight, externalOutputMode } = resolveModes(config);
20994
+ this._externalOutputMode = externalOutputMode;
20995
+ const initialGeometry = calculateRenderGeometry(screenMode, this._terminalWidth, this._terminalHeight, footerHeight);
20996
+ this.width = initialGeometry.renderWidth;
20997
+ this.height = initialGeometry.renderHeight;
20998
+ this._splitHeight = initialGeometry.effectiveFooterHeight;
20999
+ this.renderOffset = screenMode === "split-footer" ? 0 : initialGeometry.renderOffset;
19201
21000
  this._footerHeight = footerHeight;
19202
- this._screenMode = screenMode;
19203
21001
  this.rendererPtr = rendererPtr;
21002
+ this.clearOnShutdown = config.clearOnShutdown ?? true;
21003
+ this.lib.setClearOnShutdown(this.rendererPtr, this.clearOnShutdown);
19204
21004
  const forwardEnvKeys = config.forwardEnvKeys ?? [...DEFAULT_FORWARDED_ENV_KEYS];
19205
21005
  for (const key of forwardEnvKeys) {
19206
21006
  const value = process.env[key];
@@ -19235,7 +21035,6 @@ Captured output:
19235
21035
  this.postProcessFns = config.postProcessFns || [];
19236
21036
  this.prependedInputHandlers = config.prependInputHandlers || [];
19237
21037
  this.root = new RootRenderable(this);
19238
- this._openConsoleOnError = config.openConsoleOnError ?? false;
19239
21038
  if (this.memorySnapshotInterval > 0) {
19240
21039
  this.startMemorySnapshotTimer();
19241
21040
  }
@@ -19269,7 +21068,8 @@ Captured output:
19269
21068
  kittyKeyboardEnabled: useKittyForParsing,
19270
21069
  privateCapabilityRepliesActive: false,
19271
21070
  pixelResolutionQueryActive: false,
19272
- explicitWidthCprActive: false
21071
+ explicitWidthCprActive: false,
21072
+ startupCursorCprActive: false
19273
21073
  },
19274
21074
  clock: this.clock
19275
21075
  });
@@ -19279,7 +21079,8 @@ Captured output:
19279
21079
  });
19280
21080
  this.consoleMode = config.consoleMode ?? "console-overlay";
19281
21081
  this.applyScreenMode(screenMode, false, false);
19282
- this.externalOutputMode = externalOutputMode;
21082
+ this.stdout.write = externalOutputMode === "capture-stdout" ? this.interceptStdoutWrite : this.realStdoutWrite;
21083
+ this._openConsoleOnError = config.openConsoleOnError ?? false;
19283
21084
  this._onDestroy = config.onDestroy;
19284
21085
  global.requestAnimationFrame = (callback) => {
19285
21086
  const id = CliRenderer.animationFrameId++;
@@ -19437,163 +21238,795 @@ Captured output:
19437
21238
  return;
19438
21239
  }
19439
21240
  try {
19440
- await this.loop();
21241
+ await this.loop();
21242
+ } finally {
21243
+ this.updateScheduled = false;
21244
+ this.resolveIdleIfNeeded();
21245
+ }
21246
+ }
21247
+ get consoleMode() {
21248
+ return this._useConsole ? "console-overlay" : "disabled";
21249
+ }
21250
+ set consoleMode(mode) {
21251
+ this._useConsole = mode === "console-overlay";
21252
+ if (this._useConsole) {
21253
+ this.console.activate();
21254
+ } else {
21255
+ this.console.deactivate();
21256
+ }
21257
+ }
21258
+ get isRunning() {
21259
+ return this._isRunning;
21260
+ }
21261
+ isIdleNow() {
21262
+ return !this._isRunning && !this.rendering && !this.renderTimeout && !this.updateScheduled && !this.immediateRerenderRequested;
21263
+ }
21264
+ resolveIdleIfNeeded() {
21265
+ if (!this.isIdleNow())
21266
+ return;
21267
+ const resolvers = this.idleResolvers.splice(0);
21268
+ for (const resolve4 of resolvers) {
21269
+ resolve4();
21270
+ }
21271
+ }
21272
+ idle() {
21273
+ if (this._isDestroyed)
21274
+ return Promise.resolve();
21275
+ if (this.isIdleNow())
21276
+ return Promise.resolve();
21277
+ return new Promise((resolve4) => {
21278
+ this.idleResolvers.push(resolve4);
21279
+ });
21280
+ }
21281
+ get resolution() {
21282
+ return this._resolution;
21283
+ }
21284
+ get console() {
21285
+ return this._console;
21286
+ }
21287
+ get keyInput() {
21288
+ return this._keyHandler;
21289
+ }
21290
+ get _internalKeyInput() {
21291
+ return this._keyHandler;
21292
+ }
21293
+ get terminalWidth() {
21294
+ return this._terminalWidth;
21295
+ }
21296
+ get terminalHeight() {
21297
+ return this._terminalHeight;
21298
+ }
21299
+ get useThread() {
21300
+ return this._useThread;
21301
+ }
21302
+ get targetFps() {
21303
+ return this._targetFps;
21304
+ }
21305
+ set targetFps(targetFps) {
21306
+ this._targetFps = targetFps;
21307
+ this.targetFrameTime = 1000 / this._targetFps;
21308
+ }
21309
+ get maxFps() {
21310
+ return this._maxFps;
21311
+ }
21312
+ set maxFps(maxFps) {
21313
+ this._maxFps = maxFps;
21314
+ this.minTargetFrameTime = 1000 / this._maxFps;
21315
+ }
21316
+ get useMouse() {
21317
+ return this._useMouse;
21318
+ }
21319
+ set useMouse(useMouse) {
21320
+ if (this._useMouse === useMouse)
21321
+ return;
21322
+ this._useMouse = useMouse;
21323
+ if (useMouse) {
21324
+ this.enableMouse();
21325
+ } else {
21326
+ this.disableMouse();
21327
+ }
21328
+ }
21329
+ get screenMode() {
21330
+ return this._screenMode;
21331
+ }
21332
+ set screenMode(mode) {
21333
+ if (this.externalOutputMode === "capture-stdout" && mode !== "split-footer") {
21334
+ throw new Error('externalOutputMode "capture-stdout" requires screenMode "split-footer"');
21335
+ }
21336
+ this.applyScreenMode(mode);
21337
+ }
21338
+ get footerHeight() {
21339
+ return this._footerHeight;
21340
+ }
21341
+ set footerHeight(footerHeight) {
21342
+ const normalizedFooterHeight = normalizeFooterHeight(footerHeight);
21343
+ if (normalizedFooterHeight === this._footerHeight) {
21344
+ return;
21345
+ }
21346
+ this._footerHeight = normalizedFooterHeight;
21347
+ if (this.screenMode === "split-footer") {
21348
+ this.applyScreenMode("split-footer");
21349
+ }
21350
+ }
21351
+ get externalOutputMode() {
21352
+ return this._externalOutputMode;
21353
+ }
21354
+ set externalOutputMode(mode) {
21355
+ if (mode === "capture-stdout" && this.screenMode !== "split-footer") {
21356
+ throw new Error('externalOutputMode "capture-stdout" requires screenMode "split-footer"');
21357
+ }
21358
+ const previousMode = this._externalOutputMode;
21359
+ if (previousMode === mode) {
21360
+ return;
21361
+ }
21362
+ if (previousMode === "capture-stdout" && mode === "passthrough" && this._splitHeight > 0) {
21363
+ this.flushPendingSplitOutputBeforeTransition();
21364
+ }
21365
+ this._externalOutputMode = mode;
21366
+ this.stdout.write = mode === "capture-stdout" ? this.interceptStdoutWrite : this.realStdoutWrite;
21367
+ if (this._screenMode === "split-footer" && this._splitHeight > 0 && mode === "capture-stdout") {
21368
+ this.clearPendingSplitFooterTransition();
21369
+ this.resetSplitScrollback(this.getSplitCursorSeedRows());
21370
+ return;
21371
+ }
21372
+ if (this._screenMode === "split-footer" && this._splitHeight > 0 && previousMode === "capture-stdout" && mode === "passthrough") {
21373
+ this.clearPendingSplitFooterTransition();
21374
+ return;
21375
+ }
21376
+ this.syncSplitFooterState();
21377
+ }
21378
+ get liveRequestCount() {
21379
+ return this.liveRequestCounter;
21380
+ }
21381
+ get currentControlState() {
21382
+ return this._controlState;
21383
+ }
21384
+ get capabilities() {
21385
+ return this._capabilities;
21386
+ }
21387
+ get themeMode() {
21388
+ return this._themeMode;
21389
+ }
21390
+ waitForThemeMode(timeoutMs = 1000) {
21391
+ if (!Number.isFinite(timeoutMs) || timeoutMs < 0) {
21392
+ throw new Error("timeoutMs must be a non-negative finite number");
21393
+ }
21394
+ if (this._themeMode !== null || this._isDestroyed || timeoutMs === 0) {
21395
+ return Promise.resolve(this._themeMode);
21396
+ }
21397
+ return new Promise((resolve4) => {
21398
+ let timeoutHandle = null;
21399
+ const cleanup = () => {
21400
+ if (timeoutHandle !== null) {
21401
+ this.clock.clearTimeout(timeoutHandle);
21402
+ timeoutHandle = null;
21403
+ }
21404
+ this.off("theme_mode" /* THEME_MODE */, handleThemeMode);
21405
+ this.off("destroy" /* DESTROY */, handleDestroy);
21406
+ };
21407
+ const finish = () => {
21408
+ cleanup();
21409
+ resolve4(this._themeMode);
21410
+ };
21411
+ const handleThemeMode = () => {
21412
+ finish();
21413
+ };
21414
+ const handleDestroy = () => {
21415
+ finish();
21416
+ };
21417
+ this.on("theme_mode" /* THEME_MODE */, handleThemeMode);
21418
+ this.on("destroy" /* DESTROY */, handleDestroy);
21419
+ timeoutHandle = this.clock.setTimeout(finish, timeoutMs);
21420
+ });
21421
+ }
21422
+ getDebugInputs() {
21423
+ return [...this._debugInputs];
21424
+ }
21425
+ get useKittyKeyboard() {
21426
+ return this.lib.getKittyKeyboardFlags(this.rendererPtr) > 0;
21427
+ }
21428
+ set useKittyKeyboard(use) {
21429
+ const flags = use ? KITTY_FLAG_DISAMBIGUATE | KITTY_FLAG_ALTERNATE_KEYS : 0;
21430
+ this.lib.setKittyKeyboardFlags(this.rendererPtr, flags);
21431
+ }
21432
+ createScrollbackSurface(options = {}) {
21433
+ if (this._screenMode !== "split-footer" || this._externalOutputMode !== "capture-stdout") {
21434
+ throw new Error('createScrollbackSurface requires screenMode "split-footer" and externalOutputMode "capture-stdout"');
21435
+ }
21436
+ const renderer = this;
21437
+ const surfaceId = scrollbackSurfaceCounter++;
21438
+ const startOnNewLine = options.startOnNewLine ?? true;
21439
+ const firstLineOffset = !startOnNewLine && renderer.splitTailColumn > 0 && renderer.splitTailColumn < renderer.width ? renderer.splitTailColumn : 0;
21440
+ const snapshotContext = new ScrollbackSnapshotRenderContext(renderer.width, 1, renderer.widthMethod);
21441
+ let firstLineOffsetOwner = null;
21442
+ const renderContext = Object.create(snapshotContext);
21443
+ Object.defineProperty(renderContext, "claimFirstLineOffset", {
21444
+ value: (renderable) => {
21445
+ if (firstLineOffsetOwner?.isDestroyed) {
21446
+ firstLineOffsetOwner = null;
21447
+ }
21448
+ if (firstLineOffsetOwner) {
21449
+ return 0;
21450
+ }
21451
+ firstLineOffsetOwner = renderable ?? null;
21452
+ return firstLineOffset;
21453
+ },
21454
+ enumerable: true,
21455
+ configurable: true
21456
+ });
21457
+ const internalRoot = new RootRenderable(renderContext);
21458
+ const publicRoot = new BoxRenderable(renderContext, {
21459
+ id: `scrollback-surface-root-${surfaceId}`,
21460
+ position: "absolute",
21461
+ left: 0,
21462
+ top: 0,
21463
+ width: renderer.width,
21464
+ height: "auto",
21465
+ border: false,
21466
+ backgroundColor: "transparent",
21467
+ shouldFill: false,
21468
+ flexDirection: "column"
21469
+ });
21470
+ internalRoot.add(publicRoot);
21471
+ let surfaceWidth = renderer.width;
21472
+ let surfaceHeight = 1;
21473
+ let surfaceWidthMethod = renderer.widthMethod;
21474
+ let surfaceDestroyed = false;
21475
+ let hasRendered = false;
21476
+ let nextCommitStartOnNewLine = startOnNewLine;
21477
+ let backingBuffer = OptimizedBuffer.create(surfaceWidth, surfaceHeight, surfaceWidthMethod, {
21478
+ id: `scrollback-surface-buffer-${surfaceId}`
21479
+ });
21480
+ const destroyListener = () => {
21481
+ destroySurface();
21482
+ };
21483
+ const assertNotDestroyed = () => {
21484
+ if (surfaceDestroyed) {
21485
+ throw new Error("ScrollbackSurface is destroyed");
21486
+ }
21487
+ };
21488
+ const assertRendered = () => {
21489
+ if (!hasRendered) {
21490
+ throw new Error("ScrollbackSurface.commitRows requires render() before commitRows()");
21491
+ }
21492
+ };
21493
+ const assertGeometryStillCurrent = () => {
21494
+ if (renderer.width !== surfaceWidth || renderer.widthMethod !== surfaceWidthMethod) {
21495
+ throw new Error("ScrollbackSurface.commitRows requires render() after renderer geometry changes");
21496
+ }
21497
+ };
21498
+ const assertRowRange = (startRow, endRowExclusive) => {
21499
+ if (!Number.isInteger(startRow) || !Number.isInteger(endRowExclusive)) {
21500
+ throw new Error("ScrollbackSurface.commitRows requires finite integer row bounds");
21501
+ }
21502
+ if (startRow < 0) {
21503
+ throw new Error("ScrollbackSurface.commitRows requires startRow >= 0");
21504
+ }
21505
+ if (endRowExclusive < startRow) {
21506
+ throw new Error("ScrollbackSurface.commitRows requires endRowExclusive >= startRow");
21507
+ }
21508
+ if (endRowExclusive > surfaceHeight) {
21509
+ throw new Error("ScrollbackSurface.commitRows row range exceeds rendered surface height");
21510
+ }
21511
+ };
21512
+ const collectPendingCodeRenderables = (node) => {
21513
+ const pending = [];
21514
+ if (node instanceof CodeRenderable && node.isHighlighting) {
21515
+ pending.push(node);
21516
+ }
21517
+ for (const child of node.getChildren()) {
21518
+ pending.push(...collectPendingCodeRenderables(child));
21519
+ }
21520
+ return pending;
21521
+ };
21522
+ const waitForPendingHighlights = async (pending, timeoutMs) => {
21523
+ await new Promise((resolve4, reject) => {
21524
+ let settled = false;
21525
+ const timeoutHandle = renderer.clock.setTimeout(() => {
21526
+ if (settled) {
21527
+ return;
21528
+ }
21529
+ settled = true;
21530
+ reject(new Error("ScrollbackSurface.settle timed out waiting for CodeRenderable highlighting"));
21531
+ }, timeoutMs);
21532
+ Promise.all(pending.map((renderable) => renderable.highlightingDone)).then(() => {
21533
+ if (settled) {
21534
+ return;
21535
+ }
21536
+ settled = true;
21537
+ renderer.clock.clearTimeout(timeoutHandle);
21538
+ resolve4();
21539
+ }, (error) => {
21540
+ if (settled) {
21541
+ return;
21542
+ }
21543
+ settled = true;
21544
+ renderer.clock.clearTimeout(timeoutHandle);
21545
+ reject(error);
21546
+ });
21547
+ });
21548
+ };
21549
+ const renderSurface = () => {
21550
+ assertNotDestroyed();
21551
+ const width = renderer.width;
21552
+ const widthMethod = renderer.widthMethod;
21553
+ snapshotContext.width = width;
21554
+ snapshotContext.widthMethod = widthMethod;
21555
+ publicRoot.width = width;
21556
+ const renderPass = (height) => {
21557
+ snapshotContext.height = height;
21558
+ internalRoot.resize(width, height);
21559
+ backingBuffer.resize(width, height);
21560
+ backingBuffer.clear(TRANSPARENT_RGBA);
21561
+ snapshotContext.frameId += 1;
21562
+ internalRoot.render(backingBuffer, 0);
21563
+ };
21564
+ let targetHeight = Math.max(1, surfaceHeight);
21565
+ if (surfaceWidthMethod !== widthMethod) {
21566
+ backingBuffer.destroy();
21567
+ backingBuffer = OptimizedBuffer.create(width, targetHeight, widthMethod, {
21568
+ id: `scrollback-surface-buffer-${surfaceId}`
21569
+ });
21570
+ } else {
21571
+ backingBuffer.resize(width, targetHeight);
21572
+ }
21573
+ for (let pass = 0;pass < MAX_SCROLLBACK_SURFACE_HEIGHT_PASSES; pass += 1) {
21574
+ renderPass(targetHeight);
21575
+ const measuredHeight = Math.max(1, publicRoot.height);
21576
+ if (measuredHeight === targetHeight) {
21577
+ surfaceWidth = width;
21578
+ surfaceHeight = measuredHeight;
21579
+ surfaceWidthMethod = widthMethod;
21580
+ hasRendered = true;
21581
+ return;
21582
+ }
21583
+ targetHeight = measuredHeight;
21584
+ }
21585
+ renderPass(targetHeight);
21586
+ surfaceWidth = width;
21587
+ surfaceHeight = targetHeight;
21588
+ surfaceWidthMethod = widthMethod;
21589
+ hasRendered = true;
21590
+ };
21591
+ const settleSurface = async (timeoutMs = 2000) => {
21592
+ assertNotDestroyed();
21593
+ const startedAt = renderer.clock.now();
21594
+ renderSurface();
21595
+ while (true) {
21596
+ assertNotDestroyed();
21597
+ const pending = collectPendingCodeRenderables(publicRoot);
21598
+ if (pending.length === 0) {
21599
+ return;
21600
+ }
21601
+ const remainingMs = timeoutMs - (renderer.clock.now() - startedAt);
21602
+ if (remainingMs <= 0) {
21603
+ throw new Error("ScrollbackSurface.settle timed out waiting for CodeRenderable highlighting");
21604
+ }
21605
+ await waitForPendingHighlights(pending, remainingMs);
21606
+ assertNotDestroyed();
21607
+ renderSurface();
21608
+ }
21609
+ };
21610
+ const commitRows = (startRow, endRowExclusive, commitOptions = {}) => {
21611
+ assertNotDestroyed();
21612
+ assertRendered();
21613
+ assertGeometryStillCurrent();
21614
+ assertRowRange(startRow, endRowExclusive);
21615
+ if (startRow === endRowExclusive) {
21616
+ return;
21617
+ }
21618
+ const rowCount = endRowExclusive - startRow;
21619
+ const commitBuffer = OptimizedBuffer.create(surfaceWidth, rowCount, surfaceWidthMethod, {
21620
+ id: `scrollback-surface-commit-${surfaceId}`
21621
+ });
21622
+ try {
21623
+ commitBuffer.drawFrameBuffer(0, 0, backingBuffer, 0, startRow, surfaceWidth, rowCount);
21624
+ renderer.enqueueRenderedScrollbackCommit({
21625
+ snapshot: commitBuffer,
21626
+ rowColumns: commitOptions.rowColumns,
21627
+ startOnNewLine: nextCommitStartOnNewLine,
21628
+ trailingNewline: commitOptions.trailingNewline ?? false
21629
+ });
21630
+ nextCommitStartOnNewLine = false;
21631
+ } catch (error) {
21632
+ commitBuffer.destroy();
21633
+ throw error;
21634
+ }
21635
+ };
21636
+ const destroySurface = () => {
21637
+ if (surfaceDestroyed) {
21638
+ return;
21639
+ }
21640
+ surfaceDestroyed = true;
21641
+ renderer.off("destroy" /* DESTROY */, destroyListener);
21642
+ let destroyError = null;
21643
+ try {
21644
+ internalRoot.destroyRecursively();
21645
+ } catch (error) {
21646
+ destroyError = error;
21647
+ }
21648
+ try {
21649
+ backingBuffer.destroy();
21650
+ } catch (error) {
21651
+ if (destroyError === null) {
21652
+ destroyError = error;
21653
+ }
21654
+ }
21655
+ renderContext.removeAllListeners();
21656
+ snapshotContext.removeAllListeners();
21657
+ if (destroyError !== null) {
21658
+ throw destroyError;
21659
+ }
21660
+ };
21661
+ renderer.on("destroy" /* DESTROY */, destroyListener);
21662
+ return {
21663
+ get renderContext() {
21664
+ return renderContext;
21665
+ },
21666
+ get root() {
21667
+ return publicRoot;
21668
+ },
21669
+ get width() {
21670
+ return surfaceWidth;
21671
+ },
21672
+ get height() {
21673
+ return surfaceHeight;
21674
+ },
21675
+ get isDestroyed() {
21676
+ return surfaceDestroyed;
21677
+ },
21678
+ render: renderSurface,
21679
+ settle: settleSurface,
21680
+ commitRows,
21681
+ destroy: destroySurface
21682
+ };
21683
+ }
21684
+ writeToScrollback(write) {
21685
+ if (this._screenMode !== "split-footer" || this._externalOutputMode !== "capture-stdout") {
21686
+ throw new Error('writeToScrollback requires screenMode "split-footer" and externalOutputMode "capture-stdout"');
21687
+ }
21688
+ const snapshotContext = new ScrollbackSnapshotRenderContext(this.width, this.height, this.widthMethod);
21689
+ const snapshot = write({
21690
+ width: this.width,
21691
+ widthMethod: this.widthMethod,
21692
+ tailColumn: this.splitTailColumn,
21693
+ renderContext: snapshotContext
21694
+ });
21695
+ if (!snapshot || !snapshot.root) {
21696
+ throw new Error("writeToScrollback must return a snapshot root renderable");
21697
+ }
21698
+ let renderFailed = false;
21699
+ let snapshotRoot = null;
21700
+ let snapshotBuffer = null;
21701
+ try {
21702
+ const rootRenderable = snapshot.root;
21703
+ const snapshotWidth = this.getSnapshotWidth(snapshot.width, rootRenderable.width);
21704
+ const snapshotHeight = this.getSnapshotHeight(snapshot.height, rootRenderable.height);
21705
+ snapshotContext.width = snapshotWidth;
21706
+ snapshotContext.height = snapshotHeight;
21707
+ snapshotContext.widthMethod = this.widthMethod;
21708
+ snapshotRoot = new RootRenderable(snapshotContext);
21709
+ snapshotBuffer = OptimizedBuffer.create(snapshotWidth, snapshotHeight, this.widthMethod, {
21710
+ id: "scrollback-snapshot-commit"
21711
+ });
21712
+ snapshotRoot.add(rootRenderable);
21713
+ snapshotRoot.render(snapshotBuffer, 0);
21714
+ this.enqueueRenderedScrollbackCommit({
21715
+ snapshot: snapshotBuffer,
21716
+ rowColumns: snapshot.rowColumns,
21717
+ startOnNewLine: snapshot.startOnNewLine,
21718
+ trailingNewline: snapshot.trailingNewline
21719
+ });
21720
+ } catch (error) {
21721
+ renderFailed = true;
21722
+ snapshotBuffer?.destroy();
21723
+ throw error;
19441
21724
  } finally {
19442
- this.updateScheduled = false;
19443
- this.resolveIdleIfNeeded();
21725
+ let cleanupError = null;
21726
+ try {
21727
+ if (snapshotRoot) {
21728
+ snapshotRoot.destroyRecursively();
21729
+ } else {
21730
+ snapshot.root.destroyRecursively();
21731
+ }
21732
+ } catch (error) {
21733
+ cleanupError = error;
21734
+ }
21735
+ try {
21736
+ snapshot.teardown?.();
21737
+ } catch (error) {
21738
+ if (cleanupError === null) {
21739
+ cleanupError = error;
21740
+ }
21741
+ }
21742
+ if (!renderFailed && cleanupError) {
21743
+ throw cleanupError;
21744
+ }
19444
21745
  }
19445
21746
  }
19446
- get consoleMode() {
19447
- return this._useConsole ? "console-overlay" : "disabled";
19448
- }
19449
- set consoleMode(mode) {
19450
- this._useConsole = mode === "console-overlay";
19451
- if (this._useConsole) {
19452
- this.console.activate();
19453
- } else {
19454
- this.console.deactivate();
21747
+ getSnapshotWidth(value, fallback) {
21748
+ const rawValue = value ?? fallback;
21749
+ if (!Number.isFinite(rawValue)) {
21750
+ throw new Error("writeToScrollback produced a non-finite width");
19455
21751
  }
21752
+ return Math.min(Math.max(Math.trunc(rawValue), 1), Math.max(this.width, 1));
19456
21753
  }
19457
- get isRunning() {
19458
- return this._isRunning;
21754
+ getSnapshotHeight(value, fallback) {
21755
+ const rawValue = value ?? fallback;
21756
+ if (!Number.isFinite(rawValue)) {
21757
+ throw new Error("writeToScrollback produced a non-finite height");
21758
+ }
21759
+ return Math.max(Math.trunc(rawValue), 1);
19459
21760
  }
19460
- isIdleNow() {
19461
- return !this._isRunning && !this.rendering && !this.renderTimeout && !this.updateScheduled && !this.immediateRerenderRequested;
21761
+ getSnapshotRowWidths(snapshot, rowColumns) {
21762
+ const widths = [];
21763
+ const limit = Math.min(Math.max(Math.trunc(rowColumns), 0), snapshot.width);
21764
+ const chars = snapshot.buffers.char;
21765
+ for (let y = 0;y < snapshot.height; y += 1) {
21766
+ let x = limit;
21767
+ while (x > 0) {
21768
+ const cp = chars[y * snapshot.width + x - 1];
21769
+ if (cp === 0 || (cp & CHAR_FLAG_MASK) === CHAR_FLAG_CONTINUATION) {
21770
+ x -= 1;
21771
+ continue;
21772
+ }
21773
+ break;
21774
+ }
21775
+ widths.push(x);
21776
+ }
21777
+ return widths;
19462
21778
  }
19463
- resolveIdleIfNeeded() {
19464
- if (!this.isIdleNow())
21779
+ publishSplitTailColumns(columns) {
21780
+ if (columns <= 0) {
19465
21781
  return;
19466
- const resolvers = this.idleResolvers.splice(0);
19467
- for (const resolve4 of resolvers) {
19468
- resolve4();
19469
21782
  }
21783
+ const width = Math.max(this.width, 1);
21784
+ let tail = this.splitTailColumn;
21785
+ let remaining = columns;
21786
+ while (remaining > 0) {
21787
+ if (tail >= width) {
21788
+ tail = 0;
21789
+ }
21790
+ const step = Math.min(remaining, width - tail);
21791
+ tail += step;
21792
+ remaining -= step;
21793
+ if (remaining > 0 && tail >= width) {
21794
+ tail = 0;
21795
+ }
21796
+ }
21797
+ this.splitTailColumn = tail;
19470
21798
  }
19471
- idle() {
19472
- if (this._isDestroyed)
19473
- return Promise.resolve();
19474
- if (this.isIdleNow())
19475
- return Promise.resolve();
19476
- return new Promise((resolve4) => {
19477
- this.idleResolvers.push(resolve4);
19478
- });
21799
+ recordSplitCommit(commit) {
21800
+ if (commit.startOnNewLine && this.splitTailColumn > 0) {
21801
+ this.splitTailColumn = 0;
21802
+ }
21803
+ const rowWidths = this.getSnapshotRowWidths(commit.snapshot, commit.rowColumns);
21804
+ for (const [index, rowWidth] of rowWidths.entries()) {
21805
+ this.publishSplitTailColumns(rowWidth);
21806
+ if (index < rowWidths.length - 1 || commit.trailingNewline) {
21807
+ this.splitTailColumn = 0;
21808
+ }
21809
+ }
19479
21810
  }
19480
- get resolution() {
19481
- return this._resolution;
21811
+ enqueueRenderedScrollbackCommit(options) {
21812
+ if (this._screenMode !== "split-footer" || this._externalOutputMode !== "capture-stdout") {
21813
+ throw new Error('scrollback commit requires screenMode "split-footer" and externalOutputMode "capture-stdout"');
21814
+ }
21815
+ const rowColumns = Math.min(Math.max(Math.trunc(options.rowColumns ?? options.snapshot.width), 0), options.snapshot.width);
21816
+ this.enqueueSplitCommit({
21817
+ snapshot: options.snapshot,
21818
+ rowColumns,
21819
+ startOnNewLine: options.startOnNewLine ?? true,
21820
+ trailingNewline: options.trailingNewline ?? true
21821
+ });
21822
+ this.requestRender();
19482
21823
  }
19483
- get console() {
19484
- return this._console;
21824
+ enqueueSplitCommit(commit) {
21825
+ this.recordSplitCommit(commit);
21826
+ this.externalOutputQueue.writeSnapshot(commit);
21827
+ }
21828
+ createStdoutSnapshotCommit(line, trailingNewline) {
21829
+ const snapshotContext = new ScrollbackSnapshotRenderContext(this.width, 1, this.widthMethod);
21830
+ const maxWidth = Math.max(1, this.width);
21831
+ const lineCells = [...line];
21832
+ const rowColumns = Math.min(lineCells.length, maxWidth);
21833
+ const renderedLine = lineCells.slice(0, maxWidth).join("");
21834
+ const snapshotRoot = new RootRenderable(snapshotContext);
21835
+ const snapshotRenderable = new TextRenderable(snapshotContext, {
21836
+ id: "captured-stdout-snapshot",
21837
+ position: "absolute",
21838
+ left: 0,
21839
+ top: 0,
21840
+ width: Math.max(1, rowColumns),
21841
+ height: 1,
21842
+ content: renderedLine
21843
+ });
21844
+ const snapshotBuffer = OptimizedBuffer.create(Math.max(1, rowColumns), 1, this.widthMethod, {
21845
+ id: "captured-stdout-snapshot"
21846
+ });
21847
+ try {
21848
+ snapshotRoot.add(snapshotRenderable);
21849
+ snapshotRoot.render(snapshotBuffer, 0);
21850
+ return {
21851
+ snapshot: snapshotBuffer,
21852
+ rowColumns,
21853
+ startOnNewLine: false,
21854
+ trailingNewline
21855
+ };
21856
+ } catch (error) {
21857
+ snapshotBuffer.destroy();
21858
+ throw error;
21859
+ } finally {
21860
+ snapshotRoot.destroyRecursively();
21861
+ }
19485
21862
  }
19486
- get keyInput() {
19487
- return this._keyHandler;
21863
+ splitStdoutRows(text) {
21864
+ const rows = [];
21865
+ let current = "";
21866
+ for (const char of text) {
21867
+ if (char === "\r") {
21868
+ current = "";
21869
+ continue;
21870
+ }
21871
+ if (char === `
21872
+ `) {
21873
+ rows.push({ line: current, trailingNewline: true });
21874
+ current = "";
21875
+ continue;
21876
+ }
21877
+ current += char;
21878
+ }
21879
+ if (current.length > 0) {
21880
+ rows.push({ line: current, trailingNewline: false });
21881
+ }
21882
+ return rows;
19488
21883
  }
19489
- get _internalKeyInput() {
19490
- return this._keyHandler;
21884
+ createStdoutSnapshotCommits(text) {
21885
+ if (text.length === 0) {
21886
+ return [];
21887
+ }
21888
+ const commits = [];
21889
+ const chunkWidth = Math.max(1, this.width);
21890
+ for (const row of this.splitStdoutRows(text)) {
21891
+ const rowCells = [...row.line];
21892
+ if (rowCells.length === 0) {
21893
+ commits.push(this.createStdoutSnapshotCommit("", row.trailingNewline));
21894
+ continue;
21895
+ }
21896
+ let offset = 0;
21897
+ while (offset < rowCells.length) {
21898
+ const chunk = rowCells.slice(offset, offset + chunkWidth).join("");
21899
+ offset += chunkWidth;
21900
+ const isLastChunk = offset >= rowCells.length;
21901
+ commits.push(this.createStdoutSnapshotCommit(chunk, isLastChunk ? row.trailingNewline : false));
21902
+ }
21903
+ }
21904
+ return commits;
19491
21905
  }
19492
- get terminalWidth() {
19493
- return this._terminalWidth;
21906
+ flushPendingSplitCommits(forceFooterRepaint = false) {
21907
+ const commits = this.externalOutputQueue.claim(this.maxSplitCommitsPerFrame);
21908
+ let hasCommittedOutput = false;
21909
+ const lastCommitIndex = commits.length - 1;
21910
+ for (const [index, commit] of commits.entries()) {
21911
+ const forceCommit = forceFooterRepaint && index === lastCommitIndex;
21912
+ const beginFrame = index === 0;
21913
+ const finalizeFrame = index === lastCommitIndex;
21914
+ try {
21915
+ this.renderOffset = this.lib.commitSplitFooterSnapshot(this.rendererPtr, commit.snapshot, commit.rowColumns, commit.startOnNewLine, commit.trailingNewline, this.getSplitPinnedRenderOffset(), forceCommit, beginFrame, finalizeFrame);
21916
+ hasCommittedOutput = true;
21917
+ } finally {
21918
+ commit.snapshot.destroy();
21919
+ }
21920
+ }
21921
+ if (!hasCommittedOutput) {
21922
+ this.renderOffset = this.lib.repaintSplitFooter(this.rendererPtr, this.getSplitPinnedRenderOffset(), forceFooterRepaint);
21923
+ }
21924
+ this.pendingSplitFooterTransition = null;
21925
+ if (this.externalOutputQueue.size > 0) {
21926
+ this.requestRender();
21927
+ }
19494
21928
  }
19495
- get terminalHeight() {
19496
- return this._terminalHeight;
21929
+ interceptStdoutWrite = (chunk, encoding, callback) => {
21930
+ const resolvedCallback = typeof encoding === "function" ? encoding : callback;
21931
+ const resolvedEncoding = typeof encoding === "string" ? encoding : undefined;
21932
+ const text = typeof chunk === "string" ? chunk : chunk?.toString(resolvedEncoding) ?? "";
21933
+ if (this._externalOutputMode === "capture-stdout" && this._screenMode === "split-footer" && this._splitHeight > 0) {
21934
+ const commits = this.createStdoutSnapshotCommits(text);
21935
+ for (const commit of commits) {
21936
+ this.enqueueSplitCommit(commit);
21937
+ }
21938
+ if (commits.length > 0) {
21939
+ this.requestRender();
21940
+ }
21941
+ }
21942
+ if (typeof resolvedCallback === "function") {
21943
+ process.nextTick(resolvedCallback);
21944
+ }
21945
+ return true;
21946
+ };
21947
+ getSplitPinnedRenderOffset() {
21948
+ return this._screenMode === "split-footer" ? Math.max(this._terminalHeight - this._splitHeight, 0) : 0;
19497
21949
  }
19498
- get useThread() {
19499
- return this._useThread;
21950
+ getSplitCursorSeedRows() {
21951
+ const cursorState = this.lib.getCursorState(this.rendererPtr);
21952
+ const cursorRow = Number.isFinite(cursorState.y) ? Math.max(Math.trunc(cursorState.y), 1) : 1;
21953
+ return Math.min(cursorRow, Math.max(this._terminalHeight, 1));
19500
21954
  }
19501
- get targetFps() {
19502
- return this._targetFps;
21955
+ flushPendingSplitOutputBeforeTransition(forceFooterRepaint = false) {
21956
+ if (this._screenMode !== "split-footer" || this._splitHeight <= 0 || this._externalOutputMode !== "capture-stdout") {
21957
+ return;
21958
+ }
21959
+ if (this.externalOutputQueue.size === 0 && !forceFooterRepaint) {
21960
+ return;
21961
+ }
21962
+ this.flushPendingSplitCommits(forceFooterRepaint);
19503
21963
  }
19504
- set targetFps(targetFps) {
19505
- this._targetFps = targetFps;
19506
- this.targetFrameTime = 1000 / this._targetFps;
21964
+ resetSplitScrollback(seedRows = 0) {
21965
+ this.splitTailColumn = 0;
21966
+ this.renderOffset = this.lib.resetSplitScrollback(this.rendererPtr, seedRows, this.getSplitPinnedRenderOffset());
19507
21967
  }
19508
- get maxFps() {
19509
- return this._maxFps;
21968
+ syncSplitScrollback() {
21969
+ this.renderOffset = this.lib.syncSplitScrollback(this.rendererPtr, this.getSplitPinnedRenderOffset());
19510
21970
  }
19511
- set maxFps(maxFps) {
19512
- this._maxFps = maxFps;
19513
- this.minTargetFrameTime = 1000 / this._maxFps;
21971
+ clearPendingSplitFooterTransition() {
21972
+ if (this.pendingSplitFooterTransition === null) {
21973
+ return;
21974
+ }
21975
+ this.pendingSplitFooterTransition = null;
21976
+ this.lib.clearPendingSplitFooterTransition(this.rendererPtr);
19514
21977
  }
19515
- get useMouse() {
19516
- return this._useMouse;
21978
+ setPendingSplitFooterTransition(transition) {
21979
+ this.pendingSplitFooterTransition = transition;
21980
+ this.lib.setPendingSplitFooterTransition(this.rendererPtr, transition.mode === "viewport-scroll" ? 1 : 2, transition.sourceTopLine, transition.sourceHeight, transition.targetTopLine, transition.targetHeight);
19517
21981
  }
19518
- set useMouse(useMouse) {
19519
- if (this._useMouse === useMouse)
21982
+ syncSplitFooterState() {
21983
+ const splitActive = this._screenMode === "split-footer" && this._splitHeight > 0;
21984
+ if (!splitActive) {
21985
+ this.clearPendingSplitFooterTransition();
21986
+ this.splitTailColumn = 0;
21987
+ this.lib.resetSplitScrollback(this.rendererPtr, 0, 0);
21988
+ this.renderOffset = 0;
21989
+ this.lib.setRenderOffset(this.rendererPtr, this.renderOffset);
19520
21990
  return;
19521
- this._useMouse = useMouse;
19522
- if (useMouse) {
19523
- this.enableMouse();
21991
+ }
21992
+ if (this._externalOutputMode === "capture-stdout") {
21993
+ this.syncSplitScrollback();
19524
21994
  } else {
19525
- this.disableMouse();
21995
+ this.clearPendingSplitFooterTransition();
21996
+ this.splitTailColumn = 0;
21997
+ this.lib.resetSplitScrollback(this.rendererPtr, 0, 0);
21998
+ this.renderOffset = this.getSplitPinnedRenderOffset();
21999
+ this.lib.setRenderOffset(this.rendererPtr, this.renderOffset);
19526
22000
  }
19527
22001
  }
19528
- get screenMode() {
19529
- return this._screenMode;
19530
- }
19531
- set screenMode(mode) {
19532
- if (this.externalOutputMode === "capture-stdout" && mode !== "split-footer") {
19533
- throw new Error('externalOutputMode "capture-stdout" requires screenMode "split-footer"');
22002
+ clearStaleSplitSurfaceRows(previousTopLine, previousHeight, nextTopLine, nextHeight) {
22003
+ if (!this._terminalIsSetup || previousHeight <= 0 || this._terminalHeight <= 0) {
22004
+ return;
19534
22005
  }
19535
- this.applyScreenMode(mode);
19536
- }
19537
- get footerHeight() {
19538
- return this._footerHeight;
19539
- }
19540
- set footerHeight(footerHeight) {
19541
- const normalizedFooterHeight = normalizeFooterHeight(footerHeight);
19542
- if (normalizedFooterHeight === this._footerHeight) {
22006
+ const terminalBottom = this._terminalHeight;
22007
+ const previousStart = Math.max(1, previousTopLine);
22008
+ const previousEnd = Math.min(terminalBottom, previousTopLine + previousHeight - 1);
22009
+ if (previousEnd < previousStart) {
19543
22010
  return;
19544
22011
  }
19545
- this._footerHeight = normalizedFooterHeight;
19546
- if (this.screenMode === "split-footer") {
19547
- this.applyScreenMode("split-footer");
22012
+ const nextStart = Math.max(1, nextTopLine);
22013
+ const nextEnd = Math.min(terminalBottom, nextTopLine + Math.max(nextHeight, 0) - 1);
22014
+ let clear = "";
22015
+ for (let line = previousStart;line <= previousEnd; line += 1) {
22016
+ if (line >= nextStart && line <= nextEnd) {
22017
+ continue;
22018
+ }
22019
+ clear += `${ANSI.moveCursor(line, 1)}\x1B[2K`;
19548
22020
  }
19549
- }
19550
- get externalOutputMode() {
19551
- return this._externalOutputMode;
19552
- }
19553
- set externalOutputMode(mode) {
19554
- if (mode === "capture-stdout" && this.screenMode !== "split-footer") {
19555
- throw new Error('externalOutputMode "capture-stdout" requires screenMode "split-footer"');
22021
+ if (clear.length > 0) {
22022
+ this.writeOut(clear);
19556
22023
  }
19557
- this._externalOutputMode = mode;
19558
- this.stdout.write = mode === "capture-stdout" ? this.interceptStdoutWrite : this.realStdoutWrite;
19559
- }
19560
- get liveRequestCount() {
19561
- return this.liveRequestCounter;
19562
- }
19563
- get currentControlState() {
19564
- return this._controlState;
19565
- }
19566
- get capabilities() {
19567
- return this._capabilities;
19568
- }
19569
- get themeMode() {
19570
- return this._themeMode;
19571
- }
19572
- getDebugInputs() {
19573
- return [...this._debugInputs];
19574
- }
19575
- get useKittyKeyboard() {
19576
- return this.lib.getKittyKeyboardFlags(this.rendererPtr) > 0;
19577
- }
19578
- set useKittyKeyboard(use) {
19579
- const flags = use ? KITTY_FLAG_DISAMBIGUATE | KITTY_FLAG_ALTERNATE_KEYS : 0;
19580
- this.lib.setKittyKeyboardFlags(this.rendererPtr, flags);
19581
22024
  }
19582
- interceptStdoutWrite = (chunk, encoding, callback) => {
19583
- const text = chunk.toString();
19584
- capture.write("stdout", text);
19585
- if (this._splitHeight > 0) {
19586
- this.requestRender();
19587
- }
19588
- if (typeof callback === "function") {
19589
- process.nextTick(callback);
19590
- }
19591
- return true;
19592
- };
19593
22025
  applyScreenMode(screenMode, emitResize = true, requestRender = true) {
19594
22026
  const prevScreenMode = this._screenMode;
19595
22027
  const prevSplitHeight = this._splitHeight;
19596
- const nextSplitHeight = screenMode === "split-footer" ? this._footerHeight : 0;
22028
+ const nextGeometry = calculateRenderGeometry(screenMode, this._terminalWidth, this._terminalHeight, this._footerHeight);
22029
+ const nextSplitHeight = nextGeometry.effectiveFooterHeight;
19597
22030
  if (prevScreenMode === screenMode && prevSplitHeight === nextSplitHeight) {
19598
22031
  return;
19599
22032
  }
@@ -19601,10 +22034,24 @@ Captured output:
19601
22034
  const nextUseAlternateScreen = screenMode === "alternate-screen";
19602
22035
  const terminalScreenModeChanged = this._terminalIsSetup && prevUseAlternateScreen !== nextUseAlternateScreen;
19603
22036
  const leavingSplitFooter = prevSplitHeight > 0 && nextSplitHeight === 0;
22037
+ if (this._terminalIsSetup && prevSplitHeight > 0) {
22038
+ this.flushPendingSplitOutputBeforeTransition();
22039
+ }
22040
+ const previousSurfaceTopLine = this.renderOffset + 1;
22041
+ const previousPinnedRenderOffset = Math.max(this._terminalHeight - prevSplitHeight, 0);
22042
+ const splitWasSettled = prevSplitHeight === 0 || this.renderOffset >= previousPinnedRenderOffset;
22043
+ const shouldUseViewportScrollTransitions = this._externalOutputMode !== "capture-stdout" || splitWasSettled;
22044
+ const shouldDeferSplitFooterResizeTransition = this._terminalIsSetup && prevScreenMode === "split-footer" && screenMode === "split-footer" && this._externalOutputMode === "capture-stdout" && prevSplitHeight > 0 && nextSplitHeight > 0 && !terminalScreenModeChanged;
22045
+ const splitStartupSeedBlocksFirstNativeFrame = this.pendingSplitStartupCursorSeed && this.splitStartupSeedTimeoutId !== null;
22046
+ const splitTransitionSourceTopLine = this.pendingSplitFooterTransition?.sourceTopLine ?? previousSurfaceTopLine;
22047
+ const splitTransitionSourceHeight = this.pendingSplitFooterTransition?.sourceHeight ?? prevSplitHeight;
22048
+ const splitTransitionMode = this.pendingSplitFooterTransition?.mode ?? (splitWasSettled ? "viewport-scroll" : "clear-stale-rows");
19604
22049
  if (this._terminalIsSetup && leavingSplitFooter) {
19605
- this.flushStdoutCache(this._terminalHeight, true);
22050
+ this.clearPendingSplitFooterTransition();
22051
+ this.renderOffset = 0;
22052
+ this.lib.setRenderOffset(this.rendererPtr, 0);
19606
22053
  }
19607
- if (this._terminalIsSetup && !terminalScreenModeChanged) {
22054
+ if (this._terminalIsSetup && !terminalScreenModeChanged && shouldUseViewportScrollTransitions && !shouldDeferSplitFooterResizeTransition) {
19608
22055
  if (prevSplitHeight === 0 && nextSplitHeight > 0) {
19609
22056
  const freedLines = this._terminalHeight - nextSplitHeight;
19610
22057
  const scrollDown = ANSI.scrollDown(freedLines);
@@ -19619,18 +22066,39 @@ Captured output:
19619
22066
  this.writeOut(scrollUp);
19620
22067
  }
19621
22068
  }
19622
- if (prevSplitHeight === 0 && nextSplitHeight > 0) {
19623
- capture.on("write", this.captureCallback);
19624
- } else if (prevSplitHeight > 0 && nextSplitHeight === 0) {
19625
- capture.off("write", this.captureCallback);
19626
- }
19627
22069
  this._screenMode = screenMode;
19628
22070
  this._splitHeight = nextSplitHeight;
19629
- this.renderOffset = nextSplitHeight > 0 ? this._terminalHeight - nextSplitHeight : 0;
19630
- this.width = this._terminalWidth;
19631
- this.height = nextSplitHeight > 0 ? nextSplitHeight : this._terminalHeight;
19632
- this.lib.setRenderOffset(this.rendererPtr, this.renderOffset);
22071
+ this.width = nextGeometry.renderWidth;
22072
+ this.height = nextGeometry.renderHeight;
19633
22073
  this.lib.resizeRenderer(this.rendererPtr, this.width, this.height);
22074
+ if (this._screenMode === "split-footer" && this._externalOutputMode === "capture-stdout") {
22075
+ if (prevScreenMode !== "split-footer") {
22076
+ this.resetSplitScrollback(this.getSplitCursorSeedRows());
22077
+ } else {
22078
+ this.syncSplitScrollback();
22079
+ }
22080
+ if (shouldDeferSplitFooterResizeTransition) {
22081
+ if (splitStartupSeedBlocksFirstNativeFrame) {
22082
+ this.clearPendingSplitFooterTransition();
22083
+ } else {
22084
+ this.setPendingSplitFooterTransition({
22085
+ mode: splitTransitionMode,
22086
+ sourceTopLine: splitTransitionSourceTopLine,
22087
+ sourceHeight: splitTransitionSourceHeight,
22088
+ targetTopLine: this.renderOffset + 1,
22089
+ targetHeight: nextSplitHeight
22090
+ });
22091
+ }
22092
+ this.forceFullRepaintRequested = true;
22093
+ } else if (!shouldUseViewportScrollTransitions && prevSplitHeight > 0 && nextSplitHeight > 0) {
22094
+ this.clearPendingSplitFooterTransition();
22095
+ this.clearStaleSplitSurfaceRows(previousSurfaceTopLine, prevSplitHeight, this.renderOffset + 1, nextSplitHeight);
22096
+ } else {
22097
+ this.clearPendingSplitFooterTransition();
22098
+ }
22099
+ } else {
22100
+ this.syncSplitFooterState();
22101
+ }
19634
22102
  this.nextRenderBuffer = this.lib.getNextBuffer(this.rendererPtr);
19635
22103
  this.currentRenderBuffer = this.lib.getCurrentBuffer(this.rendererPtr);
19636
22104
  this._console.resize(this.width, this.height);
@@ -19650,12 +22118,18 @@ Captured output:
19650
22118
  }
19651
22119
  }
19652
22120
  flushStdoutCache(space, force = false) {
19653
- if (capture.size === 0 && !force)
22121
+ if (this.externalOutputQueue.size === 0 && !force)
19654
22122
  return false;
19655
- const output = capture.claimOutput();
19656
- const rendererStartLine = this._terminalHeight - this._splitHeight;
22123
+ const outputCommits = this.externalOutputQueue.claim();
22124
+ let output = "";
22125
+ for (const commit of outputCommits) {
22126
+ output += `[snapshot ${commit.snapshot.width}x${commit.snapshot.height}]
22127
+ `;
22128
+ commit.snapshot.destroy();
22129
+ }
22130
+ const rendererStartLine = this.renderOffset + 1;
19657
22131
  const flush = ANSI.moveCursorAndClear(rendererStartLine, 1);
19658
- const outputLine = this._terminalHeight - this._splitHeight;
22132
+ const outputLine = this.renderOffset + 1;
19659
22133
  const move = ANSI.moveCursor(outputLine, 1);
19660
22134
  let clear = "";
19661
22135
  if (space > 0) {
@@ -19697,9 +22171,11 @@ Captured output:
19697
22171
  if (this._terminalIsSetup)
19698
22172
  return;
19699
22173
  this._terminalIsSetup = true;
22174
+ const startupCursorCprActive = this._screenMode === "split-footer" && this._externalOutputMode === "capture-stdout";
19700
22175
  this.updateStdinParserProtocolContext({
19701
22176
  privateCapabilityRepliesActive: true,
19702
- explicitWidthCprActive: true
22177
+ explicitWidthCprActive: true,
22178
+ startupCursorCprActive
19703
22179
  });
19704
22180
  this.lib.setupTerminal(this.rendererPtr, this._screenMode === "alternate-screen");
19705
22181
  this._capabilities = this.lib.getTerminalCapabilities(this.rendererPtr);
@@ -19713,15 +22189,40 @@ Captured output:
19713
22189
  }
19714
22190
  this.capabilityTimeoutId = this.clock.setTimeout(() => {
19715
22191
  this.capabilityTimeoutId = null;
22192
+ this.pendingSplitStartupCursorSeed = false;
22193
+ if (this.splitStartupSeedTimeoutId !== null) {
22194
+ this.clock.clearTimeout(this.splitStartupSeedTimeoutId);
22195
+ this.splitStartupSeedTimeoutId = null;
22196
+ }
22197
+ if (this._screenMode === "split-footer" && this._externalOutputMode === "capture-stdout") {
22198
+ this.requestRender();
22199
+ }
19716
22200
  this.removeInputHandler(this.capabilityHandler);
19717
22201
  this.updateStdinParserProtocolContext({
19718
22202
  privateCapabilityRepliesActive: false,
19719
- explicitWidthCprActive: false
22203
+ explicitWidthCprActive: false,
22204
+ startupCursorCprActive: false
19720
22205
  }, true);
19721
22206
  }, 5000);
19722
22207
  if (this._useMouse) {
19723
22208
  this.enableMouse();
19724
22209
  }
22210
+ if (this._screenMode === "split-footer" && this._externalOutputMode === "capture-stdout") {
22211
+ this.pendingSplitStartupCursorSeed = true;
22212
+ if (this.splitStartupSeedTimeoutId !== null) {
22213
+ this.clock.clearTimeout(this.splitStartupSeedTimeoutId);
22214
+ }
22215
+ this.splitStartupSeedTimeoutId = this.clock.setTimeout(() => {
22216
+ this.splitStartupSeedTimeoutId = null;
22217
+ if (!this.pendingSplitStartupCursorSeed) {
22218
+ return;
22219
+ }
22220
+ this.updateStdinParserProtocolContext({ startupCursorCprActive: false });
22221
+ if (this._screenMode === "split-footer" && this._externalOutputMode === "capture-stdout") {
22222
+ this.requestRender();
22223
+ }
22224
+ }, 120);
22225
+ }
19725
22226
  this.queryPixelResolution();
19726
22227
  }
19727
22228
  stdinListener = ((chunk) => {
@@ -19757,14 +22258,32 @@ Captured output:
19757
22258
  this.oscSubscribers.delete(handler);
19758
22259
  };
19759
22260
  }
19760
- capabilityHandler = ((sequence) => {
19761
- if (isCapabilityResponse(sequence)) {
19762
- this.lib.processCapabilityResponse(this.rendererPtr, sequence);
19763
- this._capabilities = this.lib.getTerminalCapabilities(this.rendererPtr);
19764
- this.emit("capabilities" /* CAPABILITIES */, this._capabilities);
19765
- return true;
22261
+ processCapabilitySequence(sequence, hasCursorReport) {
22262
+ const hasStandardCapabilitySignature = isCapabilityResponse(sequence);
22263
+ const shouldProcessAsCapability = hasStandardCapabilitySignature || hasCursorReport && this.capabilityTimeoutId !== null;
22264
+ if (!shouldProcessAsCapability) {
22265
+ return false;
19766
22266
  }
19767
- return false;
22267
+ this.lib.processCapabilityResponse(this.rendererPtr, sequence);
22268
+ this._capabilities = this.lib.getTerminalCapabilities(this.rendererPtr);
22269
+ this.emit("capabilities" /* CAPABILITIES */, this._capabilities);
22270
+ const hadPendingSplitStartupCursorSeed = this.pendingSplitStartupCursorSeed;
22271
+ if (hadPendingSplitStartupCursorSeed && hasCursorReport && this._screenMode === "split-footer" && this._externalOutputMode === "capture-stdout") {
22272
+ this.resetSplitScrollback(this.getSplitCursorSeedRows());
22273
+ this.clearPendingSplitFooterTransition();
22274
+ this.pendingSplitStartupCursorSeed = false;
22275
+ this.updateStdinParserProtocolContext({ startupCursorCprActive: false });
22276
+ if (this.splitStartupSeedTimeoutId !== null) {
22277
+ this.clock.clearTimeout(this.splitStartupSeedTimeoutId);
22278
+ this.splitStartupSeedTimeoutId = null;
22279
+ }
22280
+ this.requestRender();
22281
+ }
22282
+ const consumeStartupCursorReport = hadPendingSplitStartupCursorSeed && hasCursorReport && this.splitStartupSeedTimeoutId !== null;
22283
+ return hasStandardCapabilitySignature || consumeStartupCursorReport;
22284
+ }
22285
+ capabilityHandler = ((sequence) => {
22286
+ return this.processCapabilitySequence(sequence, false);
19768
22287
  }).bind(this);
19769
22288
  focusHandler = ((sequence) => {
19770
22289
  if (sequence === "\x1B[I") {
@@ -19790,21 +22309,50 @@ Captured output:
19790
22309
  }).bind(this);
19791
22310
  themeModeHandler = ((sequence) => {
19792
22311
  if (sequence === "\x1B[?997;1n") {
19793
- if (this._themeMode !== "dark") {
19794
- this._themeMode = "dark";
19795
- this.emit("theme_mode" /* THEME_MODE */, "dark");
19796
- }
22312
+ this.applyThemeMode("dark", "csi");
22313
+ this._themeFallbackPending = false;
19797
22314
  return true;
19798
22315
  }
19799
22316
  if (sequence === "\x1B[?997;2n") {
19800
- if (this._themeMode !== "light") {
19801
- this._themeMode = "light";
19802
- this.emit("theme_mode" /* THEME_MODE */, "light");
22317
+ this.applyThemeMode("light", "csi");
22318
+ this._themeFallbackPending = false;
22319
+ return true;
22320
+ }
22321
+ let handledOscThemeResponse = false;
22322
+ let match;
22323
+ OSC_THEME_RESPONSE.lastIndex = 0;
22324
+ while (match = OSC_THEME_RESPONSE.exec(sequence)) {
22325
+ handledOscThemeResponse = true;
22326
+ const color = oscThemeColorToHex(match[2], match[3], match[4], match[5]);
22327
+ if (match[1] === "10") {
22328
+ this._themeOscForeground = color;
22329
+ } else {
22330
+ this._themeOscBackground = color;
19803
22331
  }
22332
+ }
22333
+ if (!handledOscThemeResponse) {
22334
+ return false;
22335
+ }
22336
+ if (!this._themeFallbackPending) {
19804
22337
  return true;
19805
22338
  }
19806
- return false;
22339
+ if (this._themeOscForeground && this._themeOscBackground) {
22340
+ this.applyThemeMode(inferThemeModeFromBackgroundColor(this._themeOscBackground), "osc");
22341
+ this._themeFallbackPending = false;
22342
+ }
22343
+ return true;
19807
22344
  }).bind(this);
22345
+ applyThemeMode(mode, source) {
22346
+ if (source === "osc" && this._themeModeSource === "csi") {
22347
+ return;
22348
+ }
22349
+ const changed = this._themeMode !== mode;
22350
+ this._themeMode = mode;
22351
+ this._themeModeSource = source;
22352
+ if (changed) {
22353
+ this.emit("theme_mode" /* THEME_MODE */, mode);
22354
+ }
22355
+ }
19808
22356
  dispatchSequenceHandlers(sequence) {
19809
22357
  if (this._debugModeEnabled) {
19810
22358
  this._debugInputs.push({
@@ -19849,6 +22397,9 @@ Captured output:
19849
22397
  subscriber(event.sequence);
19850
22398
  }
19851
22399
  }
22400
+ if (event.protocol === "cpr" && this.processCapabilitySequence(event.sequence, true)) {
22401
+ return;
22402
+ }
19852
22403
  this.dispatchSequenceHandlers(event.sequence);
19853
22404
  return;
19854
22405
  }
@@ -20135,28 +22686,45 @@ Captured output:
20135
22686
  processResize(width, height) {
20136
22687
  if (width === this._terminalWidth && height === this._terminalHeight)
20137
22688
  return;
22689
+ if (this._terminalIsSetup && this._controlState !== "explicit_suspended" /* EXPLICIT_SUSPENDED */) {
22690
+ this.flushPendingSplitOutputBeforeTransition();
22691
+ }
22692
+ const pendingSplitFooterTransition = this.pendingSplitFooterTransition;
22693
+ const previousGeometry = calculateRenderGeometry(this._screenMode, this._terminalWidth, this._terminalHeight, this._footerHeight);
20138
22694
  const prevWidth = this._terminalWidth;
22695
+ const previousTerminalHeight = this._terminalHeight;
22696
+ const visiblePreviousSplitHeight = pendingSplitFooterTransition?.sourceHeight ?? previousGeometry.effectiveFooterHeight;
20139
22697
  this._terminalWidth = width;
20140
22698
  this._terminalHeight = height;
20141
22699
  this.queryPixelResolution();
20142
22700
  this.setCapturedRenderable(undefined);
20143
22701
  this.stdinParser?.resetMouseState();
20144
- if (this._splitHeight > 0) {
20145
- if (width < prevWidth) {
20146
- const start = this._terminalHeight - this._splitHeight * 2;
20147
- const flush = ANSI.moveCursorAndClear(start, 1);
22702
+ const nextGeometry = calculateRenderGeometry(this._screenMode, this._terminalWidth, this._terminalHeight, this._footerHeight);
22703
+ const splitFooterActive = this._screenMode === "split-footer";
22704
+ if (splitFooterActive) {
22705
+ let clearStart = null;
22706
+ if (width < prevWidth && visiblePreviousSplitHeight > 0) {
22707
+ clearStart = Math.max(previousTerminalHeight - visiblePreviousSplitHeight * 2, 1);
22708
+ }
22709
+ if (pendingSplitFooterTransition !== null) {
22710
+ clearStart = clearStart === null ? pendingSplitFooterTransition.sourceTopLine : Math.min(clearStart, pendingSplitFooterTransition.sourceTopLine);
22711
+ }
22712
+ if (clearStart !== null) {
22713
+ const flush = ANSI.moveCursorAndClear(clearStart, 1);
20148
22714
  this.writeOut(flush);
20149
22715
  }
20150
- this.renderOffset = height - this._splitHeight;
20151
- this.width = width;
20152
- this.height = this._splitHeight;
20153
22716
  this.currentRenderBuffer.clear(this.backgroundColor);
20154
- this.lib.setRenderOffset(this.rendererPtr, this.renderOffset);
20155
- } else {
20156
- this.width = width;
20157
- this.height = height;
20158
22717
  }
22718
+ this.clearPendingSplitFooterTransition();
22719
+ this._splitHeight = nextGeometry.effectiveFooterHeight;
22720
+ this.width = nextGeometry.renderWidth;
22721
+ this.height = nextGeometry.renderHeight;
20159
22722
  this.lib.resizeRenderer(this.rendererPtr, this.width, this.height);
22723
+ if (this._screenMode === "split-footer" && this._externalOutputMode === "capture-stdout") {
22724
+ this.syncSplitScrollback();
22725
+ } else {
22726
+ this.syncSplitFooterState();
22727
+ }
20160
22728
  this.nextRenderBuffer = this.lib.getNextBuffer(this.rendererPtr);
20161
22729
  this.currentRenderBuffer = this.lib.getCurrentBuffer(this.rendererPtr);
20162
22730
  this._console.resize(this.width, this.height);
@@ -20304,6 +22872,9 @@ Captured output:
20304
22872
  this._previousControlState = this._controlState;
20305
22873
  this._controlState = "explicit_suspended" /* EXPLICIT_SUSPENDED */;
20306
22874
  this.internalPause();
22875
+ if (this._terminalIsSetup) {
22876
+ this.flushPendingSplitOutputBeforeTransition(true);
22877
+ }
20307
22878
  this._suspendedMouseEnabled = this._useMouse;
20308
22879
  this.disableMouse();
20309
22880
  this.removeExitListeners();
@@ -20311,7 +22882,8 @@ Captured output:
20311
22882
  this.updateStdinParserProtocolContext({
20312
22883
  privateCapabilityRepliesActive: false,
20313
22884
  pixelResolutionQueryActive: false,
20314
- explicitWidthCprActive: false
22885
+ explicitWidthCprActive: false,
22886
+ startupCursorCprActive: false
20315
22887
  });
20316
22888
  this.stdinParser?.reset();
20317
22889
  this.stdin.removeListener("data", this.stdinListener);
@@ -20330,6 +22902,9 @@ Captured output:
20330
22902
  this.stdin.resume();
20331
22903
  this.addExitListeners();
20332
22904
  this.lib.resumeRenderer(this.rendererPtr);
22905
+ if (this._screenMode === "split-footer" && this._splitHeight > 0) {
22906
+ this.syncSplitFooterState();
22907
+ }
20333
22908
  if (this._suspendedMouseEnabled) {
20334
22909
  this.enableMouse();
20335
22910
  }
@@ -20391,7 +22966,6 @@ Captured output:
20391
22966
  process.removeListener("unhandledRejection", this.handleError);
20392
22967
  process.removeListener("warning", this.warningHandler);
20393
22968
  process.removeListener("beforeExit", this.exitHandler);
20394
- capture.removeListener("write", this.captureCallback);
20395
22969
  this.removeExitListeners();
20396
22970
  if (this.resizeTimeoutId !== null) {
20397
22971
  this.clock.clearTimeout(this.resizeTimeoutId);
@@ -20401,6 +22975,10 @@ Captured output:
20401
22975
  this.clock.clearTimeout(this.capabilityTimeoutId);
20402
22976
  this.capabilityTimeoutId = null;
20403
22977
  }
22978
+ if (this.splitStartupSeedTimeoutId !== null) {
22979
+ this.clock.clearTimeout(this.splitStartupSeedTimeoutId);
22980
+ this.splitStartupSeedTimeoutId = null;
22981
+ }
20404
22982
  if (this.memorySnapshotTimer) {
20405
22983
  this.clock.clearInterval(this.memorySnapshotTimer);
20406
22984
  this.memorySnapshotTimer = null;
@@ -20414,7 +22992,8 @@ Captured output:
20414
22992
  this.updateStdinParserProtocolContext({
20415
22993
  privateCapabilityRepliesActive: false,
20416
22994
  pixelResolutionQueryActive: false,
20417
- explicitWidthCprActive: false
22995
+ explicitWidthCprActive: false,
22996
+ startupCursorCprActive: false
20418
22997
  }, true);
20419
22998
  this._useMouse = false;
20420
22999
  this.setCapturedRenderable(undefined);
@@ -20456,6 +23035,16 @@ Captured output:
20456
23035
  this.stdinParser = null;
20457
23036
  this.oscSubscribers.clear();
20458
23037
  this._console.destroy();
23038
+ if (this._splitHeight > 0 && this._terminalIsSetup && this._controlState !== "explicit_suspended" /* EXPLICIT_SUSPENDED */) {
23039
+ this.flushPendingSplitOutputBeforeTransition(true);
23040
+ this.renderOffset = 0;
23041
+ if (this.clearOnShutdown) {
23042
+ this.lib.setRenderOffset(this.rendererPtr, 0);
23043
+ }
23044
+ }
23045
+ this._externalOutputMode = "passthrough";
23046
+ this.stdout.write = this.realStdoutWrite;
23047
+ this.externalOutputQueue.clear();
20459
23048
  this.lib.destroyRenderer(this.rendererPtr);
20460
23049
  rendererTracker.removeRenderer(this);
20461
23050
  if (this._onDestroy) {
@@ -20564,12 +23153,21 @@ Captured output:
20564
23153
  console.error("Rendering called concurrently");
20565
23154
  throw new Error("Rendering called concurrently");
20566
23155
  }
20567
- let force = false;
20568
- if (this._splitHeight > 0) {
20569
- force = this.flushStdoutCache(this._splitHeight);
20570
- }
20571
23156
  this.renderingNative = true;
20572
- this.lib.render(this.rendererPtr, force);
23157
+ if (this.pendingSplitStartupCursorSeed && this.splitStartupSeedTimeoutId !== null && this._splitHeight > 0 && this._externalOutputMode === "capture-stdout") {
23158
+ this.renderingNative = false;
23159
+ return;
23160
+ }
23161
+ if (this._splitHeight > 0 && this._externalOutputMode === "capture-stdout") {
23162
+ const forceSplitRepaint = this.forceFullRepaintRequested;
23163
+ this.forceFullRepaintRequested = false;
23164
+ this.flushPendingSplitCommits(forceSplitRepaint);
23165
+ this.pendingSplitFooterTransition = null;
23166
+ } else {
23167
+ this.forceFullRepaintRequested = false;
23168
+ this.pendingSplitFooterTransition = null;
23169
+ this.lib.render(this.rendererPtr, false);
23170
+ }
20573
23171
  this.renderingNative = false;
20574
23172
  }
20575
23173
  collectStatSample(frameTime) {
@@ -20751,7 +23349,7 @@ Captured output:
20751
23349
  }
20752
23350
  }
20753
23351
 
20754
- 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 };
23352
+ 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 };
20755
23353
 
20756
- //# debugId=1EF2FC5D3CFF9D9764756E2164756E21
20757
- //# sourceMappingURL=index-w7h7gtsh.js.map
23354
+ //# debugId=8C02D415BAC7A02B64756E2164756E21
23355
+ //# sourceMappingURL=index-t3rrpex7.js.map