@opentui/core 0.1.24 → 0.1.26

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.
Files changed (59) hide show
  1. package/3d.js +1 -1
  2. package/README.md +5 -1
  3. package/Renderable.d.ts +18 -8
  4. package/animation/Timeline.d.ts +2 -1
  5. package/ansi.d.ts +2 -17
  6. package/assets/javascript/highlights.scm +205 -0
  7. package/assets/javascript/tree-sitter-javascript.wasm +0 -0
  8. package/assets/typescript/highlights.scm +604 -0
  9. package/assets/typescript/tree-sitter-typescript.wasm +0 -0
  10. package/{index-0yx9rnxg.js → index-pxa2sv92.js} +1798 -258
  11. package/index-pxa2sv92.js.map +52 -0
  12. package/index.js +449 -246
  13. package/index.js.map +15 -13
  14. package/lib/KeyHandler.d.ts +51 -9
  15. package/lib/data-paths.d.ts +26 -0
  16. package/lib/debounce.d.ts +42 -0
  17. package/lib/env.d.ts +42 -0
  18. package/lib/hast-styled-text.d.ts +3 -23
  19. package/lib/index.d.ts +6 -0
  20. package/lib/parse.keypress.d.ts +2 -2
  21. package/lib/queue.d.ts +15 -0
  22. package/lib/scroll-acceleration.d.ts +43 -0
  23. package/{singleton.d.ts → lib/singleton.d.ts} +2 -0
  24. package/lib/styled-text.d.ts +0 -15
  25. package/lib/syntax-style.d.ts +36 -0
  26. package/lib/tree-sitter/assets/update.d.ts +11 -0
  27. package/lib/tree-sitter/client.d.ts +46 -0
  28. package/lib/tree-sitter/default-parsers.d.ts +2 -0
  29. package/lib/tree-sitter/download-utils.d.ts +21 -0
  30. package/lib/tree-sitter/index.d.ts +10 -0
  31. package/lib/tree-sitter/parser.worker.d.ts +1 -0
  32. package/lib/tree-sitter/resolve-ft.d.ts +2 -0
  33. package/lib/tree-sitter/types.d.ts +64 -0
  34. package/lib/tree-sitter-styled-text.d.ts +7 -0
  35. package/lib/validate-dir-name.d.ts +1 -0
  36. package/package.json +21 -8
  37. package/parser.worker.js +640 -0
  38. package/parser.worker.js.map +11 -0
  39. package/renderables/ASCIIFont.d.ts +1 -1
  40. package/renderables/Code.d.ts +31 -0
  41. package/renderables/Input.d.ts +4 -4
  42. package/renderables/ScrollBar.d.ts +2 -2
  43. package/renderables/ScrollBox.d.ts +7 -3
  44. package/renderables/Select.d.ts +2 -2
  45. package/renderables/TabSelect.d.ts +2 -2
  46. package/renderables/Text.d.ts +11 -65
  47. package/renderables/TextBufferRenderable.d.ts +81 -0
  48. package/renderables/TextNode.d.ts +1 -0
  49. package/renderables/index.d.ts +2 -0
  50. package/renderer.d.ts +5 -3
  51. package/testing/mock-keys.d.ts +1 -0
  52. package/testing/spy.d.ts +7 -0
  53. package/testing/test-renderer.d.ts +1 -0
  54. package/testing.d.ts +1 -0
  55. package/testing.js +31 -6
  56. package/testing.js.map +6 -5
  57. package/types.d.ts +2 -1
  58. package/zig.d.ts +1 -0
  59. package/index-0yx9rnxg.js.map +0 -38
@@ -1763,7 +1763,7 @@ var BorderCharArrays = {
1763
1763
  };
1764
1764
 
1765
1765
  // src/lib/parse.keypress.ts
1766
- import { Buffer } from "buffer";
1766
+ import { Buffer as Buffer2 } from "buffer";
1767
1767
 
1768
1768
  // src/lib/parse.keypress-kitty.ts
1769
1769
  var kittyKeyMap = {
@@ -2080,7 +2080,7 @@ var isCtrlKey = (code) => {
2080
2080
  };
2081
2081
  var parseKeypress = (s = "", options = {}) => {
2082
2082
  let parts;
2083
- if (Buffer.isBuffer(s)) {
2083
+ if (Buffer2.isBuffer(s)) {
2084
2084
  if (s[0] > 127 && s[1] === undefined) {
2085
2085
  s[0] -= 128;
2086
2086
  s = "\x1B" + String(s);
@@ -2143,6 +2143,10 @@ var parseKeypress = (s = "", options = {}) => {
2143
2143
  key.meta = true;
2144
2144
  key.shift = /^[A-Z]$/.test(parts[1]);
2145
2145
  key.name = parts[1];
2146
+ } else if (s.length === 2 && s[0] === "\x1B" && s[1] <= "\x1A") {
2147
+ key.meta = true;
2148
+ key.ctrl = true;
2149
+ key.name = String.fromCharCode(s.charCodeAt(1) + 97 - 1);
2146
2150
  } else if (parts = fnKeyRe.exec(s)) {
2147
2151
  const segs = [...s];
2148
2152
  if (segs[0] === "\x1B" && segs[1] === "\x1B") {
@@ -2175,57 +2179,162 @@ var parseKeypress = (s = "", options = {}) => {
2175
2179
  // src/lib/KeyHandler.ts
2176
2180
  import { EventEmitter } from "events";
2177
2181
 
2178
- // src/singleton.ts
2179
- var singletonCacheSymbol = Symbol.for("@opentui/core/singleton");
2180
- function singleton(key, factory) {
2181
- const bag = globalThis[singletonCacheSymbol] ??= {};
2182
- if (!(key in bag)) {
2183
- bag[key] = factory();
2182
+ // src/ansi.ts
2183
+ var ANSI = {
2184
+ switchToAlternateScreen: "\x1B[?1049h",
2185
+ switchToMainScreen: "\x1B[?1049l",
2186
+ reset: "\x1B[0m",
2187
+ scrollDown: (lines) => `\x1B[${lines}T`,
2188
+ scrollUp: (lines) => `\x1B[${lines}S`,
2189
+ moveCursor: (row, col) => `\x1B[${row};${col}H`,
2190
+ moveCursorAndClear: (row, col) => `\x1B[${row};${col}H\x1B[J`,
2191
+ setRgbBackground: (r, g, b) => `\x1B[48;2;${r};${g};${b}m`,
2192
+ resetBackground: "\x1B[49m",
2193
+ bracketedPasteStart: "\x1B[200~",
2194
+ bracketedPasteEnd: "\x1B[201~"
2195
+ };
2196
+
2197
+ // src/lib/KeyHandler.ts
2198
+ class KeyEvent {
2199
+ name;
2200
+ ctrl;
2201
+ meta;
2202
+ shift;
2203
+ option;
2204
+ sequence;
2205
+ number;
2206
+ raw;
2207
+ eventType;
2208
+ code;
2209
+ super;
2210
+ hyper;
2211
+ capsLock;
2212
+ numLock;
2213
+ baseCode;
2214
+ _defaultPrevented = false;
2215
+ constructor(key) {
2216
+ this.name = key.name;
2217
+ this.ctrl = key.ctrl;
2218
+ this.meta = key.meta;
2219
+ this.shift = key.shift;
2220
+ this.option = key.option;
2221
+ this.sequence = key.sequence;
2222
+ this.number = key.number;
2223
+ this.raw = key.raw;
2224
+ this.eventType = key.eventType;
2225
+ this.code = key.code;
2226
+ this.super = key.super;
2227
+ this.hyper = key.hyper;
2228
+ this.capsLock = key.capsLock;
2229
+ this.numLock = key.numLock;
2230
+ this.baseCode = key.baseCode;
2231
+ }
2232
+ get defaultPrevented() {
2233
+ return this._defaultPrevented;
2234
+ }
2235
+ preventDefault() {
2236
+ this._defaultPrevented = true;
2237
+ }
2238
+ }
2239
+
2240
+ class PasteEvent {
2241
+ text;
2242
+ _defaultPrevented = false;
2243
+ constructor(text) {
2244
+ this.text = text;
2245
+ }
2246
+ get defaultPrevented() {
2247
+ return this._defaultPrevented;
2248
+ }
2249
+ preventDefault() {
2250
+ this._defaultPrevented = true;
2184
2251
  }
2185
- return bag[key];
2186
2252
  }
2187
2253
 
2188
- // src/lib/KeyHandler.ts
2189
2254
  class KeyHandler extends EventEmitter {
2190
2255
  stdin;
2191
2256
  useKittyKeyboard;
2257
+ listener;
2258
+ pasteMode = false;
2259
+ pasteBuffer = [];
2192
2260
  constructor(stdin, useKittyKeyboard = false) {
2193
2261
  super();
2194
2262
  this.stdin = stdin || process.stdin;
2195
2263
  this.useKittyKeyboard = useKittyKeyboard;
2196
- if (this.stdin.setRawMode) {
2197
- this.stdin.setRawMode(true);
2198
- }
2199
- this.stdin.resume();
2200
- this.stdin.setEncoding("utf8");
2201
- this.stdin.on("data", (key) => {
2264
+ this.listener = (key) => {
2265
+ let data = key.toString();
2266
+ if (data.startsWith(ANSI.bracketedPasteStart)) {
2267
+ this.pasteMode = true;
2268
+ }
2269
+ if (this.pasteMode) {
2270
+ this.pasteBuffer.push(Bun.stripANSI(data));
2271
+ if (data.endsWith(ANSI.bracketedPasteEnd)) {
2272
+ this.pasteMode = false;
2273
+ this.emit("paste", new PasteEvent(this.pasteBuffer.join("")));
2274
+ this.pasteBuffer = [];
2275
+ }
2276
+ return;
2277
+ }
2202
2278
  const parsedKey = parseKeypress(key, { useKittyKeyboard: this.useKittyKeyboard });
2203
2279
  switch (parsedKey.eventType) {
2204
2280
  case "press":
2205
- this.emit("keypress", parsedKey);
2281
+ this.emit("keypress", new KeyEvent(parsedKey));
2206
2282
  break;
2207
2283
  case "repeat":
2208
- this.emit("keyrepeat", parsedKey);
2284
+ this.emit("keyrepeat", new KeyEvent(parsedKey));
2209
2285
  break;
2210
2286
  case "release":
2211
- this.emit("keyrelease", parsedKey);
2287
+ this.emit("keyrelease", new KeyEvent(parsedKey));
2212
2288
  break;
2213
2289
  default:
2214
- this.emit("keypress", parsedKey);
2290
+ this.emit("keypress", new KeyEvent(parsedKey));
2215
2291
  break;
2216
2292
  }
2217
- });
2293
+ };
2294
+ this.stdin.on("data", this.listener);
2218
2295
  }
2219
2296
  destroy() {
2220
- this.stdin.removeAllListeners("data");
2297
+ this.stdin.removeListener("data", this.listener);
2221
2298
  }
2222
2299
  }
2223
- var keyHandler = null;
2224
- function getKeyHandler(useKittyKeyboard = false) {
2225
- if (!keyHandler) {
2226
- keyHandler = singleton("KeyHandler", () => new KeyHandler(process.stdin, useKittyKeyboard));
2300
+
2301
+ class InternalKeyHandler extends KeyHandler {
2302
+ renderableHandlers = new Map;
2303
+ constructor(stdin, useKittyKeyboard = false) {
2304
+ super(stdin, useKittyKeyboard);
2305
+ }
2306
+ emit(event, ...args) {
2307
+ return this.emitWithPriority(event, ...args);
2308
+ }
2309
+ emitWithPriority(event, ...args) {
2310
+ const hasGlobalListeners = super.emit(event, ...args);
2311
+ const renderableSet = this.renderableHandlers.get(event);
2312
+ let hasRenderableListeners = false;
2313
+ if (renderableSet && renderableSet.size > 0) {
2314
+ hasRenderableListeners = true;
2315
+ if (event === "keypress" || event === "keyrepeat" || event === "keyrelease" || event === "paste") {
2316
+ const keyEvent = args[0];
2317
+ if (keyEvent.defaultPrevented)
2318
+ return hasGlobalListeners || hasRenderableListeners;
2319
+ }
2320
+ for (const handler of renderableSet) {
2321
+ handler(...args);
2322
+ }
2323
+ }
2324
+ return hasGlobalListeners || hasRenderableListeners;
2325
+ }
2326
+ onInternal(event, handler) {
2327
+ if (!this.renderableHandlers.has(event)) {
2328
+ this.renderableHandlers.set(event, new Set);
2329
+ }
2330
+ this.renderableHandlers.get(event).add(handler);
2331
+ }
2332
+ offInternal(event, handler) {
2333
+ const handlers = this.renderableHandlers.get(event);
2334
+ if (handlers) {
2335
+ handlers.delete(handler);
2336
+ }
2227
2337
  }
2228
- return keyHandler;
2229
2338
  }
2230
2339
 
2231
2340
  // src/lib/RGBA.ts
@@ -4079,60 +4188,9 @@ function isStyledText(obj) {
4079
4188
  class StyledText {
4080
4189
  [BrandedStyledText] = true;
4081
4190
  chunks;
4082
- textRenderable;
4083
4191
  constructor(chunks) {
4084
4192
  this.chunks = chunks;
4085
4193
  }
4086
- mount(textRenderable) {
4087
- this.textRenderable = textRenderable;
4088
- }
4089
- insert(chunk, index) {
4090
- const originalLength = this.chunks.length;
4091
- if (this.textRenderable) {
4092
- this.textRenderable.insertChunk(chunk, index ?? originalLength);
4093
- let newChunks;
4094
- if (index === undefined || index === originalLength) {
4095
- newChunks = [...this.chunks, chunk];
4096
- } else {
4097
- newChunks = [...this.chunks.slice(0, index), chunk, ...this.chunks.slice(index)];
4098
- }
4099
- this.chunks = newChunks;
4100
- }
4101
- return this;
4102
- }
4103
- remove(chunk) {
4104
- if (this.textRenderable) {
4105
- this.textRenderable.removeChunk(chunk);
4106
- const originalLength = this.chunks.length;
4107
- const index = this.chunks.indexOf(chunk);
4108
- if (index === -1)
4109
- return this;
4110
- let newChunks;
4111
- if (index === originalLength - 1) {
4112
- newChunks = this.chunks.slice(0, -1);
4113
- } else {
4114
- newChunks = [...this.chunks.slice(0, index), ...this.chunks.slice(index + 1)];
4115
- }
4116
- this.chunks = newChunks;
4117
- }
4118
- return this;
4119
- }
4120
- replace(chunk, oldChunk) {
4121
- if (this.textRenderable) {
4122
- this.textRenderable.replaceChunk(chunk, oldChunk);
4123
- const index = this.chunks.indexOf(oldChunk);
4124
- if (index === -1)
4125
- return this;
4126
- let newChunks;
4127
- if (index === this.chunks.length - 1) {
4128
- newChunks = [...this.chunks.slice(0, -1), chunk];
4129
- } else {
4130
- newChunks = [...this.chunks.slice(0, index), chunk, ...this.chunks.slice(index + 1)];
4131
- }
4132
- this.chunks = newChunks;
4133
- }
4134
- return this;
4135
- }
4136
4194
  }
4137
4195
  function stringToStyledText(content) {
4138
4196
  const chunk = {
@@ -4228,7 +4286,36 @@ function t(strings, ...values) {
4228
4286
  return new StyledText(chunks);
4229
4287
  }
4230
4288
 
4231
- // src/lib/hast-styled-text.ts
4289
+ // src/lib/syntax-style.ts
4290
+ function convertThemeToStyles(theme) {
4291
+ const flatStyles = {};
4292
+ for (const tokenStyle of theme) {
4293
+ const styleDefinition = {};
4294
+ if (tokenStyle.style.foreground) {
4295
+ styleDefinition.fg = parseColor(tokenStyle.style.foreground);
4296
+ }
4297
+ if (tokenStyle.style.background) {
4298
+ styleDefinition.bg = parseColor(tokenStyle.style.background);
4299
+ }
4300
+ if (tokenStyle.style.bold !== undefined) {
4301
+ styleDefinition.bold = tokenStyle.style.bold;
4302
+ }
4303
+ if (tokenStyle.style.italic !== undefined) {
4304
+ styleDefinition.italic = tokenStyle.style.italic;
4305
+ }
4306
+ if (tokenStyle.style.underline !== undefined) {
4307
+ styleDefinition.underline = tokenStyle.style.underline;
4308
+ }
4309
+ if (tokenStyle.style.dim !== undefined) {
4310
+ styleDefinition.dim = tokenStyle.style.dim;
4311
+ }
4312
+ for (const scope of tokenStyle.scope) {
4313
+ flatStyles[scope] = styleDefinition;
4314
+ }
4315
+ }
4316
+ return flatStyles;
4317
+ }
4318
+
4232
4319
  class SyntaxStyle {
4233
4320
  styles;
4234
4321
  mergedStyleCache;
@@ -4236,6 +4323,10 @@ class SyntaxStyle {
4236
4323
  this.styles = styles;
4237
4324
  this.mergedStyleCache = new Map;
4238
4325
  }
4326
+ static fromTheme(theme) {
4327
+ const flatStyles = convertThemeToStyles(theme);
4328
+ return new SyntaxStyle(flatStyles);
4329
+ }
4239
4330
  mergeStyles(...styleNames) {
4240
4331
  const cacheKey = styleNames.join(":");
4241
4332
  const cached = this.mergedStyleCache.get(cacheKey);
@@ -4243,7 +4334,7 @@ class SyntaxStyle {
4243
4334
  return cached;
4244
4335
  const styleDefinition = {};
4245
4336
  for (const name of styleNames) {
4246
- const style = this.styles[name];
4337
+ const style = this.getStyle(name);
4247
4338
  if (!style)
4248
4339
  continue;
4249
4340
  if (style.fg)
@@ -4273,6 +4364,18 @@ class SyntaxStyle {
4273
4364
  this.mergedStyleCache.set(cacheKey, merged);
4274
4365
  return merged;
4275
4366
  }
4367
+ getStyle(name) {
4368
+ if (Object.prototype.hasOwnProperty.call(this.styles, name)) {
4369
+ return this.styles[name];
4370
+ }
4371
+ if (name.includes(".")) {
4372
+ const baseName = name.split(".")[0];
4373
+ if (Object.prototype.hasOwnProperty.call(this.styles, baseName)) {
4374
+ return this.styles[baseName];
4375
+ }
4376
+ }
4377
+ return;
4378
+ }
4276
4379
  clearCache() {
4277
4380
  this.mergedStyleCache.clear();
4278
4381
  }
@@ -4280,6 +4383,8 @@ class SyntaxStyle {
4280
4383
  return this.mergedStyleCache.size;
4281
4384
  }
4282
4385
  }
4386
+
4387
+ // src/lib/hast-styled-text.ts
4283
4388
  function hastToTextChunks(node, syntaxStyle, parentStyles = []) {
4284
4389
  const chunks = [];
4285
4390
  if (node.type === "text") {
@@ -4311,6 +4416,55 @@ function hastToStyledText(hast, syntaxStyle) {
4311
4416
  return new StyledText(chunks);
4312
4417
  }
4313
4418
 
4419
+ // src/lib/scroll-acceleration.ts
4420
+ class LinearScrollAccel {
4421
+ tick(_now) {
4422
+ return 1;
4423
+ }
4424
+ reset() {}
4425
+ }
4426
+
4427
+ class MacOSScrollAccel {
4428
+ opts;
4429
+ lastTickTime = 0;
4430
+ velocityHistory = [];
4431
+ historySize = 3;
4432
+ streakTimeout = 150;
4433
+ minTickInterval = 6;
4434
+ constructor(opts = {}) {
4435
+ this.opts = opts;
4436
+ }
4437
+ tick(now = Date.now()) {
4438
+ const A = this.opts.A ?? 0.8;
4439
+ const tau = this.opts.tau ?? 3;
4440
+ const maxMultiplier = this.opts.maxMultiplier ?? 6;
4441
+ const dt = this.lastTickTime ? now - this.lastTickTime : Infinity;
4442
+ if (dt === Infinity || dt > this.streakTimeout) {
4443
+ this.lastTickTime = now;
4444
+ this.velocityHistory = [];
4445
+ return 1;
4446
+ }
4447
+ if (dt < this.minTickInterval) {
4448
+ return 1;
4449
+ }
4450
+ this.lastTickTime = now;
4451
+ this.velocityHistory.push(dt);
4452
+ if (this.velocityHistory.length > this.historySize) {
4453
+ this.velocityHistory.shift();
4454
+ }
4455
+ const avgInterval = this.velocityHistory.reduce((a, b) => a + b, 0) / this.velocityHistory.length;
4456
+ const referenceInterval = 100;
4457
+ const velocity = referenceInterval / avgInterval;
4458
+ const x = velocity / tau;
4459
+ const multiplier = 1 + A * (Math.exp(x) - 1);
4460
+ return Math.min(multiplier, maxMultiplier);
4461
+ }
4462
+ reset() {
4463
+ this.lastTickTime = 0;
4464
+ this.velocityHistory = [];
4465
+ }
4466
+ }
4467
+
4314
4468
  // src/lib/yoga.options.ts
4315
4469
  function parseAlign(value) {
4316
4470
  switch (value.toLowerCase()) {
@@ -4739,74 +4893,1356 @@ function convertGlobalToLocalSelection(globalSelection, localX, localY) {
4739
4893
  };
4740
4894
  }
4741
4895
 
4742
- class ASCIIFontSelectionHelper {
4743
- getText;
4744
- getFont;
4745
- localSelection = null;
4746
- constructor(getText, getFont) {
4747
- this.getText = getText;
4748
- this.getFont = getFont;
4896
+ class ASCIIFontSelectionHelper {
4897
+ getText;
4898
+ getFont;
4899
+ localSelection = null;
4900
+ constructor(getText, getFont) {
4901
+ this.getText = getText;
4902
+ this.getFont = getFont;
4903
+ }
4904
+ hasSelection() {
4905
+ return this.localSelection !== null;
4906
+ }
4907
+ getSelection() {
4908
+ return this.localSelection;
4909
+ }
4910
+ shouldStartSelection(localX, localY, width, height) {
4911
+ if (localX < 0 || localX >= width || localY < 0 || localY >= height) {
4912
+ return false;
4913
+ }
4914
+ const text = this.getText();
4915
+ const font = this.getFont();
4916
+ const charIndex = coordinateToCharacterIndex(localX, text, font);
4917
+ return charIndex >= 0 && charIndex <= text.length;
4918
+ }
4919
+ onLocalSelectionChanged(localSelection, width, height) {
4920
+ const previousSelection = this.localSelection;
4921
+ if (!localSelection?.isActive) {
4922
+ this.localSelection = null;
4923
+ return previousSelection !== null;
4924
+ }
4925
+ const text = this.getText();
4926
+ const font = this.getFont();
4927
+ const selStart = { x: localSelection.anchorX, y: localSelection.anchorY };
4928
+ const selEnd = { x: localSelection.focusX, y: localSelection.focusY };
4929
+ if (height - 1 < selStart.y || 0 > selEnd.y) {
4930
+ this.localSelection = null;
4931
+ return previousSelection !== null;
4932
+ }
4933
+ let startCharIndex = 0;
4934
+ let endCharIndex = text.length;
4935
+ if (selStart.y > height - 1) {
4936
+ this.localSelection = null;
4937
+ return previousSelection !== null;
4938
+ } else if (selStart.y >= 0 && selStart.y <= height - 1) {
4939
+ if (selStart.x > 0) {
4940
+ startCharIndex = coordinateToCharacterIndex(selStart.x, text, font);
4941
+ }
4942
+ }
4943
+ if (selEnd.y < 0) {
4944
+ this.localSelection = null;
4945
+ return previousSelection !== null;
4946
+ } else if (selEnd.y >= 0 && selEnd.y <= height - 1) {
4947
+ if (selEnd.x >= 0) {
4948
+ endCharIndex = coordinateToCharacterIndex(selEnd.x, text, font);
4949
+ } else {
4950
+ endCharIndex = 0;
4951
+ }
4952
+ }
4953
+ if (startCharIndex < endCharIndex && startCharIndex >= 0 && endCharIndex <= text.length) {
4954
+ this.localSelection = { start: startCharIndex, end: endCharIndex };
4955
+ } else {
4956
+ this.localSelection = null;
4957
+ }
4958
+ return previousSelection?.start !== this.localSelection?.start || previousSelection?.end !== this.localSelection?.end;
4959
+ }
4960
+ }
4961
+
4962
+ // src/lib/singleton.ts
4963
+ var singletonCacheSymbol = Symbol.for("@opentui/core/singleton");
4964
+ function singleton(key, factory) {
4965
+ const bag = globalThis[singletonCacheSymbol] ??= {};
4966
+ if (!(key in bag)) {
4967
+ bag[key] = factory();
4968
+ }
4969
+ return bag[key];
4970
+ }
4971
+ function destroySingleton(key) {
4972
+ const bag = globalThis[singletonCacheSymbol];
4973
+ if (bag && key in bag) {
4974
+ delete bag[key];
4975
+ }
4976
+ }
4977
+ function hasSingleton(key) {
4978
+ const bag = globalThis[singletonCacheSymbol];
4979
+ return bag && key in bag;
4980
+ }
4981
+
4982
+ // src/lib/env.ts
4983
+ var envRegistry = singleton("env-registry", () => ({}));
4984
+ function registerEnvVar(config) {
4985
+ const existing = envRegistry[config.name];
4986
+ if (existing) {
4987
+ if (existing.description !== config.description || existing.type !== config.type || existing.default !== config.default) {
4988
+ throw new Error(`Environment variable "${config.name}" is already registered with different configuration. ` + `Existing: ${JSON.stringify(existing)}, New: ${JSON.stringify(config)}`);
4989
+ }
4990
+ return;
4991
+ }
4992
+ envRegistry[config.name] = config;
4993
+ }
4994
+ function normalizeBoolean(value) {
4995
+ const lowerValue = value.toLowerCase();
4996
+ return ["true", "1", "on", "yes"].includes(lowerValue);
4997
+ }
4998
+ function parseEnvValue(config) {
4999
+ const envValue = process.env[config.name];
5000
+ if (envValue === undefined && config.default !== undefined) {
5001
+ return config.default;
5002
+ }
5003
+ if (envValue === undefined) {
5004
+ throw new Error(`Required environment variable ${config.name} is not set. ${config.description}`);
5005
+ }
5006
+ switch (config.type) {
5007
+ case "boolean":
5008
+ return typeof envValue === "boolean" ? envValue : normalizeBoolean(envValue);
5009
+ case "number":
5010
+ const numValue = Number(envValue);
5011
+ if (isNaN(numValue)) {
5012
+ throw new Error(`Environment variable ${config.name} must be a valid number, got: ${envValue}`);
5013
+ }
5014
+ return numValue;
5015
+ case "string":
5016
+ default:
5017
+ return envValue;
5018
+ }
5019
+ }
5020
+
5021
+ class EnvStore {
5022
+ parsedValues = new Map;
5023
+ get(key) {
5024
+ if (this.parsedValues.has(key)) {
5025
+ return this.parsedValues.get(key);
5026
+ }
5027
+ if (!(key in envRegistry)) {
5028
+ throw new Error(`Environment variable ${key} is not registered.`);
5029
+ }
5030
+ try {
5031
+ const value = parseEnvValue(envRegistry[key]);
5032
+ this.parsedValues.set(key, value);
5033
+ return value;
5034
+ } catch (error) {
5035
+ throw new Error(`Failed to parse env var ${key}: ${error instanceof Error ? error.message : String(error)}`);
5036
+ }
5037
+ }
5038
+ has(key) {
5039
+ return key in envRegistry;
5040
+ }
5041
+ clearCache() {
5042
+ this.parsedValues.clear();
5043
+ }
5044
+ }
5045
+ var envStore = singleton("env-store", () => new EnvStore);
5046
+ function clearEnvCache() {
5047
+ envStore.clearCache();
5048
+ }
5049
+ function generateEnvMarkdown() {
5050
+ const configs = Object.values(envRegistry);
5051
+ if (configs.length === 0) {
5052
+ return `# Environment Variables
5053
+
5054
+ No environment variables registered.
5055
+ `;
5056
+ }
5057
+ let markdown = `# Environment Variables
5058
+
5059
+ `;
5060
+ for (const config of configs) {
5061
+ markdown += `## ${config.name}
5062
+
5063
+ `;
5064
+ markdown += `${config.description}
5065
+
5066
+ `;
5067
+ markdown += `**Type:** \`${config.type || "string"}\`
5068
+ `;
5069
+ if (config.default !== undefined) {
5070
+ const defaultValue = typeof config.default === "string" ? `"${config.default}"` : String(config.default);
5071
+ markdown += `**Default:** \`${defaultValue}\`
5072
+ `;
5073
+ } else {
5074
+ markdown += `**Default:** *Required*
5075
+ `;
5076
+ }
5077
+ markdown += `
5078
+ `;
5079
+ }
5080
+ return markdown;
5081
+ }
5082
+ function generateEnvColored() {
5083
+ const configs = Object.values(envRegistry);
5084
+ if (configs.length === 0) {
5085
+ return `\x1B[1;36mEnvironment Variables\x1B[0m
5086
+
5087
+ No environment variables registered.
5088
+ `;
5089
+ }
5090
+ let output = `\x1B[1;36mEnvironment Variables\x1B[0m
5091
+
5092
+ `;
5093
+ for (const config of configs) {
5094
+ output += `\x1B[1;33m${config.name}\x1B[0m
5095
+ `;
5096
+ output += `${config.description}
5097
+ `;
5098
+ output += `\x1B[32mType:\x1B[0m \x1B[36m${config.type || "string"}\x1B[0m
5099
+ `;
5100
+ if (config.default !== undefined) {
5101
+ const defaultValue = typeof config.default === "string" ? `"${config.default}"` : String(config.default);
5102
+ output += `\x1B[32mDefault:\x1B[0m \x1B[35m${defaultValue}\x1B[0m
5103
+ `;
5104
+ } else {
5105
+ output += `\x1B[32mDefault:\x1B[0m \x1B[31mRequired\x1B[0m
5106
+ `;
5107
+ }
5108
+ output += `
5109
+ `;
5110
+ }
5111
+ return output;
5112
+ }
5113
+ var env = new Proxy({}, {
5114
+ get(target, prop) {
5115
+ if (typeof prop !== "string") {
5116
+ return;
5117
+ }
5118
+ return envStore.get(prop);
5119
+ },
5120
+ has(target, prop) {
5121
+ return envStore.has(prop);
5122
+ },
5123
+ ownKeys() {
5124
+ return Object.keys(envRegistry);
5125
+ },
5126
+ getOwnPropertyDescriptor(target, prop) {
5127
+ if (envStore.has(prop)) {
5128
+ return {
5129
+ enumerable: true,
5130
+ configurable: true,
5131
+ get: () => envStore.get(prop)
5132
+ };
5133
+ }
5134
+ return;
5135
+ }
5136
+ });
5137
+
5138
+ // src/lib/tree-sitter-styled-text.ts
5139
+ function treeSitterToTextChunks(content, highlights, syntaxStyle) {
5140
+ const chunks = [];
5141
+ const defaultStyle = syntaxStyle.getStyle("default");
5142
+ let currentIndex = 0;
5143
+ for (let i = 0;i < highlights.length; i++) {
5144
+ const [startIndex, endIndex, group] = highlights[i];
5145
+ if (startIndex < currentIndex)
5146
+ continue;
5147
+ if (currentIndex < startIndex) {
5148
+ const text2 = content.slice(currentIndex, startIndex);
5149
+ chunks.push({
5150
+ __isChunk: true,
5151
+ text: text2,
5152
+ fg: defaultStyle?.fg,
5153
+ bg: defaultStyle?.bg,
5154
+ attributes: defaultStyle ? createTextAttributes({
5155
+ bold: defaultStyle.bold,
5156
+ italic: defaultStyle.italic,
5157
+ underline: defaultStyle.underline,
5158
+ dim: defaultStyle.dim
5159
+ }) : 0
5160
+ });
5161
+ currentIndex = startIndex;
5162
+ }
5163
+ let resolvedStyle = syntaxStyle.getStyle(group);
5164
+ let j = i + 1;
5165
+ while (j < highlights.length && highlights[j][0] === startIndex) {
5166
+ const [, , nextGroup] = highlights[j];
5167
+ const nextStyle = syntaxStyle.getStyle(nextGroup);
5168
+ if (nextStyle) {
5169
+ resolvedStyle = nextStyle;
5170
+ }
5171
+ j++;
5172
+ }
5173
+ i = j - 1;
5174
+ const text = content.slice(startIndex, endIndex);
5175
+ const styleToUse = resolvedStyle || defaultStyle;
5176
+ chunks.push({
5177
+ __isChunk: true,
5178
+ text,
5179
+ fg: styleToUse?.fg,
5180
+ bg: styleToUse?.bg,
5181
+ attributes: styleToUse ? createTextAttributes({
5182
+ bold: styleToUse.bold,
5183
+ italic: styleToUse.italic,
5184
+ underline: styleToUse.underline,
5185
+ dim: styleToUse.dim
5186
+ }) : 0
5187
+ });
5188
+ currentIndex = endIndex;
5189
+ }
5190
+ if (currentIndex < content.length) {
5191
+ const text = content.slice(currentIndex);
5192
+ chunks.push({
5193
+ __isChunk: true,
5194
+ text,
5195
+ fg: defaultStyle?.fg,
5196
+ bg: defaultStyle?.bg,
5197
+ attributes: defaultStyle ? createTextAttributes({
5198
+ bold: defaultStyle.bold,
5199
+ italic: defaultStyle.italic,
5200
+ underline: defaultStyle.underline,
5201
+ dim: defaultStyle.dim
5202
+ }) : 0
5203
+ });
5204
+ }
5205
+ return chunks;
5206
+ }
5207
+ async function treeSitterToStyledText(content, filetype, syntaxStyle, client) {
5208
+ const result = await client.highlightOnce(content, filetype);
5209
+ if (result.highlights && result.highlights.length > 0) {
5210
+ const chunks = treeSitterToTextChunks(content, result.highlights, syntaxStyle);
5211
+ return new StyledText(chunks);
5212
+ } else {
5213
+ const defaultStyle = syntaxStyle.mergeStyles("default");
5214
+ const chunks = [
5215
+ {
5216
+ __isChunk: true,
5217
+ text: content,
5218
+ fg: defaultStyle.fg,
5219
+ bg: defaultStyle.bg,
5220
+ attributes: defaultStyle.attributes
5221
+ }
5222
+ ];
5223
+ return new StyledText(chunks);
5224
+ }
5225
+ }
5226
+
5227
+ // src/lib/tree-sitter/client.ts
5228
+ import { EventEmitter as EventEmitter2 } from "events";
5229
+
5230
+ // src/lib/debounce.ts
5231
+ var TIMERS_MAP = new Map;
5232
+
5233
+ class DebounceController {
5234
+ scopeId;
5235
+ constructor(scopeId) {
5236
+ this.scopeId = scopeId;
5237
+ if (!TIMERS_MAP.has(this.scopeId)) {
5238
+ TIMERS_MAP.set(this.scopeId, new Map);
5239
+ }
5240
+ }
5241
+ debounce(id, ms, fn) {
5242
+ const scopeMap = TIMERS_MAP.get(this.scopeId);
5243
+ return new Promise((resolve, reject) => {
5244
+ if (scopeMap.has(id)) {
5245
+ clearTimeout(scopeMap.get(id));
5246
+ }
5247
+ const timerId = setTimeout(() => {
5248
+ try {
5249
+ resolve(fn());
5250
+ } catch (error) {
5251
+ reject(error);
5252
+ }
5253
+ scopeMap.delete(id);
5254
+ }, ms);
5255
+ scopeMap.set(id, timerId);
5256
+ });
5257
+ }
5258
+ clearDebounce(id) {
5259
+ const scopeMap = TIMERS_MAP.get(this.scopeId);
5260
+ if (scopeMap && scopeMap.has(id)) {
5261
+ clearTimeout(scopeMap.get(id));
5262
+ scopeMap.delete(id);
5263
+ }
5264
+ }
5265
+ clear() {
5266
+ const scopeMap = TIMERS_MAP.get(this.scopeId);
5267
+ if (scopeMap) {
5268
+ scopeMap.forEach((timerId) => clearTimeout(timerId));
5269
+ scopeMap.clear();
5270
+ }
5271
+ }
5272
+ }
5273
+ function createDebounce(scopeId) {
5274
+ return new DebounceController(scopeId);
5275
+ }
5276
+ function clearDebounceScope(scopeId) {
5277
+ const scopeMap = TIMERS_MAP.get(scopeId);
5278
+ if (scopeMap) {
5279
+ scopeMap.forEach((timerId) => clearTimeout(timerId));
5280
+ scopeMap.clear();
5281
+ }
5282
+ }
5283
+
5284
+ // src/lib/queue.ts
5285
+ class ProcessQueue {
5286
+ processor;
5287
+ queue = [];
5288
+ processing = false;
5289
+ autoProcess = true;
5290
+ constructor(processor, autoProcess = true) {
5291
+ this.processor = processor;
5292
+ this.autoProcess = autoProcess;
5293
+ }
5294
+ enqueue(item) {
5295
+ this.queue.push(item);
5296
+ if (!this.processing && this.autoProcess) {
5297
+ this.processQueue();
5298
+ }
5299
+ }
5300
+ processQueue() {
5301
+ if (this.queue.length === 0) {
5302
+ return;
5303
+ }
5304
+ this.processing = true;
5305
+ queueMicrotask(async () => {
5306
+ if (this.queue.length === 0) {
5307
+ this.processing = false;
5308
+ return;
5309
+ }
5310
+ const item = this.queue.shift();
5311
+ try {
5312
+ await this.processor(item);
5313
+ } catch (error) {
5314
+ console.error("Error processing queue item:", error);
5315
+ }
5316
+ if (this.queue.length > 0) {
5317
+ this.processQueue();
5318
+ } else {
5319
+ this.processing = false;
5320
+ }
5321
+ });
5322
+ }
5323
+ clear() {
5324
+ this.queue = [];
5325
+ }
5326
+ isProcessing() {
5327
+ return this.processing;
5328
+ }
5329
+ size() {
5330
+ return this.queue.length;
5331
+ }
5332
+ }
5333
+
5334
+ // src/lib/tree-sitter/default-parsers.ts
5335
+ import { resolve, dirname } from "path";
5336
+ import { fileURLToPath } from "url";
5337
+ import javascript_highlights from "./assets/javascript/highlights.scm" with { type: "file" };
5338
+ import javascript_language from "./assets/javascript/tree-sitter-javascript.wasm" with { type: "file" };
5339
+ import typescript_highlights from "./assets/typescript/highlights.scm" with { type: "file" };
5340
+ import typescript_language from "./assets/typescript/tree-sitter-typescript.wasm" with { type: "file" };
5341
+ var _cachedParsers;
5342
+ function getParsers() {
5343
+ if (!_cachedParsers) {
5344
+ _cachedParsers = [
5345
+ {
5346
+ filetype: "javascript",
5347
+ queries: {
5348
+ highlights: [resolve(dirname(fileURLToPath(import.meta.url)), javascript_highlights)]
5349
+ },
5350
+ wasm: resolve(dirname(fileURLToPath(import.meta.url)), javascript_language)
5351
+ },
5352
+ {
5353
+ filetype: "typescript",
5354
+ queries: {
5355
+ highlights: [resolve(dirname(fileURLToPath(import.meta.url)), typescript_highlights)]
5356
+ },
5357
+ wasm: resolve(dirname(fileURLToPath(import.meta.url)), typescript_language)
5358
+ }
5359
+ ];
5360
+ }
5361
+ return _cachedParsers;
5362
+ }
5363
+
5364
+ // src/lib/tree-sitter/client.ts
5365
+ import { resolve as resolve2, isAbsolute } from "path";
5366
+ import { existsSync } from "fs";
5367
+ import { parse } from "path";
5368
+ registerEnvVar({
5369
+ name: "OTUI_TREE_SITTER_WORKER_PATH",
5370
+ description: "Path to the TreeSitter worker",
5371
+ type: "string",
5372
+ default: ""
5373
+ });
5374
+ var DEFAULT_PARSERS = getParsers();
5375
+ function addDefaultParsers(parsers) {
5376
+ for (const parser of parsers) {
5377
+ const existingIndex = DEFAULT_PARSERS.findIndex((p) => p.filetype === parser.filetype);
5378
+ if (existingIndex >= 0) {
5379
+ DEFAULT_PARSERS[existingIndex] = parser;
5380
+ } else {
5381
+ DEFAULT_PARSERS.push(parser);
5382
+ }
5383
+ }
5384
+ }
5385
+ var isUrl = (path) => path.startsWith("http://") || path.startsWith("https://");
5386
+
5387
+ class TreeSitterClient extends EventEmitter2 {
5388
+ initialized = false;
5389
+ worker;
5390
+ buffers = new Map;
5391
+ initializePromise;
5392
+ initializeResolvers;
5393
+ messageCallbacks = new Map;
5394
+ messageIdCounter = 0;
5395
+ editQueues = new Map;
5396
+ debouncer;
5397
+ options;
5398
+ constructor(options) {
5399
+ super();
5400
+ this.options = options;
5401
+ this.debouncer = createDebounce("tree-sitter-client");
5402
+ this.startWorker();
5403
+ }
5404
+ emitError(error, bufferId) {
5405
+ if (this.listenerCount("error") > 0) {
5406
+ this.emit("error", error, bufferId);
5407
+ }
5408
+ }
5409
+ emitWarning(warning, bufferId) {
5410
+ if (this.listenerCount("warning") > 0) {
5411
+ this.emit("warning", warning, bufferId);
5412
+ }
5413
+ }
5414
+ startWorker() {
5415
+ if (this.worker) {
5416
+ return;
5417
+ }
5418
+ let worker_path;
5419
+ if (env.OTUI_TREE_SITTER_WORKER_PATH) {
5420
+ worker_path = env.OTUI_TREE_SITTER_WORKER_PATH;
5421
+ } else if (typeof OTUI_TREE_SITTER_WORKER_PATH !== "undefined") {
5422
+ worker_path = OTUI_TREE_SITTER_WORKER_PATH;
5423
+ } else if (this.options.workerPath) {
5424
+ worker_path = this.options.workerPath;
5425
+ } else {
5426
+ worker_path = new URL("./parser.worker.js", import.meta.url).href;
5427
+ if (!existsSync(resolve2(import.meta.dirname, "parser.worker.js"))) {
5428
+ worker_path = new URL("./parser.worker.ts", import.meta.url).href;
5429
+ }
5430
+ }
5431
+ this.worker = new Worker(worker_path);
5432
+ this.worker.onmessage = this.handleWorkerMessage.bind(this);
5433
+ this.worker.onerror = (error) => {
5434
+ console.error("TreeSitter worker error:", error.message);
5435
+ if (this.initializeResolvers) {
5436
+ clearTimeout(this.initializeResolvers.timeoutId);
5437
+ this.initializeResolvers.reject(new Error(`Worker error: ${error.message}`));
5438
+ this.initializeResolvers = undefined;
5439
+ }
5440
+ this.emitError(`Worker error: ${error.message}`);
5441
+ };
5442
+ }
5443
+ stopWorker() {
5444
+ if (!this.worker) {
5445
+ return;
5446
+ }
5447
+ this.worker.terminate();
5448
+ this.worker = undefined;
5449
+ }
5450
+ handleReset() {
5451
+ this.buffers.clear();
5452
+ this.stopWorker();
5453
+ this.startWorker();
5454
+ this.initializePromise = undefined;
5455
+ this.initializeResolvers = undefined;
5456
+ return this.initialize();
5457
+ }
5458
+ async initialize() {
5459
+ if (this.initializePromise) {
5460
+ return this.initializePromise;
5461
+ }
5462
+ this.initializePromise = new Promise((resolve3, reject) => {
5463
+ const timeoutMs = this.options.initTimeout ?? 1e4;
5464
+ const timeoutId = setTimeout(() => {
5465
+ const error = new Error("Worker initialization timed out");
5466
+ console.error("TreeSitter client:", error.message);
5467
+ this.initializeResolvers = undefined;
5468
+ reject(error);
5469
+ }, timeoutMs);
5470
+ this.initializeResolvers = { resolve: resolve3, reject, timeoutId };
5471
+ this.worker?.postMessage({
5472
+ type: "INIT",
5473
+ dataPath: this.options.dataPath
5474
+ });
5475
+ });
5476
+ await this.initializePromise;
5477
+ await this.registerDefaultParsers();
5478
+ return this.initializePromise;
5479
+ }
5480
+ async registerDefaultParsers() {
5481
+ for (const parser of DEFAULT_PARSERS) {
5482
+ this.addFiletypeParser(parser);
5483
+ }
5484
+ }
5485
+ resolvePath(path) {
5486
+ if (isUrl(path)) {
5487
+ return path;
5488
+ }
5489
+ if (/\$bunfs/.test(path)) {
5490
+ return "/$bunfs/root/" + parse(path).base;
5491
+ }
5492
+ if (!isAbsolute(path)) {
5493
+ return resolve2(path);
5494
+ }
5495
+ return path;
5496
+ }
5497
+ addFiletypeParser(filetypeParser) {
5498
+ const resolvedParser = {
5499
+ ...filetypeParser,
5500
+ wasm: this.resolvePath(filetypeParser.wasm),
5501
+ queries: {
5502
+ highlights: filetypeParser.queries.highlights.map((path) => this.resolvePath(path))
5503
+ }
5504
+ };
5505
+ this.worker?.postMessage({ type: "ADD_FILETYPE_PARSER", filetypeParser: resolvedParser });
5506
+ }
5507
+ async getPerformance() {
5508
+ const messageId = `performance_${this.messageIdCounter++}`;
5509
+ return new Promise((resolve3) => {
5510
+ this.messageCallbacks.set(messageId, resolve3);
5511
+ this.worker?.postMessage({ type: "GET_PERFORMANCE", messageId });
5512
+ });
5513
+ }
5514
+ async highlightOnce(content, filetype) {
5515
+ if (!this.initialized) {
5516
+ try {
5517
+ await this.initialize();
5518
+ } catch (error) {
5519
+ return { error: "Could not highlight because of initialization error" };
5520
+ }
5521
+ }
5522
+ const messageId = `oneshot_${this.messageIdCounter++}`;
5523
+ return new Promise((resolve3) => {
5524
+ this.messageCallbacks.set(messageId, resolve3);
5525
+ this.worker?.postMessage({
5526
+ type: "ONESHOT_HIGHLIGHT",
5527
+ content,
5528
+ filetype,
5529
+ messageId
5530
+ });
5531
+ });
5532
+ }
5533
+ handleWorkerMessage(event) {
5534
+ const { type, bufferId, error, highlights, warning, messageId, hasParser, performance: performance2, version } = event.data;
5535
+ if (type === "HIGHLIGHT_RESPONSE") {
5536
+ const buffer = this.buffers.get(bufferId);
5537
+ if (!buffer || !buffer.hasParser)
5538
+ return;
5539
+ if (buffer.version !== version) {
5540
+ this.resetBuffer(bufferId, buffer.version, buffer.content);
5541
+ return;
5542
+ }
5543
+ this.emit("highlights:response", bufferId, version, highlights);
5544
+ }
5545
+ if (type === "INIT_RESPONSE") {
5546
+ if (this.initializeResolvers) {
5547
+ clearTimeout(this.initializeResolvers.timeoutId);
5548
+ if (error) {
5549
+ console.error("TreeSitter client initialization failed:", error);
5550
+ this.initializeResolvers.reject(new Error(error));
5551
+ } else {
5552
+ this.initialized = true;
5553
+ this.initializeResolvers.resolve();
5554
+ }
5555
+ this.initializeResolvers = undefined;
5556
+ return;
5557
+ }
5558
+ }
5559
+ if (type === "PARSER_INIT_RESPONSE") {
5560
+ const callback = this.messageCallbacks.get(messageId);
5561
+ if (callback) {
5562
+ this.messageCallbacks.delete(messageId);
5563
+ callback({ hasParser, warning, error });
5564
+ }
5565
+ return;
5566
+ }
5567
+ if (type === "PRELOAD_PARSER_RESPONSE") {
5568
+ const callback = this.messageCallbacks.get(messageId);
5569
+ if (callback) {
5570
+ this.messageCallbacks.delete(messageId);
5571
+ callback({ hasParser });
5572
+ }
5573
+ return;
5574
+ }
5575
+ if (type === "BUFFER_DISPOSED") {
5576
+ const callback = this.messageCallbacks.get(`dispose_${bufferId}`);
5577
+ if (callback) {
5578
+ this.messageCallbacks.delete(`dispose_${bufferId}`);
5579
+ callback(true);
5580
+ }
5581
+ this.emit("buffer:disposed", bufferId);
5582
+ return;
5583
+ }
5584
+ if (type === "PERFORMANCE_RESPONSE") {
5585
+ const callback = this.messageCallbacks.get(messageId);
5586
+ if (callback) {
5587
+ this.messageCallbacks.delete(messageId);
5588
+ callback(performance2);
5589
+ }
5590
+ return;
5591
+ }
5592
+ if (type === "ONESHOT_HIGHLIGHT_RESPONSE") {
5593
+ const callback = this.messageCallbacks.get(messageId);
5594
+ if (callback) {
5595
+ this.messageCallbacks.delete(messageId);
5596
+ callback({ highlights, warning, error });
5597
+ }
5598
+ return;
5599
+ }
5600
+ if (type === "UPDATE_DATA_PATH_RESPONSE") {
5601
+ const callback = this.messageCallbacks.get(messageId);
5602
+ if (callback) {
5603
+ this.messageCallbacks.delete(messageId);
5604
+ callback({ error });
5605
+ }
5606
+ return;
5607
+ }
5608
+ if (warning) {
5609
+ this.emitWarning(warning, bufferId);
5610
+ return;
5611
+ }
5612
+ if (error) {
5613
+ this.emitError(error, bufferId);
5614
+ return;
5615
+ }
5616
+ if (type === "WORKER_LOG") {
5617
+ const { logType, data } = event.data;
5618
+ const message = data.join(" ");
5619
+ this.emit("worker:log", logType, message);
5620
+ if (logType === "log") {
5621
+ console.log("Worker stdout:", ...data);
5622
+ } else if (logType === "error") {
5623
+ console.error("Worker stderr:", ...data);
5624
+ }
5625
+ return;
5626
+ }
5627
+ }
5628
+ async preloadParser(filetype) {
5629
+ const messageId = `has_parser_${this.messageIdCounter++}`;
5630
+ const response = await new Promise((resolve3) => {
5631
+ this.messageCallbacks.set(messageId, resolve3);
5632
+ this.worker?.postMessage({
5633
+ type: "PRELOAD_PARSER",
5634
+ filetype,
5635
+ messageId
5636
+ });
5637
+ });
5638
+ return response.hasParser;
5639
+ }
5640
+ async createBuffer(id, content, filetype, version = 1, autoInitialize = true) {
5641
+ if (!this.initialized) {
5642
+ if (!autoInitialize) {
5643
+ this.emitError("Could not create buffer because client is not initialized");
5644
+ return false;
5645
+ }
5646
+ try {
5647
+ await this.initialize();
5648
+ } catch (error) {
5649
+ this.emitError("Could not create buffer because of initialization error");
5650
+ return false;
5651
+ }
5652
+ }
5653
+ if (this.buffers.has(id)) {
5654
+ throw new Error(`Buffer with id ${id} already exists`);
5655
+ }
5656
+ this.buffers.set(id, { id, content, filetype, version, hasParser: false });
5657
+ const messageId = `init_${this.messageIdCounter++}`;
5658
+ const response = await new Promise((resolve3) => {
5659
+ this.messageCallbacks.set(messageId, resolve3);
5660
+ this.worker?.postMessage({
5661
+ type: "INITIALIZE_PARSER",
5662
+ bufferId: id,
5663
+ version,
5664
+ content,
5665
+ filetype,
5666
+ messageId
5667
+ });
5668
+ });
5669
+ if (!response.hasParser) {
5670
+ this.emit("buffer:initialized", id, false);
5671
+ if (filetype !== "plaintext") {
5672
+ this.emitWarning(response.warning || response.error || "Buffer has no parser", id);
5673
+ }
5674
+ return false;
5675
+ }
5676
+ const bufferState = { id, content, filetype, version, hasParser: true };
5677
+ this.buffers.set(id, bufferState);
5678
+ this.emit("buffer:initialized", id, true);
5679
+ return true;
5680
+ }
5681
+ async updateBuffer(id, edits, newContent, version) {
5682
+ if (!this.initialized) {
5683
+ return;
5684
+ }
5685
+ const buffer = this.buffers.get(id);
5686
+ if (!buffer || !buffer.hasParser) {
5687
+ return;
5688
+ }
5689
+ this.buffers.set(id, { ...buffer, content: newContent, version });
5690
+ if (!this.editQueues.has(id)) {
5691
+ this.editQueues.set(id, new ProcessQueue((item) => this.processEdit(id, item.edits, item.newContent, item.version, item.isReset)));
5692
+ }
5693
+ const bufferQueue = this.editQueues.get(id);
5694
+ bufferQueue.enqueue({ edits, newContent, version });
5695
+ }
5696
+ async processEdit(bufferId, edits, newContent, version, isReset = false) {
5697
+ this.worker?.postMessage({
5698
+ type: isReset ? "RESET_BUFFER" : "HANDLE_EDITS",
5699
+ bufferId,
5700
+ version,
5701
+ content: newContent,
5702
+ edits
5703
+ });
5704
+ }
5705
+ async removeBuffer(bufferId) {
5706
+ if (!this.initialized) {
5707
+ return;
5708
+ }
5709
+ this.buffers.delete(bufferId);
5710
+ if (this.editQueues.has(bufferId)) {
5711
+ this.editQueues.get(bufferId)?.clear();
5712
+ this.editQueues.delete(bufferId);
5713
+ }
5714
+ if (this.worker) {
5715
+ await new Promise((resolve3) => {
5716
+ const messageId = `dispose_${bufferId}`;
5717
+ this.messageCallbacks.set(messageId, resolve3);
5718
+ try {
5719
+ this.worker.postMessage({
5720
+ type: "DISPOSE_BUFFER",
5721
+ bufferId
5722
+ });
5723
+ } catch (error) {
5724
+ console.error("Error disposing buffer", error);
5725
+ resolve3(false);
5726
+ }
5727
+ setTimeout(() => {
5728
+ if (this.messageCallbacks.has(messageId)) {
5729
+ this.messageCallbacks.delete(messageId);
5730
+ console.warn({ bufferId }, "Timed out waiting for buffer to be disposed");
5731
+ resolve3(false);
5732
+ }
5733
+ }, 3000);
5734
+ });
5735
+ }
5736
+ this.debouncer.clearDebounce(`reset-${bufferId}`);
5737
+ }
5738
+ async destroy() {
5739
+ if (this.initializeResolvers) {
5740
+ clearTimeout(this.initializeResolvers.timeoutId);
5741
+ this.initializeResolvers = undefined;
5742
+ }
5743
+ for (const [messageId, callback] of this.messageCallbacks.entries()) {
5744
+ if (typeof callback === "function") {
5745
+ try {
5746
+ callback({ error: "Client destroyed" });
5747
+ } catch (e) {}
5748
+ }
5749
+ }
5750
+ this.messageCallbacks.clear();
5751
+ clearDebounceScope("tree-sitter-client");
5752
+ this.debouncer.clear();
5753
+ this.editQueues.clear();
5754
+ this.buffers.clear();
5755
+ this.stopWorker();
5756
+ this.initialized = false;
5757
+ this.initializePromise = undefined;
5758
+ }
5759
+ async resetBuffer(bufferId, version, content) {
5760
+ if (!this.initialized) {
5761
+ return;
5762
+ }
5763
+ const buffer = this.buffers.get(bufferId);
5764
+ if (!buffer || !buffer.hasParser) {
5765
+ this.emitError("Cannot reset buffer with no parser", bufferId);
5766
+ return;
5767
+ }
5768
+ this.buffers.set(bufferId, { ...buffer, content, version });
5769
+ this.debouncer.debounce(`reset-${bufferId}`, 10, () => this.processEdit(bufferId, [], content, version, true));
5770
+ }
5771
+ getBuffer(bufferId) {
5772
+ return this.buffers.get(bufferId);
5773
+ }
5774
+ getAllBuffers() {
5775
+ return Array.from(this.buffers.values());
5776
+ }
5777
+ isInitialized() {
5778
+ return this.initialized;
5779
+ }
5780
+ async setDataPath(dataPath) {
5781
+ if (this.options.dataPath === dataPath) {
5782
+ return;
5783
+ }
5784
+ this.options.dataPath = dataPath;
5785
+ if (this.initialized && this.worker) {
5786
+ const messageId = `update_datapath_${this.messageIdCounter++}`;
5787
+ return new Promise((resolve3, reject) => {
5788
+ this.messageCallbacks.set(messageId, (response) => {
5789
+ if (response.error) {
5790
+ reject(new Error(response.error));
5791
+ } else {
5792
+ resolve3();
5793
+ }
5794
+ });
5795
+ this.worker.postMessage({
5796
+ type: "UPDATE_DATA_PATH",
5797
+ dataPath,
5798
+ messageId
5799
+ });
5800
+ });
5801
+ }
5802
+ }
5803
+ }
5804
+
5805
+ // src/lib/data-paths.ts
5806
+ import os from "os";
5807
+ import path from "path";
5808
+ import { EventEmitter as EventEmitter3 } from "events";
5809
+
5810
+ // src/lib/validate-dir-name.ts
5811
+ function isValidDirectoryName(name) {
5812
+ if (!name || typeof name !== "string") {
5813
+ return false;
5814
+ }
5815
+ if (name.trim().length === 0) {
5816
+ return false;
5817
+ }
5818
+ const reservedNames = [
5819
+ "CON",
5820
+ "PRN",
5821
+ "AUX",
5822
+ "NUL",
5823
+ "COM1",
5824
+ "COM2",
5825
+ "COM3",
5826
+ "COM4",
5827
+ "COM5",
5828
+ "COM6",
5829
+ "COM7",
5830
+ "COM8",
5831
+ "COM9",
5832
+ "LPT1",
5833
+ "LPT2",
5834
+ "LPT3",
5835
+ "LPT4",
5836
+ "LPT5",
5837
+ "LPT6",
5838
+ "LPT7",
5839
+ "LPT8",
5840
+ "LPT9"
5841
+ ];
5842
+ if (reservedNames.includes(name.toUpperCase())) {
5843
+ return false;
5844
+ }
5845
+ const invalidChars = /[<>:"|?*\/\\\x00-\x1f]/;
5846
+ if (invalidChars.test(name)) {
5847
+ return false;
5848
+ }
5849
+ if (name.endsWith(".") || name.endsWith(" ")) {
5850
+ return false;
5851
+ }
5852
+ if (name === "." || name === "..") {
5853
+ return false;
5854
+ }
5855
+ return true;
5856
+ }
5857
+
5858
+ // src/lib/data-paths.ts
5859
+ registerEnvVar({
5860
+ name: "XDG_CONFIG_HOME",
5861
+ description: "Base directory for user-specific configuration files",
5862
+ type: "string",
5863
+ default: ""
5864
+ });
5865
+ registerEnvVar({
5866
+ name: "XDG_DATA_HOME",
5867
+ description: "Base directory for user-specific data files",
5868
+ type: "string",
5869
+ default: ""
5870
+ });
5871
+
5872
+ class DataPathsManager extends EventEmitter3 {
5873
+ _appName;
5874
+ _globalConfigPath;
5875
+ _globalConfigFile;
5876
+ _localConfigFile;
5877
+ _globalDataPath;
5878
+ constructor() {
5879
+ super();
5880
+ this._appName = "opentui";
4749
5881
  }
4750
- hasSelection() {
4751
- return this.localSelection !== null;
5882
+ get appName() {
5883
+ return this._appName;
4752
5884
  }
4753
- getSelection() {
4754
- return this.localSelection;
5885
+ set appName(value) {
5886
+ if (!isValidDirectoryName(value)) {
5887
+ throw new Error(`Invalid app name "${value}": must be a valid directory name`);
5888
+ }
5889
+ if (this._appName !== value) {
5890
+ this._appName = value;
5891
+ this._globalConfigPath = undefined;
5892
+ this._globalConfigFile = undefined;
5893
+ this._localConfigFile = undefined;
5894
+ this._globalDataPath = undefined;
5895
+ this.emit("paths:changed", this.toObject());
5896
+ }
4755
5897
  }
4756
- shouldStartSelection(localX, localY, width, height) {
4757
- if (localX < 0 || localX >= width || localY < 0 || localY >= height) {
4758
- return false;
5898
+ get globalConfigPath() {
5899
+ if (this._globalConfigPath === undefined) {
5900
+ const homeDir = os.homedir();
5901
+ const xdgConfigHome = env.XDG_CONFIG_HOME;
5902
+ const baseConfigDir = xdgConfigHome || path.join(homeDir, ".config");
5903
+ this._globalConfigPath = path.join(baseConfigDir, this._appName);
4759
5904
  }
4760
- const text = this.getText();
4761
- const font = this.getFont();
4762
- const charIndex = coordinateToCharacterIndex(localX, text, font);
4763
- return charIndex >= 0 && charIndex <= text.length;
5905
+ return this._globalConfigPath;
4764
5906
  }
4765
- onLocalSelectionChanged(localSelection, width, height) {
4766
- const previousSelection = this.localSelection;
4767
- if (!localSelection?.isActive) {
4768
- this.localSelection = null;
4769
- return previousSelection !== null;
5907
+ get globalConfigFile() {
5908
+ if (this._globalConfigFile === undefined) {
5909
+ this._globalConfigFile = path.join(this.globalConfigPath, "init.ts");
4770
5910
  }
4771
- const text = this.getText();
4772
- const font = this.getFont();
4773
- const selStart = { x: localSelection.anchorX, y: localSelection.anchorY };
4774
- const selEnd = { x: localSelection.focusX, y: localSelection.focusY };
4775
- if (height - 1 < selStart.y || 0 > selEnd.y) {
4776
- this.localSelection = null;
4777
- return previousSelection !== null;
5911
+ return this._globalConfigFile;
5912
+ }
5913
+ get localConfigFile() {
5914
+ if (this._localConfigFile === undefined) {
5915
+ this._localConfigFile = path.join(process.cwd(), `.${this._appName}.ts`);
4778
5916
  }
4779
- let startCharIndex = 0;
4780
- let endCharIndex = text.length;
4781
- if (selStart.y > height - 1) {
4782
- this.localSelection = null;
4783
- return previousSelection !== null;
4784
- } else if (selStart.y >= 0 && selStart.y <= height - 1) {
4785
- if (selStart.x > 0) {
4786
- startCharIndex = coordinateToCharacterIndex(selStart.x, text, font);
4787
- }
5917
+ return this._localConfigFile;
5918
+ }
5919
+ get globalDataPath() {
5920
+ if (this._globalDataPath === undefined) {
5921
+ const homeDir = os.homedir();
5922
+ const xdgDataHome = env.XDG_DATA_HOME;
5923
+ const baseDataDir = xdgDataHome || path.join(homeDir, ".local/share");
5924
+ this._globalDataPath = path.join(baseDataDir, this._appName);
4788
5925
  }
4789
- if (selEnd.y < 0) {
4790
- this.localSelection = null;
4791
- return previousSelection !== null;
4792
- } else if (selEnd.y >= 0 && selEnd.y <= height - 1) {
4793
- if (selEnd.x >= 0) {
4794
- endCharIndex = coordinateToCharacterIndex(selEnd.x, text, font);
5926
+ return this._globalDataPath;
5927
+ }
5928
+ toObject() {
5929
+ return {
5930
+ globalConfigPath: this.globalConfigPath,
5931
+ globalConfigFile: this.globalConfigFile,
5932
+ localConfigFile: this.localConfigFile,
5933
+ globalDataPath: this.globalDataPath
5934
+ };
5935
+ }
5936
+ }
5937
+ function getDataPaths() {
5938
+ return singleton("data-paths-opentui", () => new DataPathsManager);
5939
+ }
5940
+
5941
+ // src/lib/tree-sitter/resolve-ft.ts
5942
+ function extToFiletype(extension) {
5943
+ const extensionToFiletype = new Map([
5944
+ ["js", "javascript"],
5945
+ ["jsx", "javascriptreact"],
5946
+ ["ts", "typescript"],
5947
+ ["tsx", "typescriptreact"],
5948
+ ["md", "markdown"],
5949
+ ["json", "json"],
5950
+ ["py", "python"],
5951
+ ["rb", "ruby"],
5952
+ ["go", "go"],
5953
+ ["rs", "rust"],
5954
+ ["c", "c"],
5955
+ ["cpp", "cpp"],
5956
+ ["h", "c"],
5957
+ ["hpp", "cpp"],
5958
+ ["html", "html"],
5959
+ ["css", "css"],
5960
+ ["scss", "scss"],
5961
+ ["less", "less"],
5962
+ ["sh", "shell"],
5963
+ ["bash", "shell"],
5964
+ ["zsh", "shell"],
5965
+ ["vim", "vim"],
5966
+ ["yaml", "yaml"],
5967
+ ["yml", "yaml"],
5968
+ ["toml", "toml"],
5969
+ ["xml", "xml"],
5970
+ ["zig", "zig"]
5971
+ ]);
5972
+ return extensionToFiletype.get(extension);
5973
+ }
5974
+ function pathToFiletype(path2) {
5975
+ if (typeof path2 !== "string")
5976
+ return;
5977
+ const lastDot = path2.lastIndexOf(".");
5978
+ if (lastDot === -1 || lastDot === path2.length - 1) {
5979
+ return;
5980
+ }
5981
+ const extension = path2.substring(lastDot + 1);
5982
+ return extToFiletype(extension);
5983
+ }
5984
+
5985
+ // src/lib/tree-sitter/assets/update.ts
5986
+ import { readFile, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
5987
+ import * as path3 from "path";
5988
+
5989
+ // src/lib/tree-sitter/download-utils.ts
5990
+ import { mkdir, writeFile } from "fs/promises";
5991
+ import * as path2 from "path";
5992
+
5993
+ class DownloadUtils {
5994
+ static hashUrl(url) {
5995
+ let hash = 0;
5996
+ for (let i = 0;i < url.length; i++) {
5997
+ const char = url.charCodeAt(i);
5998
+ hash = (hash << 5) - hash + char;
5999
+ hash = hash & hash;
6000
+ }
6001
+ return Math.abs(hash).toString(16);
6002
+ }
6003
+ static async downloadOrLoad(source, cacheDir, cacheSubdir, fileExtension, useHashForCache = true, filetype) {
6004
+ const isUrl2 = source.startsWith("http://") || source.startsWith("https://");
6005
+ if (isUrl2) {
6006
+ let cacheFileName;
6007
+ if (useHashForCache) {
6008
+ const hash = this.hashUrl(source);
6009
+ cacheFileName = filetype ? `${filetype}-${hash}${fileExtension}` : `${hash}${fileExtension}`;
4795
6010
  } else {
4796
- endCharIndex = 0;
6011
+ cacheFileName = path2.basename(source);
6012
+ }
6013
+ const cacheFile = path2.join(cacheDir, cacheSubdir, cacheFileName);
6014
+ await mkdir(path2.dirname(cacheFile), { recursive: true });
6015
+ try {
6016
+ const cachedContent = await Bun.file(cacheFile).arrayBuffer();
6017
+ if (cachedContent.byteLength > 0) {
6018
+ console.log(`Loaded from cache: ${cacheFile} (${source})`);
6019
+ return { content: cachedContent, filePath: cacheFile };
6020
+ }
6021
+ } catch (error) {}
6022
+ try {
6023
+ console.log(`Downloading from URL: ${source}`);
6024
+ const response = await fetch(source);
6025
+ if (!response.ok) {
6026
+ return { error: `Failed to fetch from ${source}: ${response.statusText}` };
6027
+ }
6028
+ const content = await response.arrayBuffer();
6029
+ try {
6030
+ await writeFile(cacheFile, Buffer.from(content));
6031
+ console.log(`Cached: ${source}`);
6032
+ } catch (cacheError) {
6033
+ console.warn(`Failed to cache: ${cacheError}`);
6034
+ }
6035
+ return { content, filePath: cacheFile };
6036
+ } catch (error) {
6037
+ return { error: `Error downloading from ${source}: ${error}` };
6038
+ }
6039
+ } else {
6040
+ try {
6041
+ console.log(`Loading from local path: ${source}`);
6042
+ const content = await Bun.file(source).arrayBuffer();
6043
+ return { content, filePath: source };
6044
+ } catch (error) {
6045
+ return { error: `Error loading from local path ${source}: ${error}` };
4797
6046
  }
4798
6047
  }
4799
- if (startCharIndex < endCharIndex && startCharIndex >= 0 && endCharIndex <= text.length) {
4800
- this.localSelection = { start: startCharIndex, end: endCharIndex };
6048
+ }
6049
+ static async downloadToPath(source, targetPath) {
6050
+ const isUrl2 = source.startsWith("http://") || source.startsWith("https://");
6051
+ await mkdir(path2.dirname(targetPath), { recursive: true });
6052
+ if (isUrl2) {
6053
+ try {
6054
+ console.log(`Downloading from URL: ${source}`);
6055
+ const response = await fetch(source);
6056
+ if (!response.ok) {
6057
+ return { error: `Failed to fetch from ${source}: ${response.statusText}` };
6058
+ }
6059
+ const content = await response.arrayBuffer();
6060
+ await writeFile(targetPath, Buffer.from(content));
6061
+ console.log(`Downloaded: ${source} -> ${targetPath}`);
6062
+ return { content, filePath: targetPath };
6063
+ } catch (error) {
6064
+ return { error: `Error downloading from ${source}: ${error}` };
6065
+ }
4801
6066
  } else {
4802
- this.localSelection = null;
6067
+ try {
6068
+ console.log(`Copying from local path: ${source}`);
6069
+ const content = await Bun.file(source).arrayBuffer();
6070
+ await writeFile(targetPath, Buffer.from(content));
6071
+ return { content, filePath: targetPath };
6072
+ } catch (error) {
6073
+ return { error: `Error copying from local path ${source}: ${error}` };
6074
+ }
4803
6075
  }
4804
- return previousSelection?.start !== this.localSelection?.start || previousSelection?.end !== this.localSelection?.end;
4805
6076
  }
6077
+ static async fetchHighlightQueries(sources, cacheDir, filetype) {
6078
+ const queryPromises = sources.map((source) => this.fetchHighlightQuery(source, cacheDir, filetype));
6079
+ const queryResults = await Promise.all(queryPromises);
6080
+ const validQueries = queryResults.filter((query) => query.trim().length > 0);
6081
+ return validQueries.join(`
6082
+ `);
6083
+ }
6084
+ static async fetchHighlightQuery(source, cacheDir, filetype) {
6085
+ const result = await this.downloadOrLoad(source, cacheDir, "queries", ".scm", true, filetype);
6086
+ if (result.error) {
6087
+ console.error(`Error fetching highlight query from ${source}:`, result.error);
6088
+ return "";
6089
+ }
6090
+ if (result.content) {
6091
+ return new TextDecoder().decode(result.content);
6092
+ }
6093
+ return "";
6094
+ }
6095
+ }
6096
+
6097
+ // src/lib/tree-sitter/assets/update.ts
6098
+ var __dirname = "/Users/runner/work/opentui/opentui/packages/core/src/lib/tree-sitter/assets";
6099
+ function getDefaultOptions() {
6100
+ return {
6101
+ configPath: path3.resolve(__dirname, "../parsers-config.json"),
6102
+ assetsDir: path3.resolve(__dirname),
6103
+ outputPath: path3.resolve(__dirname, "../default-parsers.ts")
6104
+ };
6105
+ }
6106
+ async function loadConfig(configPath) {
6107
+ const configContent = await readFile(configPath, "utf-8");
6108
+ return JSON.parse(configContent);
6109
+ }
6110
+ async function downloadLanguage(filetype, languageUrl, assetsDir, outputPath) {
6111
+ const languageDir = path3.join(assetsDir, filetype);
6112
+ const languageFilename = path3.basename(languageUrl);
6113
+ const languagePath = path3.join(languageDir, languageFilename);
6114
+ const result = await DownloadUtils.downloadToPath(languageUrl, languagePath);
6115
+ if (result.error) {
6116
+ throw new Error(`Failed to download language for ${filetype}: ${result.error}`);
6117
+ }
6118
+ return "./" + path3.relative(path3.dirname(outputPath), languagePath);
6119
+ }
6120
+ async function downloadAndCombineQueries(filetype, queryUrls, assetsDir, outputPath) {
6121
+ const queriesDir = path3.join(assetsDir, filetype);
6122
+ const highlightsPath = path3.join(queriesDir, "highlights.scm");
6123
+ const queryContents = [];
6124
+ for (let i = 0;i < queryUrls.length; i++) {
6125
+ const queryUrl = queryUrls[i];
6126
+ console.log(` Downloading query ${i + 1}/${queryUrls.length}: ${queryUrl}`);
6127
+ try {
6128
+ const response = await fetch(queryUrl);
6129
+ if (!response.ok) {
6130
+ console.warn(`Failed to download query from ${queryUrl}: ${response.statusText}`);
6131
+ continue;
6132
+ }
6133
+ const content = await response.text();
6134
+ if (content.trim()) {
6135
+ queryContents.push(`; Query from: ${queryUrl}
6136
+ ${content}`);
6137
+ console.log(` \u2713 Downloaded ${content.split(`
6138
+ `).length} lines`);
6139
+ }
6140
+ } catch (error) {
6141
+ console.warn(`Failed to download query from ${queryUrl}: ${error}`);
6142
+ continue;
6143
+ }
6144
+ }
6145
+ const combinedContent = queryContents.join(`
6146
+
6147
+ `);
6148
+ await writeFile2(highlightsPath, combinedContent, "utf-8");
6149
+ console.log(` Combined ${queryContents.length} queries into ${highlightsPath}`);
6150
+ return "./" + path3.relative(path3.dirname(outputPath), highlightsPath);
6151
+ }
6152
+ async function generateDefaultParsersFile(parsers, outputPath) {
6153
+ const imports = parsers.map((parser) => {
6154
+ const safeFiletype = parser.filetype.replace(/[^a-zA-Z0-9]/g, "_");
6155
+ return `import ${safeFiletype}_highlights from "${parser.highlightsPath}" with { type: "file" }
6156
+ import ${safeFiletype}_language from "${parser.languagePath}" with { type: "file" }`;
6157
+ }).join(`
6158
+ `);
6159
+ const parserDefinitions = parsers.map((parser) => {
6160
+ const safeFiletype = parser.filetype.replace(/[^a-zA-Z0-9]/g, "_");
6161
+ return ` {
6162
+ filetype: "${parser.filetype}",
6163
+ queries: {
6164
+ highlights: [resolve(dirname(fileURLToPath(import.meta.url)), ${safeFiletype}_highlights)],
6165
+ },
6166
+ wasm: resolve(dirname(fileURLToPath(import.meta.url)), ${safeFiletype}_language),
6167
+ }`;
6168
+ }).join(`,
6169
+ `);
6170
+ const fileContent = `// This file is generated by assets/update.ts - DO NOT EDIT MANUALLY
6171
+ // Run 'bun assets/update.ts' to regenerate this file
6172
+ // Last generated: ${new Date().toISOString()}
6173
+
6174
+ import type { FiletypeParserOptions } from "./types"
6175
+ import { resolve, dirname } from "path"
6176
+ import { fileURLToPath } from "url"
6177
+
6178
+ ${imports}
6179
+
6180
+ // Cached parsers to avoid re-resolving paths on every call
6181
+ let _cachedParsers: FiletypeParserOptions[] | undefined
6182
+
6183
+ export function getParsers(): FiletypeParserOptions[] {
6184
+ if (!_cachedParsers) {
6185
+ _cachedParsers = [
6186
+ ${parserDefinitions},
6187
+ ]
6188
+ }
6189
+ return _cachedParsers
6190
+ }
6191
+ `;
6192
+ await mkdir2(path3.dirname(outputPath), { recursive: true });
6193
+ await writeFile2(outputPath, fileContent, "utf-8");
6194
+ console.log(`Generated ${path3.basename(outputPath)} with ${parsers.length} parsers`);
6195
+ }
6196
+ async function main(options) {
6197
+ const opts = { ...getDefaultOptions(), ...options };
6198
+ try {
6199
+ console.log("Loading parsers configuration...");
6200
+ console.log(` Config: ${opts.configPath}`);
6201
+ console.log(` Assets Dir: ${opts.assetsDir}`);
6202
+ console.log(` Output: ${opts.outputPath}`);
6203
+ const config = await loadConfig(opts.configPath);
6204
+ console.log(`Found ${config.parsers.length} parsers to process`);
6205
+ const generatedParsers = [];
6206
+ for (const parser of config.parsers) {
6207
+ console.log(`Processing ${parser.filetype}...`);
6208
+ console.log(` Downloading language...`);
6209
+ const languagePath = await downloadLanguage(parser.filetype, parser.wasm, opts.assetsDir, opts.outputPath);
6210
+ console.log(` Downloading ${parser.queries.highlights.length} highlight queries...`);
6211
+ const highlightsPath = await downloadAndCombineQueries(parser.filetype, parser.queries.highlights, opts.assetsDir, opts.outputPath);
6212
+ generatedParsers.push({
6213
+ filetype: parser.filetype,
6214
+ languagePath,
6215
+ highlightsPath
6216
+ });
6217
+ console.log(` \u2713 Completed ${parser.filetype}`);
6218
+ }
6219
+ console.log("Generating output file...");
6220
+ await generateDefaultParsersFile(generatedParsers, opts.outputPath);
6221
+ console.log("\u2705 Update completed successfully!");
6222
+ } catch (error) {
6223
+ console.error("\u274C Update failed:", error);
6224
+ process.exit(1);
6225
+ }
6226
+ }
6227
+ if (false) {}
6228
+
6229
+ // src/lib/tree-sitter/index.ts
6230
+ function getTreeSitterClient() {
6231
+ const dataPathsManager = getDataPaths();
6232
+ const defaultOptions = {
6233
+ dataPath: dataPathsManager.globalDataPath
6234
+ };
6235
+ return singleton("tree-sitter-client", () => {
6236
+ const client2 = new TreeSitterClient(defaultOptions);
6237
+ dataPathsManager.on("paths:changed", (paths) => {
6238
+ client2.setDataPath(paths.globalDataPath);
6239
+ });
6240
+ return client2;
6241
+ });
4806
6242
  }
4807
6243
  // src/zig.ts
4808
6244
  import { dlopen, toArrayBuffer as toArrayBuffer2, JSCallback, ptr } from "bun:ffi";
4809
- import { existsSync } from "fs";
6245
+ import { existsSync as existsSync2 } from "fs";
4810
6246
 
4811
6247
  // src/buffer.ts
4812
6248
  import { toArrayBuffer } from "bun:ffi";
@@ -5007,9 +6443,24 @@ class OptimizedBuffer {
5007
6443
  // src/zig.ts
5008
6444
  var module = await import(`@opentui/core-${process.platform}-${process.arch}/index.ts`);
5009
6445
  var targetLibPath = module.default;
5010
- if (!existsSync(targetLibPath)) {
6446
+ if (/\$bunfs/.test(targetLibPath)) {
6447
+ targetLibPath = targetLibPath.replace("../", "");
6448
+ }
6449
+ if (!existsSync2(targetLibPath)) {
5011
6450
  throw new Error(`opentui is not supported on the current platform: ${process.platform}-${process.arch}`);
5012
6451
  }
6452
+ registerEnvVar({
6453
+ name: "OTUI_DEBUG_FFI",
6454
+ description: "Enable debug logging for the FFI bindings.",
6455
+ type: "boolean",
6456
+ default: false
6457
+ });
6458
+ registerEnvVar({
6459
+ name: "OTUI_TRACE_FFI",
6460
+ description: "Enable tracing for the FFI bindings.",
6461
+ type: "boolean",
6462
+ default: false
6463
+ });
5013
6464
  function getOpenTUILib(libPath) {
5014
6465
  const resolvedLibPath = libPath || targetLibPath;
5015
6466
  const rawSymbols = dlopen(resolvedLibPath, {
@@ -5057,6 +6508,10 @@ function getOpenTUILib(libPath) {
5057
6508
  args: ["ptr"],
5058
6509
  returns: "ptr"
5059
6510
  },
6511
+ queryPixelResolution: {
6512
+ args: ["ptr"],
6513
+ returns: "void"
6514
+ },
5060
6515
  createOptimizedBuffer: {
5061
6516
  args: ["u32", "u32", "bool", "u8", "ptr", "usize"],
5062
6517
  returns: "ptr"
@@ -5346,7 +6801,7 @@ function getOpenTUILib(libPath) {
5346
6801
  returns: "void"
5347
6802
  }
5348
6803
  });
5349
- if (process.env.DEBUG_FFI === "true" || process.env.TRACE_FFI === "true") {
6804
+ if (env.OTUI_DEBUG_FFI || env.OTUI_TRACE_FFI) {
5350
6805
  return {
5351
6806
  symbols: convertToDebugSymbols(rawSymbols.symbols)
5352
6807
  };
@@ -5360,7 +6815,7 @@ function convertToDebugSymbols(symbols) {
5360
6815
  Object.entries(symbols).forEach(([key, value]) => {
5361
6816
  debugSymbols[key] = value;
5362
6817
  });
5363
- if (process.env.DEBUG_FFI === "true") {
6818
+ if (env.OTUI_DEBUG_FFI) {
5364
6819
  Object.entries(symbols).forEach(([key, value]) => {
5365
6820
  if (typeof value === "function") {
5366
6821
  debugSymbols[key] = (...args) => {
@@ -5372,7 +6827,7 @@ function convertToDebugSymbols(symbols) {
5372
6827
  }
5373
6828
  });
5374
6829
  }
5375
- if (process.env.TRACE_FFI === "true") {
6830
+ if (env.OTUI_TRACE_FFI) {
5376
6831
  hasTracing = true;
5377
6832
  Object.entries(symbols).forEach(([key, value]) => {
5378
6833
  if (typeof value === "function") {
@@ -5734,6 +7189,9 @@ class FFIRenderLib {
5734
7189
  setupTerminal(renderer, useAlternateScreen) {
5735
7190
  this.opentui.symbols.setupTerminal(renderer, useAlternateScreen);
5736
7191
  }
7192
+ queryPixelResolution(renderer) {
7193
+ this.opentui.symbols.queryPixelResolution(renderer);
7194
+ }
5737
7195
  createTextBuffer(widthMethod) {
5738
7196
  const widthMethodCode = widthMethod === "wcwidth" ? 0 : 1;
5739
7197
  const bufferPtr = this.opentui.symbols.createTextBuffer(widthMethodCode);
@@ -6092,7 +7550,7 @@ class TextBuffer {
6092
7550
  }
6093
7551
 
6094
7552
  // src/Renderable.ts
6095
- import { EventEmitter as EventEmitter2 } from "events";
7553
+ import { EventEmitter as EventEmitter4 } from "events";
6096
7554
 
6097
7555
  // src/lib/renderable.validations.ts
6098
7556
  function validateOptions(id, options) {
@@ -6185,7 +7643,7 @@ function isRenderable(obj) {
6185
7643
  return !!obj?.[BrandedRenderable];
6186
7644
  }
6187
7645
 
6188
- class BaseRenderable extends EventEmitter2 {
7646
+ class BaseRenderable extends EventEmitter4 {
6189
7647
  [BrandedRenderable] = true;
6190
7648
  static renderableNumber = 1;
6191
7649
  _id;
@@ -6222,6 +7680,9 @@ class BaseRenderable extends EventEmitter2 {
6222
7680
  this._visible = value;
6223
7681
  }
6224
7682
  }
7683
+ var yogaConfig = src_default.Config.create();
7684
+ yogaConfig.setUseWebDefaults(false);
7685
+ yogaConfig.setPointScaleFactor(1);
6225
7686
 
6226
7687
  class Renderable extends BaseRenderable {
6227
7688
  static renderablesByNumber = new Map;
@@ -6242,11 +7703,13 @@ class Renderable extends BaseRenderable {
6242
7703
  _focusable = false;
6243
7704
  _focused = false;
6244
7705
  keypressHandler = null;
7706
+ pasteHandler = null;
6245
7707
  _live = false;
6246
7708
  _liveCount = 0;
6247
7709
  _sizeChangeListener = undefined;
6248
7710
  _mouseListener = null;
6249
7711
  _mouseListeners = {};
7712
+ _pasteListener = undefined;
6250
7713
  _keyListeners = {};
6251
7714
  yogaNode;
6252
7715
  _positionType = "relative";
@@ -6283,7 +7746,7 @@ class Renderable extends BaseRenderable {
6283
7746
  this.buffered = options.buffered ?? false;
6284
7747
  this._live = options.live ?? false;
6285
7748
  this._liveCount = this._live && this._visible ? 1 : 0;
6286
- this.yogaNode = src_default.Node.create();
7749
+ this.yogaNode = src_default.Node.create(yogaConfig);
6287
7750
  this.yogaNode.setDisplay(this._visible ? Display.Flex : Display.None);
6288
7751
  this.setupYogaProperties(options);
6289
7752
  this.applyEventOptions(options);
@@ -6352,11 +7815,18 @@ class Renderable extends BaseRenderable {
6352
7815
  this.requestRender();
6353
7816
  this.keypressHandler = (key) => {
6354
7817
  this._keyListeners["down"]?.(key);
6355
- if (this.handleKeyPress) {
7818
+ if (!key.defaultPrevented && this.handleKeyPress) {
6356
7819
  this.handleKeyPress(key);
6357
7820
  }
6358
7821
  };
6359
- this.ctx.keyInput.on("keypress", this.keypressHandler);
7822
+ this.pasteHandler = (event) => {
7823
+ this._pasteListener?.call(this, event.text);
7824
+ if (!event.defaultPrevented && this.handlePaste) {
7825
+ this.handlePaste(event.text);
7826
+ }
7827
+ };
7828
+ this.ctx._internalKeyInput.onInternal("keypress", this.keypressHandler);
7829
+ this.ctx._internalKeyInput.onInternal("paste", this.pasteHandler);
6360
7830
  this.emit("focused" /* FOCUSED */);
6361
7831
  }
6362
7832
  blur() {
@@ -6365,9 +7835,13 @@ class Renderable extends BaseRenderable {
6365
7835
  this._focused = false;
6366
7836
  this.requestRender();
6367
7837
  if (this.keypressHandler) {
6368
- this.ctx.keyInput.off("keypress", this.keypressHandler);
7838
+ this.ctx._internalKeyInput.offInternal("keypress", this.keypressHandler);
6369
7839
  this.keypressHandler = null;
6370
7840
  }
7841
+ if (this.pasteHandler) {
7842
+ this.ctx._internalKeyInput.offInternal("paste", this.pasteHandler);
7843
+ this.pasteHandler = null;
7844
+ }
6371
7845
  this.emit("blurred" /* BLURRED */);
6372
7846
  }
6373
7847
  get focused() {
@@ -6396,9 +7870,11 @@ class Renderable extends BaseRenderable {
6396
7870
  for (const child of this._childrenInLayoutOrder) {
6397
7871
  if (child.id === id)
6398
7872
  return child;
6399
- const found = child.findDescendantById(id);
6400
- if (found)
6401
- return found;
7873
+ if (isRenderable(child)) {
7874
+ const found = child.findDescendantById(id);
7875
+ if (found)
7876
+ return found;
7877
+ }
6402
7878
  }
6403
7879
  return;
6404
7880
  }
@@ -6551,8 +8027,7 @@ class Renderable extends BaseRenderable {
6551
8027
  if (options.flexShrink !== undefined) {
6552
8028
  node.setFlexShrink(options.flexShrink);
6553
8029
  } else {
6554
- const shrinkValue = options.flexGrow && options.flexGrow > 0 ? 1 : 0;
6555
- node.setFlexShrink(shrinkValue);
8030
+ node.setFlexShrink(1);
6556
8031
  }
6557
8032
  if (options.flexDirection !== undefined) {
6558
8033
  node.setFlexDirection(parseFlexDirection(options.flexDirection));
@@ -6965,10 +8440,11 @@ class Renderable extends BaseRenderable {
6965
8440
  if (renderable.parent === this) {
6966
8441
  this.yogaNode.removeChild(renderable.getLayoutNode());
6967
8442
  this._childrenInLayoutOrder.splice(this._childrenInLayoutOrder.indexOf(renderable), 1);
6968
- } else if (renderable.parent) {
8443
+ } else {
6969
8444
  this.replaceParent(renderable);
6970
8445
  this.needsZIndexSort = true;
6971
8446
  this.renderableMapById.set(renderable.id, renderable);
8447
+ this._childrenInZIndexOrder.push(renderable);
6972
8448
  if (typeof renderable.onLifecyclePass === "function") {
6973
8449
  this._ctx.registerLifecyclePass(renderable);
6974
8450
  }
@@ -7013,6 +8489,16 @@ class Renderable extends BaseRenderable {
7013
8489
  if (zIndexIndex !== -1) {
7014
8490
  this._childrenInZIndexOrder.splice(zIndexIndex, 1);
7015
8491
  }
8492
+ if (this._forceLayoutUpdateFor) {
8493
+ const forceIndex = this._forceLayoutUpdateFor.findIndex((obj2) => obj2.id === id);
8494
+ if (forceIndex !== -1) {
8495
+ this._forceLayoutUpdateFor?.splice(forceIndex, 1);
8496
+ }
8497
+ }
8498
+ const newChildIndex = this._newChildren.findIndex((obj2) => obj2.id === id);
8499
+ if (newChildIndex !== -1) {
8500
+ this._newChildren?.splice(newChildIndex, 1);
8501
+ }
7016
8502
  this.childrenPrimarySortDirty = true;
7017
8503
  }
7018
8504
  }
@@ -7121,7 +8607,8 @@ class Renderable extends BaseRenderable {
7121
8607
  } catch (e) {}
7122
8608
  }
7123
8609
  destroyRecursively() {
7124
- for (const child of this._childrenInLayoutOrder) {
8610
+ const children = [...this._childrenInLayoutOrder];
8611
+ for (const child of children) {
7125
8612
  child.destroyRecursively();
7126
8613
  }
7127
8614
  this.destroy();
@@ -7196,6 +8683,12 @@ class Renderable extends BaseRenderable {
7196
8683
  else
7197
8684
  delete this._mouseListeners["scroll"];
7198
8685
  }
8686
+ set onPaste(handler) {
8687
+ this._pasteListener = handler;
8688
+ }
8689
+ get onPaste() {
8690
+ return this._pasteListener;
8691
+ }
7199
8692
  set onKeyDown(handler) {
7200
8693
  if (handler)
7201
8694
  this._keyListeners["down"] = handler;
@@ -7222,23 +8715,20 @@ class Renderable extends BaseRenderable {
7222
8715
  this.onMouseOver = options.onMouseOver;
7223
8716
  this.onMouseOut = options.onMouseOut;
7224
8717
  this.onMouseScroll = options.onMouseScroll;
8718
+ this.onPaste = options.onPaste;
7225
8719
  this.onKeyDown = options.onKeyDown;
7226
8720
  this.onSizeChange = options.onSizeChange;
7227
8721
  }
7228
8722
  }
7229
8723
 
7230
8724
  class RootRenderable extends Renderable {
7231
- yogaConfig;
7232
8725
  renderList = [];
7233
8726
  constructor(ctx) {
7234
8727
  super(ctx, { id: "__root__", zIndex: 0, visible: true, width: ctx.width, height: ctx.height, enableLayout: true });
7235
- this.yogaConfig = src_default.Config.create();
7236
- this.yogaConfig.setUseWebDefaults(false);
7237
- this.yogaConfig.setPointScaleFactor(1);
7238
8728
  if (this.yogaNode) {
7239
8729
  this.yogaNode.free();
7240
8730
  }
7241
- this.yogaNode = src_default.Node.create(this.yogaConfig);
8731
+ this.yogaNode = src_default.Node.create(yogaConfig);
7242
8732
  this.yogaNode.setWidth(ctx.width);
7243
8733
  this.yogaNode.setHeight(ctx.height);
7244
8734
  this.yogaNode.setFlexDirection(FlexDirection.Column);
@@ -7288,11 +8778,6 @@ class RootRenderable extends Renderable {
7288
8778
  this.height = height;
7289
8779
  this.emit("resized" /* RESIZED */, { width, height });
7290
8780
  }
7291
- destroySelf() {
7292
- try {
7293
- this.yogaConfig.free();
7294
- } catch (error) {}
7295
- }
7296
8781
  }
7297
8782
 
7298
8783
  // src/renderables/composition/vnode.ts
@@ -7462,17 +8947,17 @@ function delegate(mapping, vnode) {
7462
8947
  }
7463
8948
 
7464
8949
  // src/console.ts
7465
- import { EventEmitter as EventEmitter4 } from "events";
8950
+ import { EventEmitter as EventEmitter6 } from "events";
7466
8951
  import { Console } from "console";
7467
8952
  import fs from "fs";
7468
- import path from "path";
8953
+ import path4 from "path";
7469
8954
  import util2 from "util";
7470
8955
 
7471
8956
  // src/lib/output.capture.ts
7472
8957
  import { Writable } from "stream";
7473
- import { EventEmitter as EventEmitter3 } from "events";
8958
+ import { EventEmitter as EventEmitter5 } from "events";
7474
8959
 
7475
- class Capture extends EventEmitter3 {
8960
+ class Capture extends EventEmitter5 {
7476
8961
  output = [];
7477
8962
  constructor() {
7478
8963
  super();
@@ -7535,8 +9020,20 @@ function getCallerInfo() {
7535
9020
  return { functionName, fullPath, fileName, lineNumber, columnNumber };
7536
9021
  }
7537
9022
  var capture = singleton("ConsoleCapture", () => new Capture);
9023
+ registerEnvVar({
9024
+ name: "OTUI_USE_CONSOLE",
9025
+ description: "Whether to use the console. Will not capture console output if set to false.",
9026
+ type: "boolean",
9027
+ default: true
9028
+ });
9029
+ registerEnvVar({
9030
+ name: "SHOW_CONSOLE",
9031
+ description: "Show the console at startup if set to true.",
9032
+ type: "boolean",
9033
+ default: false
9034
+ });
7538
9035
 
7539
- class TerminalConsoleCache extends EventEmitter4 {
9036
+ class TerminalConsoleCache extends EventEmitter6 {
7540
9037
  _cachedLogs = [];
7541
9038
  MAX_CACHE_SIZE = 1000;
7542
9039
  _collectCallerInfo = false;
@@ -7552,7 +9049,7 @@ class TerminalConsoleCache extends EventEmitter4 {
7552
9049
  this.overrideConsoleMethods();
7553
9050
  }
7554
9051
  setupConsoleCapture() {
7555
- if (process.env.OTUI_USE_CONSOLE === "false")
9052
+ if (!env.OTUI_USE_CONSOLE)
7556
9053
  return;
7557
9054
  const mockStdout = new CapturedWritableStream("stdout", capture);
7558
9055
  const mockStderr = new CapturedWritableStream("stderr", capture);
@@ -7657,7 +9154,7 @@ var DEFAULT_CONSOLE_OPTIONS = {
7657
9154
  };
7658
9155
  var INDENT_WIDTH = 2;
7659
9156
 
7660
- class TerminalConsole extends EventEmitter4 {
9157
+ class TerminalConsole extends EventEmitter6 {
7661
9158
  isVisible = false;
7662
9159
  isFocused = false;
7663
9160
  renderer;
@@ -7717,7 +9214,7 @@ class TerminalConsole extends EventEmitter4 {
7717
9214
  terminalConsoleCache.on("entry", (logEntry) => {
7718
9215
  this._handleNewLog(logEntry);
7719
9216
  });
7720
- if (process.env.SHOW_CONSOLE === "true") {
9217
+ if (env.SHOW_CONSOLE) {
7721
9218
  this.show();
7722
9219
  }
7723
9220
  }
@@ -8096,7 +9593,7 @@ class TerminalConsole extends EventEmitter4 {
8096
9593
  try {
8097
9594
  const timestamp = Date.now();
8098
9595
  const filename = `_console_${timestamp}.log`;
8099
- const filepath = path.join(process.cwd(), filename);
9596
+ const filepath = path4.join(process.cwd(), filename);
8100
9597
  const allLogEntries = [...this._allLogEntries, ...terminalConsoleCache.cachedLogs];
8101
9598
  const logLines = [];
8102
9599
  for (const [date, level, args, callerInfo] of allLogEntries) {
@@ -8116,39 +9613,8 @@ class TerminalConsole extends EventEmitter4 {
8116
9613
  }
8117
9614
  }
8118
9615
 
8119
- // src/ansi.ts
8120
- var ANSI = {
8121
- switchToAlternateScreen: "\x1B[?1049h",
8122
- switchToMainScreen: "\x1B[?1049l",
8123
- reset: "\x1B[0m",
8124
- hideCursor: "\x1B[?25l",
8125
- showCursor: "\x1B[?25h",
8126
- resetCursorColor: "\x1B]12;default\x07",
8127
- saveCursorState: "\x1B[s",
8128
- restoreCursorState: "\x1B[u",
8129
- queryPixelSize: "\x1B[14t",
8130
- scrollDown: (lines) => `\x1B[${lines}T`,
8131
- scrollUp: (lines) => `\x1B[${lines}S`,
8132
- moveCursor: (row, col) => `\x1B[${row};${col}H`,
8133
- moveCursorAndClear: (row, col) => `\x1B[${row};${col}H\x1B[J`,
8134
- clearFromCursor: "\x1B[J",
8135
- setRgbBackground: (r, g, b) => `\x1B[48;2;${r};${g};${b}m`,
8136
- resetBackground: "\x1B[49m",
8137
- enableMouseTracking: "\x1B[?1000h",
8138
- disableMouseTracking: "\x1B[?1000l",
8139
- enableButtonEventTracking: "\x1B[?1002h",
8140
- disableButtonEventTracking: "\x1B[?1002l",
8141
- enableAnyEventTracking: "\x1B[?1003h",
8142
- disableAnyEventTracking: "\x1B[?1003l",
8143
- enableSGRMouseMode: "\x1B[?1006h",
8144
- disableSGRMouseMode: "\x1B[?1006l",
8145
- makeRoomForRenderer: (height) => `
8146
- `.repeat(height) + `\x1B[${height}A`,
8147
- clearRendererSpace: (height) => `\x1B[${height}A\x1B[1G\x1B[J`
8148
- };
8149
-
8150
9616
  // src/renderer.ts
8151
- import { EventEmitter as EventEmitter5 } from "events";
9617
+ import { EventEmitter as EventEmitter7 } from "events";
8152
9618
 
8153
9619
  // src/lib/objects-in-viewport.ts
8154
9620
  function getObjectsInViewport(viewport, objects, direction = "column", padding = 10, minTriggerSize = 16) {
@@ -8226,6 +9692,31 @@ function getObjectsInViewport(viewport, objects, direction = "column", padding =
8226
9692
  }
8227
9693
 
8228
9694
  // src/renderer.ts
9695
+ registerEnvVar({
9696
+ name: "OTUI_DUMP_CAPTURES",
9697
+ description: "Dump captured output when the renderer exits.",
9698
+ type: "boolean",
9699
+ default: false
9700
+ });
9701
+ registerEnvVar({
9702
+ name: "OTUI_NO_NATIVE_RENDER",
9703
+ description: "Disable native rendering. This will not actually output ansi and is useful for debugging.",
9704
+ type: "boolean",
9705
+ default: false
9706
+ });
9707
+ registerEnvVar({
9708
+ name: "OTUI_USE_ALTERNATE_SCREEN",
9709
+ description: "Whether to use the console. Will not capture console output if set to false.",
9710
+ type: "boolean",
9711
+ default: true
9712
+ });
9713
+ registerEnvVar({
9714
+ name: "OTUI_OVERRIDE_STDOUT",
9715
+ description: "Override the stdout stream. This is useful for debugging.",
9716
+ type: "boolean",
9717
+ default: true
9718
+ });
9719
+
8229
9720
  class MouseEvent {
8230
9721
  type;
8231
9722
  button;
@@ -8277,9 +9768,27 @@ singleton("ProcessExitSignals", () => {
8277
9768
  });
8278
9769
  });
8279
9770
  });
9771
+ var rendererTracker = singleton("RendererTracker", () => {
9772
+ const renderers = new Set;
9773
+ return {
9774
+ addRenderer: (renderer) => {
9775
+ renderers.add(renderer);
9776
+ },
9777
+ removeRenderer: (renderer) => {
9778
+ renderers.delete(renderer);
9779
+ if (renderers.size === 0) {
9780
+ process.stdin.pause();
9781
+ if (hasSingleton("tree-sitter-client")) {
9782
+ getTreeSitterClient().destroy();
9783
+ destroySingleton("tree-sitter-client");
9784
+ }
9785
+ }
9786
+ }
9787
+ };
9788
+ });
8280
9789
  async function createCliRenderer(config = {}) {
8281
9790
  if (process.argv.includes("--delay-start")) {
8282
- await new Promise((resolve) => setTimeout(resolve, 5000));
9791
+ await new Promise((resolve4) => setTimeout(resolve4, 5000));
8283
9792
  }
8284
9793
  const stdin = config.stdin || process.stdin;
8285
9794
  const stdout = config.stdout || process.stdout;
@@ -8306,14 +9815,14 @@ var CliRenderEvents;
8306
9815
  ((CliRenderEvents2) => {
8307
9816
  CliRenderEvents2["DEBUG_OVERLAY_TOGGLE"] = "debugOverlay:toggle";
8308
9817
  })(CliRenderEvents ||= {});
8309
- class CliRenderer extends EventEmitter5 {
9818
+ class CliRenderer extends EventEmitter7 {
8310
9819
  static animationFrameId = 0;
8311
9820
  lib;
8312
9821
  rendererPtr;
8313
9822
  stdin;
8314
9823
  stdout;
8315
9824
  exitOnCtrlC;
8316
- isDestroyed = false;
9825
+ _isDestroyed = false;
8317
9826
  nextRenderBuffer;
8318
9827
  currentRenderBuffer;
8319
9828
  _isRunning = false;
@@ -8367,7 +9876,7 @@ class CliRenderer extends EventEmitter5 {
8367
9876
  resizeDebounceDelay = 100;
8368
9877
  enableMouseMovement = false;
8369
9878
  _useMouse = true;
8370
- _useAlternateScreen = true;
9879
+ _useAlternateScreen = env.OTUI_USE_ALTERNATE_SCREEN;
8371
9880
  capturedRenderable;
8372
9881
  lastOverRenderableNum = 0;
8373
9882
  lastOverRenderable;
@@ -8398,9 +9907,9 @@ class CliRenderer extends EventEmitter5 {
8398
9907
  handleError = ((error) => {
8399
9908
  this.stop();
8400
9909
  this.destroy();
8401
- new Promise((resolve) => {
9910
+ new Promise((resolve4) => {
8402
9911
  setTimeout(() => {
8403
- resolve(true);
9912
+ resolve4(true);
8404
9913
  }, 100);
8405
9914
  }).then(() => {
8406
9915
  this.realStdoutWrite.call(this.stdout, `
@@ -8408,17 +9917,7 @@ class CliRenderer extends EventEmitter5 {
8408
9917
  this.realStdoutWrite.call(this.stdout, `
8409
9918
  === FATAL ERROR OCCURRED ===
8410
9919
  `);
8411
- this.realStdoutWrite.call(this.stdout, `Console cache:
8412
- `);
8413
- this.realStdoutWrite.call(this.stdout, this.console.getCachedLogs());
8414
- this.realStdoutWrite.call(this.stdout, `
8415
- Captured output:
8416
- `);
8417
- const capturedOutput = capture.claimOutput();
8418
- if (capturedOutput) {
8419
- this.realStdoutWrite.call(this.stdout, capturedOutput + `
8420
- `);
8421
- }
9920
+ this.dumpOutputCache();
8422
9921
  this.realStdoutWrite.call(this.stdout, `
8423
9922
  Error details:
8424
9923
  `);
@@ -8431,14 +9930,39 @@ Error details:
8431
9930
  process.exit(1);
8432
9931
  });
8433
9932
  }).bind(this);
9933
+ dumpOutputCache(optionalMessage = "") {
9934
+ const cachedLogs = this.console.getCachedLogs();
9935
+ const capturedOutput = capture.claimOutput();
9936
+ if (capturedOutput.length > 0 || cachedLogs.length > 0) {
9937
+ this.realStdoutWrite.call(this.stdout, optionalMessage);
9938
+ }
9939
+ if (cachedLogs.length > 0) {
9940
+ this.realStdoutWrite.call(this.stdout, `Console cache:
9941
+ `);
9942
+ this.realStdoutWrite.call(this.stdout, cachedLogs);
9943
+ }
9944
+ if (capturedOutput.length > 0) {
9945
+ this.realStdoutWrite.call(this.stdout, `
9946
+ Captured output:
9947
+ `);
9948
+ this.realStdoutWrite.call(this.stdout, capturedOutput + `
9949
+ `);
9950
+ }
9951
+ this.realStdoutWrite.call(this.stdout, ANSI.reset);
9952
+ }
8434
9953
  exitHandler = (() => {
8435
9954
  this.destroy();
9955
+ if (env.OTUI_DUMP_CAPTURES) {
9956
+ this.dumpOutputCache(`=== CAPTURED OUTPUT ===
9957
+ `);
9958
+ }
8436
9959
  }).bind(this);
8437
9960
  warningHandler = ((warning) => {
8438
9961
  console.warn(JSON.stringify(warning.message, null, 2));
8439
9962
  }).bind(this);
8440
9963
  constructor(lib, rendererPtr, stdin, stdout, width, height, config = {}) {
8441
9964
  super();
9965
+ rendererTracker.addRenderer(this);
8442
9966
  this.stdin = stdin;
8443
9967
  this.stdout = stdout;
8444
9968
  this.realStdoutWrite = stdout.write;
@@ -8464,7 +9988,7 @@ Error details:
8464
9988
  this.maxStatSamples = config.maxStatSamples || 300;
8465
9989
  this.enableMouseMovement = config.enableMouseMovement || true;
8466
9990
  this._useMouse = config.useMouse ?? true;
8467
- this._useAlternateScreen = config.useAlternateScreen ?? true;
9991
+ this._useAlternateScreen = config.useAlternateScreen ?? env.OTUI_USE_ALTERNATE_SCREEN;
8468
9992
  this.nextRenderBuffer = this.lib.getNextBuffer(this.rendererPtr);
8469
9993
  this.currentRenderBuffer = this.lib.getCurrentBuffer(this.rendererPtr);
8470
9994
  this.postProcessFns = config.postProcessFns || [];
@@ -8472,7 +9996,9 @@ Error details:
8472
9996
  if (this.memorySnapshotInterval > 0) {
8473
9997
  this.startMemorySnapshotTimer();
8474
9998
  }
8475
- this.stdout.write = this.interceptStdoutWrite.bind(this);
9999
+ if (env.OTUI_OVERRIDE_STDOUT) {
10000
+ this.stdout.write = this.interceptStdoutWrite.bind(this);
10001
+ }
8476
10002
  process.on("SIGWINCH", this.sigwinchHandler);
8477
10003
  process.on("warning", this.warningHandler);
8478
10004
  process.on("uncaughtException", this.handleError);
@@ -8480,7 +10006,7 @@ Error details:
8480
10006
  process.on("exit", this.exitHandler);
8481
10007
  this._console = new TerminalConsole(this, config.consoleOptions);
8482
10008
  this.useConsole = config.useConsole ?? true;
8483
- this._keyHandler = new KeyHandler(this.stdin, config.useKittyKeyboard ?? false);
10009
+ this._keyHandler = new InternalKeyHandler(this.stdin, config.useKittyKeyboard ?? false);
8484
10010
  global.requestAnimationFrame = (callback) => {
8485
10011
  const id = CliRenderer.animationFrameId++;
8486
10012
  this.animationRequest.set(id, callback);
@@ -8495,7 +10021,7 @@ Error details:
8495
10021
  global.window = {};
8496
10022
  }
8497
10023
  global.window.requestAnimationFrame = requestAnimationFrame;
8498
- if (process.env.OTUI_NO_NATIVE_RENDER === "true") {
10024
+ if (env.OTUI_NO_NATIVE_RENDER) {
8499
10025
  this.renderNative = () => {
8500
10026
  if (this._splitHeight > 0) {
8501
10027
  this.flushStdoutCache(this._splitHeight);
@@ -8504,6 +10030,9 @@ Error details:
8504
10030
  }
8505
10031
  this.setupInput();
8506
10032
  }
10033
+ get isDestroyed() {
10034
+ return this._isDestroyed;
10035
+ }
8507
10036
  registerLifecyclePass(renderable) {
8508
10037
  this.lifecyclePasses.add(renderable);
8509
10038
  }
@@ -8568,6 +10097,9 @@ Error details:
8568
10097
  get keyInput() {
8569
10098
  return this._keyHandler;
8570
10099
  }
10100
+ get _internalKeyInput() {
10101
+ return this._keyHandler;
10102
+ }
8571
10103
  get terminalWidth() {
8572
10104
  return this._terminalWidth;
8573
10105
  }
@@ -8698,21 +10230,16 @@ Error details:
8698
10230
  if (this._terminalIsSetup)
8699
10231
  return;
8700
10232
  this._terminalIsSetup = true;
8701
- if (this.stdin.setRawMode) {
8702
- this.stdin.setRawMode(true);
8703
- }
8704
- this.stdin.resume();
8705
- this.stdin.setEncoding("utf8");
8706
- await new Promise((resolve) => {
10233
+ await new Promise((resolve4) => {
8707
10234
  const timeout = setTimeout(() => {
8708
10235
  this.stdin.off("data", capListener);
8709
- resolve(true);
10236
+ resolve4(true);
8710
10237
  }, 100);
8711
10238
  const capListener = (str) => {
8712
10239
  clearTimeout(timeout);
8713
10240
  this.lib.processCapabilityResponse(this.rendererPtr, str);
8714
10241
  this.stdin.off("data", capListener);
8715
- resolve(true);
10242
+ resolve4(true);
8716
10243
  };
8717
10244
  this.stdin.on("data", capListener);
8718
10245
  this.lib.setupTerminal(this.rendererPtr, this._useAlternateScreen);
@@ -8739,7 +10266,7 @@ Error details:
8739
10266
  }
8740
10267
  if (this.exitOnCtrlC && str === "\x03") {
8741
10268
  process.nextTick(() => {
8742
- process.exit();
10269
+ this.destroy();
8743
10270
  });
8744
10271
  return;
8745
10272
  }
@@ -8749,6 +10276,11 @@ Error details:
8749
10276
  this.emit("key", data);
8750
10277
  }).bind(this);
8751
10278
  setupInput() {
10279
+ if (this.stdin.setRawMode) {
10280
+ this.stdin.setRawMode(true);
10281
+ }
10282
+ this.stdin.resume();
10283
+ this.stdin.setEncoding("utf8");
8752
10284
  this.stdin.on("data", this.stdinListener);
8753
10285
  }
8754
10286
  handleMouseData(data) {
@@ -8864,7 +10396,7 @@ Error details:
8864
10396
  return false;
8865
10397
  }
8866
10398
  takeMemorySnapshot() {
8867
- if (this.isDestroyed)
10399
+ if (this._isDestroyed)
8868
10400
  return;
8869
10401
  const memoryUsage = process.memoryUsage();
8870
10402
  this.lastMemorySnapshot = {
@@ -8897,7 +10429,7 @@ Error details:
8897
10429
  }
8898
10430
  }
8899
10431
  handleResize(width, height) {
8900
- if (this.isDestroyed)
10432
+ if (this._isDestroyed)
8901
10433
  return;
8902
10434
  if (this._splitHeight > 0) {
8903
10435
  this.processResize(width, height);
@@ -8914,7 +10446,7 @@ Error details:
8914
10446
  }
8915
10447
  queryPixelResolution() {
8916
10448
  this.waitingForPixelResolution = true;
8917
- this.writeOut(ANSI.queryPixelSize);
10449
+ this.lib.queryPixelResolution(this.rendererPtr);
8918
10450
  }
8919
10451
  processResize(width, height) {
8920
10452
  if (width === this._terminalWidth && height === this._terminalHeight)
@@ -8977,9 +10509,6 @@ Error details:
8977
10509
  this.lib.setDebugOverlay(this.rendererPtr, this.debugOverlay.enabled, this.debugOverlay.corner);
8978
10510
  this.requestRender();
8979
10511
  }
8980
- clearTerminal() {
8981
- this.lib.clearTerminal(this.rendererPtr);
8982
- }
8983
10512
  setTerminalTitle(title) {
8984
10513
  this.lib.setTerminalTitle(this.rendererPtr, title);
8985
10514
  }
@@ -9059,7 +10588,7 @@ Error details:
9059
10588
  this.controlState = this._isRunning ? "auto_started" /* AUTO_STARTED */ : "idle" /* IDLE */;
9060
10589
  }
9061
10590
  internalStart() {
9062
- if (!this._isRunning && !this.isDestroyed) {
10591
+ if (!this._isRunning && !this._isDestroyed) {
9063
10592
  this._isRunning = true;
9064
10593
  if (this.memorySnapshotInterval > 0) {
9065
10594
  this.startMemorySnapshotTimer();
@@ -9079,7 +10608,7 @@ Error details:
9079
10608
  this.internalStop();
9080
10609
  }
9081
10610
  internalStop() {
9082
- if (this.isRunning && !this.isDestroyed) {
10611
+ if (this.isRunning && !this._isDestroyed) {
9083
10612
  this._isRunning = false;
9084
10613
  if (this.memorySnapshotTimer) {
9085
10614
  clearInterval(this.memorySnapshotTimer);
@@ -9092,32 +10621,41 @@ Error details:
9092
10621
  }
9093
10622
  }
9094
10623
  destroy() {
9095
- this.stdin.removeListener("data", this.stdinListener);
9096
10624
  process.removeListener("SIGWINCH", this.sigwinchHandler);
9097
10625
  process.removeListener("uncaughtException", this.handleError);
9098
10626
  process.removeListener("unhandledRejection", this.handleError);
9099
- process.removeListener("exit", this.exitHandler);
9100
10627
  process.removeListener("warning", this.warningHandler);
9101
10628
  capture.removeListener("write", this.captureCallback);
9102
10629
  if (this.memorySnapshotTimer) {
9103
10630
  clearInterval(this.memorySnapshotTimer);
9104
10631
  }
9105
- if (this.stdin.setRawMode) {
9106
- this.stdin.setRawMode(false);
9107
- }
9108
- if (this.isDestroyed)
10632
+ if (this._isDestroyed)
9109
10633
  return;
9110
- this.isDestroyed = true;
10634
+ this._isDestroyed = true;
10635
+ if (this.renderTimeout) {
10636
+ clearTimeout(this.renderTimeout);
10637
+ this.renderTimeout = null;
10638
+ }
10639
+ this._isRunning = false;
9111
10640
  this.waitingForPixelResolution = false;
9112
10641
  this.capturedRenderable = undefined;
9113
- this.root.destroyRecursively();
10642
+ try {
10643
+ this.root.destroyRecursively();
10644
+ } catch (e) {
10645
+ console.error("Error destroying root renderable:", e instanceof Error ? e.stack : String(e));
10646
+ }
9114
10647
  this._keyHandler.destroy();
9115
10648
  this._console.deactivate();
9116
10649
  this.disableStdoutInterception();
9117
10650
  if (this._splitHeight > 0) {
9118
10651
  this.flushStdoutCache(this._splitHeight, true);
9119
10652
  }
10653
+ if (this.stdin.setRawMode) {
10654
+ this.stdin.setRawMode(false);
10655
+ }
10656
+ this.stdin.removeListener("data", this.stdinListener);
9120
10657
  this.lib.destroyRenderer(this.rendererPtr);
10658
+ rendererTracker.removeRenderer(this);
9121
10659
  }
9122
10660
  startRenderLoop() {
9123
10661
  if (!this._isRunning)
@@ -9130,7 +10668,7 @@ Error details:
9130
10668
  this.loop();
9131
10669
  }
9132
10670
  async loop() {
9133
- if (this.rendering || this.isDestroyed)
10671
+ if (this.rendering || this._isDestroyed)
9134
10672
  return;
9135
10673
  this.rendering = true;
9136
10674
  if (this.renderTimeout) {
@@ -9174,15 +10712,17 @@ Error details:
9174
10712
  postProcessFn(this.nextRenderBuffer, deltaTime);
9175
10713
  }
9176
10714
  this._console.renderToBuffer(this.nextRenderBuffer);
9177
- this.renderNative();
9178
- const overallFrameTime = performance.now() - overallStart;
9179
- this.lib.updateStats(this.rendererPtr, overallFrameTime, this.renderStats.fps, this.renderStats.frameCallbackTime);
9180
- if (this.gatherStats) {
9181
- this.collectStatSample(overallFrameTime);
9182
- }
9183
- if (this._isRunning) {
9184
- const delay = Math.max(1, this.targetFrameTime - Math.floor(overallFrameTime));
9185
- this.renderTimeout = setTimeout(() => this.loop(), delay);
10715
+ if (!this._isDestroyed) {
10716
+ this.renderNative();
10717
+ const overallFrameTime = performance.now() - overallStart;
10718
+ this.lib.updateStats(this.rendererPtr, overallFrameTime, this.renderStats.fps, this.renderStats.frameCallbackTime);
10719
+ if (this.gatherStats) {
10720
+ this.collectStatSample(overallFrameTime);
10721
+ }
10722
+ if (this._isRunning) {
10723
+ const delay = Math.max(1, this.targetFrameTime - Math.floor(overallFrameTime));
10724
+ this.renderTimeout = setTimeout(() => this.loop(), delay);
10725
+ }
9186
10726
  }
9187
10727
  this.rendering = false;
9188
10728
  if (this.immediateRerenderRequested) {
@@ -9341,7 +10881,7 @@ Error details:
9341
10881
  }
9342
10882
  }
9343
10883
 
9344
- export { __toESM, __commonJS, __export, __require, Edge, Gutter, exports_src, BorderChars, getBorderFromSides, getBorderSides, borderCharsToArray, BorderCharArrays, nonAlphanumericKeys, parseKeypress, KeyHandler, getKeyHandler, RGBA, hexToRgb, rgbToHex, hsvToRgb, parseColor, fonts, measureText, getCharacterPositions, coordinateToCharacterIndex, renderFontToFrameBuffer, TextAttributes, DebugOverlayCorner, createTextAttributes, 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, t, SyntaxStyle, hastToStyledText, parseAlign, parseBoxSizing, parseDimension, parseDirection, parseDisplay, parseEdge, parseFlexDirection, parseGutter, parseJustify, parseLogLevel, parseMeasureMode, parseOverflow, parsePositionType, parseUnit, parseWrap, MouseParser, Selection, convertGlobalToLocalSelection, ASCIIFontSelectionHelper, TextBuffer, LogLevel2 as LogLevel, setRenderLibPath, resolveRenderLib, OptimizedBuffer, h, isVNode, maybeMakeRenderable, wrapWithDelegates, instantiate, delegate, isValidPercentage, LayoutEvents, RenderableEvents, isRenderable, BaseRenderable, Renderable, RootRenderable, capture, ConsolePosition, TerminalConsole, getObjectsInViewport, MouseEvent, MouseButton, createCliRenderer, CliRenderEvents, CliRenderer };
10884
+ export { __toESM, __commonJS, __export, __require, Edge, Gutter, exports_src, BorderChars, getBorderFromSides, getBorderSides, borderCharsToArray, BorderCharArrays, nonAlphanumericKeys, parseKeypress, ANSI, KeyEvent, PasteEvent, KeyHandler, InternalKeyHandler, RGBA, hexToRgb, rgbToHex, hsvToRgb, parseColor, fonts, measureText, getCharacterPositions, coordinateToCharacterIndex, renderFontToFrameBuffer, TextAttributes, DebugOverlayCorner, createTextAttributes, 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, t, convertThemeToStyles, SyntaxStyle, hastToStyledText, LinearScrollAccel, MacOSScrollAccel, parseAlign, parseBoxSizing, parseDimension, parseDirection, parseDisplay, parseEdge, parseFlexDirection, parseGutter, parseJustify, parseLogLevel, parseMeasureMode, parseOverflow, parsePositionType, parseUnit, parseWrap, MouseParser, Selection, convertGlobalToLocalSelection, ASCIIFontSelectionHelper, envRegistry, registerEnvVar, clearEnvCache, generateEnvMarkdown, generateEnvColored, env, treeSitterToTextChunks, treeSitterToStyledText, addDefaultParsers, TreeSitterClient, DataPathsManager, getDataPaths, extToFiletype, pathToFiletype, main, getTreeSitterClient, TextBuffer, LogLevel2 as LogLevel, setRenderLibPath, resolveRenderLib, OptimizedBuffer, h, isVNode, maybeMakeRenderable, wrapWithDelegates, instantiate, delegate, isValidPercentage, LayoutEvents, RenderableEvents, isRenderable, BaseRenderable, Renderable, RootRenderable, capture, ConsolePosition, TerminalConsole, getObjectsInViewport, MouseEvent, MouseButton, createCliRenderer, CliRenderEvents, CliRenderer };
9345
10885
 
9346
- //# debugId=F7540B181A9A091164756E2164756E21
9347
- //# sourceMappingURL=index-0yx9rnxg.js.map
10886
+ //# debugId=66FAF32C902A5E0B64756E2164756E21
10887
+ //# sourceMappingURL=index-pxa2sv92.js.map