@opentui/core 0.0.0-20250908-4906ddad

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 (64) hide show
  1. package/3d/SpriteResourceManager.d.ts +74 -0
  2. package/3d/SpriteUtils.d.ts +13 -0
  3. package/3d/TextureUtils.d.ts +24 -0
  4. package/3d/WGPURenderer.d.ts +59 -0
  5. package/3d/animation/ExplodingSpriteEffect.d.ts +71 -0
  6. package/3d/animation/PhysicsExplodingSpriteEffect.d.ts +76 -0
  7. package/3d/animation/SpriteAnimator.d.ts +124 -0
  8. package/3d/animation/SpriteParticleGenerator.d.ts +62 -0
  9. package/3d/canvas.d.ts +42 -0
  10. package/3d/index.d.ts +11 -0
  11. package/3d/physics/PlanckPhysicsAdapter.d.ts +19 -0
  12. package/3d/physics/RapierPhysicsAdapter.d.ts +19 -0
  13. package/3d/physics/physics-interface.d.ts +27 -0
  14. package/3d.d.ts +2 -0
  15. package/3d.js +33847 -0
  16. package/3d.js.map +154 -0
  17. package/LICENSE +21 -0
  18. package/README.md +43 -0
  19. package/Renderable.d.ts +266 -0
  20. package/animation/Timeline.d.ts +125 -0
  21. package/ansi.d.ts +28 -0
  22. package/buffer.d.ts +74 -0
  23. package/console.d.ts +86 -0
  24. package/index-d6kwx5pm.js +8837 -0
  25. package/index-d6kwx5pm.js.map +36 -0
  26. package/index.d.ts +12 -0
  27. package/index.js +3721 -0
  28. package/index.js.map +23 -0
  29. package/lib/KeyHandler.d.ts +11 -0
  30. package/lib/RGBA.d.ts +24 -0
  31. package/lib/TrackedNode.d.ts +36 -0
  32. package/lib/ascii.font.d.ts +301 -0
  33. package/lib/border.d.ts +47 -0
  34. package/lib/hast-styled-text.d.ts +38 -0
  35. package/lib/index.d.ts +11 -0
  36. package/lib/output.capture.d.ts +24 -0
  37. package/lib/parse.keypress.d.ts +14 -0
  38. package/lib/parse.mouse.d.ts +23 -0
  39. package/lib/selection.d.ts +63 -0
  40. package/lib/styled-text.d.ts +73 -0
  41. package/lib/word-jumps.d.ts +2 -0
  42. package/lib/yoga.options.d.ts +31 -0
  43. package/package.json +48 -0
  44. package/post/filters.d.ts +105 -0
  45. package/renderables/ASCIIFont.d.ts +43 -0
  46. package/renderables/Box.d.ts +70 -0
  47. package/renderables/FrameBuffer.d.ts +16 -0
  48. package/renderables/Input.d.ts +70 -0
  49. package/renderables/ScrollBar.d.ts +77 -0
  50. package/renderables/ScrollBox.d.ts +82 -0
  51. package/renderables/Select.d.ts +102 -0
  52. package/renderables/Slider.d.ts +31 -0
  53. package/renderables/TabSelect.d.ts +86 -0
  54. package/renderables/Text.d.ts +72 -0
  55. package/renderables/composition/VRenderable.d.ts +16 -0
  56. package/renderables/composition/constructs.d.ts +12 -0
  57. package/renderables/composition/vnode.d.ts +46 -0
  58. package/renderables/index.d.ts +12 -0
  59. package/renderer.d.ts +232 -0
  60. package/singleton.d.ts +5 -0
  61. package/text-buffer.d.ts +52 -0
  62. package/types.d.ts +56 -0
  63. package/utils.d.ts +10 -0
  64. package/zig.d.ts +110 -0
package/index.js ADDED
@@ -0,0 +1,3721 @@
1
+ // @bun
2
+ import {
3
+ ASCIIFontSelectionHelper,
4
+ BorderCharArrays,
5
+ BorderChars,
6
+ CliRenderEvents,
7
+ CliRenderer,
8
+ ConsolePosition,
9
+ DebugOverlayCorner,
10
+ Edge,
11
+ Gutter,
12
+ KeyHandler,
13
+ LayoutEvents,
14
+ LogLevel,
15
+ MeasureMode,
16
+ MouseButton,
17
+ MouseEvent,
18
+ MouseParser,
19
+ OptimizedBuffer,
20
+ RGBA,
21
+ Renderable,
22
+ RenderableEvents,
23
+ RootRenderable,
24
+ Selection,
25
+ StyledText,
26
+ SyntaxStyle,
27
+ TerminalConsole,
28
+ TextAttributes,
29
+ TextBuffer,
30
+ TrackedNode,
31
+ bg,
32
+ bgBlack,
33
+ bgBlue,
34
+ bgCyan,
35
+ bgGreen,
36
+ bgMagenta,
37
+ bgRed,
38
+ bgWhite,
39
+ bgYellow,
40
+ black,
41
+ blink,
42
+ blue,
43
+ bold,
44
+ borderCharsToArray,
45
+ brightBlack,
46
+ brightBlue,
47
+ brightCyan,
48
+ brightGreen,
49
+ brightMagenta,
50
+ brightRed,
51
+ brightWhite,
52
+ brightYellow,
53
+ capture,
54
+ convertGlobalToLocalSelection,
55
+ coordinateToCharacterIndex,
56
+ createCliRenderer,
57
+ createTextAttributes,
58
+ createTrackedNode,
59
+ cyan,
60
+ delegate,
61
+ dim,
62
+ fg,
63
+ fonts,
64
+ getBorderFromSides,
65
+ getBorderSides,
66
+ getCharacterPositions,
67
+ getKeyHandler,
68
+ green,
69
+ h,
70
+ hastToStyledText,
71
+ hexToRgb,
72
+ hsvToRgb,
73
+ instantiate,
74
+ isDimensionType,
75
+ isFlexBasisType,
76
+ isMarginType,
77
+ isOverflowType,
78
+ isPaddingType,
79
+ isPositionType,
80
+ isPositionTypeType,
81
+ isRenderable,
82
+ isSizeType,
83
+ isVNode,
84
+ isValidPercentage,
85
+ italic,
86
+ magenta,
87
+ maybeMakeRenderable,
88
+ measureText,
89
+ nonAlphanumericKeys,
90
+ parseAlign,
91
+ parseBoxSizing,
92
+ parseColor,
93
+ parseDimension,
94
+ parseDirection,
95
+ parseDisplay,
96
+ parseEdge,
97
+ parseFlexDirection,
98
+ parseGutter,
99
+ parseJustify,
100
+ parseKeypress,
101
+ parseLogLevel,
102
+ parseMeasureMode,
103
+ parseOverflow,
104
+ parsePositionType,
105
+ parseUnit,
106
+ parseWrap,
107
+ red,
108
+ renderFontToFrameBuffer,
109
+ resolveRenderLib,
110
+ reverse,
111
+ rgbToHex,
112
+ setRenderLibPath,
113
+ strikethrough,
114
+ stringToStyledText,
115
+ t,
116
+ underline,
117
+ white,
118
+ wrapWithDelegates,
119
+ yellow
120
+ } from "./index-d6kwx5pm.js";
121
+ // src/post/filters.ts
122
+ function applyScanlines(buffer, strength = 0.8, step = 2) {
123
+ const width = buffer.width;
124
+ const height = buffer.height;
125
+ const bg2 = buffer.buffers.bg;
126
+ for (let y = 0;y < height; y += step) {
127
+ for (let x = 0;x < width; x++) {
128
+ const colorIndex = (y * width + x) * 4;
129
+ bg2[colorIndex] *= strength;
130
+ bg2[colorIndex + 1] *= strength;
131
+ bg2[colorIndex + 2] *= strength;
132
+ }
133
+ }
134
+ }
135
+ function applyGrayscale(buffer) {
136
+ const size = buffer.width * buffer.height;
137
+ const fg2 = buffer.buffers.fg;
138
+ const bg2 = buffer.buffers.bg;
139
+ for (let i = 0;i < size; i++) {
140
+ const colorIndex = i * 4;
141
+ const fgR = fg2[colorIndex];
142
+ const fgG = fg2[colorIndex + 1];
143
+ const fgB = fg2[colorIndex + 2];
144
+ const fgLum = 0.299 * fgR + 0.587 * fgG + 0.114 * fgB;
145
+ fg2[colorIndex] = fgLum;
146
+ fg2[colorIndex + 1] = fgLum;
147
+ fg2[colorIndex + 2] = fgLum;
148
+ const bgR = bg2[colorIndex];
149
+ const bgG = bg2[colorIndex + 1];
150
+ const bgB = bg2[colorIndex + 2];
151
+ const bgLum = 0.299 * bgR + 0.587 * bgG + 0.114 * bgB;
152
+ bg2[colorIndex] = bgLum;
153
+ bg2[colorIndex + 1] = bgLum;
154
+ bg2[colorIndex + 2] = bgLum;
155
+ }
156
+ }
157
+ function applySepia(buffer) {
158
+ const size = buffer.width * buffer.height;
159
+ const fg2 = buffer.buffers.fg;
160
+ const bg2 = buffer.buffers.bg;
161
+ for (let i = 0;i < size; i++) {
162
+ const colorIndex = i * 4;
163
+ let fgR = fg2[colorIndex];
164
+ let fgG = fg2[colorIndex + 1];
165
+ let fgB = fg2[colorIndex + 2];
166
+ let newFgR = Math.min(1, fgR * 0.393 + fgG * 0.769 + fgB * 0.189);
167
+ let newFgG = Math.min(1, fgR * 0.349 + fgG * 0.686 + fgB * 0.168);
168
+ let newFgB = Math.min(1, fgR * 0.272 + fgG * 0.534 + fgB * 0.131);
169
+ fg2[colorIndex] = newFgR;
170
+ fg2[colorIndex + 1] = newFgG;
171
+ fg2[colorIndex + 2] = newFgB;
172
+ let bgR = bg2[colorIndex];
173
+ let bgG = bg2[colorIndex + 1];
174
+ let bgB = bg2[colorIndex + 2];
175
+ let newBgR = Math.min(1, bgR * 0.393 + bgG * 0.769 + bgB * 0.189);
176
+ let newBgG = Math.min(1, bgR * 0.349 + bgG * 0.686 + bgB * 0.168);
177
+ let newBgB = Math.min(1, bgR * 0.272 + bgG * 0.534 + bgB * 0.131);
178
+ bg2[colorIndex] = newBgR;
179
+ bg2[colorIndex + 1] = newBgG;
180
+ bg2[colorIndex + 2] = newBgB;
181
+ }
182
+ }
183
+ function applyInvert(buffer) {
184
+ const size = buffer.width * buffer.height;
185
+ const fg2 = buffer.buffers.fg;
186
+ const bg2 = buffer.buffers.bg;
187
+ for (let i = 0;i < size; i++) {
188
+ const colorIndex = i * 4;
189
+ fg2[colorIndex] = 1 - fg2[colorIndex];
190
+ fg2[colorIndex + 1] = 1 - fg2[colorIndex + 1];
191
+ fg2[colorIndex + 2] = 1 - fg2[colorIndex + 2];
192
+ bg2[colorIndex] = 1 - bg2[colorIndex];
193
+ bg2[colorIndex + 1] = 1 - bg2[colorIndex + 1];
194
+ bg2[colorIndex + 2] = 1 - bg2[colorIndex + 2];
195
+ }
196
+ }
197
+ function applyNoise(buffer, strength = 0.1) {
198
+ const size = buffer.width * buffer.height;
199
+ const fg2 = buffer.buffers.fg;
200
+ const bg2 = buffer.buffers.bg;
201
+ for (let i = 0;i < size; i++) {
202
+ const colorIndex = i * 4;
203
+ const noise = (Math.random() - 0.5) * strength;
204
+ fg2[colorIndex] = Math.max(0, Math.min(1, fg2[colorIndex] + noise));
205
+ fg2[colorIndex + 1] = Math.max(0, Math.min(1, fg2[colorIndex + 1] + noise));
206
+ fg2[colorIndex + 2] = Math.max(0, Math.min(1, fg2[colorIndex + 2] + noise));
207
+ bg2[colorIndex] = Math.max(0, Math.min(1, bg2[colorIndex] + noise));
208
+ bg2[colorIndex + 1] = Math.max(0, Math.min(1, bg2[colorIndex + 1] + noise));
209
+ bg2[colorIndex + 2] = Math.max(0, Math.min(1, bg2[colorIndex + 2] + noise));
210
+ }
211
+ }
212
+ function applyChromaticAberration(buffer, strength = 1) {
213
+ const width = buffer.width;
214
+ const height = buffer.height;
215
+ const srcFg = Float32Array.from(buffer.buffers.fg);
216
+ const destFg = buffer.buffers.fg;
217
+ const centerX = width / 2;
218
+ const centerY = height / 2;
219
+ for (let y = 0;y < height; y++) {
220
+ for (let x = 0;x < width; x++) {
221
+ const dx = x - centerX;
222
+ const dy = y - centerY;
223
+ const offset = Math.round(Math.sqrt(dx * dx + dy * dy) / Math.max(centerX, centerY) * strength);
224
+ const rX = Math.max(0, Math.min(width - 1, x - offset));
225
+ const bX = Math.max(0, Math.min(width - 1, x + offset));
226
+ const rIndex = (y * width + rX) * 4;
227
+ const gIndex = (y * width + x) * 4;
228
+ const bIndex = (y * width + bX) * 4;
229
+ const destIndex = (y * width + x) * 4;
230
+ destFg[destIndex] = srcFg[rIndex];
231
+ destFg[destIndex + 1] = srcFg[gIndex + 1];
232
+ destFg[destIndex + 2] = srcFg[bIndex + 2];
233
+ }
234
+ }
235
+ }
236
+ function applyAsciiArt(buffer, ramp = " .:-=+*#%@") {
237
+ const width = buffer.width;
238
+ const height = buffer.height;
239
+ const chars = buffer.buffers.char;
240
+ const bg2 = buffer.buffers.bg;
241
+ const rampLength = ramp.length;
242
+ for (let y = 0;y < height; y++) {
243
+ for (let x = 0;x < width; x++) {
244
+ const index = y * width + x;
245
+ const colorIndex = index * 4;
246
+ const bgR = bg2[colorIndex];
247
+ const bgG = bg2[colorIndex + 1];
248
+ const bgB = bg2[colorIndex + 2];
249
+ const lum = 0.299 * bgR + 0.587 * bgG + 0.114 * bgB;
250
+ const rampIndex = Math.min(rampLength - 1, Math.floor(lum * rampLength));
251
+ chars[index] = ramp[rampIndex].charCodeAt(0);
252
+ }
253
+ }
254
+ }
255
+
256
+ class DistortionEffect {
257
+ glitchChancePerSecond = 0.5;
258
+ maxGlitchLines = 3;
259
+ minGlitchDuration = 0.05;
260
+ maxGlitchDuration = 0.2;
261
+ maxShiftAmount = 10;
262
+ shiftFlipRatio = 0.6;
263
+ colorGlitchChance = 0.2;
264
+ lastGlitchTime = 0;
265
+ glitchDuration = 0;
266
+ activeGlitches = [];
267
+ constructor(options) {
268
+ if (options) {
269
+ Object.assign(this, options);
270
+ }
271
+ }
272
+ apply(buffer, deltaTime) {
273
+ const width = buffer.width;
274
+ const height = buffer.height;
275
+ const buf = buffer.buffers;
276
+ this.lastGlitchTime += deltaTime;
277
+ if (this.activeGlitches.length > 0 && this.lastGlitchTime >= this.glitchDuration) {
278
+ this.activeGlitches = [];
279
+ this.glitchDuration = 0;
280
+ }
281
+ if (this.activeGlitches.length === 0 && Math.random() < this.glitchChancePerSecond * deltaTime) {
282
+ this.lastGlitchTime = 0;
283
+ this.glitchDuration = this.minGlitchDuration + Math.random() * (this.maxGlitchDuration - this.minGlitchDuration);
284
+ const numGlitches = 1 + Math.floor(Math.random() * this.maxGlitchLines);
285
+ for (let i = 0;i < numGlitches; i++) {
286
+ const y = Math.floor(Math.random() * height);
287
+ let type;
288
+ let amount = 0;
289
+ const typeRoll = Math.random();
290
+ if (typeRoll < this.colorGlitchChance) {
291
+ type = "color";
292
+ } else {
293
+ const shiftRoll = (typeRoll - this.colorGlitchChance) / (1 - this.colorGlitchChance);
294
+ if (shiftRoll < this.shiftFlipRatio) {
295
+ type = "shift";
296
+ amount = Math.floor((Math.random() - 0.5) * 2 * this.maxShiftAmount);
297
+ } else {
298
+ type = "flip";
299
+ }
300
+ }
301
+ if (!this.activeGlitches.some((g) => g.y === y)) {
302
+ this.activeGlitches.push({ y, type, amount });
303
+ }
304
+ }
305
+ }
306
+ if (this.activeGlitches.length > 0) {
307
+ let tempChar = null;
308
+ let tempFg = null;
309
+ let tempBg = null;
310
+ let tempAttr = null;
311
+ for (const glitch of this.activeGlitches) {
312
+ const y = glitch.y;
313
+ if (y < 0 || y >= height)
314
+ continue;
315
+ const baseIndex = y * width;
316
+ if (glitch.type === "shift" || glitch.type === "flip") {
317
+ if (!tempChar) {
318
+ tempChar = new Uint32Array(width);
319
+ tempFg = new Float32Array(width * 4);
320
+ tempBg = new Float32Array(width * 4);
321
+ tempAttr = new Uint8Array(width);
322
+ }
323
+ try {
324
+ tempChar.set(buf.char.subarray(baseIndex, baseIndex + width));
325
+ tempFg.set(buf.fg.subarray(baseIndex * 4, (baseIndex + width) * 4));
326
+ tempBg.set(buf.bg.subarray(baseIndex * 4, (baseIndex + width) * 4));
327
+ tempAttr.set(buf.attributes.subarray(baseIndex, baseIndex + width));
328
+ } catch (e) {
329
+ console.error(`Error copying row ${y} for distortion:`, e);
330
+ continue;
331
+ }
332
+ if (glitch.type === "shift") {
333
+ const shift = glitch.amount;
334
+ for (let x = 0;x < width; x++) {
335
+ const srcX = (x - shift + width) % width;
336
+ const destIndex = baseIndex + x;
337
+ const srcTempIndex = srcX;
338
+ buf.char[destIndex] = tempChar[srcTempIndex];
339
+ buf.attributes[destIndex] = tempAttr[srcTempIndex];
340
+ const destColorIndex = destIndex * 4;
341
+ const srcTempColorIndex = srcTempIndex * 4;
342
+ buf.fg.set(tempFg.subarray(srcTempColorIndex, srcTempColorIndex + 4), destColorIndex);
343
+ buf.bg.set(tempBg.subarray(srcTempColorIndex, srcTempColorIndex + 4), destColorIndex);
344
+ }
345
+ } else {
346
+ for (let x = 0;x < width; x++) {
347
+ const srcX = width - 1 - x;
348
+ const destIndex = baseIndex + x;
349
+ const srcTempIndex = srcX;
350
+ buf.char[destIndex] = tempChar[srcTempIndex];
351
+ buf.attributes[destIndex] = tempAttr[srcTempIndex];
352
+ const destColorIndex = destIndex * 4;
353
+ const srcTempColorIndex = srcTempIndex * 4;
354
+ buf.fg.set(tempFg.subarray(srcTempColorIndex, srcTempColorIndex + 4), destColorIndex);
355
+ buf.bg.set(tempBg.subarray(srcTempColorIndex, srcTempColorIndex + 4), destColorIndex);
356
+ }
357
+ }
358
+ } else if (glitch.type === "color") {
359
+ const glitchStart = Math.floor(Math.random() * width);
360
+ const maxPossibleLength = width - glitchStart;
361
+ let glitchLength = Math.floor(Math.random() * maxPossibleLength) + 1;
362
+ if (Math.random() < 0.2) {
363
+ glitchLength = Math.floor(Math.random() * (width / 4)) + 1;
364
+ }
365
+ glitchLength = Math.min(glitchLength, maxPossibleLength);
366
+ for (let x = glitchStart;x < glitchStart + glitchLength; x++) {
367
+ if (x >= width)
368
+ break;
369
+ const destIndex = baseIndex + x;
370
+ const destColorIndex = destIndex * 4;
371
+ let rFg, gFg, bFg, rBg, gBg, bBg;
372
+ const colorMode = Math.random();
373
+ if (colorMode < 0.33) {
374
+ rFg = Math.random();
375
+ gFg = Math.random();
376
+ bFg = Math.random();
377
+ rBg = Math.random();
378
+ gBg = Math.random();
379
+ bBg = Math.random();
380
+ } else if (colorMode < 0.66) {
381
+ const emphasis = Math.random();
382
+ if (emphasis < 0.25) {
383
+ rFg = Math.random();
384
+ gFg = 0;
385
+ bFg = 0;
386
+ } else if (emphasis < 0.5) {
387
+ rFg = 0;
388
+ gFg = Math.random();
389
+ bFg = 0;
390
+ } else if (emphasis < 0.75) {
391
+ rFg = 0;
392
+ gFg = 0;
393
+ bFg = Math.random();
394
+ } else {
395
+ const glitchColorRoll = Math.random();
396
+ if (glitchColorRoll < 0.33) {
397
+ rFg = 1;
398
+ gFg = 0;
399
+ bFg = 1;
400
+ } else if (glitchColorRoll < 0.66) {
401
+ rFg = 0;
402
+ gFg = 1;
403
+ bFg = 1;
404
+ } else {
405
+ rFg = 1;
406
+ gFg = 1;
407
+ bFg = 0;
408
+ }
409
+ }
410
+ if (Math.random() < 0.5) {
411
+ rBg = 1 - rFg;
412
+ gBg = 1 - gFg;
413
+ bBg = 1 - bFg;
414
+ } else {
415
+ rBg = rFg * (Math.random() * 0.5 + 0.2);
416
+ gBg = gFg * (Math.random() * 0.5 + 0.2);
417
+ bBg = bFg * (Math.random() * 0.5 + 0.2);
418
+ }
419
+ } else {
420
+ rFg = Math.random() > 0.5 ? 1 : 0;
421
+ gFg = Math.random() > 0.5 ? 1 : 0;
422
+ bFg = Math.random() > 0.5 ? 1 : 0;
423
+ rBg = 1 - rFg;
424
+ gBg = 1 - gFg;
425
+ bBg = 1 - bFg;
426
+ }
427
+ buf.fg[destColorIndex] = rFg;
428
+ buf.fg[destColorIndex + 1] = gFg;
429
+ buf.fg[destColorIndex + 2] = bFg;
430
+ buf.bg[destColorIndex] = rBg;
431
+ buf.bg[destColorIndex + 1] = gBg;
432
+ buf.bg[destColorIndex + 2] = bBg;
433
+ }
434
+ }
435
+ }
436
+ }
437
+ }
438
+ }
439
+
440
+ class VignetteEffect {
441
+ _strength;
442
+ precomputedBaseAttenuation = null;
443
+ cachedWidth = -1;
444
+ cachedHeight = -1;
445
+ constructor(strength = 0.5) {
446
+ this._strength = strength;
447
+ }
448
+ set strength(newStrength) {
449
+ this._strength = Math.max(0, newStrength);
450
+ }
451
+ get strength() {
452
+ return this._strength;
453
+ }
454
+ _computeFactors(width, height) {
455
+ this.precomputedBaseAttenuation = new Float32Array(width * height);
456
+ const centerX = width / 2;
457
+ const centerY = height / 2;
458
+ const maxDistSq = centerX * centerX + centerY * centerY;
459
+ const safeMaxDistSq = maxDistSq === 0 ? 1 : maxDistSq;
460
+ for (let y = 0;y < height; y++) {
461
+ const dy = y - centerY;
462
+ const dySq = dy * dy;
463
+ for (let x = 0;x < width; x++) {
464
+ const dx = x - centerX;
465
+ const distSq = dx * dx + dySq;
466
+ const baseAttenuation = Math.min(1, distSq / safeMaxDistSq);
467
+ const index = y * width + x;
468
+ this.precomputedBaseAttenuation[index] = baseAttenuation;
469
+ }
470
+ }
471
+ this.cachedWidth = width;
472
+ this.cachedHeight = height;
473
+ }
474
+ apply(buffer) {
475
+ const width = buffer.width;
476
+ const height = buffer.height;
477
+ const buf = buffer.buffers;
478
+ const size = width * height;
479
+ if (width !== this.cachedWidth || height !== this.cachedHeight || !this.precomputedBaseAttenuation) {
480
+ this._computeFactors(width, height);
481
+ }
482
+ for (let i = 0;i < size; i++) {
483
+ const factor = Math.max(0, 1 - this.precomputedBaseAttenuation[i] * this._strength);
484
+ const colorIndex = i * 4;
485
+ buf.fg[colorIndex] *= factor;
486
+ buf.fg[colorIndex + 1] *= factor;
487
+ buf.fg[colorIndex + 2] *= factor;
488
+ buf.bg[colorIndex] *= factor;
489
+ buf.bg[colorIndex + 1] *= factor;
490
+ buf.bg[colorIndex + 2] *= factor;
491
+ }
492
+ }
493
+ }
494
+
495
+ class BrightnessEffect {
496
+ _brightness;
497
+ constructor(brightness = 1) {
498
+ this._brightness = Math.max(0, brightness);
499
+ }
500
+ set brightness(newBrightness) {
501
+ this._brightness = Math.max(0, newBrightness);
502
+ }
503
+ get brightness() {
504
+ return this._brightness;
505
+ }
506
+ apply(buffer) {
507
+ const size = buffer.width * buffer.height;
508
+ const fg2 = buffer.buffers.fg;
509
+ const bg2 = buffer.buffers.bg;
510
+ const factor = this._brightness;
511
+ if (factor === 1) {
512
+ return;
513
+ }
514
+ for (let i = 0;i < size; i++) {
515
+ const colorIndex = i * 4;
516
+ fg2[colorIndex] = Math.min(1, fg2[colorIndex] * factor);
517
+ fg2[colorIndex + 1] = Math.min(1, fg2[colorIndex + 1] * factor);
518
+ fg2[colorIndex + 2] = Math.min(1, fg2[colorIndex + 2] * factor);
519
+ bg2[colorIndex] = Math.min(1, bg2[colorIndex] * factor);
520
+ bg2[colorIndex + 1] = Math.min(1, bg2[colorIndex + 1] * factor);
521
+ bg2[colorIndex + 2] = Math.min(1, bg2[colorIndex + 2] * factor);
522
+ }
523
+ }
524
+ }
525
+
526
+ class BlurEffect {
527
+ _radius;
528
+ constructor(radius = 1) {
529
+ this._radius = Math.max(0, Math.round(radius));
530
+ }
531
+ set radius(newRadius) {
532
+ this._radius = Math.max(0, Math.round(newRadius));
533
+ }
534
+ get radius() {
535
+ return this._radius;
536
+ }
537
+ apply(buffer) {
538
+ const radius = this._radius;
539
+ if (radius <= 0)
540
+ return;
541
+ const width = buffer.width;
542
+ const height = buffer.height;
543
+ const buf = buffer.buffers;
544
+ const srcFg = buf.fg;
545
+ const srcBg = buf.bg;
546
+ const destFg = buf.fg;
547
+ const destBg = buf.bg;
548
+ const chars = buf.char;
549
+ const size = width * height;
550
+ const numChannels = 4;
551
+ const tempBufferFg = new Float32Array(size * numChannels);
552
+ const tempBufferBg = new Float32Array(size * numChannels);
553
+ const windowSize = radius * 2 + 1;
554
+ for (let y = 0;y < height; y++) {
555
+ let sumR = 0, sumG = 0, sumB = 0, sumA = 0;
556
+ const baseRowIndex = y * width;
557
+ for (let x = -radius;x <= radius; x++) {
558
+ const sampleX = Math.max(0, Math.min(width - 1, x));
559
+ const srcIndex = (baseRowIndex + sampleX) * numChannels;
560
+ sumR += srcFg[srcIndex];
561
+ sumG += srcFg[srcIndex + 1];
562
+ sumB += srcFg[srcIndex + 2];
563
+ sumA += srcFg[srcIndex + 3];
564
+ }
565
+ for (let x = 0;x < width; x++) {
566
+ const destIndex = (baseRowIndex + x) * numChannels;
567
+ tempBufferFg[destIndex] = sumR / windowSize;
568
+ tempBufferFg[destIndex + 1] = sumG / windowSize;
569
+ tempBufferFg[destIndex + 2] = sumB / windowSize;
570
+ tempBufferFg[destIndex + 3] = sumA / windowSize;
571
+ const leavingX = Math.max(0, Math.min(width - 1, x - radius));
572
+ const leavingIndex = (baseRowIndex + leavingX) * numChannels;
573
+ sumR -= srcFg[leavingIndex];
574
+ sumG -= srcFg[leavingIndex + 1];
575
+ sumB -= srcFg[leavingIndex + 2];
576
+ sumA -= srcFg[leavingIndex + 3];
577
+ const enteringX = Math.max(0, Math.min(width - 1, x + radius + 1));
578
+ const enteringIndex = (baseRowIndex + enteringX) * numChannels;
579
+ sumR += srcFg[enteringIndex];
580
+ sumG += srcFg[enteringIndex + 1];
581
+ sumB += srcFg[enteringIndex + 2];
582
+ sumA += srcFg[enteringIndex + 3];
583
+ }
584
+ }
585
+ for (let y = 0;y < height; y++) {
586
+ let sumR = 0, sumG = 0, sumB = 0, sumA = 0;
587
+ const baseRowIndex = y * width;
588
+ for (let x = -radius;x <= radius; x++) {
589
+ const sampleX = Math.max(0, Math.min(width - 1, x));
590
+ const srcIndex = (baseRowIndex + sampleX) * numChannels;
591
+ sumR += srcBg[srcIndex];
592
+ sumG += srcBg[srcIndex + 1];
593
+ sumB += srcBg[srcIndex + 2];
594
+ sumA += srcBg[srcIndex + 3];
595
+ }
596
+ for (let x = 0;x < width; x++) {
597
+ const destIndex = (baseRowIndex + x) * numChannels;
598
+ tempBufferBg[destIndex] = sumR / windowSize;
599
+ tempBufferBg[destIndex + 1] = sumG / windowSize;
600
+ tempBufferBg[destIndex + 2] = sumB / windowSize;
601
+ tempBufferBg[destIndex + 3] = sumA / windowSize;
602
+ const leavingX = Math.max(0, Math.min(width - 1, x - radius));
603
+ const leavingIndex = (baseRowIndex + leavingX) * numChannels;
604
+ sumR -= srcBg[leavingIndex];
605
+ sumG -= srcBg[leavingIndex + 1];
606
+ sumB -= srcBg[leavingIndex + 2];
607
+ sumA -= srcBg[leavingIndex + 3];
608
+ const enteringX = Math.max(0, Math.min(width - 1, x + radius + 1));
609
+ const enteringIndex = (baseRowIndex + enteringX) * numChannels;
610
+ sumR += srcBg[enteringIndex];
611
+ sumG += srcBg[enteringIndex + 1];
612
+ sumB += srcBg[enteringIndex + 2];
613
+ sumA += srcBg[enteringIndex + 3];
614
+ }
615
+ }
616
+ for (let x = 0;x < width; x++) {
617
+ let sumR = 0, sumG = 0, sumB = 0, sumA = 0;
618
+ for (let y = -radius;y <= radius; y++) {
619
+ const sampleY = Math.max(0, Math.min(height - 1, y));
620
+ const srcIndex = (sampleY * width + x) * numChannels;
621
+ sumR += tempBufferFg[srcIndex];
622
+ sumG += tempBufferFg[srcIndex + 1];
623
+ sumB += tempBufferFg[srcIndex + 2];
624
+ sumA += tempBufferFg[srcIndex + 3];
625
+ }
626
+ for (let y = 0;y < height; y++) {
627
+ const destIndex = (y * width + x) * numChannels;
628
+ destFg[destIndex] = sumR / windowSize;
629
+ destFg[destIndex + 1] = sumG / windowSize;
630
+ destFg[destIndex + 2] = sumB / windowSize;
631
+ destFg[destIndex + 3] = sumA / windowSize;
632
+ const leavingY = Math.max(0, Math.min(height - 1, y - radius));
633
+ const leavingIndex = (leavingY * width + x) * numChannels;
634
+ sumR -= tempBufferFg[leavingIndex];
635
+ sumG -= tempBufferFg[leavingIndex + 1];
636
+ sumB -= tempBufferFg[leavingIndex + 2];
637
+ sumA -= tempBufferFg[leavingIndex + 3];
638
+ const enteringY = Math.max(0, Math.min(height - 1, y + radius + 1));
639
+ const enteringIndex = (enteringY * width + x) * numChannels;
640
+ sumR += tempBufferFg[enteringIndex];
641
+ sumG += tempBufferFg[enteringIndex + 1];
642
+ sumB += tempBufferFg[enteringIndex + 2];
643
+ sumA += tempBufferFg[enteringIndex + 3];
644
+ }
645
+ }
646
+ for (let x = 0;x < width; x++) {
647
+ let sumR = 0, sumG = 0, sumB = 0, sumA = 0;
648
+ for (let y = -radius;y <= radius; y++) {
649
+ const sampleY = Math.max(0, Math.min(height - 1, y));
650
+ const srcIndex = (sampleY * width + x) * numChannels;
651
+ sumR += tempBufferBg[srcIndex];
652
+ sumG += tempBufferBg[srcIndex + 1];
653
+ sumB += tempBufferBg[srcIndex + 2];
654
+ sumA += tempBufferBg[srcIndex + 3];
655
+ }
656
+ for (let y = 0;y < height; y++) {
657
+ const destIndex = (y * width + x) * numChannels;
658
+ destBg[destIndex] = sumR / windowSize;
659
+ destBg[destIndex + 1] = sumG / windowSize;
660
+ destBg[destIndex + 2] = sumB / windowSize;
661
+ destBg[destIndex + 3] = sumA / windowSize;
662
+ const leavingY = Math.max(0, Math.min(height - 1, y - radius));
663
+ const leavingIndex = (leavingY * width + x) * numChannels;
664
+ sumR -= tempBufferBg[leavingIndex];
665
+ sumG -= tempBufferBg[leavingIndex + 1];
666
+ sumB -= tempBufferBg[leavingIndex + 2];
667
+ sumA -= tempBufferBg[leavingIndex + 3];
668
+ const enteringY = Math.max(0, Math.min(height - 1, y + radius + 1));
669
+ const enteringIndex = (enteringY * width + x) * numChannels;
670
+ sumR += tempBufferBg[enteringIndex];
671
+ sumG += tempBufferBg[enteringIndex + 1];
672
+ sumB += tempBufferBg[enteringIndex + 2];
673
+ sumA += tempBufferBg[enteringIndex + 3];
674
+ }
675
+ }
676
+ const charRamp = [" ", "\u2591", "\u2592", "\u2593", " "];
677
+ const rampLength = charRamp.length;
678
+ for (let i = 0;i < size; i++) {
679
+ const alphaIndex = i * numChannels + 3;
680
+ const fgAlpha = destFg[alphaIndex];
681
+ const clampedAlpha = Math.max(0, Math.min(1, fgAlpha));
682
+ const rampIndex = Math.min(rampLength - 1, Math.floor(clampedAlpha * rampLength));
683
+ chars[i] = charRamp[rampIndex].charCodeAt(0);
684
+ }
685
+ }
686
+ }
687
+
688
+ class BloomEffect {
689
+ _threshold;
690
+ _strength;
691
+ _radius;
692
+ constructor(threshold = 0.8, strength = 0.2, radius = 2) {
693
+ this._threshold = Math.max(0, Math.min(1, threshold));
694
+ this._strength = Math.max(0, strength);
695
+ this._radius = Math.max(0, Math.round(radius));
696
+ }
697
+ set threshold(newThreshold) {
698
+ this._threshold = Math.max(0, Math.min(1, newThreshold));
699
+ }
700
+ get threshold() {
701
+ return this._threshold;
702
+ }
703
+ set strength(newStrength) {
704
+ this._strength = Math.max(0, newStrength);
705
+ }
706
+ get strength() {
707
+ return this._strength;
708
+ }
709
+ set radius(newRadius) {
710
+ this._radius = Math.max(0, Math.round(newRadius));
711
+ }
712
+ get radius() {
713
+ return this._radius;
714
+ }
715
+ apply(buffer) {
716
+ const threshold = this._threshold;
717
+ const strength = this._strength;
718
+ const radius = this._radius;
719
+ if (strength <= 0 || radius <= 0)
720
+ return;
721
+ const width = buffer.width;
722
+ const height = buffer.height;
723
+ const srcFg = Float32Array.from(buffer.buffers.fg);
724
+ const srcBg = Float32Array.from(buffer.buffers.bg);
725
+ const destFg = buffer.buffers.fg;
726
+ const destBg = buffer.buffers.bg;
727
+ const brightPixels = [];
728
+ for (let y = 0;y < height; y++) {
729
+ for (let x = 0;x < width; x++) {
730
+ const index = (y * width + x) * 4;
731
+ const fgLum = 0.299 * srcFg[index] + 0.587 * srcFg[index + 1] + 0.114 * srcFg[index + 2];
732
+ const bgLum = 0.299 * srcBg[index] + 0.587 * srcBg[index + 1] + 0.114 * srcBg[index + 2];
733
+ const lum = Math.max(fgLum, bgLum);
734
+ if (lum > threshold) {
735
+ const intensity = (lum - threshold) / (1 - threshold + 0.000001);
736
+ brightPixels.push({ x, y, intensity: Math.max(0, intensity) });
737
+ }
738
+ }
739
+ }
740
+ if (brightPixels.length === 0)
741
+ return;
742
+ destFg.set(srcFg);
743
+ destBg.set(srcBg);
744
+ for (const bright of brightPixels) {
745
+ for (let ky = -radius;ky <= radius; ky++) {
746
+ for (let kx = -radius;kx <= radius; kx++) {
747
+ if (kx === 0 && ky === 0)
748
+ continue;
749
+ const sampleX = bright.x + kx;
750
+ const sampleY = bright.y + ky;
751
+ if (sampleX >= 0 && sampleX < width && sampleY >= 0 && sampleY < height) {
752
+ const distSq = kx * kx + ky * ky;
753
+ const radiusSq = radius * radius;
754
+ if (distSq <= radiusSq) {
755
+ const falloff = 1 - distSq / radiusSq;
756
+ const bloomAmount = bright.intensity * strength * falloff;
757
+ const destIndex = (sampleY * width + sampleX) * 4;
758
+ destFg[destIndex] = Math.min(1, destFg[destIndex] + bloomAmount);
759
+ destFg[destIndex + 1] = Math.min(1, destFg[destIndex + 1] + bloomAmount);
760
+ destFg[destIndex + 2] = Math.min(1, destFg[destIndex + 2] + bloomAmount);
761
+ destBg[destIndex] = Math.min(1, destBg[destIndex] + bloomAmount);
762
+ destBg[destIndex + 1] = Math.min(1, destBg[destIndex + 1] + bloomAmount);
763
+ destBg[destIndex + 2] = Math.min(1, destBg[destIndex + 2] + bloomAmount);
764
+ }
765
+ }
766
+ }
767
+ }
768
+ }
769
+ }
770
+ }
771
+ // src/animation/Timeline.ts
772
+ var easingFunctions = {
773
+ linear: (t2) => t2,
774
+ inQuad: (t2) => t2 * t2,
775
+ outQuad: (t2) => t2 * (2 - t2),
776
+ inOutQuad: (t2) => t2 < 0.5 ? 2 * t2 * t2 : -1 + (4 - 2 * t2) * t2,
777
+ inExpo: (t2) => t2 === 0 ? 0 : Math.pow(2, 10 * (t2 - 1)),
778
+ outExpo: (t2) => t2 === 1 ? 1 : 1 - Math.pow(2, -10 * t2),
779
+ inOutSine: (t2) => -(Math.cos(Math.PI * t2) - 1) / 2,
780
+ outBounce: (t2) => {
781
+ const n1 = 7.5625;
782
+ const d1 = 2.75;
783
+ if (t2 < 1 / d1) {
784
+ return n1 * t2 * t2;
785
+ } else if (t2 < 2 / d1) {
786
+ return n1 * (t2 -= 1.5 / d1) * t2 + 0.75;
787
+ } else if (t2 < 2.5 / d1) {
788
+ return n1 * (t2 -= 2.25 / d1) * t2 + 0.9375;
789
+ } else {
790
+ return n1 * (t2 -= 2.625 / d1) * t2 + 0.984375;
791
+ }
792
+ },
793
+ outElastic: (t2) => {
794
+ const c4 = 2 * Math.PI / 3;
795
+ return t2 === 0 ? 0 : t2 === 1 ? 1 : Math.pow(2, -10 * t2) * Math.sin((t2 * 10 - 0.75) * c4) + 1;
796
+ },
797
+ inBounce: (t2) => 1 - easingFunctions.outBounce(1 - t2),
798
+ inCirc: (t2) => 1 - Math.sqrt(1 - t2 * t2),
799
+ outCirc: (t2) => Math.sqrt(1 - Math.pow(t2 - 1, 2)),
800
+ inOutCirc: (t2) => {
801
+ if ((t2 *= 2) < 1)
802
+ return -0.5 * (Math.sqrt(1 - t2 * t2) - 1);
803
+ return 0.5 * (Math.sqrt(1 - (t2 -= 2) * t2) + 1);
804
+ },
805
+ inBack: (t2, s = 1.70158) => t2 * t2 * ((s + 1) * t2 - s),
806
+ outBack: (t2, s = 1.70158) => --t2 * t2 * ((s + 1) * t2 + s) + 1,
807
+ inOutBack: (t2, s = 1.70158) => {
808
+ s *= 1.525;
809
+ if ((t2 *= 2) < 1)
810
+ return 0.5 * (t2 * t2 * ((s + 1) * t2 - s));
811
+ return 0.5 * ((t2 -= 2) * t2 * ((s + 1) * t2 + s) + 2);
812
+ }
813
+ };
814
+ function captureInitialValues(item) {
815
+ if (!item.properties)
816
+ return;
817
+ if (!item.initialValues || item.initialValues.length === 0) {
818
+ const initialValues = [];
819
+ for (let i = 0;i < item.target.length; i++) {
820
+ const target = item.target[i];
821
+ const targetInitialValues = {};
822
+ for (const key of Object.keys(item.properties)) {
823
+ if (typeof target[key] === "number") {
824
+ targetInitialValues[key] = target[key];
825
+ }
826
+ }
827
+ initialValues.push(targetInitialValues);
828
+ }
829
+ item.initialValues = initialValues;
830
+ }
831
+ }
832
+ function applyAnimationAtProgress(item, progress, reversed, timelineTime, deltaTime = 0) {
833
+ if (!item.properties || !item.initialValues)
834
+ return;
835
+ const easingFn = easingFunctions[item.ease || "linear"] || easingFunctions.linear;
836
+ const easedProgress = easingFn(Math.max(0, Math.min(1, progress)));
837
+ const finalProgress = reversed ? 1 - easedProgress : easedProgress;
838
+ for (let i = 0;i < item.target.length; i++) {
839
+ const target = item.target[i];
840
+ const targetInitialValues = item.initialValues[i];
841
+ if (!targetInitialValues)
842
+ continue;
843
+ for (const [key, endValue] of Object.entries(item.properties)) {
844
+ const startValue = targetInitialValues[key];
845
+ const newValue = startValue + (endValue - startValue) * finalProgress;
846
+ target[key] = newValue;
847
+ }
848
+ }
849
+ if (item.onUpdate) {
850
+ const animation = {
851
+ targets: item.target,
852
+ progress: easedProgress,
853
+ currentTime: timelineTime,
854
+ deltaTime
855
+ };
856
+ item.onUpdate(animation);
857
+ }
858
+ }
859
+ function evaluateAnimation(item, timelineTime, deltaTime = 0) {
860
+ if (timelineTime < item.startTime) {
861
+ return;
862
+ }
863
+ const animationTime = timelineTime - item.startTime;
864
+ const duration = item.duration || 0;
865
+ if (timelineTime >= item.startTime && !item.started) {
866
+ captureInitialValues(item);
867
+ if (item.onStart) {
868
+ item.onStart();
869
+ }
870
+ item.started = true;
871
+ }
872
+ if (duration === 0) {
873
+ if (!item.completed) {
874
+ applyAnimationAtProgress(item, 1, false, timelineTime, deltaTime);
875
+ if (item.onComplete) {
876
+ item.onComplete();
877
+ }
878
+ item.completed = true;
879
+ }
880
+ return;
881
+ }
882
+ const maxLoops = !item.loop || item.loop === 1 ? 1 : typeof item.loop === "number" ? item.loop : Infinity;
883
+ const loopDelay = item.loopDelay || 0;
884
+ const cycleTime = duration + loopDelay;
885
+ let currentCycle = Math.floor(animationTime / cycleTime);
886
+ let timeInCycle = animationTime % cycleTime;
887
+ if (item.onLoop && item.currentLoop !== undefined && currentCycle > item.currentLoop && currentCycle < maxLoops) {
888
+ item.onLoop();
889
+ }
890
+ item.currentLoop = currentCycle;
891
+ if (item.onComplete && !item.completed && currentCycle === maxLoops - 1 && timeInCycle >= duration) {
892
+ const finalLoopReversed = (item.alternate || false) && currentCycle % 2 === 1;
893
+ applyAnimationAtProgress(item, 1, finalLoopReversed, timelineTime, deltaTime);
894
+ item.onComplete();
895
+ item.completed = true;
896
+ return;
897
+ }
898
+ if (currentCycle >= maxLoops) {
899
+ if (!item.completed) {
900
+ const finalReversed = (item.alternate || false) && (maxLoops - 1) % 2 === 1;
901
+ applyAnimationAtProgress(item, 1, finalReversed, timelineTime, deltaTime);
902
+ if (item.onComplete) {
903
+ item.onComplete();
904
+ }
905
+ item.completed = true;
906
+ }
907
+ return;
908
+ }
909
+ if (timeInCycle === 0 && animationTime > 0 && currentCycle < maxLoops) {
910
+ currentCycle = currentCycle - 1;
911
+ timeInCycle = cycleTime;
912
+ }
913
+ if (timeInCycle >= duration) {
914
+ const isReversed2 = (item.alternate || false) && currentCycle % 2 === 1;
915
+ applyAnimationAtProgress(item, 1, isReversed2, timelineTime, deltaTime);
916
+ return;
917
+ }
918
+ const progress = timeInCycle / duration;
919
+ const isReversed = (item.alternate || false) && currentCycle % 2 === 1;
920
+ applyAnimationAtProgress(item, progress, isReversed, timelineTime, deltaTime);
921
+ }
922
+ function evaluateCallback(item, timelineTime) {
923
+ if (!item.executed && timelineTime >= item.startTime && item.callback) {
924
+ item.callback();
925
+ item.executed = true;
926
+ }
927
+ }
928
+ function evaluateTimelineSync(item, timelineTime, deltaTime = 0) {
929
+ if (!item.timeline)
930
+ return;
931
+ if (timelineTime < item.startTime) {
932
+ return;
933
+ }
934
+ if (!item.timelineStarted) {
935
+ item.timelineStarted = true;
936
+ item.timeline.play();
937
+ const overshoot = timelineTime - item.startTime;
938
+ item.timeline.update(overshoot);
939
+ return;
940
+ }
941
+ item.timeline.update(deltaTime);
942
+ }
943
+ function evaluateItem(item, timelineTime, deltaTime = 0) {
944
+ if (item.type === "animation") {
945
+ evaluateAnimation(item, timelineTime, deltaTime);
946
+ } else if (item.type === "callback") {
947
+ evaluateCallback(item, timelineTime);
948
+ }
949
+ }
950
+
951
+ class Timeline {
952
+ items = [];
953
+ subTimelines = [];
954
+ currentTime = 0;
955
+ isPlaying = false;
956
+ isComplete = false;
957
+ duration;
958
+ loop;
959
+ synced = false;
960
+ autoplay;
961
+ onComplete;
962
+ onPause;
963
+ stateChangeListeners = [];
964
+ constructor(options = {}) {
965
+ this.duration = options.duration || 1000;
966
+ this.loop = options.loop === true;
967
+ this.autoplay = options.autoplay !== false;
968
+ this.onComplete = options.onComplete;
969
+ this.onPause = options.onPause;
970
+ }
971
+ addStateChangeListener(listener) {
972
+ this.stateChangeListeners.push(listener);
973
+ }
974
+ removeStateChangeListener(listener) {
975
+ this.stateChangeListeners = this.stateChangeListeners.filter((l) => l !== listener);
976
+ }
977
+ notifyStateChange() {
978
+ for (const listener of this.stateChangeListeners) {
979
+ listener(this);
980
+ }
981
+ }
982
+ add(target, properties, startTime = 0) {
983
+ const resolvedStartTime = typeof startTime === "string" ? 0 : startTime;
984
+ const animationProperties = {};
985
+ for (const key in properties) {
986
+ if (!["duration", "ease", "onUpdate", "onComplete", "onStart", "onLoop", "loop", "loopDelay", "alternate"].includes(key)) {
987
+ if (typeof properties[key] === "number") {
988
+ animationProperties[key] = properties[key];
989
+ }
990
+ }
991
+ }
992
+ this.items.push({
993
+ type: "animation",
994
+ startTime: resolvedStartTime,
995
+ target: Array.isArray(target) ? target : [target],
996
+ properties: animationProperties,
997
+ initialValues: [],
998
+ duration: properties.duration !== undefined ? properties.duration : 1000,
999
+ ease: properties.ease || "linear",
1000
+ loop: properties.loop,
1001
+ loopDelay: properties.loopDelay || 0,
1002
+ alternate: properties.alternate || false,
1003
+ onUpdate: properties.onUpdate,
1004
+ onComplete: properties.onComplete,
1005
+ onStart: properties.onStart,
1006
+ onLoop: properties.onLoop,
1007
+ completed: false,
1008
+ started: false,
1009
+ currentLoop: 0,
1010
+ once: properties.once ?? false
1011
+ });
1012
+ return this;
1013
+ }
1014
+ once(target, properties) {
1015
+ this.add(target, {
1016
+ ...properties,
1017
+ once: true
1018
+ }, this.currentTime);
1019
+ return this;
1020
+ }
1021
+ call(callback, startTime = 0) {
1022
+ const resolvedStartTime = typeof startTime === "string" ? 0 : startTime;
1023
+ this.items.push({
1024
+ type: "callback",
1025
+ startTime: resolvedStartTime,
1026
+ callback,
1027
+ executed: false
1028
+ });
1029
+ return this;
1030
+ }
1031
+ sync(timeline, startTime = 0) {
1032
+ if (timeline.synced) {
1033
+ throw new Error("Timeline already synced");
1034
+ }
1035
+ this.subTimelines.push({
1036
+ type: "timeline",
1037
+ startTime,
1038
+ timeline
1039
+ });
1040
+ timeline.synced = true;
1041
+ return this;
1042
+ }
1043
+ play() {
1044
+ if (this.isComplete) {
1045
+ return this.restart();
1046
+ }
1047
+ this.subTimelines.forEach((subTimeline) => {
1048
+ if (subTimeline.timelineStarted) {
1049
+ subTimeline.timeline.play();
1050
+ }
1051
+ });
1052
+ this.isPlaying = true;
1053
+ this.notifyStateChange();
1054
+ return this;
1055
+ }
1056
+ pause() {
1057
+ this.subTimelines.forEach((subTimeline) => {
1058
+ subTimeline.timeline.pause();
1059
+ });
1060
+ this.isPlaying = false;
1061
+ if (this.onPause) {
1062
+ this.onPause();
1063
+ }
1064
+ this.notifyStateChange();
1065
+ return this;
1066
+ }
1067
+ resetItems() {
1068
+ this.items.forEach((item) => {
1069
+ if (item.type === "callback") {
1070
+ item.executed = false;
1071
+ } else if (item.type === "animation") {
1072
+ item.completed = false;
1073
+ item.started = false;
1074
+ item.currentLoop = 0;
1075
+ }
1076
+ });
1077
+ this.subTimelines.forEach((subTimeline) => {
1078
+ subTimeline.timelineStarted = false;
1079
+ if (subTimeline.timeline) {
1080
+ subTimeline.timeline.restart();
1081
+ subTimeline.timeline.pause();
1082
+ }
1083
+ });
1084
+ }
1085
+ restart() {
1086
+ this.isComplete = false;
1087
+ this.currentTime = 0;
1088
+ this.isPlaying = true;
1089
+ this.resetItems();
1090
+ this.notifyStateChange();
1091
+ return this;
1092
+ }
1093
+ update(deltaTime) {
1094
+ for (const subTimeline of this.subTimelines) {
1095
+ evaluateTimelineSync(subTimeline, this.currentTime + deltaTime, deltaTime);
1096
+ }
1097
+ if (!this.isPlaying)
1098
+ return;
1099
+ this.currentTime += deltaTime;
1100
+ for (const item of this.items) {
1101
+ evaluateItem(item, this.currentTime, deltaTime);
1102
+ }
1103
+ for (let i = this.items.length - 1;i >= 0; i--) {
1104
+ const item = this.items[i];
1105
+ if (item.type === "animation" && item.once && item.completed) {
1106
+ this.items.splice(i, 1);
1107
+ }
1108
+ }
1109
+ if (this.loop && this.currentTime >= this.duration) {
1110
+ const overshoot = this.currentTime % this.duration;
1111
+ this.resetItems();
1112
+ this.currentTime = 0;
1113
+ if (overshoot > 0) {
1114
+ this.update(overshoot);
1115
+ }
1116
+ } else if (!this.loop && this.currentTime >= this.duration) {
1117
+ this.currentTime = this.duration;
1118
+ this.isPlaying = false;
1119
+ this.isComplete = true;
1120
+ if (this.onComplete) {
1121
+ this.onComplete();
1122
+ }
1123
+ this.notifyStateChange();
1124
+ }
1125
+ }
1126
+ }
1127
+
1128
+ class TimelineEngine {
1129
+ timelines = new Set;
1130
+ renderer = null;
1131
+ frameCallback = null;
1132
+ isLive = false;
1133
+ defaults = {
1134
+ frameRate: 60
1135
+ };
1136
+ attach(renderer) {
1137
+ if (this.renderer) {
1138
+ this.detach();
1139
+ }
1140
+ this.renderer = renderer;
1141
+ this.frameCallback = async (deltaTime) => {
1142
+ this.update(deltaTime);
1143
+ };
1144
+ renderer.setFrameCallback(this.frameCallback);
1145
+ }
1146
+ detach() {
1147
+ if (this.renderer && this.frameCallback) {
1148
+ this.renderer.removeFrameCallback(this.frameCallback);
1149
+ if (this.isLive) {
1150
+ this.renderer.dropLive();
1151
+ this.isLive = false;
1152
+ }
1153
+ }
1154
+ this.renderer = null;
1155
+ this.frameCallback = null;
1156
+ }
1157
+ updateLiveState() {
1158
+ if (!this.renderer)
1159
+ return;
1160
+ const hasRunningTimelines = Array.from(this.timelines).some((timeline) => !timeline.synced && timeline.isPlaying && !timeline.isComplete);
1161
+ if (hasRunningTimelines && !this.isLive) {
1162
+ this.renderer.requestLive();
1163
+ this.isLive = true;
1164
+ } else if (!hasRunningTimelines && this.isLive) {
1165
+ this.renderer.dropLive();
1166
+ this.isLive = false;
1167
+ }
1168
+ }
1169
+ onTimelineStateChange = (timeline) => {
1170
+ this.updateLiveState();
1171
+ };
1172
+ register(timeline) {
1173
+ if (!this.timelines.has(timeline)) {
1174
+ this.timelines.add(timeline);
1175
+ timeline.addStateChangeListener(this.onTimelineStateChange);
1176
+ this.updateLiveState();
1177
+ }
1178
+ }
1179
+ unregister(timeline) {
1180
+ if (this.timelines.has(timeline)) {
1181
+ this.timelines.delete(timeline);
1182
+ timeline.removeStateChangeListener(this.onTimelineStateChange);
1183
+ this.updateLiveState();
1184
+ }
1185
+ }
1186
+ clear() {
1187
+ for (const timeline of this.timelines) {
1188
+ timeline.removeStateChangeListener(this.onTimelineStateChange);
1189
+ }
1190
+ this.timelines.clear();
1191
+ this.updateLiveState();
1192
+ }
1193
+ update(deltaTime) {
1194
+ for (const timeline of this.timelines) {
1195
+ if (!timeline.synced) {
1196
+ timeline.update(deltaTime);
1197
+ }
1198
+ }
1199
+ }
1200
+ }
1201
+ var engine = new TimelineEngine;
1202
+ function createTimeline(options = {}) {
1203
+ const timeline = new Timeline(options);
1204
+ if (options.autoplay !== false) {
1205
+ timeline.play();
1206
+ }
1207
+ engine.register(timeline);
1208
+ return timeline;
1209
+ }
1210
+ // src/renderables/Box.ts
1211
+ function isGapType(value) {
1212
+ if (value === undefined) {
1213
+ return true;
1214
+ }
1215
+ if (typeof value === "number" && !Number.isNaN(value)) {
1216
+ return true;
1217
+ }
1218
+ return isValidPercentage(value);
1219
+ }
1220
+
1221
+ class BoxRenderable extends Renderable {
1222
+ _backgroundColor;
1223
+ _border;
1224
+ _borderStyle;
1225
+ _borderColor;
1226
+ _focusedBorderColor;
1227
+ _customBorderCharsObj;
1228
+ _customBorderChars;
1229
+ borderSides;
1230
+ shouldFill;
1231
+ _title;
1232
+ _titleAlignment;
1233
+ _defaultOptions = {
1234
+ backgroundColor: "transparent",
1235
+ borderStyle: "single",
1236
+ border: false,
1237
+ borderColor: "#FFFFFF",
1238
+ shouldFill: true,
1239
+ titleAlignment: "left",
1240
+ focusedBorderColor: "#00AAFF"
1241
+ };
1242
+ constructor(ctx, options) {
1243
+ super(ctx, options);
1244
+ this._backgroundColor = parseColor(options.backgroundColor || this._defaultOptions.backgroundColor);
1245
+ this._border = options.border ?? this._defaultOptions.border;
1246
+ if (!options.border && (options.borderStyle || options.borderColor || options.focusedBorderColor || options.customBorderChars)) {
1247
+ this._border = true;
1248
+ }
1249
+ this._borderStyle = options.borderStyle || this._defaultOptions.borderStyle;
1250
+ this._borderColor = parseColor(options.borderColor || this._defaultOptions.borderColor);
1251
+ this._focusedBorderColor = parseColor(options.focusedBorderColor || this._defaultOptions.focusedBorderColor);
1252
+ this._customBorderCharsObj = options.customBorderChars;
1253
+ this._customBorderChars = this._customBorderCharsObj ? borderCharsToArray(this._customBorderCharsObj) : undefined;
1254
+ this.borderSides = getBorderSides(this._border);
1255
+ this.shouldFill = options.shouldFill ?? this._defaultOptions.shouldFill;
1256
+ this._title = options.title;
1257
+ this._titleAlignment = options.titleAlignment || this._defaultOptions.titleAlignment;
1258
+ this.applyYogaBorders();
1259
+ const hasInitialGapProps = options.gap !== undefined || options.rowGap !== undefined || options.columnGap !== undefined;
1260
+ if (hasInitialGapProps) {
1261
+ this.applyYogaGap(options);
1262
+ }
1263
+ }
1264
+ get customBorderChars() {
1265
+ return this._customBorderCharsObj;
1266
+ }
1267
+ set customBorderChars(value) {
1268
+ this._customBorderCharsObj = value;
1269
+ this._customBorderChars = value ? borderCharsToArray(value) : undefined;
1270
+ this.requestRender();
1271
+ }
1272
+ get backgroundColor() {
1273
+ return this._backgroundColor;
1274
+ }
1275
+ set backgroundColor(value) {
1276
+ const newColor = parseColor(value ?? this._defaultOptions.backgroundColor);
1277
+ if (this._backgroundColor !== newColor) {
1278
+ this._backgroundColor = newColor;
1279
+ this.requestRender();
1280
+ }
1281
+ }
1282
+ get border() {
1283
+ return this._border;
1284
+ }
1285
+ set border(value) {
1286
+ if (this._border !== value) {
1287
+ this._border = value;
1288
+ this.borderSides = getBorderSides(value);
1289
+ this.applyYogaBorders();
1290
+ this.requestRender();
1291
+ }
1292
+ }
1293
+ get borderStyle() {
1294
+ return this._borderStyle;
1295
+ }
1296
+ set borderStyle(value) {
1297
+ let _value = value ?? this._defaultOptions.borderStyle;
1298
+ if (this._borderStyle !== _value) {
1299
+ this._borderStyle = _value;
1300
+ this._customBorderChars = undefined;
1301
+ this.requestRender();
1302
+ }
1303
+ }
1304
+ get borderColor() {
1305
+ return this._borderColor;
1306
+ }
1307
+ set borderColor(value) {
1308
+ const newColor = parseColor(value ?? this._defaultOptions.borderColor);
1309
+ if (this._borderColor !== newColor) {
1310
+ this._borderColor = newColor;
1311
+ this.requestRender();
1312
+ }
1313
+ }
1314
+ get focusedBorderColor() {
1315
+ return this._focusedBorderColor;
1316
+ }
1317
+ set focusedBorderColor(value) {
1318
+ const newColor = parseColor(value ?? this._defaultOptions.focusedBorderColor);
1319
+ if (this._focusedBorderColor !== newColor) {
1320
+ this._focusedBorderColor = newColor;
1321
+ if (this._focused) {
1322
+ this.requestRender();
1323
+ }
1324
+ }
1325
+ }
1326
+ get title() {
1327
+ return this._title;
1328
+ }
1329
+ set title(value) {
1330
+ if (this._title !== value) {
1331
+ this._title = value;
1332
+ this.requestRender();
1333
+ }
1334
+ }
1335
+ get titleAlignment() {
1336
+ return this._titleAlignment;
1337
+ }
1338
+ set titleAlignment(value) {
1339
+ if (this._titleAlignment !== value) {
1340
+ this._titleAlignment = value;
1341
+ this.requestRender();
1342
+ }
1343
+ }
1344
+ renderSelf(buffer) {
1345
+ const currentBorderColor = this._focused ? this._focusedBorderColor : this._borderColor;
1346
+ buffer.drawBox({
1347
+ x: this.x,
1348
+ y: this.y,
1349
+ width: this.width,
1350
+ height: this.height,
1351
+ borderStyle: this._borderStyle,
1352
+ customBorderChars: this._customBorderChars,
1353
+ border: this._border,
1354
+ borderColor: currentBorderColor,
1355
+ backgroundColor: this._backgroundColor,
1356
+ shouldFill: this.shouldFill,
1357
+ title: this._title,
1358
+ titleAlignment: this._titleAlignment
1359
+ });
1360
+ }
1361
+ getScissorRect() {
1362
+ const baseRect = super.getScissorRect();
1363
+ if (!this.borderSides.top && !this.borderSides.right && !this.borderSides.bottom && !this.borderSides.left) {
1364
+ return baseRect;
1365
+ }
1366
+ const leftInset = this.borderSides.left ? 1 : 0;
1367
+ const rightInset = this.borderSides.right ? 1 : 0;
1368
+ const topInset = this.borderSides.top ? 1 : 0;
1369
+ const bottomInset = this.borderSides.bottom ? 1 : 0;
1370
+ return {
1371
+ x: baseRect.x + leftInset,
1372
+ y: baseRect.y + topInset,
1373
+ width: Math.max(0, baseRect.width - leftInset - rightInset),
1374
+ height: Math.max(0, baseRect.height - topInset - bottomInset)
1375
+ };
1376
+ }
1377
+ applyYogaBorders() {
1378
+ const node = this.layoutNode.yogaNode;
1379
+ node.setBorder(Edge.Left, this.borderSides.left ? 1 : 0);
1380
+ node.setBorder(Edge.Right, this.borderSides.right ? 1 : 0);
1381
+ node.setBorder(Edge.Top, this.borderSides.top ? 1 : 0);
1382
+ node.setBorder(Edge.Bottom, this.borderSides.bottom ? 1 : 0);
1383
+ this.requestRender();
1384
+ }
1385
+ applyYogaGap(options) {
1386
+ const node = this.layoutNode.yogaNode;
1387
+ if (isGapType(options.gap)) {
1388
+ node.setGap(Gutter.All, options.gap);
1389
+ }
1390
+ if (isGapType(options.rowGap)) {
1391
+ node.setGap(Gutter.Row, options.rowGap);
1392
+ }
1393
+ if (isGapType(options.columnGap)) {
1394
+ node.setGap(Gutter.Column, options.columnGap);
1395
+ }
1396
+ }
1397
+ set gap(gap) {
1398
+ if (isGapType(gap)) {
1399
+ this.layoutNode.yogaNode.setGap(Gutter.All, gap);
1400
+ this.requestRender();
1401
+ }
1402
+ }
1403
+ set rowGap(rowGap) {
1404
+ if (isGapType(rowGap)) {
1405
+ this.layoutNode.yogaNode.setGap(Gutter.Row, rowGap);
1406
+ this.requestRender();
1407
+ }
1408
+ }
1409
+ set columnGap(columnGap) {
1410
+ if (isGapType(columnGap)) {
1411
+ this.layoutNode.yogaNode.setGap(Gutter.Column, columnGap);
1412
+ this.requestRender();
1413
+ }
1414
+ }
1415
+ }
1416
+ // src/renderables/FrameBuffer.ts
1417
+ class FrameBufferRenderable extends Renderable {
1418
+ frameBuffer;
1419
+ respectAlpha;
1420
+ constructor(ctx, options) {
1421
+ super(ctx, options);
1422
+ this.respectAlpha = options.respectAlpha || false;
1423
+ this.frameBuffer = OptimizedBuffer.create(options.width, options.height, this._ctx.widthMethod, {
1424
+ respectAlpha: this.respectAlpha,
1425
+ id: options.id || `framebufferrenderable-${this.id}`
1426
+ });
1427
+ }
1428
+ onResize(width, height) {
1429
+ if (width <= 0 || height <= 0) {
1430
+ throw new Error(`Invalid resize dimensions for FrameBufferRenderable ${this.id}: ${width}x${height}`);
1431
+ }
1432
+ this.frameBuffer.resize(width, height);
1433
+ super.onResize(width, height);
1434
+ this.requestRender();
1435
+ }
1436
+ renderSelf(buffer) {
1437
+ if (!this.visible)
1438
+ return;
1439
+ buffer.drawFrameBuffer(this.x, this.y, this.frameBuffer);
1440
+ }
1441
+ destroySelf() {
1442
+ this.frameBuffer?.destroy();
1443
+ super.destroySelf();
1444
+ }
1445
+ }
1446
+ // src/renderables/Text.ts
1447
+ class TextRenderable extends Renderable {
1448
+ selectable = true;
1449
+ _text;
1450
+ _defaultFg;
1451
+ _defaultBg;
1452
+ _defaultAttributes;
1453
+ _selectionBg;
1454
+ _selectionFg;
1455
+ lastLocalSelection = null;
1456
+ textBuffer;
1457
+ _lineInfo = { lineStarts: [], lineWidths: [] };
1458
+ _defaultOptions = {
1459
+ content: "",
1460
+ fg: RGBA.fromValues(1, 1, 1, 1),
1461
+ bg: RGBA.fromValues(0, 0, 0, 0),
1462
+ selectionBg: undefined,
1463
+ selectionFg: undefined,
1464
+ selectable: true,
1465
+ attributes: 0
1466
+ };
1467
+ constructor(ctx, options) {
1468
+ super(ctx, options);
1469
+ const content = options.content ?? this._defaultOptions.content;
1470
+ const styledText = typeof content === "string" ? stringToStyledText(content) : content;
1471
+ this._text = styledText;
1472
+ this._defaultFg = parseColor(options.fg ?? this._defaultOptions.fg);
1473
+ this._defaultBg = parseColor(options.bg ?? this._defaultOptions.bg);
1474
+ this._defaultAttributes = options.attributes ?? this._defaultOptions.attributes;
1475
+ this._selectionBg = options.selectionBg ? parseColor(options.selectionBg) : this._defaultOptions.selectionBg;
1476
+ this._selectionFg = options.selectionFg ? parseColor(options.selectionFg) : this._defaultOptions.selectionFg;
1477
+ this.selectable = options.selectable ?? this._defaultOptions.selectable;
1478
+ this.textBuffer = TextBuffer.create(64, this._ctx.widthMethod);
1479
+ this.textBuffer.setDefaultFg(this._defaultFg);
1480
+ this.textBuffer.setDefaultBg(this._defaultBg);
1481
+ this.textBuffer.setDefaultAttributes(this._defaultAttributes);
1482
+ this.setupMeasureFunc();
1483
+ this.updateTextBuffer(styledText);
1484
+ this._text.mount(this);
1485
+ this.updateTextInfo();
1486
+ }
1487
+ updateTextBuffer(styledText) {
1488
+ this.textBuffer.setStyledText(styledText);
1489
+ this.clearChunks(styledText);
1490
+ }
1491
+ clearChunks(styledText) {
1492
+ styledText.chunks.forEach((chunk) => {
1493
+ chunk.text = undefined;
1494
+ chunk.plainText = undefined;
1495
+ });
1496
+ }
1497
+ get content() {
1498
+ return this._text;
1499
+ }
1500
+ get plainText() {
1501
+ return this.textBuffer.getPlainText();
1502
+ }
1503
+ get textLength() {
1504
+ return this.textBuffer.length;
1505
+ }
1506
+ get chunks() {
1507
+ return this._text.chunks;
1508
+ }
1509
+ set content(value) {
1510
+ const styledText = typeof value === "string" ? stringToStyledText(value) : value;
1511
+ if (this._text !== styledText) {
1512
+ this._text = styledText;
1513
+ styledText.mount(this);
1514
+ this.updateTextBuffer(styledText);
1515
+ this.updateTextInfo();
1516
+ }
1517
+ }
1518
+ get fg() {
1519
+ return this._defaultFg;
1520
+ }
1521
+ set fg(value) {
1522
+ const newColor = parseColor(value ?? this._defaultOptions.fg);
1523
+ if (this._defaultFg !== newColor) {
1524
+ this._defaultFg = newColor;
1525
+ this.textBuffer.setDefaultFg(this._defaultFg);
1526
+ this.requestRender();
1527
+ }
1528
+ }
1529
+ get selectionBg() {
1530
+ return this._selectionBg;
1531
+ }
1532
+ set selectionBg(value) {
1533
+ const newColor = value ? parseColor(value) : this._defaultOptions.selectionBg;
1534
+ if (this._selectionBg !== newColor) {
1535
+ this._selectionBg = newColor;
1536
+ if (this.lastLocalSelection) {
1537
+ this.updateLocalSelection(this.lastLocalSelection);
1538
+ }
1539
+ this.requestRender();
1540
+ }
1541
+ }
1542
+ get selectionFg() {
1543
+ return this._selectionFg;
1544
+ }
1545
+ set selectionFg(value) {
1546
+ const newColor = value ? parseColor(value) : this._defaultOptions.selectionFg;
1547
+ if (this._selectionFg !== newColor) {
1548
+ this._selectionFg = newColor;
1549
+ if (this.lastLocalSelection) {
1550
+ this.updateLocalSelection(this.lastLocalSelection);
1551
+ }
1552
+ this.requestRender();
1553
+ }
1554
+ }
1555
+ get bg() {
1556
+ return this._defaultBg;
1557
+ }
1558
+ set bg(value) {
1559
+ const newColor = parseColor(value ?? this._defaultOptions.bg);
1560
+ if (this._defaultBg !== newColor) {
1561
+ this._defaultBg = newColor;
1562
+ this.textBuffer.setDefaultBg(this._defaultBg);
1563
+ this.requestRender();
1564
+ }
1565
+ }
1566
+ get attributes() {
1567
+ return this._defaultAttributes;
1568
+ }
1569
+ set attributes(value) {
1570
+ if (this._defaultAttributes !== value) {
1571
+ this._defaultAttributes = value;
1572
+ this.textBuffer.setDefaultAttributes(this._defaultAttributes);
1573
+ this.requestRender();
1574
+ }
1575
+ }
1576
+ onResize(width, height) {
1577
+ if (this.lastLocalSelection) {
1578
+ const changed = this.updateLocalSelection(this.lastLocalSelection);
1579
+ if (changed) {
1580
+ this.requestRender();
1581
+ }
1582
+ }
1583
+ }
1584
+ updateLocalSelection(localSelection) {
1585
+ if (!localSelection?.isActive) {
1586
+ this.textBuffer.resetLocalSelection();
1587
+ return true;
1588
+ }
1589
+ return this.textBuffer.setLocalSelection(localSelection.anchorX, localSelection.anchorY, localSelection.focusX, localSelection.focusY, this._selectionBg, this._selectionFg);
1590
+ }
1591
+ updateTextInfo() {
1592
+ const lineInfo = this.textBuffer.lineInfo;
1593
+ this._lineInfo.lineStarts = lineInfo.lineStarts;
1594
+ this._lineInfo.lineWidths = lineInfo.lineWidths;
1595
+ if (this.lastLocalSelection) {
1596
+ const changed = this.updateLocalSelection(this.lastLocalSelection);
1597
+ if (changed) {
1598
+ this.requestRender();
1599
+ }
1600
+ }
1601
+ this.layoutNode.yogaNode.markDirty();
1602
+ this.requestRender();
1603
+ }
1604
+ setupMeasureFunc() {
1605
+ const measureFunc = (width, widthMode, height, heightMode) => {
1606
+ const maxLineWidth = Math.max(...this._lineInfo.lineWidths, 0);
1607
+ const numLines = this._lineInfo.lineStarts.length || 1;
1608
+ let measuredWidth = maxLineWidth;
1609
+ let measuredHeight = numLines;
1610
+ if (widthMode === MeasureMode.Exactly) {
1611
+ measuredWidth = width;
1612
+ } else if (widthMode === MeasureMode.AtMost) {
1613
+ measuredWidth = Math.min(maxLineWidth, width);
1614
+ }
1615
+ if (heightMode === MeasureMode.Exactly) {
1616
+ measuredHeight = height;
1617
+ } else if (heightMode === MeasureMode.AtMost) {
1618
+ measuredHeight = Math.min(numLines, height);
1619
+ }
1620
+ return {
1621
+ width: Math.max(1, measuredWidth),
1622
+ height: Math.max(1, measuredHeight)
1623
+ };
1624
+ };
1625
+ this.layoutNode.yogaNode.setMeasureFunc(measureFunc);
1626
+ }
1627
+ insertChunk(chunk, index) {
1628
+ this.textBuffer.insertEncodedChunkGroup(index ?? this.textBuffer.chunkGroupCount, chunk.text, chunk.fg, chunk.bg, chunk.attributes);
1629
+ this.updateTextInfo();
1630
+ this.clearChunks(this._text);
1631
+ }
1632
+ removeChunk(chunk) {
1633
+ const index = this._text.chunks.indexOf(chunk);
1634
+ if (index === -1)
1635
+ return;
1636
+ this.textBuffer.removeChunkGroup(index);
1637
+ this.updateTextInfo();
1638
+ this.clearChunks(this._text);
1639
+ }
1640
+ replaceChunk(chunk, oldChunk) {
1641
+ const index = this._text.chunks.indexOf(oldChunk);
1642
+ if (index === -1)
1643
+ return;
1644
+ this.textBuffer.replaceEncodedChunkGroup(index, chunk.text, chunk.fg, chunk.bg, chunk.attributes);
1645
+ this.updateTextInfo();
1646
+ this.clearChunks(this._text);
1647
+ }
1648
+ shouldStartSelection(x, y) {
1649
+ if (!this.selectable)
1650
+ return false;
1651
+ const localX = x - this.x;
1652
+ const localY = y - this.y;
1653
+ return localX >= 0 && localX < this.width && localY >= 0 && localY < this.height;
1654
+ }
1655
+ onSelectionChanged(selection) {
1656
+ const localSelection = convertGlobalToLocalSelection(selection, this.x, this.y);
1657
+ this.lastLocalSelection = localSelection;
1658
+ const changed = this.updateLocalSelection(localSelection);
1659
+ if (changed) {
1660
+ this.requestRender();
1661
+ }
1662
+ return this.hasSelection();
1663
+ }
1664
+ getSelectedText() {
1665
+ return this.textBuffer.getSelectedText();
1666
+ }
1667
+ hasSelection() {
1668
+ return this.textBuffer.hasSelection();
1669
+ }
1670
+ getSelection() {
1671
+ return this.textBuffer.getSelection();
1672
+ }
1673
+ renderSelf(buffer) {
1674
+ if (this.textBuffer.ptr) {
1675
+ const clipRect = {
1676
+ x: this.x,
1677
+ y: this.y,
1678
+ width: this.width,
1679
+ height: this.height
1680
+ };
1681
+ buffer.drawTextBuffer(this.textBuffer, this.x, this.y, clipRect);
1682
+ }
1683
+ }
1684
+ destroy() {
1685
+ this.textBuffer.destroy();
1686
+ super.destroy();
1687
+ }
1688
+ }
1689
+ // src/renderables/ASCIIFont.ts
1690
+ class ASCIIFontRenderable extends FrameBufferRenderable {
1691
+ selectable = true;
1692
+ _text;
1693
+ _font;
1694
+ _fg;
1695
+ _bg;
1696
+ _selectionBg;
1697
+ _selectionFg;
1698
+ lastLocalSelection = null;
1699
+ selectionHelper;
1700
+ constructor(ctx, options) {
1701
+ const font = options.font || "tiny";
1702
+ const text = options.text || "";
1703
+ const measurements = measureText({ text, font });
1704
+ super(ctx, {
1705
+ ...options,
1706
+ width: measurements.width || 1,
1707
+ height: measurements.height || 1,
1708
+ respectAlpha: true
1709
+ });
1710
+ this._text = text;
1711
+ this._font = font;
1712
+ this._fg = Array.isArray(options.fg) ? options.fg : [options.fg || RGBA.fromInts(255, 255, 255, 255)];
1713
+ this._bg = options.bg || RGBA.fromValues(0, 0, 0, 0);
1714
+ this._selectionBg = options.selectionBg ? parseColor(options.selectionBg) : undefined;
1715
+ this._selectionFg = options.selectionFg ? parseColor(options.selectionFg) : undefined;
1716
+ this.selectable = options.selectable ?? true;
1717
+ this.selectionHelper = new ASCIIFontSelectionHelper(() => this._text, () => this._font);
1718
+ this.renderFontToBuffer();
1719
+ }
1720
+ get text() {
1721
+ return this._text;
1722
+ }
1723
+ set text(value) {
1724
+ this._text = value;
1725
+ this.updateDimensions();
1726
+ if (this.lastLocalSelection) {
1727
+ this.selectionHelper.onLocalSelectionChanged(this.lastLocalSelection, this.width, this.height);
1728
+ }
1729
+ this.renderFontToBuffer();
1730
+ this.requestRender();
1731
+ }
1732
+ get font() {
1733
+ return this._font;
1734
+ }
1735
+ set font(value) {
1736
+ this._font = value;
1737
+ this.updateDimensions();
1738
+ if (this.lastLocalSelection) {
1739
+ this.selectionHelper.onLocalSelectionChanged(this.lastLocalSelection, this.width, this.height);
1740
+ }
1741
+ this.renderFontToBuffer();
1742
+ this.requestRender();
1743
+ }
1744
+ get fg() {
1745
+ return this._fg;
1746
+ }
1747
+ set fg(value) {
1748
+ if (Array.isArray(value)) {
1749
+ this._fg = value.map((color) => typeof color === "string" ? parseColor(color) : color);
1750
+ } else {
1751
+ this._fg = [typeof value === "string" ? parseColor(value) : value];
1752
+ }
1753
+ this.renderFontToBuffer();
1754
+ this.requestRender();
1755
+ }
1756
+ get bg() {
1757
+ return this._bg;
1758
+ }
1759
+ set bg(value) {
1760
+ this._bg = typeof value === "string" ? parseColor(value) : value;
1761
+ this.renderFontToBuffer();
1762
+ this.requestRender();
1763
+ }
1764
+ updateDimensions() {
1765
+ const measurements = measureText({ text: this._text, font: this._font });
1766
+ this.width = measurements.width;
1767
+ this.height = measurements.height;
1768
+ }
1769
+ shouldStartSelection(x, y) {
1770
+ const localX = x - this.x;
1771
+ const localY = y - this.y;
1772
+ return this.selectionHelper.shouldStartSelection(localX, localY, this.width, this.height);
1773
+ }
1774
+ onSelectionChanged(selection) {
1775
+ const localSelection = convertGlobalToLocalSelection(selection, this.x, this.y);
1776
+ this.lastLocalSelection = localSelection;
1777
+ const changed = this.selectionHelper.onLocalSelectionChanged(localSelection, this.width, this.height);
1778
+ if (changed) {
1779
+ this.renderFontToBuffer();
1780
+ this.requestRender();
1781
+ }
1782
+ return changed;
1783
+ }
1784
+ getSelectedText() {
1785
+ const selection = this.selectionHelper.getSelection();
1786
+ if (!selection)
1787
+ return "";
1788
+ return this._text.slice(selection.start, selection.end);
1789
+ }
1790
+ hasSelection() {
1791
+ return this.selectionHelper.hasSelection();
1792
+ }
1793
+ onResize(width, height) {
1794
+ super.onResize(width, height);
1795
+ this.renderFontToBuffer();
1796
+ }
1797
+ renderFontToBuffer() {
1798
+ this.frameBuffer.clear(this._bg);
1799
+ renderFontToFrameBuffer(this.frameBuffer, {
1800
+ text: this._text,
1801
+ x: 0,
1802
+ y: 0,
1803
+ fg: this._fg,
1804
+ bg: this._bg,
1805
+ font: this._font
1806
+ });
1807
+ const selection = this.selectionHelper.getSelection();
1808
+ if (selection && (this._selectionBg || this._selectionFg)) {
1809
+ this.renderSelectionHighlight(selection);
1810
+ }
1811
+ }
1812
+ renderSelectionHighlight(selection) {
1813
+ if (!this._selectionBg && !this._selectionFg)
1814
+ return;
1815
+ const selectedText = this._text.slice(selection.start, selection.end);
1816
+ if (!selectedText)
1817
+ return;
1818
+ const positions = getCharacterPositions(this._text, this._font);
1819
+ const startX = positions[selection.start] || 0;
1820
+ const endX = selection.end < positions.length ? positions[selection.end] : measureText({ text: this._text, font: this._font }).width;
1821
+ if (this._selectionBg) {
1822
+ this.frameBuffer.fillRect(startX, 0, endX - startX, this.height, this._selectionBg);
1823
+ }
1824
+ if (this._selectionFg || this._selectionBg) {
1825
+ renderFontToFrameBuffer(this.frameBuffer, {
1826
+ text: selectedText,
1827
+ x: startX,
1828
+ y: 0,
1829
+ fg: this._selectionFg ? [this._selectionFg] : this._fg,
1830
+ bg: this._selectionBg || this._bg,
1831
+ font: this._font
1832
+ });
1833
+ }
1834
+ }
1835
+ }
1836
+ // src/renderables/Input.ts
1837
+ var InputRenderableEvents;
1838
+ ((InputRenderableEvents2) => {
1839
+ InputRenderableEvents2["INPUT"] = "input";
1840
+ InputRenderableEvents2["CHANGE"] = "change";
1841
+ InputRenderableEvents2["ENTER"] = "enter";
1842
+ })(InputRenderableEvents ||= {});
1843
+
1844
+ class InputRenderable extends Renderable {
1845
+ focusable = true;
1846
+ _value = "";
1847
+ _cursorPosition = 0;
1848
+ _placeholder;
1849
+ _backgroundColor;
1850
+ _textColor;
1851
+ _focusedBackgroundColor;
1852
+ _focusedTextColor;
1853
+ _placeholderColor;
1854
+ _cursorColor;
1855
+ _maxLength;
1856
+ _lastCommittedValue = "";
1857
+ _defaultOptions = {
1858
+ backgroundColor: "transparent",
1859
+ textColor: "#FFFFFF",
1860
+ focusedBackgroundColor: "#1a1a1a",
1861
+ focusedTextColor: "#FFFFFF",
1862
+ placeholder: "",
1863
+ placeholderColor: "#666666",
1864
+ cursorColor: "#FFFFFF",
1865
+ maxLength: 1000,
1866
+ value: ""
1867
+ };
1868
+ constructor(ctx, options) {
1869
+ super(ctx, { ...options, buffered: true });
1870
+ this._backgroundColor = parseColor(options.backgroundColor || this._defaultOptions.backgroundColor);
1871
+ this._textColor = parseColor(options.textColor || this._defaultOptions.textColor);
1872
+ this._focusedBackgroundColor = parseColor(options.focusedBackgroundColor || options.backgroundColor || this._defaultOptions.focusedBackgroundColor);
1873
+ this._focusedTextColor = parseColor(options.focusedTextColor || options.textColor || this._defaultOptions.focusedTextColor);
1874
+ this._placeholder = options.placeholder || this._defaultOptions.placeholder;
1875
+ this._value = options.value || this._defaultOptions.value;
1876
+ this._lastCommittedValue = this._value;
1877
+ this._cursorPosition = this._value.length;
1878
+ this._maxLength = options.maxLength || this._defaultOptions.maxLength;
1879
+ this._placeholderColor = parseColor(options.placeholderColor || this._defaultOptions.placeholderColor);
1880
+ this._cursorColor = parseColor(options.cursorColor || this._defaultOptions.cursorColor);
1881
+ }
1882
+ updateCursorPosition() {
1883
+ if (!this._focused)
1884
+ return;
1885
+ const contentX = 0;
1886
+ const contentY = 0;
1887
+ const contentWidth = this.width;
1888
+ const maxVisibleChars = contentWidth - 1;
1889
+ let displayStartIndex = 0;
1890
+ if (this._cursorPosition >= maxVisibleChars) {
1891
+ displayStartIndex = this._cursorPosition - maxVisibleChars + 1;
1892
+ }
1893
+ const cursorDisplayX = this._cursorPosition - displayStartIndex;
1894
+ if (cursorDisplayX >= 0 && cursorDisplayX < contentWidth) {
1895
+ const absoluteCursorX = this.x + contentX + cursorDisplayX + 1;
1896
+ const absoluteCursorY = this.y + contentY + 1;
1897
+ this._ctx.setCursorPosition(absoluteCursorX, absoluteCursorY, true);
1898
+ this._ctx.setCursorColor(this._cursorColor);
1899
+ }
1900
+ }
1901
+ focus() {
1902
+ super.focus();
1903
+ this._ctx.setCursorStyle("block", true);
1904
+ this._ctx.setCursorColor(this._cursorColor);
1905
+ this.updateCursorPosition();
1906
+ }
1907
+ blur() {
1908
+ super.blur();
1909
+ this._ctx.setCursorPosition(0, 0, false);
1910
+ if (this._value !== this._lastCommittedValue) {
1911
+ this._lastCommittedValue = this._value;
1912
+ this.emit("change" /* CHANGE */, this._value);
1913
+ }
1914
+ }
1915
+ renderSelf(buffer, deltaTime) {
1916
+ if (!this.visible || !this.frameBuffer)
1917
+ return;
1918
+ if (this.isDirty) {
1919
+ this.refreshFrameBuffer();
1920
+ }
1921
+ }
1922
+ refreshFrameBuffer() {
1923
+ if (!this.frameBuffer)
1924
+ return;
1925
+ const bgColor = this._focused ? this._focusedBackgroundColor : this._backgroundColor;
1926
+ this.frameBuffer.clear(bgColor);
1927
+ const contentX = 0;
1928
+ const contentY = 0;
1929
+ const contentWidth = this.width;
1930
+ const contentHeight = this.height;
1931
+ const displayText = this._value || this._placeholder;
1932
+ const isPlaceholder = !this._value && this._placeholder;
1933
+ const baseTextColor = this._focused ? this._focusedTextColor : this._textColor;
1934
+ const textColor = isPlaceholder ? this._placeholderColor : baseTextColor;
1935
+ const maxVisibleChars = contentWidth - 1;
1936
+ let displayStartIndex = 0;
1937
+ if (this._cursorPosition >= maxVisibleChars) {
1938
+ displayStartIndex = this._cursorPosition - maxVisibleChars + 1;
1939
+ }
1940
+ const visibleText = displayText.substring(displayStartIndex, displayStartIndex + maxVisibleChars);
1941
+ if (visibleText) {
1942
+ this.frameBuffer.drawText(visibleText, contentX, contentY, textColor);
1943
+ }
1944
+ if (this._focused) {
1945
+ this.updateCursorPosition();
1946
+ }
1947
+ }
1948
+ get value() {
1949
+ return this._value;
1950
+ }
1951
+ set value(value) {
1952
+ const newValue = value.substring(0, this._maxLength);
1953
+ if (this._value !== newValue) {
1954
+ this._value = newValue;
1955
+ this._cursorPosition = Math.min(this._cursorPosition, this._value.length);
1956
+ this.requestRender();
1957
+ this.updateCursorPosition();
1958
+ this.emit("input" /* INPUT */, this._value);
1959
+ }
1960
+ }
1961
+ set placeholder(placeholder) {
1962
+ if (this._placeholder !== placeholder) {
1963
+ this._placeholder = placeholder;
1964
+ this.requestRender();
1965
+ }
1966
+ }
1967
+ get cursorPosition() {
1968
+ return this._cursorPosition;
1969
+ }
1970
+ set cursorPosition(position) {
1971
+ const newPosition = Math.max(0, Math.min(position, this._value.length));
1972
+ if (this._cursorPosition !== newPosition) {
1973
+ this._cursorPosition = newPosition;
1974
+ this.requestRender();
1975
+ this.updateCursorPosition();
1976
+ }
1977
+ }
1978
+ insertText(text) {
1979
+ if (this._value.length + text.length > this._maxLength) {
1980
+ return;
1981
+ }
1982
+ const beforeCursor = this._value.substring(0, this._cursorPosition);
1983
+ const afterCursor = this._value.substring(this._cursorPosition);
1984
+ this._value = beforeCursor + text + afterCursor;
1985
+ this._cursorPosition += text.length;
1986
+ this.requestRender();
1987
+ this.updateCursorPosition();
1988
+ this.emit("input" /* INPUT */, this._value);
1989
+ }
1990
+ deleteCharacter(direction) {
1991
+ if (direction === "backward" && this._cursorPosition > 0) {
1992
+ const beforeCursor = this._value.substring(0, this._cursorPosition - 1);
1993
+ const afterCursor = this._value.substring(this._cursorPosition);
1994
+ this._value = beforeCursor + afterCursor;
1995
+ this._cursorPosition--;
1996
+ this.requestRender();
1997
+ this.updateCursorPosition();
1998
+ this.emit("input" /* INPUT */, this._value);
1999
+ } else if (direction === "forward" && this._cursorPosition < this._value.length) {
2000
+ const beforeCursor = this._value.substring(0, this._cursorPosition);
2001
+ const afterCursor = this._value.substring(this._cursorPosition + 1);
2002
+ this._value = beforeCursor + afterCursor;
2003
+ this.requestRender();
2004
+ this.updateCursorPosition();
2005
+ this.emit("input" /* INPUT */, this._value);
2006
+ }
2007
+ }
2008
+ handleKeyPress(key) {
2009
+ const keyName = typeof key === "string" ? key : key.name;
2010
+ const keySequence = typeof key === "string" ? key : key.sequence;
2011
+ switch (keyName) {
2012
+ case "left":
2013
+ this.cursorPosition = this._cursorPosition - 1;
2014
+ return true;
2015
+ case "right":
2016
+ this.cursorPosition = this._cursorPosition + 1;
2017
+ return true;
2018
+ case "home":
2019
+ this.cursorPosition = 0;
2020
+ return true;
2021
+ case "end":
2022
+ this.cursorPosition = this._value.length;
2023
+ return true;
2024
+ case "backspace":
2025
+ this.deleteCharacter("backward");
2026
+ return true;
2027
+ case "delete":
2028
+ this.deleteCharacter("forward");
2029
+ return true;
2030
+ case "return":
2031
+ case "enter":
2032
+ if (this._value !== this._lastCommittedValue) {
2033
+ this._lastCommittedValue = this._value;
2034
+ this.emit("change" /* CHANGE */, this._value);
2035
+ }
2036
+ this.emit("enter" /* ENTER */, this._value);
2037
+ return true;
2038
+ default:
2039
+ if (keySequence && keySequence.length === 1 && keySequence.charCodeAt(0) >= 32 && keySequence.charCodeAt(0) <= 126) {
2040
+ this.insertText(keySequence);
2041
+ return true;
2042
+ }
2043
+ break;
2044
+ }
2045
+ return false;
2046
+ }
2047
+ set maxLength(maxLength) {
2048
+ this._maxLength = maxLength;
2049
+ if (this._value.length > maxLength) {
2050
+ this._value = this._value.substring(0, maxLength);
2051
+ this.requestRender();
2052
+ }
2053
+ }
2054
+ set backgroundColor(value) {
2055
+ const newColor = parseColor(value ?? this._defaultOptions.backgroundColor);
2056
+ if (this._backgroundColor !== newColor) {
2057
+ this._backgroundColor = newColor;
2058
+ this.requestRender();
2059
+ }
2060
+ }
2061
+ set textColor(value) {
2062
+ const newColor = parseColor(value ?? this._defaultOptions.textColor);
2063
+ if (this._textColor !== newColor) {
2064
+ this._textColor = newColor;
2065
+ this.requestRender();
2066
+ }
2067
+ }
2068
+ set focusedBackgroundColor(value) {
2069
+ const newColor = parseColor(value ?? this._defaultOptions.focusedBackgroundColor);
2070
+ if (this._focusedBackgroundColor !== newColor) {
2071
+ this._focusedBackgroundColor = newColor;
2072
+ this.requestRender();
2073
+ }
2074
+ }
2075
+ set focusedTextColor(value) {
2076
+ const newColor = parseColor(value ?? this._defaultOptions.focusedTextColor);
2077
+ if (this._focusedTextColor !== newColor) {
2078
+ this._focusedTextColor = newColor;
2079
+ this.requestRender();
2080
+ }
2081
+ }
2082
+ set placeholderColor(value) {
2083
+ const newColor = parseColor(value ?? this._defaultOptions.placeholderColor);
2084
+ if (this._placeholderColor !== newColor) {
2085
+ this._placeholderColor = newColor;
2086
+ this.requestRender();
2087
+ }
2088
+ }
2089
+ set cursorColor(value) {
2090
+ const newColor = parseColor(value ?? this._defaultOptions.cursorColor);
2091
+ if (this._cursorColor !== newColor) {
2092
+ this._cursorColor = newColor;
2093
+ this.requestRender();
2094
+ }
2095
+ }
2096
+ updateFromLayout() {
2097
+ super.updateFromLayout();
2098
+ this.updateCursorPosition();
2099
+ }
2100
+ onResize(width, height) {
2101
+ super.onResize(width, height);
2102
+ this.updateCursorPosition();
2103
+ }
2104
+ onRemove() {
2105
+ if (this._focused) {
2106
+ this._ctx.setCursorPosition(0, 0, false);
2107
+ }
2108
+ }
2109
+ }
2110
+ // src/renderables/Select.ts
2111
+ var SelectRenderableEvents;
2112
+ ((SelectRenderableEvents2) => {
2113
+ SelectRenderableEvents2["SELECTION_CHANGED"] = "selectionChanged";
2114
+ SelectRenderableEvents2["ITEM_SELECTED"] = "itemSelected";
2115
+ })(SelectRenderableEvents ||= {});
2116
+
2117
+ class SelectRenderable extends Renderable {
2118
+ focusable = true;
2119
+ _options = [];
2120
+ selectedIndex = 0;
2121
+ scrollOffset = 0;
2122
+ maxVisibleItems;
2123
+ _backgroundColor;
2124
+ _textColor;
2125
+ _focusedBackgroundColor;
2126
+ _focusedTextColor;
2127
+ _selectedBackgroundColor;
2128
+ _selectedTextColor;
2129
+ _descriptionColor;
2130
+ _selectedDescriptionColor;
2131
+ _showScrollIndicator;
2132
+ _wrapSelection;
2133
+ _showDescription;
2134
+ _font;
2135
+ _itemSpacing;
2136
+ linesPerItem;
2137
+ fontHeight;
2138
+ _fastScrollStep;
2139
+ _defaultOptions = {
2140
+ backgroundColor: "transparent",
2141
+ textColor: "#FFFFFF",
2142
+ focusedBackgroundColor: "#1a1a1a",
2143
+ focusedTextColor: "#FFFFFF",
2144
+ selectedBackgroundColor: "#334455",
2145
+ selectedTextColor: "#FFFF00",
2146
+ descriptionColor: "#888888",
2147
+ selectedDescriptionColor: "#CCCCCC",
2148
+ showScrollIndicator: false,
2149
+ wrapSelection: false,
2150
+ showDescription: true,
2151
+ itemSpacing: 0,
2152
+ fastScrollStep: 5
2153
+ };
2154
+ constructor(ctx, options) {
2155
+ super(ctx, { ...options, buffered: true });
2156
+ this._backgroundColor = parseColor(options.backgroundColor || this._defaultOptions.backgroundColor);
2157
+ this._textColor = parseColor(options.textColor || this._defaultOptions.textColor);
2158
+ this._focusedBackgroundColor = parseColor(options.focusedBackgroundColor || this._defaultOptions.focusedBackgroundColor);
2159
+ this._focusedTextColor = parseColor(options.focusedTextColor || this._defaultOptions.focusedTextColor);
2160
+ this._options = options.options || [];
2161
+ this._showScrollIndicator = options.showScrollIndicator ?? this._defaultOptions.showScrollIndicator;
2162
+ this._wrapSelection = options.wrapSelection ?? this._defaultOptions.wrapSelection;
2163
+ this._showDescription = options.showDescription ?? this._defaultOptions.showDescription;
2164
+ this._font = options.font;
2165
+ this._itemSpacing = options.itemSpacing || this._defaultOptions.itemSpacing;
2166
+ this.fontHeight = this._font ? measureText({ text: "A", font: this._font }).height : 1;
2167
+ this.linesPerItem = this._showDescription ? this._font ? this.fontHeight + 1 : 2 : this._font ? this.fontHeight : 1;
2168
+ this.linesPerItem += this._itemSpacing;
2169
+ this.maxVisibleItems = Math.max(1, Math.floor(this.height / this.linesPerItem));
2170
+ this._selectedBackgroundColor = parseColor(options.selectedBackgroundColor || this._defaultOptions.selectedBackgroundColor);
2171
+ this._selectedTextColor = parseColor(options.selectedTextColor || this._defaultOptions.selectedTextColor);
2172
+ this._descriptionColor = parseColor(options.descriptionColor || this._defaultOptions.descriptionColor);
2173
+ this._selectedDescriptionColor = parseColor(options.selectedDescriptionColor || this._defaultOptions.selectedDescriptionColor);
2174
+ this._fastScrollStep = options.fastScrollStep || this._defaultOptions.fastScrollStep;
2175
+ this.requestRender();
2176
+ }
2177
+ renderSelf(buffer, deltaTime) {
2178
+ if (!this.visible || !this.frameBuffer)
2179
+ return;
2180
+ if (this.isDirty) {
2181
+ this.refreshFrameBuffer();
2182
+ }
2183
+ }
2184
+ refreshFrameBuffer() {
2185
+ if (!this.frameBuffer || this._options.length === 0)
2186
+ return;
2187
+ const bgColor = this._focused ? this._focusedBackgroundColor : this._backgroundColor;
2188
+ this.frameBuffer.clear(bgColor);
2189
+ const contentX = 0;
2190
+ const contentY = 0;
2191
+ const contentWidth = this.width;
2192
+ const contentHeight = this.height;
2193
+ const visibleOptions = this._options.slice(this.scrollOffset, this.scrollOffset + this.maxVisibleItems);
2194
+ for (let i = 0;i < visibleOptions.length; i++) {
2195
+ const actualIndex = this.scrollOffset + i;
2196
+ const option = visibleOptions[i];
2197
+ const isSelected = actualIndex === this.selectedIndex;
2198
+ const itemY = contentY + i * this.linesPerItem;
2199
+ if (itemY + this.linesPerItem - 1 >= contentY + contentHeight)
2200
+ break;
2201
+ if (isSelected) {
2202
+ const contentHeight2 = this.linesPerItem - this._itemSpacing;
2203
+ this.frameBuffer.fillRect(contentX, itemY, contentWidth, contentHeight2, this._selectedBackgroundColor);
2204
+ }
2205
+ const nameContent = `${isSelected ? "\u25B6 " : " "}${option.name}`;
2206
+ const baseTextColor = this._focused ? this._focusedTextColor : this._textColor;
2207
+ const nameColor = isSelected ? this._selectedTextColor : baseTextColor;
2208
+ let descX = contentX + 3;
2209
+ if (this._font) {
2210
+ const indicator = isSelected ? "\u25B6 " : " ";
2211
+ this.frameBuffer.drawText(indicator, contentX + 1, itemY, nameColor);
2212
+ const indicatorWidth = 2;
2213
+ renderFontToFrameBuffer(this.frameBuffer, {
2214
+ text: option.name,
2215
+ x: contentX + 1 + indicatorWidth,
2216
+ y: itemY,
2217
+ fg: nameColor,
2218
+ bg: isSelected ? this._selectedBackgroundColor : bgColor,
2219
+ font: this._font
2220
+ });
2221
+ descX = contentX + 1 + indicatorWidth;
2222
+ } else {
2223
+ this.frameBuffer.drawText(nameContent, contentX + 1, itemY, nameColor);
2224
+ }
2225
+ if (this._showDescription && itemY + this.fontHeight < contentY + contentHeight) {
2226
+ const descColor = isSelected ? this._selectedDescriptionColor : this._descriptionColor;
2227
+ const descBg = this._focused ? this._focusedBackgroundColor : this._backgroundColor;
2228
+ this.frameBuffer.drawText(option.description, descX, itemY + this.fontHeight, descColor);
2229
+ }
2230
+ }
2231
+ if (this._showScrollIndicator && this._options.length > this.maxVisibleItems) {
2232
+ this.renderScrollIndicatorToFrameBuffer(contentX, contentY, contentWidth, contentHeight);
2233
+ }
2234
+ }
2235
+ renderScrollIndicatorToFrameBuffer(contentX, contentY, contentWidth, contentHeight) {
2236
+ if (!this.frameBuffer)
2237
+ return;
2238
+ const scrollPercent = this.selectedIndex / Math.max(1, this._options.length - 1);
2239
+ const indicatorHeight = Math.max(1, contentHeight - 2);
2240
+ const indicatorY = contentY + 1 + Math.floor(scrollPercent * indicatorHeight);
2241
+ const indicatorX = contentX + contentWidth - 1;
2242
+ this.frameBuffer.drawText("\u2588", indicatorX, indicatorY, parseColor("#666666"));
2243
+ }
2244
+ get options() {
2245
+ return this._options;
2246
+ }
2247
+ set options(options) {
2248
+ this._options = options;
2249
+ this.selectedIndex = Math.min(this.selectedIndex, Math.max(0, options.length - 1));
2250
+ this.updateScrollOffset();
2251
+ this.requestRender();
2252
+ }
2253
+ getSelectedOption() {
2254
+ return this._options[this.selectedIndex] || null;
2255
+ }
2256
+ getSelectedIndex() {
2257
+ return this.selectedIndex;
2258
+ }
2259
+ moveUp(steps = 1) {
2260
+ const newIndex = this.selectedIndex - steps;
2261
+ if (newIndex >= 0) {
2262
+ this.selectedIndex = newIndex;
2263
+ } else if (this._wrapSelection && this._options.length > 0) {
2264
+ this.selectedIndex = this._options.length - 1;
2265
+ } else {
2266
+ this.selectedIndex = 0;
2267
+ }
2268
+ this.updateScrollOffset();
2269
+ this.requestRender();
2270
+ this.emit("selectionChanged" /* SELECTION_CHANGED */, this.selectedIndex, this.getSelectedOption());
2271
+ }
2272
+ moveDown(steps = 1) {
2273
+ const newIndex = this.selectedIndex + steps;
2274
+ if (newIndex < this._options.length) {
2275
+ this.selectedIndex = newIndex;
2276
+ } else if (this._wrapSelection && this._options.length > 0) {
2277
+ this.selectedIndex = 0;
2278
+ } else {
2279
+ this.selectedIndex = this._options.length - 1;
2280
+ }
2281
+ this.updateScrollOffset();
2282
+ this.requestRender();
2283
+ this.emit("selectionChanged" /* SELECTION_CHANGED */, this.selectedIndex, this.getSelectedOption());
2284
+ }
2285
+ selectCurrent() {
2286
+ const selected = this.getSelectedOption();
2287
+ if (selected) {
2288
+ this.emit("itemSelected" /* ITEM_SELECTED */, this.selectedIndex, selected);
2289
+ }
2290
+ }
2291
+ setSelectedIndex(index) {
2292
+ if (index >= 0 && index < this._options.length) {
2293
+ this.selectedIndex = index;
2294
+ this.updateScrollOffset();
2295
+ this.requestRender();
2296
+ this.emit("selectionChanged" /* SELECTION_CHANGED */, this.selectedIndex, this.getSelectedOption());
2297
+ }
2298
+ }
2299
+ updateScrollOffset() {
2300
+ if (!this._options)
2301
+ return;
2302
+ const halfVisible = Math.floor(this.maxVisibleItems / 2);
2303
+ const newScrollOffset = Math.max(0, Math.min(this.selectedIndex - halfVisible, this._options.length - this.maxVisibleItems));
2304
+ if (newScrollOffset !== this.scrollOffset) {
2305
+ this.scrollOffset = newScrollOffset;
2306
+ this.requestRender();
2307
+ }
2308
+ }
2309
+ onResize(width, height) {
2310
+ this.maxVisibleItems = Math.max(1, Math.floor(height / this.linesPerItem));
2311
+ this.updateScrollOffset();
2312
+ this.requestRender();
2313
+ }
2314
+ handleKeyPress(key) {
2315
+ const keyName = typeof key === "string" ? key : key.name;
2316
+ const isShift = typeof key !== "string" && key.shift;
2317
+ switch (keyName) {
2318
+ case "up":
2319
+ case "k":
2320
+ this.moveUp(isShift ? this._fastScrollStep : 1);
2321
+ return true;
2322
+ case "down":
2323
+ case "j":
2324
+ this.moveDown(isShift ? this._fastScrollStep : 1);
2325
+ return true;
2326
+ case "return":
2327
+ case "enter":
2328
+ this.selectCurrent();
2329
+ return true;
2330
+ }
2331
+ return false;
2332
+ }
2333
+ get showScrollIndicator() {
2334
+ return this._showScrollIndicator;
2335
+ }
2336
+ set showScrollIndicator(show) {
2337
+ this._showScrollIndicator = show;
2338
+ this.requestRender();
2339
+ }
2340
+ get showDescription() {
2341
+ return this._showDescription;
2342
+ }
2343
+ set showDescription(show) {
2344
+ if (this._showDescription !== show) {
2345
+ this._showDescription = show;
2346
+ this.linesPerItem = this._showDescription ? this._font ? this.fontHeight + 1 : 2 : this._font ? this.fontHeight : 1;
2347
+ this.linesPerItem += this._itemSpacing;
2348
+ this.maxVisibleItems = Math.max(1, Math.floor(this.height / this.linesPerItem));
2349
+ this.updateScrollOffset();
2350
+ this.requestRender();
2351
+ }
2352
+ }
2353
+ get wrapSelection() {
2354
+ return this._wrapSelection;
2355
+ }
2356
+ set wrapSelection(wrap) {
2357
+ this._wrapSelection = wrap;
2358
+ }
2359
+ set backgroundColor(value) {
2360
+ const newColor = parseColor(value ?? this._defaultOptions.backgroundColor);
2361
+ if (this._backgroundColor !== newColor) {
2362
+ this._backgroundColor = newColor;
2363
+ this.requestRender();
2364
+ }
2365
+ }
2366
+ set textColor(value) {
2367
+ const newColor = parseColor(value ?? this._defaultOptions.textColor);
2368
+ if (this._textColor !== newColor) {
2369
+ this._textColor = newColor;
2370
+ this.requestRender();
2371
+ }
2372
+ }
2373
+ set focusedBackgroundColor(value) {
2374
+ const newColor = parseColor(value ?? this._defaultOptions.focusedBackgroundColor);
2375
+ if (this._focusedBackgroundColor !== newColor) {
2376
+ this._focusedBackgroundColor = newColor;
2377
+ this.requestRender();
2378
+ }
2379
+ }
2380
+ set focusedTextColor(value) {
2381
+ const newColor = parseColor(value ?? this._defaultOptions.focusedTextColor);
2382
+ if (this._focusedTextColor !== newColor) {
2383
+ this._focusedTextColor = newColor;
2384
+ this.requestRender();
2385
+ }
2386
+ }
2387
+ set selectedBackgroundColor(value) {
2388
+ const newColor = parseColor(value ?? this._defaultOptions.selectedBackgroundColor);
2389
+ if (this._selectedBackgroundColor !== newColor) {
2390
+ this._selectedBackgroundColor = newColor;
2391
+ this.requestRender();
2392
+ }
2393
+ }
2394
+ set selectedTextColor(value) {
2395
+ const newColor = parseColor(value ?? this._defaultOptions.selectedTextColor);
2396
+ if (this._selectedTextColor !== newColor) {
2397
+ this._selectedTextColor = newColor;
2398
+ this.requestRender();
2399
+ }
2400
+ }
2401
+ set descriptionColor(value) {
2402
+ const newColor = parseColor(value ?? this._defaultOptions.descriptionColor);
2403
+ if (this._descriptionColor !== newColor) {
2404
+ this._descriptionColor = newColor;
2405
+ this.requestRender();
2406
+ }
2407
+ }
2408
+ set selectedDescriptionColor(value) {
2409
+ const newColor = parseColor(value ?? this._defaultOptions.selectedDescriptionColor);
2410
+ if (this._selectedDescriptionColor !== newColor) {
2411
+ this._selectedDescriptionColor = newColor;
2412
+ this.requestRender();
2413
+ }
2414
+ }
2415
+ set font(font) {
2416
+ this._font = font;
2417
+ this.fontHeight = measureText({ text: "A", font: this._font }).height;
2418
+ this.linesPerItem = this._showDescription ? this._font ? this.fontHeight + 1 : 2 : this._font ? this.fontHeight : 1;
2419
+ this.linesPerItem += this._itemSpacing;
2420
+ this.maxVisibleItems = Math.max(1, Math.floor(this.height / this.linesPerItem));
2421
+ this.updateScrollOffset();
2422
+ this.requestRender();
2423
+ }
2424
+ set itemSpacing(spacing) {
2425
+ this._itemSpacing = spacing;
2426
+ this.linesPerItem = this._showDescription ? this._font ? this.fontHeight + 1 : 2 : this._font ? this.fontHeight : 1;
2427
+ this.linesPerItem += this._itemSpacing;
2428
+ this.maxVisibleItems = Math.max(1, Math.floor(this.height / this.linesPerItem));
2429
+ this.updateScrollOffset();
2430
+ this.requestRender();
2431
+ }
2432
+ set fastScrollStep(step) {
2433
+ this._fastScrollStep = step;
2434
+ }
2435
+ }
2436
+ // src/renderables/TabSelect.ts
2437
+ var TabSelectRenderableEvents;
2438
+ ((TabSelectRenderableEvents2) => {
2439
+ TabSelectRenderableEvents2["SELECTION_CHANGED"] = "selectionChanged";
2440
+ TabSelectRenderableEvents2["ITEM_SELECTED"] = "itemSelected";
2441
+ })(TabSelectRenderableEvents ||= {});
2442
+ function calculateDynamicHeight(showUnderline, showDescription) {
2443
+ let height = 1;
2444
+ if (showUnderline) {
2445
+ height += 1;
2446
+ }
2447
+ if (showDescription) {
2448
+ height += 1;
2449
+ }
2450
+ return height;
2451
+ }
2452
+
2453
+ class TabSelectRenderable extends Renderable {
2454
+ focusable = true;
2455
+ _options = [];
2456
+ selectedIndex = 0;
2457
+ scrollOffset = 0;
2458
+ _tabWidth;
2459
+ maxVisibleTabs;
2460
+ _backgroundColor;
2461
+ _textColor;
2462
+ _focusedBackgroundColor;
2463
+ _focusedTextColor;
2464
+ _selectedBackgroundColor;
2465
+ _selectedTextColor;
2466
+ _selectedDescriptionColor;
2467
+ _showScrollArrows;
2468
+ _showDescription;
2469
+ _showUnderline;
2470
+ _wrapSelection;
2471
+ constructor(ctx, options) {
2472
+ const calculatedHeight = calculateDynamicHeight(options.showUnderline ?? true, options.showDescription ?? true);
2473
+ super(ctx, { ...options, height: calculatedHeight, buffered: true });
2474
+ this._backgroundColor = parseColor(options.backgroundColor || "transparent");
2475
+ this._textColor = parseColor(options.textColor || "#FFFFFF");
2476
+ this._focusedBackgroundColor = parseColor(options.focusedBackgroundColor || options.backgroundColor || "#1a1a1a");
2477
+ this._focusedTextColor = parseColor(options.focusedTextColor || options.textColor || "#FFFFFF");
2478
+ this._options = options.options || [];
2479
+ this._tabWidth = options.tabWidth || 20;
2480
+ this._showDescription = options.showDescription ?? true;
2481
+ this._showUnderline = options.showUnderline ?? true;
2482
+ this._showScrollArrows = options.showScrollArrows ?? true;
2483
+ this._wrapSelection = options.wrapSelection ?? false;
2484
+ this.maxVisibleTabs = Math.max(1, Math.floor(this.width / this._tabWidth));
2485
+ this._selectedBackgroundColor = parseColor(options.selectedBackgroundColor || "#334455");
2486
+ this._selectedTextColor = parseColor(options.selectedTextColor || "#FFFF00");
2487
+ this._selectedDescriptionColor = parseColor(options.selectedDescriptionColor || "#CCCCCC");
2488
+ }
2489
+ calculateDynamicHeight() {
2490
+ return calculateDynamicHeight(this._showUnderline, this._showDescription);
2491
+ }
2492
+ renderSelf(buffer, deltaTime) {
2493
+ if (!this.visible || !this.frameBuffer)
2494
+ return;
2495
+ if (this.isDirty) {
2496
+ this.refreshFrameBuffer();
2497
+ }
2498
+ }
2499
+ refreshFrameBuffer() {
2500
+ if (!this.frameBuffer || this._options.length === 0)
2501
+ return;
2502
+ const bgColor = this._focused ? this._focusedBackgroundColor : this._backgroundColor;
2503
+ this.frameBuffer.clear(bgColor);
2504
+ const contentX = 0;
2505
+ const contentY = 0;
2506
+ const contentWidth = this.width;
2507
+ const contentHeight = this.height;
2508
+ const visibleOptions = this._options.slice(this.scrollOffset, this.scrollOffset + this.maxVisibleTabs);
2509
+ for (let i = 0;i < visibleOptions.length; i++) {
2510
+ const actualIndex = this.scrollOffset + i;
2511
+ const option = visibleOptions[i];
2512
+ const isSelected = actualIndex === this.selectedIndex;
2513
+ const tabX = contentX + i * this._tabWidth;
2514
+ if (tabX >= contentX + contentWidth)
2515
+ break;
2516
+ const actualTabWidth = Math.min(this._tabWidth, contentWidth - i * this._tabWidth);
2517
+ if (isSelected) {
2518
+ this.frameBuffer.fillRect(tabX, contentY, actualTabWidth, 1, this._selectedBackgroundColor);
2519
+ }
2520
+ const baseTextColor = this._focused ? this._focusedTextColor : this._textColor;
2521
+ const nameColor = isSelected ? this._selectedTextColor : baseTextColor;
2522
+ const nameContent = this.truncateText(option.name, actualTabWidth - 2);
2523
+ this.frameBuffer.drawText(nameContent, tabX + 1, contentY, nameColor);
2524
+ if (isSelected && this._showUnderline && contentHeight >= 2) {
2525
+ const underlineY = contentY + 1;
2526
+ const underlineBg = isSelected ? this._selectedBackgroundColor : bgColor;
2527
+ this.frameBuffer.drawText("\u25AC".repeat(actualTabWidth), tabX, underlineY, nameColor, underlineBg);
2528
+ }
2529
+ }
2530
+ if (this._showDescription && contentHeight >= (this._showUnderline ? 3 : 2)) {
2531
+ const selectedOption = this.getSelectedOption();
2532
+ if (selectedOption) {
2533
+ const descriptionY = contentY + (this._showUnderline ? 2 : 1);
2534
+ const descColor = this._selectedDescriptionColor;
2535
+ const descContent = this.truncateText(selectedOption.description, contentWidth - 2);
2536
+ this.frameBuffer.drawText(descContent, contentX + 1, descriptionY, descColor);
2537
+ }
2538
+ }
2539
+ if (this._showScrollArrows && this._options.length > this.maxVisibleTabs) {
2540
+ this.renderScrollArrowsToFrameBuffer(contentX, contentY, contentWidth, contentHeight);
2541
+ }
2542
+ }
2543
+ truncateText(text, maxWidth) {
2544
+ if (text.length <= maxWidth)
2545
+ return text;
2546
+ return text.substring(0, Math.max(0, maxWidth - 1)) + "\u2026";
2547
+ }
2548
+ renderScrollArrowsToFrameBuffer(contentX, contentY, contentWidth, contentHeight) {
2549
+ if (!this.frameBuffer)
2550
+ return;
2551
+ const hasMoreLeft = this.scrollOffset > 0;
2552
+ const hasMoreRight = this.scrollOffset + this.maxVisibleTabs < this._options.length;
2553
+ if (hasMoreLeft) {
2554
+ this.frameBuffer.drawText("\u2039", contentX, contentY, parseColor("#AAAAAA"));
2555
+ }
2556
+ if (hasMoreRight) {
2557
+ this.frameBuffer.drawText("\u203A", contentX + contentWidth - 1, contentY, parseColor("#AAAAAA"));
2558
+ }
2559
+ }
2560
+ setOptions(options) {
2561
+ this._options = options;
2562
+ this.selectedIndex = Math.min(this.selectedIndex, Math.max(0, options.length - 1));
2563
+ this.updateScrollOffset();
2564
+ this.requestRender();
2565
+ }
2566
+ getSelectedOption() {
2567
+ return this._options[this.selectedIndex] || null;
2568
+ }
2569
+ getSelectedIndex() {
2570
+ return this.selectedIndex;
2571
+ }
2572
+ moveLeft() {
2573
+ if (this.selectedIndex > 0) {
2574
+ this.selectedIndex--;
2575
+ } else if (this._wrapSelection && this._options.length > 0) {
2576
+ this.selectedIndex = this._options.length - 1;
2577
+ } else {
2578
+ return;
2579
+ }
2580
+ this.updateScrollOffset();
2581
+ this.requestRender();
2582
+ this.emit("selectionChanged" /* SELECTION_CHANGED */, this.selectedIndex, this.getSelectedOption());
2583
+ }
2584
+ moveRight() {
2585
+ if (this.selectedIndex < this._options.length - 1) {
2586
+ this.selectedIndex++;
2587
+ } else if (this._wrapSelection && this._options.length > 0) {
2588
+ this.selectedIndex = 0;
2589
+ } else {
2590
+ return;
2591
+ }
2592
+ this.updateScrollOffset();
2593
+ this.requestRender();
2594
+ this.emit("selectionChanged" /* SELECTION_CHANGED */, this.selectedIndex, this.getSelectedOption());
2595
+ }
2596
+ selectCurrent() {
2597
+ const selected = this.getSelectedOption();
2598
+ if (selected) {
2599
+ this.emit("itemSelected" /* ITEM_SELECTED */, this.selectedIndex, selected);
2600
+ }
2601
+ }
2602
+ setSelectedIndex(index) {
2603
+ if (index >= 0 && index < this._options.length) {
2604
+ this.selectedIndex = index;
2605
+ this.updateScrollOffset();
2606
+ this.requestRender();
2607
+ this.emit("selectionChanged" /* SELECTION_CHANGED */, this.selectedIndex, this.getSelectedOption());
2608
+ }
2609
+ }
2610
+ updateScrollOffset() {
2611
+ const halfVisible = Math.floor(this.maxVisibleTabs / 2);
2612
+ const newScrollOffset = Math.max(0, Math.min(this.selectedIndex - halfVisible, this._options.length - this.maxVisibleTabs));
2613
+ if (newScrollOffset !== this.scrollOffset) {
2614
+ this.scrollOffset = newScrollOffset;
2615
+ this.requestRender();
2616
+ }
2617
+ }
2618
+ onResize(width, height) {
2619
+ this.maxVisibleTabs = Math.max(1, Math.floor(width / this._tabWidth));
2620
+ this.updateScrollOffset();
2621
+ this.requestRender();
2622
+ }
2623
+ setTabWidth(tabWidth) {
2624
+ if (this._tabWidth === tabWidth)
2625
+ return;
2626
+ this._tabWidth = tabWidth;
2627
+ this.maxVisibleTabs = Math.max(1, Math.floor(this.width / this._tabWidth));
2628
+ this.updateScrollOffset();
2629
+ this.requestRender();
2630
+ }
2631
+ getTabWidth() {
2632
+ return this._tabWidth;
2633
+ }
2634
+ handleKeyPress(key) {
2635
+ const keyName = typeof key === "string" ? key : key.name;
2636
+ switch (keyName) {
2637
+ case "left":
2638
+ case "[":
2639
+ this.moveLeft();
2640
+ return true;
2641
+ case "right":
2642
+ case "]":
2643
+ this.moveRight();
2644
+ return true;
2645
+ case "return":
2646
+ case "enter":
2647
+ this.selectCurrent();
2648
+ return true;
2649
+ }
2650
+ return false;
2651
+ }
2652
+ get options() {
2653
+ return this._options;
2654
+ }
2655
+ set options(options) {
2656
+ this._options = options;
2657
+ this.selectedIndex = Math.min(this.selectedIndex, Math.max(0, options.length - 1));
2658
+ this.updateScrollOffset();
2659
+ this.requestRender();
2660
+ }
2661
+ set backgroundColor(color) {
2662
+ this._backgroundColor = parseColor(color);
2663
+ this.requestRender();
2664
+ }
2665
+ set textColor(color) {
2666
+ this._textColor = parseColor(color);
2667
+ this.requestRender();
2668
+ }
2669
+ set focusedBackgroundColor(color) {
2670
+ this._focusedBackgroundColor = parseColor(color);
2671
+ this.requestRender();
2672
+ }
2673
+ set focusedTextColor(color) {
2674
+ this._focusedTextColor = parseColor(color);
2675
+ this.requestRender();
2676
+ }
2677
+ set selectedBackgroundColor(color) {
2678
+ this._selectedBackgroundColor = parseColor(color);
2679
+ this.requestRender();
2680
+ }
2681
+ set selectedTextColor(color) {
2682
+ this._selectedTextColor = parseColor(color);
2683
+ this.requestRender();
2684
+ }
2685
+ set selectedDescriptionColor(color) {
2686
+ this._selectedDescriptionColor = parseColor(color);
2687
+ this.requestRender();
2688
+ }
2689
+ get showDescription() {
2690
+ return this._showDescription;
2691
+ }
2692
+ set showDescription(show) {
2693
+ if (this._showDescription !== show) {
2694
+ this._showDescription = show;
2695
+ const newHeight = this.calculateDynamicHeight();
2696
+ this.height = newHeight;
2697
+ this.requestRender();
2698
+ }
2699
+ }
2700
+ get showUnderline() {
2701
+ return this._showUnderline;
2702
+ }
2703
+ set showUnderline(show) {
2704
+ if (this._showUnderline !== show) {
2705
+ this._showUnderline = show;
2706
+ const newHeight = this.calculateDynamicHeight();
2707
+ this.height = newHeight;
2708
+ this.requestRender();
2709
+ }
2710
+ }
2711
+ get showScrollArrows() {
2712
+ return this._showScrollArrows;
2713
+ }
2714
+ set showScrollArrows(show) {
2715
+ if (this._showScrollArrows !== show) {
2716
+ this._showScrollArrows = show;
2717
+ this.requestRender();
2718
+ }
2719
+ }
2720
+ get wrapSelection() {
2721
+ return this._wrapSelection;
2722
+ }
2723
+ set wrapSelection(wrap) {
2724
+ this._wrapSelection = wrap;
2725
+ }
2726
+ get tabWidth() {
2727
+ return this._tabWidth;
2728
+ }
2729
+ set tabWidth(tabWidth) {
2730
+ if (this._tabWidth === tabWidth)
2731
+ return;
2732
+ this._tabWidth = tabWidth;
2733
+ this.maxVisibleTabs = Math.max(1, Math.floor(this.width / this._tabWidth));
2734
+ this.updateScrollOffset();
2735
+ this.requestRender();
2736
+ }
2737
+ }
2738
+ // src/renderables/Slider.ts
2739
+ var defaultThumbBackgroundColor = RGBA.fromHex("#9a9ea3");
2740
+ var defaultTrackBackgroundColor = RGBA.fromHex("#252527");
2741
+
2742
+ class SliderRenderable extends Renderable {
2743
+ orientation;
2744
+ _thumbSize;
2745
+ _thumbPosition;
2746
+ _backgroundColor;
2747
+ _foregroundColor;
2748
+ _onChange;
2749
+ constructor(ctx, options) {
2750
+ super(ctx, options);
2751
+ this.orientation = options.orientation;
2752
+ this._thumbSize = options.thumbSize ?? 1;
2753
+ this._thumbPosition = options.thumbPosition ?? 0;
2754
+ this._onChange = options.onChange;
2755
+ this._backgroundColor = options.backgroundColor ? parseColor(options.backgroundColor) : defaultTrackBackgroundColor;
2756
+ this._foregroundColor = options.foregroundColor ? parseColor(options.foregroundColor) : defaultThumbBackgroundColor;
2757
+ this.setupMouseHandling();
2758
+ }
2759
+ get thumbSize() {
2760
+ return this._thumbSize;
2761
+ }
2762
+ set thumbSize(value) {
2763
+ const clamped = Math.max(1, Math.min(value, this.orientation === "vertical" ? this.height : this.width));
2764
+ if (clamped !== this._thumbSize) {
2765
+ this._thumbSize = clamped;
2766
+ this.requestRender();
2767
+ }
2768
+ }
2769
+ get thumbPosition() {
2770
+ return this._thumbPosition;
2771
+ }
2772
+ set thumbPosition(value) {
2773
+ const clamped = Math.max(0, Math.min(1, value));
2774
+ if (clamped !== this._thumbPosition) {
2775
+ this._thumbPosition = clamped;
2776
+ this._onChange?.(clamped);
2777
+ this.emit("change", { position: clamped });
2778
+ this.requestRender();
2779
+ }
2780
+ }
2781
+ get backgroundColor() {
2782
+ return this._backgroundColor;
2783
+ }
2784
+ set backgroundColor(value) {
2785
+ this._backgroundColor = parseColor(value);
2786
+ this.requestRender();
2787
+ }
2788
+ get foregroundColor() {
2789
+ return this._foregroundColor;
2790
+ }
2791
+ set foregroundColor(value) {
2792
+ this._foregroundColor = parseColor(value);
2793
+ this.requestRender();
2794
+ }
2795
+ setupMouseHandling() {
2796
+ let isDragging = false;
2797
+ let relativeStartPos = 0;
2798
+ this.onMouseDown = (event) => {
2799
+ event.stopPropagation();
2800
+ event.preventDefault();
2801
+ isDragging = true;
2802
+ const thumbRect = this.getThumbRect();
2803
+ const isOnThumb = event.x >= thumbRect.x && event.x < thumbRect.x + thumbRect.width && event.y >= thumbRect.y && event.y < thumbRect.y + thumbRect.height;
2804
+ if (isOnThumb) {
2805
+ relativeStartPos = this.orientation === "vertical" ? event.y - thumbRect.y : event.x - thumbRect.x;
2806
+ } else {
2807
+ relativeStartPos = this.orientation === "vertical" ? thumbRect.height / 2 : thumbRect.width / 2;
2808
+ }
2809
+ this.updatePositionFromMouse(event, relativeStartPos);
2810
+ };
2811
+ this.onMouseDrag = (event) => {
2812
+ if (!isDragging)
2813
+ return;
2814
+ event.stopPropagation();
2815
+ this.updatePositionFromMouse(event, relativeStartPos);
2816
+ };
2817
+ this.onMouseUp = () => {
2818
+ isDragging = false;
2819
+ };
2820
+ }
2821
+ updatePositionFromMouse(event, relativeStartPos) {
2822
+ const trackStart = this.orientation === "vertical" ? this.y : this.x;
2823
+ const trackSize = this.orientation === "vertical" ? this.height : this.width;
2824
+ const mousePos = this.orientation === "vertical" ? event.y : event.x;
2825
+ const thumbStartPos = mousePos - trackStart - relativeStartPos;
2826
+ const maxThumbStartPos = trackSize - this._thumbSize;
2827
+ const clampedThumbStartPos = Math.max(0, Math.min(maxThumbStartPos, thumbStartPos));
2828
+ const newPosition = maxThumbStartPos > 0 ? clampedThumbStartPos / maxThumbStartPos : 0;
2829
+ this.thumbPosition = newPosition;
2830
+ }
2831
+ getThumbPosition() {
2832
+ const trackSize = this.orientation === "vertical" ? this.height : this.width;
2833
+ const maxPos = trackSize - this._thumbSize;
2834
+ return Math.round(this._thumbPosition * maxPos);
2835
+ }
2836
+ getThumbRect() {
2837
+ const thumbPos = this.getThumbPosition();
2838
+ if (this.orientation === "vertical") {
2839
+ return {
2840
+ x: this.x,
2841
+ y: this.y + thumbPos,
2842
+ width: this.width,
2843
+ height: this._thumbSize
2844
+ };
2845
+ } else {
2846
+ return {
2847
+ x: this.x + thumbPos,
2848
+ y: this.y,
2849
+ width: this._thumbSize,
2850
+ height: this.height
2851
+ };
2852
+ }
2853
+ }
2854
+ renderSelf(buffer) {
2855
+ buffer.fillRect(this.x, this.y, this.width, this.height, this._backgroundColor);
2856
+ const thumbRect = this.getThumbRect();
2857
+ buffer.fillRect(thumbRect.x, thumbRect.y, thumbRect.width, thumbRect.height, this._foregroundColor);
2858
+ }
2859
+ }
2860
+
2861
+ // src/renderables/ScrollBar.ts
2862
+ class ScrollBarRenderable extends Renderable {
2863
+ slider;
2864
+ startArrow;
2865
+ endArrow;
2866
+ orientation;
2867
+ focusable = true;
2868
+ _scrollSize = 0;
2869
+ _scrollPosition = 0;
2870
+ _viewportSize = 0;
2871
+ _showArrows = false;
2872
+ _manualVisibility = false;
2873
+ _onChange;
2874
+ scrollStep = null;
2875
+ get visible() {
2876
+ return super.visible;
2877
+ }
2878
+ set visible(value) {
2879
+ this._manualVisibility = true;
2880
+ super.visible = value;
2881
+ }
2882
+ resetVisibilityControl() {
2883
+ this._manualVisibility = false;
2884
+ this.recalculateVisibility();
2885
+ }
2886
+ get scrollSize() {
2887
+ return this._scrollSize;
2888
+ }
2889
+ get scrollPosition() {
2890
+ return this._scrollPosition;
2891
+ }
2892
+ get viewportSize() {
2893
+ return this._viewportSize;
2894
+ }
2895
+ set scrollSize(value) {
2896
+ if (value === this.scrollSize)
2897
+ return;
2898
+ this._scrollSize = value;
2899
+ this.recalculateVisibility();
2900
+ this.scrollPosition = this.scrollPosition;
2901
+ }
2902
+ set scrollPosition(value) {
2903
+ const newPosition = Math.round(Math.min(Math.max(0, value), this.scrollSize - this.viewportSize));
2904
+ if (newPosition !== this._scrollPosition) {
2905
+ this._scrollPosition = newPosition;
2906
+ this.updateSliderFromScrollState();
2907
+ this._onChange?.(newPosition);
2908
+ this.emit("change", { position: newPosition });
2909
+ }
2910
+ }
2911
+ set viewportSize(value) {
2912
+ if (value === this.viewportSize)
2913
+ return;
2914
+ this._viewportSize = value;
2915
+ this.recalculateVisibility();
2916
+ this.scrollPosition = this.scrollPosition;
2917
+ }
2918
+ get showArrows() {
2919
+ return this._showArrows;
2920
+ }
2921
+ set showArrows(value) {
2922
+ if (value === this._showArrows)
2923
+ return;
2924
+ this._showArrows = value;
2925
+ this.startArrow.visible = value;
2926
+ this.endArrow.visible = value;
2927
+ }
2928
+ constructor(ctx, { trackOptions, arrowOptions, orientation, showArrows = false, ...options }) {
2929
+ super(ctx, {
2930
+ flexDirection: orientation === "vertical" ? "column" : "row",
2931
+ alignSelf: "stretch",
2932
+ alignItems: "stretch",
2933
+ ...options
2934
+ });
2935
+ this._onChange = options.onChange;
2936
+ this.orientation = orientation;
2937
+ this._showArrows = showArrows;
2938
+ this.slider = new SliderRenderable(ctx, {
2939
+ orientation,
2940
+ onChange: (position) => {
2941
+ const scrollRange = Math.max(0, this._scrollSize - this._viewportSize);
2942
+ this._scrollPosition = Math.round(position * scrollRange);
2943
+ this._onChange?.(this._scrollPosition);
2944
+ this.emit("change", { position: this._scrollPosition });
2945
+ },
2946
+ ...orientation === "vertical" ? {
2947
+ width: 2,
2948
+ height: "100%",
2949
+ marginLeft: "auto"
2950
+ } : {
2951
+ width: "100%",
2952
+ height: 1,
2953
+ marginTop: "auto"
2954
+ },
2955
+ flexGrow: 1,
2956
+ flexShrink: 1,
2957
+ ...trackOptions
2958
+ });
2959
+ this.updateSliderFromScrollState();
2960
+ const arrowOpts = arrowOptions ? {
2961
+ foregroundColor: arrowOptions.backgroundColor,
2962
+ backgroundColor: arrowOptions.backgroundColor,
2963
+ attributes: arrowOptions.attributes,
2964
+ ...arrowOptions
2965
+ } : {};
2966
+ this.startArrow = new ArrowRenderable(ctx, {
2967
+ alignSelf: "center",
2968
+ visible: this.showArrows,
2969
+ direction: this.orientation === "vertical" ? "up" : "left",
2970
+ height: this.orientation === "vertical" ? 1 : 1,
2971
+ ...arrowOpts
2972
+ });
2973
+ this.endArrow = new ArrowRenderable(ctx, {
2974
+ alignSelf: "center",
2975
+ visible: this.showArrows,
2976
+ direction: this.orientation === "vertical" ? "down" : "right",
2977
+ height: this.orientation === "vertical" ? 1 : 1,
2978
+ ...arrowOpts
2979
+ });
2980
+ this.add(this.startArrow);
2981
+ this.add(this.slider);
2982
+ this.add(this.endArrow);
2983
+ let startArrowMouseTimeout = undefined;
2984
+ let endArrowMouseTimeout = undefined;
2985
+ this.startArrow.onMouseDown = (event) => {
2986
+ event.stopPropagation();
2987
+ event.preventDefault();
2988
+ this.scrollBy(-0.5, "viewport");
2989
+ startArrowMouseTimeout = setTimeout(() => {
2990
+ this.scrollBy(-0.5, "viewport");
2991
+ startArrowMouseTimeout = setInterval(() => {
2992
+ this.scrollBy(-0.2, "viewport");
2993
+ }, 200);
2994
+ }, 500);
2995
+ };
2996
+ this.startArrow.onMouseUp = (event) => {
2997
+ event.stopPropagation();
2998
+ clearInterval(startArrowMouseTimeout);
2999
+ };
3000
+ this.endArrow.onMouseDown = (event) => {
3001
+ event.stopPropagation();
3002
+ event.preventDefault();
3003
+ this.scrollBy(0.5, "viewport");
3004
+ endArrowMouseTimeout = setTimeout(() => {
3005
+ this.scrollBy(0.5, "viewport");
3006
+ endArrowMouseTimeout = setInterval(() => {
3007
+ this.scrollBy(0.2, "viewport");
3008
+ }, 200);
3009
+ }, 500);
3010
+ };
3011
+ this.endArrow.onMouseUp = (event) => {
3012
+ event.stopPropagation();
3013
+ clearInterval(endArrowMouseTimeout);
3014
+ };
3015
+ }
3016
+ set arrowOptions(options) {
3017
+ Object.assign(this.startArrow, options);
3018
+ Object.assign(this.endArrow, options);
3019
+ this.requestRender();
3020
+ }
3021
+ set trackOptions(options) {
3022
+ Object.assign(this.slider, options);
3023
+ this.requestRender();
3024
+ }
3025
+ updateSliderFromScrollState() {
3026
+ const trackSize = this.orientation === "vertical" ? this.slider.height : this.slider.width;
3027
+ const scrollRange = Math.max(0, this._scrollSize - this._viewportSize);
3028
+ if (scrollRange === 0) {
3029
+ this.slider.thumbSize = trackSize;
3030
+ this.slider.thumbPosition = 0;
3031
+ } else {
3032
+ const sizeRatio = this._viewportSize / this._scrollSize;
3033
+ this.slider.thumbSize = Math.max(1, Math.round(sizeRatio * trackSize));
3034
+ const positionRatio = this._scrollPosition / scrollRange;
3035
+ this.slider.thumbPosition = Math.max(0, Math.min(1, positionRatio));
3036
+ }
3037
+ }
3038
+ scrollBy(delta, unit = "absolute") {
3039
+ const multiplier = unit === "viewport" ? this.viewportSize : unit === "content" ? this.scrollSize : unit === "step" ? this.scrollStep ?? 1 : 1;
3040
+ const resolvedDelta = multiplier * delta;
3041
+ this.scrollPosition += resolvedDelta;
3042
+ }
3043
+ recalculateVisibility() {
3044
+ if (!this._manualVisibility) {
3045
+ const sizeRatio = this.scrollSize <= this.viewportSize ? 1 : this.viewportSize / this.scrollSize;
3046
+ super.visible = sizeRatio < 1;
3047
+ }
3048
+ }
3049
+ handleKeyPress(key) {
3050
+ const keyName = typeof key === "string" ? key : key.name;
3051
+ switch (keyName) {
3052
+ case "left":
3053
+ case "h":
3054
+ if (this.orientation !== "horizontal")
3055
+ return false;
3056
+ this.scrollBy(-1 / 5, "viewport");
3057
+ return true;
3058
+ case "right":
3059
+ case "l":
3060
+ if (this.orientation !== "horizontal")
3061
+ return false;
3062
+ this.scrollBy(1 / 5, "viewport");
3063
+ return true;
3064
+ case "up":
3065
+ case "k":
3066
+ if (this.orientation !== "vertical")
3067
+ return false;
3068
+ this.scrollBy(-1 / 5, "viewport");
3069
+ return true;
3070
+ case "down":
3071
+ case "j":
3072
+ if (this.orientation !== "vertical")
3073
+ return false;
3074
+ this.scrollBy(1 / 5, "viewport");
3075
+ return true;
3076
+ case "pageup":
3077
+ this.scrollBy(-1 / 2, "viewport");
3078
+ return true;
3079
+ case "pagedown":
3080
+ this.scrollBy(1 / 2, "viewport");
3081
+ return true;
3082
+ case "home":
3083
+ this.scrollBy(-1, "content");
3084
+ return true;
3085
+ case "end":
3086
+ this.scrollBy(1, "content");
3087
+ return true;
3088
+ }
3089
+ return false;
3090
+ }
3091
+ }
3092
+
3093
+ class ArrowRenderable extends Renderable {
3094
+ _direction;
3095
+ _foregroundColor;
3096
+ _backgroundColor;
3097
+ _attributes;
3098
+ _arrowChars;
3099
+ constructor(ctx, options) {
3100
+ super(ctx, options);
3101
+ this._direction = options.direction;
3102
+ this._foregroundColor = options.foregroundColor ? parseColor(options.foregroundColor) : RGBA.fromValues(1, 1, 1, 1);
3103
+ this._backgroundColor = options.backgroundColor ? parseColor(options.backgroundColor) : RGBA.fromValues(0, 0, 0, 0);
3104
+ this._attributes = options.attributes ?? 0;
3105
+ this._arrowChars = {
3106
+ up: "\u25E2\u25E3",
3107
+ down: "\u25E5\u25E4",
3108
+ left: " \u25C0 ",
3109
+ right: " \u25B6 ",
3110
+ ...options.arrowChars
3111
+ };
3112
+ if (!options.width) {
3113
+ this.width = Bun.stringWidth(this.getArrowChar());
3114
+ }
3115
+ }
3116
+ get direction() {
3117
+ return this._direction;
3118
+ }
3119
+ set direction(value) {
3120
+ if (this._direction !== value) {
3121
+ this._direction = value;
3122
+ this.requestRender();
3123
+ }
3124
+ }
3125
+ get foregroundColor() {
3126
+ return this._foregroundColor;
3127
+ }
3128
+ set foregroundColor(value) {
3129
+ if (this._foregroundColor !== value) {
3130
+ this._foregroundColor = parseColor(value);
3131
+ this.requestRender();
3132
+ }
3133
+ }
3134
+ get backgroundColor() {
3135
+ return this._backgroundColor;
3136
+ }
3137
+ set backgroundColor(value) {
3138
+ if (this._backgroundColor !== value) {
3139
+ this._backgroundColor = parseColor(value);
3140
+ this.requestRender();
3141
+ }
3142
+ }
3143
+ get attributes() {
3144
+ return this._attributes;
3145
+ }
3146
+ set attributes(value) {
3147
+ if (this._attributes !== value) {
3148
+ this._attributes = value;
3149
+ this.requestRender();
3150
+ }
3151
+ }
3152
+ set arrowChars(value) {
3153
+ this._arrowChars = {
3154
+ ...this._arrowChars,
3155
+ ...value
3156
+ };
3157
+ this.requestRender();
3158
+ }
3159
+ renderSelf(buffer) {
3160
+ const char = this.getArrowChar();
3161
+ buffer.drawText(char, this.x, this.y, this._foregroundColor, this._backgroundColor, this._attributes);
3162
+ }
3163
+ getArrowChar() {
3164
+ switch (this._direction) {
3165
+ case "up":
3166
+ return this._arrowChars.up;
3167
+ case "down":
3168
+ return this._arrowChars.down;
3169
+ case "left":
3170
+ return this._arrowChars.left;
3171
+ case "right":
3172
+ return this._arrowChars.right;
3173
+ default:
3174
+ return "?";
3175
+ }
3176
+ }
3177
+ }
3178
+
3179
+ // src/renderables/ScrollBox.ts
3180
+ class ContentRenderable extends BoxRenderable {
3181
+ viewport;
3182
+ constructor(ctx, viewport, options) {
3183
+ super(ctx, options);
3184
+ this.viewport = viewport;
3185
+ }
3186
+ _getChildren() {
3187
+ return this.getChildrenInViewport(this.viewport);
3188
+ }
3189
+ }
3190
+
3191
+ class ScrollBoxRenderable extends BoxRenderable {
3192
+ static idCounter = 0;
3193
+ internalId = 0;
3194
+ wrapper;
3195
+ viewport;
3196
+ content;
3197
+ horizontalScrollBar;
3198
+ verticalScrollBar;
3199
+ focusable = true;
3200
+ selectionListener;
3201
+ autoScrollMouseX = 0;
3202
+ autoScrollMouseY = 0;
3203
+ autoScrollThresholdVertical = 3;
3204
+ autoScrollThresholdHorizontal = 3;
3205
+ autoScrollSpeedSlow = 6;
3206
+ autoScrollSpeedMedium = 36;
3207
+ autoScrollSpeedFast = 72;
3208
+ isAutoScrolling = false;
3209
+ cachedAutoScrollSpeed = 3;
3210
+ autoScrollAccumulatorX = 0;
3211
+ autoScrollAccumulatorY = 0;
3212
+ get scrollTop() {
3213
+ return this.verticalScrollBar.scrollPosition;
3214
+ }
3215
+ set scrollTop(value) {
3216
+ this.verticalScrollBar.scrollPosition = value;
3217
+ }
3218
+ get scrollLeft() {
3219
+ return this.horizontalScrollBar.scrollPosition;
3220
+ }
3221
+ set scrollLeft(value) {
3222
+ this.horizontalScrollBar.scrollPosition = value;
3223
+ }
3224
+ get scrollWidth() {
3225
+ return this.horizontalScrollBar.scrollSize;
3226
+ }
3227
+ get scrollHeight() {
3228
+ return this.verticalScrollBar.scrollSize;
3229
+ }
3230
+ constructor(ctx, {
3231
+ wrapperOptions,
3232
+ viewportOptions,
3233
+ contentOptions,
3234
+ rootOptions,
3235
+ scrollbarOptions,
3236
+ verticalScrollbarOptions,
3237
+ horizontalScrollbarOptions,
3238
+ ...options
3239
+ }) {
3240
+ super(ctx, {
3241
+ flexShrink: 1,
3242
+ flexGrow: 1,
3243
+ flexDirection: "row",
3244
+ flexWrap: "wrap",
3245
+ alignItems: "stretch",
3246
+ ...options,
3247
+ ...rootOptions
3248
+ });
3249
+ this.internalId = ScrollBoxRenderable.idCounter++;
3250
+ this.wrapper = new BoxRenderable(ctx, {
3251
+ flexDirection: "column",
3252
+ flexGrow: 1,
3253
+ flexShrink: 1,
3254
+ flexBasis: "auto",
3255
+ maxHeight: "100%",
3256
+ maxWidth: "100%",
3257
+ ...wrapperOptions,
3258
+ id: `scroll-box-wrapper-${this.internalId}`
3259
+ });
3260
+ super.add(this.wrapper);
3261
+ this.viewport = new BoxRenderable(ctx, {
3262
+ flexDirection: "column",
3263
+ flexGrow: 1,
3264
+ flexShrink: 1,
3265
+ flexBasis: "auto",
3266
+ maxHeight: "100%",
3267
+ maxWidth: "100%",
3268
+ overflow: "scroll",
3269
+ onSizeChange: () => {
3270
+ this.recalculateBarProps();
3271
+ },
3272
+ ...viewportOptions,
3273
+ id: `scroll-box-viewport-${this.internalId}`
3274
+ });
3275
+ this.wrapper.add(this.viewport);
3276
+ this.content = new ContentRenderable(ctx, this.viewport, {
3277
+ alignSelf: "flex-start",
3278
+ minWidth: "100%",
3279
+ minHeight: "100%",
3280
+ onSizeChange: () => {
3281
+ this.recalculateBarProps();
3282
+ },
3283
+ ...contentOptions,
3284
+ id: `scroll-box-content-${this.internalId}`
3285
+ });
3286
+ this.viewport.add(this.content);
3287
+ this.verticalScrollBar = new ScrollBarRenderable(ctx, {
3288
+ ...scrollbarOptions,
3289
+ ...verticalScrollbarOptions,
3290
+ arrowOptions: {
3291
+ ...scrollbarOptions?.arrowOptions,
3292
+ ...verticalScrollbarOptions?.arrowOptions
3293
+ },
3294
+ id: `scroll-box-vertical-scrollbar-${this.internalId}`,
3295
+ orientation: "vertical",
3296
+ onChange: (position) => {
3297
+ this.content.translateY = -position;
3298
+ }
3299
+ });
3300
+ super.add(this.verticalScrollBar);
3301
+ this.horizontalScrollBar = new ScrollBarRenderable(ctx, {
3302
+ ...scrollbarOptions,
3303
+ ...horizontalScrollbarOptions,
3304
+ arrowOptions: {
3305
+ ...scrollbarOptions?.arrowOptions,
3306
+ ...horizontalScrollbarOptions?.arrowOptions
3307
+ },
3308
+ id: `scroll-box-horizontal-scrollbar-${this.internalId}`,
3309
+ orientation: "horizontal",
3310
+ onChange: (position) => {
3311
+ this.content.translateX = -position;
3312
+ }
3313
+ });
3314
+ this.wrapper.add(this.horizontalScrollBar);
3315
+ this.recalculateBarProps();
3316
+ this.selectionListener = () => {
3317
+ const selection = this._ctx.getSelection();
3318
+ if (!selection || !selection.isSelecting) {
3319
+ this.stopAutoScroll();
3320
+ }
3321
+ };
3322
+ this._ctx.on("selection", this.selectionListener);
3323
+ }
3324
+ onUpdate(deltaTime) {
3325
+ this.handleAutoScroll(deltaTime);
3326
+ }
3327
+ scrollBy(delta, unit = "absolute") {
3328
+ if (typeof delta === "number") {
3329
+ this.verticalScrollBar.scrollBy(delta, unit);
3330
+ } else {
3331
+ this.verticalScrollBar.scrollBy(delta.y, unit);
3332
+ this.horizontalScrollBar.scrollBy(delta.x, unit);
3333
+ }
3334
+ }
3335
+ scrollTo(position) {
3336
+ if (typeof position === "number") {
3337
+ this.scrollTop = position;
3338
+ } else {
3339
+ this.scrollTop = position.y;
3340
+ this.scrollLeft = position.x;
3341
+ }
3342
+ }
3343
+ add(obj, index) {
3344
+ return this.content.add(obj, index);
3345
+ }
3346
+ remove(id) {
3347
+ this.content.remove(id);
3348
+ }
3349
+ getChildren() {
3350
+ return this.content.getChildren();
3351
+ }
3352
+ onMouseEvent(event) {
3353
+ if (event.type === "scroll") {
3354
+ let dir = event.scroll?.direction;
3355
+ if (event.modifiers.shift)
3356
+ dir = dir === "up" ? "left" : dir === "down" ? "right" : dir === "right" ? "down" : "up";
3357
+ if (dir === "up")
3358
+ this.scrollTop -= event.scroll?.delta ?? 0;
3359
+ else if (dir === "down")
3360
+ this.scrollTop += event.scroll?.delta ?? 0;
3361
+ else if (dir === "left")
3362
+ this.scrollLeft -= event.scroll?.delta ?? 0;
3363
+ else if (dir === "right")
3364
+ this.scrollLeft += event.scroll?.delta ?? 0;
3365
+ }
3366
+ if (event.type === "drag" && event.isSelecting) {
3367
+ this.updateAutoScroll(event.x, event.y);
3368
+ } else if (event.type === "up") {
3369
+ this.stopAutoScroll();
3370
+ }
3371
+ }
3372
+ handleKeyPress(key) {
3373
+ if (this.verticalScrollBar.handleKeyPress(key))
3374
+ return true;
3375
+ if (this.horizontalScrollBar.handleKeyPress(key))
3376
+ return true;
3377
+ return false;
3378
+ }
3379
+ startAutoScroll(mouseX, mouseY) {
3380
+ this.stopAutoScroll();
3381
+ this.autoScrollMouseX = mouseX;
3382
+ this.autoScrollMouseY = mouseY;
3383
+ this.cachedAutoScrollSpeed = this.getAutoScrollSpeed(mouseX, mouseY);
3384
+ this.isAutoScrolling = true;
3385
+ if (!this.live) {
3386
+ this.live = true;
3387
+ }
3388
+ }
3389
+ updateAutoScroll(mouseX, mouseY) {
3390
+ this.autoScrollMouseX = mouseX;
3391
+ this.autoScrollMouseY = mouseY;
3392
+ this.cachedAutoScrollSpeed = this.getAutoScrollSpeed(mouseX, mouseY);
3393
+ const scrollX = this.getAutoScrollDirectionX(mouseX);
3394
+ const scrollY = this.getAutoScrollDirectionY(mouseY);
3395
+ if (scrollX === 0 && scrollY === 0) {
3396
+ this.stopAutoScroll();
3397
+ } else if (!this.isAutoScrolling) {
3398
+ this.startAutoScroll(mouseX, mouseY);
3399
+ }
3400
+ }
3401
+ stopAutoScroll() {
3402
+ const wasAutoScrolling = this.isAutoScrolling;
3403
+ this.isAutoScrolling = false;
3404
+ this.autoScrollAccumulatorX = 0;
3405
+ this.autoScrollAccumulatorY = 0;
3406
+ if (wasAutoScrolling && !this.hasOtherLiveReasons()) {
3407
+ this.live = false;
3408
+ }
3409
+ }
3410
+ hasOtherLiveReasons() {
3411
+ return false;
3412
+ }
3413
+ handleAutoScroll(deltaTime) {
3414
+ if (!this.isAutoScrolling)
3415
+ return;
3416
+ const scrollX = this.getAutoScrollDirectionX(this.autoScrollMouseX);
3417
+ const scrollY = this.getAutoScrollDirectionY(this.autoScrollMouseY);
3418
+ const scrollAmount = this.cachedAutoScrollSpeed * (deltaTime / 1000);
3419
+ let scrolled = false;
3420
+ if (scrollX !== 0) {
3421
+ this.autoScrollAccumulatorX += scrollX * scrollAmount;
3422
+ const integerScrollX = Math.trunc(this.autoScrollAccumulatorX);
3423
+ if (integerScrollX !== 0) {
3424
+ this.scrollLeft += integerScrollX;
3425
+ this.autoScrollAccumulatorX -= integerScrollX;
3426
+ scrolled = true;
3427
+ }
3428
+ }
3429
+ if (scrollY !== 0) {
3430
+ this.autoScrollAccumulatorY += scrollY * scrollAmount;
3431
+ const integerScrollY = Math.trunc(this.autoScrollAccumulatorY);
3432
+ if (integerScrollY !== 0) {
3433
+ this.scrollTop += integerScrollY;
3434
+ this.autoScrollAccumulatorY -= integerScrollY;
3435
+ scrolled = true;
3436
+ }
3437
+ }
3438
+ if (scrolled) {
3439
+ this._ctx.requestSelectionUpdate();
3440
+ }
3441
+ if (scrollX === 0 && scrollY === 0) {
3442
+ this.stopAutoScroll();
3443
+ }
3444
+ }
3445
+ getAutoScrollDirectionX(mouseX) {
3446
+ const relativeX = mouseX - this.x;
3447
+ const distToLeft = relativeX;
3448
+ const distToRight = this.width - relativeX;
3449
+ if (distToLeft <= this.autoScrollThresholdHorizontal) {
3450
+ return this.scrollLeft > 0 ? -1 : 0;
3451
+ } else if (distToRight <= this.autoScrollThresholdHorizontal) {
3452
+ const maxScrollLeft = this.scrollWidth - this.viewport.width;
3453
+ return this.scrollLeft < maxScrollLeft ? 1 : 0;
3454
+ }
3455
+ return 0;
3456
+ }
3457
+ getAutoScrollDirectionY(mouseY) {
3458
+ const relativeY = mouseY - this.y;
3459
+ const distToTop = relativeY;
3460
+ const distToBottom = this.height - relativeY;
3461
+ if (distToTop <= this.autoScrollThresholdVertical) {
3462
+ return this.scrollTop > 0 ? -1 : 0;
3463
+ } else if (distToBottom <= this.autoScrollThresholdVertical) {
3464
+ const maxScrollTop = this.scrollHeight - this.viewport.height;
3465
+ return this.scrollTop < maxScrollTop ? 1 : 0;
3466
+ }
3467
+ return 0;
3468
+ }
3469
+ getAutoScrollSpeed(mouseX, mouseY) {
3470
+ const relativeX = mouseX - this.x;
3471
+ const relativeY = mouseY - this.y;
3472
+ const distToLeft = relativeX;
3473
+ const distToRight = this.width - relativeX;
3474
+ const distToTop = relativeY;
3475
+ const distToBottom = this.height - relativeY;
3476
+ const minDistance = Math.min(distToLeft, distToRight, distToTop, distToBottom);
3477
+ if (minDistance <= 1) {
3478
+ return this.autoScrollSpeedFast;
3479
+ } else if (minDistance <= 2) {
3480
+ return this.autoScrollSpeedMedium;
3481
+ } else {
3482
+ return this.autoScrollSpeedSlow;
3483
+ }
3484
+ }
3485
+ recalculateBarProps() {
3486
+ this.verticalScrollBar.scrollSize = this.content.height;
3487
+ this.verticalScrollBar.viewportSize = this.viewport.height;
3488
+ this.horizontalScrollBar.scrollSize = this.content.width;
3489
+ this.horizontalScrollBar.viewportSize = this.viewport.width;
3490
+ }
3491
+ set rootOptions(options) {
3492
+ Object.assign(this, options);
3493
+ this.requestRender();
3494
+ }
3495
+ set wrapperOptions(options) {
3496
+ Object.assign(this.wrapper, options);
3497
+ this.requestRender();
3498
+ }
3499
+ set viewportOptions(options) {
3500
+ Object.assign(this.viewport, options);
3501
+ this.requestRender();
3502
+ }
3503
+ set contentOptions(options) {
3504
+ Object.assign(this.content, options);
3505
+ this.requestRender();
3506
+ }
3507
+ set scrollbarOptions(options) {
3508
+ Object.assign(this.verticalScrollBar, options);
3509
+ Object.assign(this.horizontalScrollBar, options);
3510
+ this.requestRender();
3511
+ }
3512
+ set verticalScrollbarOptions(options) {
3513
+ Object.assign(this.verticalScrollBar, options);
3514
+ this.requestRender();
3515
+ }
3516
+ set horizontalScrollbarOptions(options) {
3517
+ Object.assign(this.horizontalScrollBar, options);
3518
+ this.requestRender();
3519
+ }
3520
+ destroySelf() {
3521
+ if (this.selectionListener) {
3522
+ this._ctx.off("selection", this.selectionListener);
3523
+ this.selectionListener = undefined;
3524
+ }
3525
+ super.destroySelf();
3526
+ }
3527
+ }
3528
+ // src/renderables/composition/constructs.ts
3529
+ function Generic(props, ...children) {
3530
+ return h(VRenderable, props || {}, ...children);
3531
+ }
3532
+ function Box(props, ...children) {
3533
+ return h(BoxRenderable, props || {}, ...children);
3534
+ }
3535
+ function Text(props, ...children) {
3536
+ return h(TextRenderable, props || {}, ...children);
3537
+ }
3538
+ function ASCIIFont(props, ...children) {
3539
+ return h(ASCIIFontRenderable, props || {}, ...children);
3540
+ }
3541
+ function Input(props, ...children) {
3542
+ return h(InputRenderable, props || {}, ...children);
3543
+ }
3544
+ function Select(props, ...children) {
3545
+ return h(SelectRenderable, props || {}, ...children);
3546
+ }
3547
+ function TabSelect(props, ...children) {
3548
+ return h(TabSelectRenderable, props || {}, ...children);
3549
+ }
3550
+ function FrameBuffer(props, ...children) {
3551
+ return h(FrameBufferRenderable, props, ...children);
3552
+ }
3553
+ // src/renderables/composition/VRenderable.ts
3554
+ class VRenderable extends Renderable {
3555
+ options;
3556
+ constructor(ctx, options) {
3557
+ super(ctx, options);
3558
+ this.options = options;
3559
+ }
3560
+ renderSelf(buffer, deltaTime) {
3561
+ if (this.options.render) {
3562
+ this.options.render.call(this.options, buffer, deltaTime, this);
3563
+ }
3564
+ }
3565
+ }
3566
+ export {
3567
+ yellow,
3568
+ wrapWithDelegates,
3569
+ white,
3570
+ underline,
3571
+ t,
3572
+ stringToStyledText,
3573
+ strikethrough,
3574
+ setRenderLibPath,
3575
+ rgbToHex,
3576
+ reverse,
3577
+ resolveRenderLib,
3578
+ renderFontToFrameBuffer,
3579
+ red,
3580
+ parseWrap,
3581
+ parseUnit,
3582
+ parsePositionType,
3583
+ parseOverflow,
3584
+ parseMeasureMode,
3585
+ parseLogLevel,
3586
+ parseKeypress,
3587
+ parseJustify,
3588
+ parseGutter,
3589
+ parseFlexDirection,
3590
+ parseEdge,
3591
+ parseDisplay,
3592
+ parseDirection,
3593
+ parseDimension,
3594
+ parseColor,
3595
+ parseBoxSizing,
3596
+ parseAlign,
3597
+ nonAlphanumericKeys,
3598
+ measureText,
3599
+ maybeMakeRenderable,
3600
+ magenta,
3601
+ italic,
3602
+ isValidPercentage,
3603
+ isVNode,
3604
+ isSizeType,
3605
+ isRenderable,
3606
+ isPositionTypeType,
3607
+ isPositionType,
3608
+ isPaddingType,
3609
+ isOverflowType,
3610
+ isMarginType,
3611
+ isFlexBasisType,
3612
+ isDimensionType,
3613
+ instantiate,
3614
+ hsvToRgb,
3615
+ hexToRgb,
3616
+ hastToStyledText,
3617
+ h,
3618
+ green,
3619
+ getKeyHandler,
3620
+ getCharacterPositions,
3621
+ getBorderSides,
3622
+ getBorderFromSides,
3623
+ fonts,
3624
+ fg,
3625
+ engine,
3626
+ dim,
3627
+ delegate,
3628
+ cyan,
3629
+ createTrackedNode,
3630
+ createTimeline,
3631
+ createTextAttributes,
3632
+ createCliRenderer,
3633
+ coordinateToCharacterIndex,
3634
+ convertGlobalToLocalSelection,
3635
+ capture,
3636
+ brightYellow,
3637
+ brightWhite,
3638
+ brightRed,
3639
+ brightMagenta,
3640
+ brightGreen,
3641
+ brightCyan,
3642
+ brightBlue,
3643
+ brightBlack,
3644
+ borderCharsToArray,
3645
+ bold,
3646
+ blue,
3647
+ blink,
3648
+ black,
3649
+ bgYellow,
3650
+ bgWhite,
3651
+ bgRed,
3652
+ bgMagenta,
3653
+ bgGreen,
3654
+ bgCyan,
3655
+ bgBlue,
3656
+ bgBlack,
3657
+ bg,
3658
+ applySepia,
3659
+ applyScanlines,
3660
+ applyNoise,
3661
+ applyInvert,
3662
+ applyGrayscale,
3663
+ applyChromaticAberration,
3664
+ applyAsciiArt,
3665
+ VignetteEffect,
3666
+ VRenderable,
3667
+ TrackedNode,
3668
+ Timeline,
3669
+ TextRenderable,
3670
+ TextBuffer,
3671
+ TextAttributes,
3672
+ Text,
3673
+ TerminalConsole,
3674
+ TabSelectRenderableEvents,
3675
+ TabSelectRenderable,
3676
+ TabSelect,
3677
+ SyntaxStyle,
3678
+ StyledText,
3679
+ Selection,
3680
+ SelectRenderableEvents,
3681
+ SelectRenderable,
3682
+ Select,
3683
+ ScrollBoxRenderable,
3684
+ ScrollBarRenderable,
3685
+ RootRenderable,
3686
+ RenderableEvents,
3687
+ Renderable,
3688
+ RGBA,
3689
+ OptimizedBuffer,
3690
+ MouseParser,
3691
+ MouseEvent,
3692
+ MouseButton,
3693
+ LogLevel,
3694
+ LayoutEvents,
3695
+ KeyHandler,
3696
+ InputRenderableEvents,
3697
+ InputRenderable,
3698
+ Input,
3699
+ Generic,
3700
+ FrameBufferRenderable,
3701
+ FrameBuffer,
3702
+ DistortionEffect,
3703
+ DebugOverlayCorner,
3704
+ ConsolePosition,
3705
+ CliRenderer,
3706
+ CliRenderEvents,
3707
+ BrightnessEffect,
3708
+ BoxRenderable,
3709
+ Box,
3710
+ BorderChars,
3711
+ BorderCharArrays,
3712
+ BlurEffect,
3713
+ BloomEffect,
3714
+ ArrowRenderable,
3715
+ ASCIIFontSelectionHelper,
3716
+ ASCIIFontRenderable,
3717
+ ASCIIFont
3718
+ };
3719
+
3720
+ //# debugId=D81404410A6C6E2564756E2164756E21
3721
+ //# sourceMappingURL=index.js.map