@fairyhunter13/opentui-core 0.1.130 → 0.1.132

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
  }
@@ -7184,7 +7197,8 @@ class StdinParser {
7184
7197
  kittyKeyboardEnabled: options.protocolContext?.kittyKeyboardEnabled ?? false,
7185
7198
  privateCapabilityRepliesActive: options.protocolContext?.privateCapabilityRepliesActive ?? false,
7186
7199
  pixelResolutionQueryActive: options.protocolContext?.pixelResolutionQueryActive ?? false,
7187
- explicitWidthCprActive: options.protocolContext?.explicitWidthCprActive ?? false
7200
+ explicitWidthCprActive: options.protocolContext?.explicitWidthCprActive ?? false,
7201
+ startupCursorCprActive: options.protocolContext?.startupCursorCprActive ?? false
7188
7202
  };
7189
7203
  }
7190
7204
  get bufferCapacity() {
@@ -7686,7 +7700,8 @@ class StdinParser {
7686
7700
  }
7687
7701
  if (byte >= 64 && byte <= 126) {
7688
7702
  const end = this.cursor + 1;
7689
- 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)));
7690
7705
  this.state = { tag: "ground" };
7691
7706
  this.consumePrefix(end);
7692
7707
  continue;
@@ -11895,6 +11910,10 @@ function getOpenTUILib(libPath) {
11895
11910
  args: ["ptr", "bool"],
11896
11911
  returns: "void"
11897
11912
  },
11913
+ setClearOnShutdown: {
11914
+ args: ["ptr", "bool"],
11915
+ returns: "void"
11916
+ },
11898
11917
  setBackgroundColor: {
11899
11918
  args: ["ptr", "ptr"],
11900
11919
  returns: "void"
@@ -11903,6 +11922,22 @@ function getOpenTUILib(libPath) {
11903
11922
  args: ["ptr", "u32"],
11904
11923
  returns: "void"
11905
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
+ },
11906
11941
  updateStats: {
11907
11942
  args: ["ptr", "f64", "u32", "f64"],
11908
11943
  returns: "void"
@@ -11915,6 +11950,14 @@ function getOpenTUILib(libPath) {
11915
11950
  args: ["ptr", "bool"],
11916
11951
  returns: "void"
11917
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
+ },
11918
11961
  getNextBuffer: {
11919
11962
  args: ["ptr"],
11920
11963
  returns: "ptr"
@@ -12387,6 +12430,10 @@ function getOpenTUILib(libPath) {
12387
12430
  args: ["ptr", "u8"],
12388
12431
  returns: "void"
12389
12432
  },
12433
+ textBufferViewSetFirstLineOffset: {
12434
+ args: ["ptr", "u32"],
12435
+ returns: "void"
12436
+ },
12390
12437
  textBufferViewSetViewportSize: {
12391
12438
  args: ["ptr", "u32", "u32"],
12392
12439
  returns: "void"
@@ -13124,12 +13171,27 @@ class FFIRenderLib {
13124
13171
  setUseThread(renderer, useThread) {
13125
13172
  this.opentui.symbols.setUseThread(renderer, useThread);
13126
13173
  }
13174
+ setClearOnShutdown(renderer, clear) {
13175
+ this.opentui.symbols.setClearOnShutdown(renderer, clear);
13176
+ }
13127
13177
  setBackgroundColor(renderer, color) {
13128
13178
  this.opentui.symbols.setBackgroundColor(renderer, color.buffer);
13129
13179
  }
13130
13180
  setRenderOffset(renderer, offset) {
13131
13181
  this.opentui.symbols.setRenderOffset(renderer, offset);
13132
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
+ }
13133
13195
  updateStats(renderer, time, fps, frameCallbackTime) {
13134
13196
  this.opentui.symbols.updateStats(renderer, time, fps, frameCallbackTime);
13135
13197
  }
@@ -13319,6 +13381,12 @@ class FFIRenderLib {
13319
13381
  render(renderer, force) {
13320
13382
  this.opentui.symbols.render(renderer, force);
13321
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
+ }
13322
13390
  createOptimizedBuffer(width, height, widthMethod, respectAlpha = false, id) {
13323
13391
  if (Number.isNaN(width) || Number.isNaN(height)) {
13324
13392
  console.error(new Error(`Invalid dimensions for OptimizedBuffer: ${width}x${height}`).stack);
@@ -13598,6 +13666,9 @@ class FFIRenderLib {
13598
13666
  const modeValue = mode === "none" ? 0 : mode === "char" ? 1 : 2;
13599
13667
  this.opentui.symbols.textBufferViewSetWrapMode(view, modeValue);
13600
13668
  }
13669
+ textBufferViewSetFirstLineOffset(view, offset) {
13670
+ this.opentui.symbols.textBufferViewSetFirstLineOffset(view, offset);
13671
+ }
13601
13672
  textBufferViewSetViewportSize(view, width, height) {
13602
13673
  this.opentui.symbols.textBufferViewSetViewportSize(view, width, height);
13603
13674
  }
@@ -16062,6 +16133,139 @@ function delegate(mapping, vnode) {
16062
16133
  return vnode;
16063
16134
  }
16064
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
+
16065
16269
  // src/edit-buffer.ts
16066
16270
  import { EventEmitter as EventEmitter6 } from "events";
16067
16271
 
@@ -16566,113 +16770,1542 @@ class EditorView {
16566
16770
  }
16567
16771
  }
16568
16772
 
16569
- // src/console.ts
16570
- import { EventEmitter as EventEmitter8 } from "events";
16571
- import { Console } from "console";
16572
- import fs from "fs";
16573
- import path5 from "path";
16574
- import util2 from "util";
16575
-
16576
- // src/lib/output.capture.ts
16577
- import { Writable } from "stream";
16578
- 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
+ }
16579
16802
 
16580
- class Capture extends EventEmitter7 {
16581
- output = [];
16582
- constructor() {
16583
- 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;
16584
16813
  }
16585
- get size() {
16586
- return this.output.length;
16814
+ static create() {
16815
+ const lib = resolveRenderLib();
16816
+ const ptr5 = lib.createSyntaxStyle();
16817
+ return new SyntaxStyle(lib, ptr5);
16587
16818
  }
16588
- write(stream, data) {
16589
- this.output.push({ stream, output: data });
16590
- 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;
16591
16826
  }
16592
- claimOutput() {
16593
- const output = this.output.map((o) => o.output).join("");
16594
- this.clear();
16595
- 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;
16596
16833
  }
16597
- clear() {
16598
- this.output = [];
16834
+ guard() {
16835
+ if (this._destroyed)
16836
+ throw new Error("NativeSyntaxStyle is destroyed");
16599
16837
  }
16600
- }
16601
-
16602
- class CapturedWritableStream extends Writable {
16603
- stream;
16604
- capture;
16605
- isTTY = true;
16606
- columns = process.stdout.columns || 80;
16607
- rows = process.stdout.rows || 24;
16608
- constructor(stream, capture) {
16609
- super();
16610
- this.stream = stream;
16611
- 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;
16612
16850
  }
16613
- _write(chunk, encoding, callback) {
16614
- const data = chunk.toString();
16615
- this.capture.write(this.stream, data);
16616
- 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;
16617
16861
  }
16618
- getColorDepth() {
16619
- 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;
16620
16872
  }
16621
- }
16622
-
16623
- // src/lib/keymapping.ts
16624
- var defaultKeyAliases = {
16625
- enter: "return",
16626
- esc: "escape",
16627
- kp0: "0",
16628
- kp1: "1",
16629
- kp2: "2",
16630
- kp3: "3",
16631
- kp4: "4",
16632
- kp5: "5",
16633
- kp6: "6",
16634
- kp7: "7",
16635
- kp8: "8",
16636
- kp9: "9",
16637
- kpdecimal: ".",
16638
- kpdivide: "/",
16639
- kpmultiply: "*",
16640
- kpminus: "-",
16641
- kpplus: "+",
16642
- kpenter: "enter",
16643
- kpequal: "=",
16644
- kpseparator: ",",
16645
- kpleft: "left",
16646
- kpright: "right",
16647
- kpup: "up",
16648
- kpdown: "down",
16649
- kppageup: "pageup",
16650
- kppagedown: "pagedown",
16651
- kphome: "home",
16652
- kpend: "end",
16653
- kpinsert: "insert",
16654
- kpdelete: "delete"
16655
- };
16656
- function mergeKeyAliases(defaults, custom) {
16657
- return { ...defaults, ...custom };
16658
- }
16659
- function mergeKeyBindings(defaults, custom) {
16660
- const map = new Map;
16661
- for (const binding of defaults) {
16662
- const key = getKeyBindingKey(binding);
16663
- map.set(key, binding);
16873
+ get ptr() {
16874
+ this.guard();
16875
+ return this.stylePtr;
16664
16876
  }
16665
- for (const binding of custom) {
16666
- const key = getKeyBindingKey(binding);
16667
- map.set(key, binding);
16877
+ getStyleCount() {
16878
+ this.guard();
16879
+ return this.lib.syntaxStyleGetStyleCount(this.stylePtr);
16668
16880
  }
16669
- return Array.from(map.values());
16670
- }
16671
- function getKeyBindingKey(binding) {
16672
- return `${binding.name}:${binding.ctrl ? 1 : 0}:${binding.shift ? 1 : 0}:${binding.meta ? 1 : 0}:${binding.super ? 1 : 0}`;
16673
- }
16674
- function getBaseCodeKeyName(baseCode) {
16675
- 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) {
16676
18309
  return;
16677
18310
  }
16678
18311
  try {
@@ -18745,6 +20378,27 @@ function getObjectsInViewport(viewport, objects, direction = "column", padding =
18745
20378
  return visibleChildren;
18746
20379
  }
18747
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
+
18748
20402
  // src/lib/terminal-capability-detection.ts
18749
20403
  function isCapabilityResponse(sequence) {
18750
20404
  if (/\x1b\[\?\d+(?:;\d+)*\$y/.test(sequence)) {
@@ -18782,6 +20436,26 @@ function parsePixelResolution(sequence) {
18782
20436
  }
18783
20437
 
18784
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
+ }
18785
20459
  registerEnvVar({
18786
20460
  name: "OTUI_DUMP_CAPTURES",
18787
20461
  description: "Dump captured stdout and console caches when the renderer exit handler runs.",
@@ -18819,6 +20493,9 @@ registerEnvVar({
18819
20493
  default: false
18820
20494
  });
18821
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;
18822
20499
  function normalizeFooterHeight(footerHeight) {
18823
20500
  if (footerHeight === undefined) {
18824
20501
  return DEFAULT_FOOTER_HEIGHT;
@@ -18845,11 +20522,106 @@ function resolveModes(config) {
18845
20522
  if (externalOutputMode === "capture-stdout" && screenMode !== "split-footer") {
18846
20523
  throw new Error('externalOutputMode "capture-stdout" requires screenMode "split-footer"');
18847
20524
  }
18848
- return {
18849
- screenMode,
18850
- footerHeight,
18851
- externalOutputMode
18852
- };
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) {}
18853
20625
  }
18854
20626
  var DEFAULT_FORWARDED_ENV_KEYS = [
18855
20627
  "TMUX",
@@ -18971,9 +20743,9 @@ async function createCliRenderer(config = {}) {
18971
20743
  const { screenMode, footerHeight } = resolveModes(config);
18972
20744
  const width = stdout.columns || 80;
18973
20745
  const height = stdout.rows || 24;
18974
- const renderHeight = screenMode === "split-footer" ? footerHeight : height;
20746
+ const geometry = calculateRenderGeometry(screenMode, width, height, footerHeight);
18975
20747
  const ziglib = resolveRenderLib();
18976
- const rendererPtr = ziglib.createRenderer(width, renderHeight, {
20748
+ const rendererPtr = ziglib.createRenderer(geometry.renderWidth, geometry.renderHeight, {
18977
20749
  remote: config.remote ?? false,
18978
20750
  testing: config.testing ?? false
18979
20751
  });
@@ -19090,6 +20862,8 @@ class CliRenderer extends EventEmitter9 {
19090
20862
  animationRequest = new Map;
19091
20863
  resizeTimeoutId = null;
19092
20864
  capabilityTimeoutId = null;
20865
+ splitStartupSeedTimeoutId = null;
20866
+ pendingSplitStartupCursorSeed = false;
19093
20867
  resizeDebounceDelay = 100;
19094
20868
  enableMouseMovement = false;
19095
20869
  _useMouse = true;
@@ -19097,6 +20871,7 @@ class CliRenderer extends EventEmitter9 {
19097
20871
  _screenMode = "alternate-screen";
19098
20872
  _footerHeight = DEFAULT_FOOTER_HEIGHT;
19099
20873
  _externalOutputMode = "passthrough";
20874
+ clearOnShutdown = true;
19100
20875
  _suspendedMouseEnabled = false;
19101
20876
  _previousControlState = "idle" /* IDLE */;
19102
20877
  capturedRenderable;
@@ -19107,15 +20882,15 @@ class CliRenderer extends EventEmitter9 {
19107
20882
  clipboard;
19108
20883
  _splitHeight = 0;
19109
20884
  renderOffset = 0;
20885
+ splitTailColumn = 0;
20886
+ pendingSplitFooterTransition = null;
20887
+ forceFullRepaintRequested = false;
20888
+ maxSplitCommitsPerFrame = 8;
19110
20889
  _terminalWidth = 0;
19111
20890
  _terminalHeight = 0;
19112
20891
  _terminalIsSetup = false;
20892
+ externalOutputQueue = new ExternalOutputQueue;
19113
20893
  realStdoutWrite;
19114
- captureCallback = () => {
19115
- if (this._splitHeight > 0) {
19116
- this.requestRender();
19117
- }
19118
- };
19119
20894
  _useConsole = true;
19120
20895
  sigwinchHandler = (() => {
19121
20896
  const width = this.stdout.columns || 80;
@@ -19139,6 +20914,10 @@ class CliRenderer extends EventEmitter9 {
19139
20914
  _paletteDetectionPromise = null;
19140
20915
  _onDestroy;
19141
20916
  _themeMode = null;
20917
+ _themeModeSource = "none";
20918
+ _themeFallbackPending = true;
20919
+ _themeOscForeground = null;
20920
+ _themeOscBackground = null;
19142
20921
  _terminalFocusState = null;
19143
20922
  sequenceHandlers = [];
19144
20923
  prependedInputHandlers = [];
@@ -19154,8 +20933,15 @@ class CliRenderer extends EventEmitter9 {
19154
20933
  }).bind(this);
19155
20934
  dumpOutputCache(optionalMessage = "") {
19156
20935
  const cachedLogs = this.console.getCachedLogs();
19157
- const capturedOutput = capture.claimOutput();
19158
- 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) {
19159
20945
  this.realStdoutWrite.call(this.stdout, optionalMessage);
19160
20946
  }
19161
20947
  if (cachedLogs.length > 0) {
@@ -19163,11 +20949,18 @@ class CliRenderer extends EventEmitter9 {
19163
20949
  `);
19164
20950
  this.realStdoutWrite.call(this.stdout, cachedLogs);
19165
20951
  }
19166
- 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) {
19167
20960
  this.realStdoutWrite.call(this.stdout, `
19168
- Captured output:
20961
+ Captured external output:
19169
20962
  `);
19170
- this.realStdoutWrite.call(this.stdout, capturedOutput + `
20963
+ this.realStdoutWrite.call(this.stdout, capturedExternalOutput + `
19171
20964
  `);
19172
20965
  }
19173
20966
  this.realStdoutWrite.call(this.stdout, ANSI.reset);
@@ -19196,13 +20989,18 @@ Captured output:
19196
20989
  this.lib = lib;
19197
20990
  this._terminalWidth = stdout.columns ?? width;
19198
20991
  this._terminalHeight = stdout.rows ?? height;
19199
- this.width = width;
19200
- this.height = height;
19201
20992
  this._useThread = config.useThread === undefined ? false : config.useThread;
19202
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;
19203
21000
  this._footerHeight = footerHeight;
19204
- this._screenMode = screenMode;
19205
21001
  this.rendererPtr = rendererPtr;
21002
+ this.clearOnShutdown = config.clearOnShutdown ?? true;
21003
+ this.lib.setClearOnShutdown(this.rendererPtr, this.clearOnShutdown);
19206
21004
  const forwardEnvKeys = config.forwardEnvKeys ?? [...DEFAULT_FORWARDED_ENV_KEYS];
19207
21005
  for (const key of forwardEnvKeys) {
19208
21006
  const value = process.env[key];
@@ -19237,7 +21035,6 @@ Captured output:
19237
21035
  this.postProcessFns = config.postProcessFns || [];
19238
21036
  this.prependedInputHandlers = config.prependInputHandlers || [];
19239
21037
  this.root = new RootRenderable(this);
19240
- this._openConsoleOnError = config.openConsoleOnError ?? false;
19241
21038
  if (this.memorySnapshotInterval > 0) {
19242
21039
  this.startMemorySnapshotTimer();
19243
21040
  }
@@ -19271,7 +21068,8 @@ Captured output:
19271
21068
  kittyKeyboardEnabled: useKittyForParsing,
19272
21069
  privateCapabilityRepliesActive: false,
19273
21070
  pixelResolutionQueryActive: false,
19274
- explicitWidthCprActive: false
21071
+ explicitWidthCprActive: false,
21072
+ startupCursorCprActive: false
19275
21073
  },
19276
21074
  clock: this.clock
19277
21075
  });
@@ -19281,7 +21079,8 @@ Captured output:
19281
21079
  });
19282
21080
  this.consoleMode = config.consoleMode ?? "console-overlay";
19283
21081
  this.applyScreenMode(screenMode, false, false);
19284
- this.externalOutputMode = externalOutputMode;
21082
+ this.stdout.write = externalOutputMode === "capture-stdout" ? this.interceptStdoutWrite : this.realStdoutWrite;
21083
+ this._openConsoleOnError = config.openConsoleOnError ?? false;
19285
21084
  this._onDestroy = config.onDestroy;
19286
21085
  global.requestAnimationFrame = (callback) => {
19287
21086
  const id = CliRenderer.animationFrameId++;
@@ -19439,163 +21238,795 @@ Captured output:
19439
21238
  return;
19440
21239
  }
19441
21240
  try {
19442
- 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;
19443
21724
  } finally {
19444
- this.updateScheduled = false;
19445
- 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
+ }
19446
21745
  }
19447
21746
  }
19448
- get consoleMode() {
19449
- return this._useConsole ? "console-overlay" : "disabled";
19450
- }
19451
- set consoleMode(mode) {
19452
- this._useConsole = mode === "console-overlay";
19453
- if (this._useConsole) {
19454
- this.console.activate();
19455
- } else {
19456
- 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");
19457
21751
  }
21752
+ return Math.min(Math.max(Math.trunc(rawValue), 1), Math.max(this.width, 1));
19458
21753
  }
19459
- get isRunning() {
19460
- 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);
19461
21760
  }
19462
- isIdleNow() {
19463
- 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;
19464
21778
  }
19465
- resolveIdleIfNeeded() {
19466
- if (!this.isIdleNow())
21779
+ publishSplitTailColumns(columns) {
21780
+ if (columns <= 0) {
19467
21781
  return;
19468
- const resolvers = this.idleResolvers.splice(0);
19469
- for (const resolve4 of resolvers) {
19470
- resolve4();
19471
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;
19472
21798
  }
19473
- idle() {
19474
- if (this._isDestroyed)
19475
- return Promise.resolve();
19476
- if (this.isIdleNow())
19477
- return Promise.resolve();
19478
- return new Promise((resolve4) => {
19479
- this.idleResolvers.push(resolve4);
19480
- });
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
+ }
19481
21810
  }
19482
- get resolution() {
19483
- 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();
19484
21823
  }
19485
- get console() {
19486
- 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
+ }
19487
21862
  }
19488
- get keyInput() {
19489
- 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;
19490
21883
  }
19491
- get _internalKeyInput() {
19492
- 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;
19493
21905
  }
19494
- get terminalWidth() {
19495
- 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
+ }
19496
21928
  }
19497
- get terminalHeight() {
19498
- 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;
19499
21949
  }
19500
- get useThread() {
19501
- 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));
19502
21954
  }
19503
- get targetFps() {
19504
- 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);
19505
21963
  }
19506
- set targetFps(targetFps) {
19507
- this._targetFps = targetFps;
19508
- this.targetFrameTime = 1000 / this._targetFps;
21964
+ resetSplitScrollback(seedRows = 0) {
21965
+ this.splitTailColumn = 0;
21966
+ this.renderOffset = this.lib.resetSplitScrollback(this.rendererPtr, seedRows, this.getSplitPinnedRenderOffset());
19509
21967
  }
19510
- get maxFps() {
19511
- return this._maxFps;
21968
+ syncSplitScrollback() {
21969
+ this.renderOffset = this.lib.syncSplitScrollback(this.rendererPtr, this.getSplitPinnedRenderOffset());
19512
21970
  }
19513
- set maxFps(maxFps) {
19514
- this._maxFps = maxFps;
19515
- 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);
19516
21977
  }
19517
- get useMouse() {
19518
- 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);
19519
21981
  }
19520
- set useMouse(useMouse) {
19521
- 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);
19522
21990
  return;
19523
- this._useMouse = useMouse;
19524
- if (useMouse) {
19525
- this.enableMouse();
21991
+ }
21992
+ if (this._externalOutputMode === "capture-stdout") {
21993
+ this.syncSplitScrollback();
19526
21994
  } else {
19527
- 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);
19528
22000
  }
19529
22001
  }
19530
- get screenMode() {
19531
- return this._screenMode;
19532
- }
19533
- set screenMode(mode) {
19534
- if (this.externalOutputMode === "capture-stdout" && mode !== "split-footer") {
19535
- 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;
19536
22005
  }
19537
- this.applyScreenMode(mode);
19538
- }
19539
- get footerHeight() {
19540
- return this._footerHeight;
19541
- }
19542
- set footerHeight(footerHeight) {
19543
- const normalizedFooterHeight = normalizeFooterHeight(footerHeight);
19544
- 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) {
19545
22010
  return;
19546
22011
  }
19547
- this._footerHeight = normalizedFooterHeight;
19548
- if (this.screenMode === "split-footer") {
19549
- 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`;
19550
22020
  }
19551
- }
19552
- get externalOutputMode() {
19553
- return this._externalOutputMode;
19554
- }
19555
- set externalOutputMode(mode) {
19556
- if (mode === "capture-stdout" && this.screenMode !== "split-footer") {
19557
- throw new Error('externalOutputMode "capture-stdout" requires screenMode "split-footer"');
22021
+ if (clear.length > 0) {
22022
+ this.writeOut(clear);
19558
22023
  }
19559
- this._externalOutputMode = mode;
19560
- this.stdout.write = mode === "capture-stdout" ? this.interceptStdoutWrite : this.realStdoutWrite;
19561
- }
19562
- get liveRequestCount() {
19563
- return this.liveRequestCounter;
19564
- }
19565
- get currentControlState() {
19566
- return this._controlState;
19567
- }
19568
- get capabilities() {
19569
- return this._capabilities;
19570
- }
19571
- get themeMode() {
19572
- return this._themeMode;
19573
- }
19574
- getDebugInputs() {
19575
- return [...this._debugInputs];
19576
- }
19577
- get useKittyKeyboard() {
19578
- return this.lib.getKittyKeyboardFlags(this.rendererPtr) > 0;
19579
- }
19580
- set useKittyKeyboard(use) {
19581
- const flags = use ? KITTY_FLAG_DISAMBIGUATE | KITTY_FLAG_ALTERNATE_KEYS : 0;
19582
- this.lib.setKittyKeyboardFlags(this.rendererPtr, flags);
19583
22024
  }
19584
- interceptStdoutWrite = (chunk, encoding, callback) => {
19585
- const text = chunk.toString();
19586
- capture.write("stdout", text);
19587
- if (this._splitHeight > 0) {
19588
- this.requestRender();
19589
- }
19590
- if (typeof callback === "function") {
19591
- process.nextTick(callback);
19592
- }
19593
- return true;
19594
- };
19595
22025
  applyScreenMode(screenMode, emitResize = true, requestRender = true) {
19596
22026
  const prevScreenMode = this._screenMode;
19597
22027
  const prevSplitHeight = this._splitHeight;
19598
- const nextSplitHeight = screenMode === "split-footer" ? this._footerHeight : 0;
22028
+ const nextGeometry = calculateRenderGeometry(screenMode, this._terminalWidth, this._terminalHeight, this._footerHeight);
22029
+ const nextSplitHeight = nextGeometry.effectiveFooterHeight;
19599
22030
  if (prevScreenMode === screenMode && prevSplitHeight === nextSplitHeight) {
19600
22031
  return;
19601
22032
  }
@@ -19603,10 +22034,24 @@ Captured output:
19603
22034
  const nextUseAlternateScreen = screenMode === "alternate-screen";
19604
22035
  const terminalScreenModeChanged = this._terminalIsSetup && prevUseAlternateScreen !== nextUseAlternateScreen;
19605
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");
19606
22049
  if (this._terminalIsSetup && leavingSplitFooter) {
19607
- this.flushStdoutCache(this._terminalHeight, true);
22050
+ this.clearPendingSplitFooterTransition();
22051
+ this.renderOffset = 0;
22052
+ this.lib.setRenderOffset(this.rendererPtr, 0);
19608
22053
  }
19609
- if (this._terminalIsSetup && !terminalScreenModeChanged) {
22054
+ if (this._terminalIsSetup && !terminalScreenModeChanged && shouldUseViewportScrollTransitions && !shouldDeferSplitFooterResizeTransition) {
19610
22055
  if (prevSplitHeight === 0 && nextSplitHeight > 0) {
19611
22056
  const freedLines = this._terminalHeight - nextSplitHeight;
19612
22057
  const scrollDown = ANSI.scrollDown(freedLines);
@@ -19621,18 +22066,39 @@ Captured output:
19621
22066
  this.writeOut(scrollUp);
19622
22067
  }
19623
22068
  }
19624
- if (prevSplitHeight === 0 && nextSplitHeight > 0) {
19625
- capture.on("write", this.captureCallback);
19626
- } else if (prevSplitHeight > 0 && nextSplitHeight === 0) {
19627
- capture.off("write", this.captureCallback);
19628
- }
19629
22069
  this._screenMode = screenMode;
19630
22070
  this._splitHeight = nextSplitHeight;
19631
- this.renderOffset = nextSplitHeight > 0 ? this._terminalHeight - nextSplitHeight : 0;
19632
- this.width = this._terminalWidth;
19633
- this.height = nextSplitHeight > 0 ? nextSplitHeight : this._terminalHeight;
19634
- this.lib.setRenderOffset(this.rendererPtr, this.renderOffset);
22071
+ this.width = nextGeometry.renderWidth;
22072
+ this.height = nextGeometry.renderHeight;
19635
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
+ }
19636
22102
  this.nextRenderBuffer = this.lib.getNextBuffer(this.rendererPtr);
19637
22103
  this.currentRenderBuffer = this.lib.getCurrentBuffer(this.rendererPtr);
19638
22104
  this._console.resize(this.width, this.height);
@@ -19652,12 +22118,18 @@ Captured output:
19652
22118
  }
19653
22119
  }
19654
22120
  flushStdoutCache(space, force = false) {
19655
- if (capture.size === 0 && !force)
22121
+ if (this.externalOutputQueue.size === 0 && !force)
19656
22122
  return false;
19657
- const output = capture.claimOutput();
19658
- 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;
19659
22131
  const flush = ANSI.moveCursorAndClear(rendererStartLine, 1);
19660
- const outputLine = this._terminalHeight - this._splitHeight;
22132
+ const outputLine = this.renderOffset + 1;
19661
22133
  const move = ANSI.moveCursor(outputLine, 1);
19662
22134
  let clear = "";
19663
22135
  if (space > 0) {
@@ -19699,9 +22171,11 @@ Captured output:
19699
22171
  if (this._terminalIsSetup)
19700
22172
  return;
19701
22173
  this._terminalIsSetup = true;
22174
+ const startupCursorCprActive = this._screenMode === "split-footer" && this._externalOutputMode === "capture-stdout";
19702
22175
  this.updateStdinParserProtocolContext({
19703
22176
  privateCapabilityRepliesActive: true,
19704
- explicitWidthCprActive: true
22177
+ explicitWidthCprActive: true,
22178
+ startupCursorCprActive
19705
22179
  });
19706
22180
  this.lib.setupTerminal(this.rendererPtr, this._screenMode === "alternate-screen");
19707
22181
  this._capabilities = this.lib.getTerminalCapabilities(this.rendererPtr);
@@ -19715,15 +22189,40 @@ Captured output:
19715
22189
  }
19716
22190
  this.capabilityTimeoutId = this.clock.setTimeout(() => {
19717
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
+ }
19718
22200
  this.removeInputHandler(this.capabilityHandler);
19719
22201
  this.updateStdinParserProtocolContext({
19720
22202
  privateCapabilityRepliesActive: false,
19721
- explicitWidthCprActive: false
22203
+ explicitWidthCprActive: false,
22204
+ startupCursorCprActive: false
19722
22205
  }, true);
19723
22206
  }, 5000);
19724
22207
  if (this._useMouse) {
19725
22208
  this.enableMouse();
19726
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
+ }
19727
22226
  this.queryPixelResolution();
19728
22227
  }
19729
22228
  stdinListener = ((chunk) => {
@@ -19759,14 +22258,32 @@ Captured output:
19759
22258
  this.oscSubscribers.delete(handler);
19760
22259
  };
19761
22260
  }
19762
- capabilityHandler = ((sequence) => {
19763
- if (isCapabilityResponse(sequence)) {
19764
- this.lib.processCapabilityResponse(this.rendererPtr, sequence);
19765
- this._capabilities = this.lib.getTerminalCapabilities(this.rendererPtr);
19766
- this.emit("capabilities" /* CAPABILITIES */, this._capabilities);
19767
- 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;
19768
22266
  }
19769
- 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);
19770
22287
  }).bind(this);
19771
22288
  focusHandler = ((sequence) => {
19772
22289
  if (sequence === "\x1B[I") {
@@ -19792,21 +22309,50 @@ Captured output:
19792
22309
  }).bind(this);
19793
22310
  themeModeHandler = ((sequence) => {
19794
22311
  if (sequence === "\x1B[?997;1n") {
19795
- if (this._themeMode !== "dark") {
19796
- this._themeMode = "dark";
19797
- this.emit("theme_mode" /* THEME_MODE */, "dark");
19798
- }
22312
+ this.applyThemeMode("dark", "csi");
22313
+ this._themeFallbackPending = false;
19799
22314
  return true;
19800
22315
  }
19801
22316
  if (sequence === "\x1B[?997;2n") {
19802
- if (this._themeMode !== "light") {
19803
- this._themeMode = "light";
19804
- 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;
19805
22331
  }
22332
+ }
22333
+ if (!handledOscThemeResponse) {
22334
+ return false;
22335
+ }
22336
+ if (!this._themeFallbackPending) {
19806
22337
  return true;
19807
22338
  }
19808
- return false;
22339
+ if (this._themeOscForeground && this._themeOscBackground) {
22340
+ this.applyThemeMode(inferThemeModeFromBackgroundColor(this._themeOscBackground), "osc");
22341
+ this._themeFallbackPending = false;
22342
+ }
22343
+ return true;
19809
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
+ }
19810
22356
  dispatchSequenceHandlers(sequence) {
19811
22357
  if (this._debugModeEnabled) {
19812
22358
  this._debugInputs.push({
@@ -19851,6 +22397,9 @@ Captured output:
19851
22397
  subscriber(event.sequence);
19852
22398
  }
19853
22399
  }
22400
+ if (event.protocol === "cpr" && this.processCapabilitySequence(event.sequence, true)) {
22401
+ return;
22402
+ }
19854
22403
  this.dispatchSequenceHandlers(event.sequence);
19855
22404
  return;
19856
22405
  }
@@ -20137,28 +22686,45 @@ Captured output:
20137
22686
  processResize(width, height) {
20138
22687
  if (width === this._terminalWidth && height === this._terminalHeight)
20139
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);
20140
22694
  const prevWidth = this._terminalWidth;
22695
+ const previousTerminalHeight = this._terminalHeight;
22696
+ const visiblePreviousSplitHeight = pendingSplitFooterTransition?.sourceHeight ?? previousGeometry.effectiveFooterHeight;
20141
22697
  this._terminalWidth = width;
20142
22698
  this._terminalHeight = height;
20143
22699
  this.queryPixelResolution();
20144
22700
  this.setCapturedRenderable(undefined);
20145
22701
  this.stdinParser?.resetMouseState();
20146
- if (this._splitHeight > 0) {
20147
- if (width < prevWidth) {
20148
- const start = this._terminalHeight - this._splitHeight * 2;
20149
- 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);
20150
22714
  this.writeOut(flush);
20151
22715
  }
20152
- this.renderOffset = height - this._splitHeight;
20153
- this.width = width;
20154
- this.height = this._splitHeight;
20155
22716
  this.currentRenderBuffer.clear(this.backgroundColor);
20156
- this.lib.setRenderOffset(this.rendererPtr, this.renderOffset);
20157
- } else {
20158
- this.width = width;
20159
- this.height = height;
20160
22717
  }
22718
+ this.clearPendingSplitFooterTransition();
22719
+ this._splitHeight = nextGeometry.effectiveFooterHeight;
22720
+ this.width = nextGeometry.renderWidth;
22721
+ this.height = nextGeometry.renderHeight;
20161
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
+ }
20162
22728
  this.nextRenderBuffer = this.lib.getNextBuffer(this.rendererPtr);
20163
22729
  this.currentRenderBuffer = this.lib.getCurrentBuffer(this.rendererPtr);
20164
22730
  this._console.resize(this.width, this.height);
@@ -20306,6 +22872,9 @@ Captured output:
20306
22872
  this._previousControlState = this._controlState;
20307
22873
  this._controlState = "explicit_suspended" /* EXPLICIT_SUSPENDED */;
20308
22874
  this.internalPause();
22875
+ if (this._terminalIsSetup) {
22876
+ this.flushPendingSplitOutputBeforeTransition(true);
22877
+ }
20309
22878
  this._suspendedMouseEnabled = this._useMouse;
20310
22879
  this.disableMouse();
20311
22880
  this.removeExitListeners();
@@ -20313,7 +22882,8 @@ Captured output:
20313
22882
  this.updateStdinParserProtocolContext({
20314
22883
  privateCapabilityRepliesActive: false,
20315
22884
  pixelResolutionQueryActive: false,
20316
- explicitWidthCprActive: false
22885
+ explicitWidthCprActive: false,
22886
+ startupCursorCprActive: false
20317
22887
  });
20318
22888
  this.stdinParser?.reset();
20319
22889
  this.stdin.removeListener("data", this.stdinListener);
@@ -20332,6 +22902,9 @@ Captured output:
20332
22902
  this.stdin.resume();
20333
22903
  this.addExitListeners();
20334
22904
  this.lib.resumeRenderer(this.rendererPtr);
22905
+ if (this._screenMode === "split-footer" && this._splitHeight > 0) {
22906
+ this.syncSplitFooterState();
22907
+ }
20335
22908
  if (this._suspendedMouseEnabled) {
20336
22909
  this.enableMouse();
20337
22910
  }
@@ -20393,7 +22966,6 @@ Captured output:
20393
22966
  process.removeListener("unhandledRejection", this.handleError);
20394
22967
  process.removeListener("warning", this.warningHandler);
20395
22968
  process.removeListener("beforeExit", this.exitHandler);
20396
- capture.removeListener("write", this.captureCallback);
20397
22969
  this.removeExitListeners();
20398
22970
  if (this.resizeTimeoutId !== null) {
20399
22971
  this.clock.clearTimeout(this.resizeTimeoutId);
@@ -20403,6 +22975,10 @@ Captured output:
20403
22975
  this.clock.clearTimeout(this.capabilityTimeoutId);
20404
22976
  this.capabilityTimeoutId = null;
20405
22977
  }
22978
+ if (this.splitStartupSeedTimeoutId !== null) {
22979
+ this.clock.clearTimeout(this.splitStartupSeedTimeoutId);
22980
+ this.splitStartupSeedTimeoutId = null;
22981
+ }
20406
22982
  if (this.memorySnapshotTimer) {
20407
22983
  this.clock.clearInterval(this.memorySnapshotTimer);
20408
22984
  this.memorySnapshotTimer = null;
@@ -20416,7 +22992,8 @@ Captured output:
20416
22992
  this.updateStdinParserProtocolContext({
20417
22993
  privateCapabilityRepliesActive: false,
20418
22994
  pixelResolutionQueryActive: false,
20419
- explicitWidthCprActive: false
22995
+ explicitWidthCprActive: false,
22996
+ startupCursorCprActive: false
20420
22997
  }, true);
20421
22998
  this._useMouse = false;
20422
22999
  this.setCapturedRenderable(undefined);
@@ -20458,6 +23035,16 @@ Captured output:
20458
23035
  this.stdinParser = null;
20459
23036
  this.oscSubscribers.clear();
20460
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();
20461
23048
  this.lib.destroyRenderer(this.rendererPtr);
20462
23049
  rendererTracker.removeRenderer(this);
20463
23050
  if (this._onDestroy) {
@@ -20566,12 +23153,21 @@ Captured output:
20566
23153
  console.error("Rendering called concurrently");
20567
23154
  throw new Error("Rendering called concurrently");
20568
23155
  }
20569
- let force = false;
20570
- if (this._splitHeight > 0) {
20571
- force = this.flushStdoutCache(this._splitHeight);
20572
- }
20573
23156
  this.renderingNative = true;
20574
- 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
+ }
20575
23171
  this.renderingNative = false;
20576
23172
  }
20577
23173
  collectStatSample(frameTime) {
@@ -20753,7 +23349,7 @@ Captured output:
20753
23349
  }
20754
23350
  }
20755
23351
 
20756
- 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 };
20757
23353
 
20758
- //# debugId=46FF8E37A996548C64756E2164756E21
20759
- //# sourceMappingURL=index-pr4swpf9.js.map
23354
+ //# debugId=8C02D415BAC7A02B64756E2164756E21
23355
+ //# sourceMappingURL=index-t3rrpex7.js.map