@gridland/web 0.2.3 → 0.2.5

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.
package/dist/core.js DELETED
@@ -1,2912 +0,0 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
- var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
6
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
7
- }) : x)(function(x) {
8
- if (typeof require !== "undefined") return require.apply(this, arguments);
9
- throw Error('Dynamic require of "' + x + '" is not supported');
10
- });
11
- var __export = (target, all) => {
12
- for (var name in all)
13
- __defProp(target, name, { get: all[name], enumerable: true });
14
- };
15
- var __copyProps = (to, from, except, desc) => {
16
- if (from && typeof from === "object" || typeof from === "function") {
17
- for (let key of __getOwnPropNames(from))
18
- if (!__hasOwnProp.call(to, key) && key !== except)
19
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
- }
21
- return to;
22
- };
23
- var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
24
-
25
- // src/TUI.tsx
26
- import {
27
- useRef,
28
- useEffect,
29
- useState
30
- } from "react";
31
-
32
- // src/core-shims/index.ts
33
- var core_shims_exports = {};
34
- __export(core_shims_exports, {
35
- ATTRIBUTE_BASE_BITS: () => ATTRIBUTE_BASE_BITS,
36
- ATTRIBUTE_BASE_MASK: () => ATTRIBUTE_BASE_MASK,
37
- BaseRenderable: () => BaseRenderable,
38
- BorderCharArrays: () => BorderCharArrays,
39
- BorderChars: () => BorderChars,
40
- CliRenderEvents: () => CliRenderEvents,
41
- CliRenderer: () => CliRenderer,
42
- DebugOverlayCorner: () => DebugOverlayCorner,
43
- InternalKeyHandler: () => BrowserInternalKeyHandler,
44
- KeyHandler: () => BrowserKeyHandler,
45
- LayoutEvents: () => LayoutEvents,
46
- OptimizedBuffer: () => BrowserBuffer,
47
- RGBA: () => RGBA,
48
- Renderable: () => Renderable,
49
- RenderableEvents: () => RenderableEvents,
50
- RootRenderable: () => RootRenderable,
51
- Selection: () => Selection,
52
- StyledText: () => StyledText,
53
- SyntaxStyle: () => BrowserSyntaxStyle,
54
- TextAttributes: () => TextAttributes,
55
- TextBuffer: () => BrowserTextBuffer,
56
- TextBufferView: () => BrowserTextBufferView,
57
- Timeline: () => Timeline,
58
- Yoga: () => Yoga,
59
- attributesWithLink: () => attributesWithLink,
60
- bg: () => bg,
61
- black: () => black,
62
- blink: () => blink,
63
- blue: () => blue,
64
- bold: () => bold,
65
- borderCharsToArray: () => borderCharsToArray,
66
- convertGlobalToLocalSelection: () => convertGlobalToLocalSelection,
67
- createCliRenderer: () => createCliRenderer,
68
- createTextAttributes: () => createTextAttributes,
69
- createTimeline: () => createTimeline,
70
- cyan: () => cyan,
71
- dim: () => dim,
72
- engine: () => engine,
73
- fg: () => fg,
74
- getBaseAttributes: () => getBaseAttributes,
75
- getBorderFromSides: () => getBorderFromSides,
76
- getBorderSides: () => getBorderSides,
77
- getLinkId: () => getLinkId,
78
- green: () => green,
79
- hexToRgb: () => hexToRgb,
80
- hsvToRgb: () => hsvToRgb,
81
- isDimensionType: () => isDimensionType,
82
- isFlexBasisType: () => isFlexBasisType,
83
- isMarginType: () => isMarginType,
84
- isOverflowType: () => isOverflowType,
85
- isPaddingType: () => isPaddingType,
86
- isPositionType: () => isPositionType,
87
- isPositionTypeType: () => isPositionTypeType,
88
- isRenderable: () => isRenderable,
89
- isSizeType: () => isSizeType,
90
- isStyledText: () => isStyledText,
91
- isValidBorderStyle: () => isValidBorderStyle,
92
- italic: () => italic,
93
- magenta: () => magenta,
94
- maybeMakeRenderable: () => maybeMakeRenderable,
95
- parseAlign: () => parseAlign,
96
- parseAlignItems: () => parseAlignItems,
97
- parseBorderStyle: () => parseBorderStyle,
98
- parseColor: () => parseColor,
99
- parseFlexDirection: () => parseFlexDirection,
100
- parseJustify: () => parseJustify,
101
- parseOverflow: () => parseOverflow,
102
- parsePositionType: () => parsePositionType,
103
- parseWrap: () => parseWrap,
104
- red: () => red,
105
- resolveRenderLib: () => resolveRenderLib,
106
- reverse: () => reverse,
107
- rgbToHex: () => rgbToHex,
108
- strikethrough: () => strikethrough,
109
- stringToStyledText: () => stringToStyledText,
110
- t: () => t,
111
- underline: () => underline,
112
- validateOptions: () => validateOptions,
113
- visualizeRenderableTree: () => visualizeRenderableTree,
114
- white: () => white,
115
- yellow: () => yellow
116
- });
117
-
118
- // src/core-shims/rgba.ts
119
- var RGBA = class _RGBA {
120
- buffer;
121
- constructor(buffer) {
122
- this.buffer = buffer;
123
- }
124
- static fromArray(array) {
125
- return new _RGBA(array);
126
- }
127
- static fromValues(r, g, b, a = 1) {
128
- return new _RGBA(new Float32Array([r, g, b, a]));
129
- }
130
- static fromInts(r, g, b, a = 255) {
131
- return new _RGBA(new Float32Array([r / 255, g / 255, b / 255, a / 255]));
132
- }
133
- static fromHex(hex) {
134
- return hexToRgb(hex);
135
- }
136
- get r() {
137
- return this.buffer[0];
138
- }
139
- set r(v) {
140
- this.buffer[0] = v;
141
- }
142
- get g() {
143
- return this.buffer[1];
144
- }
145
- set g(v) {
146
- this.buffer[1] = v;
147
- }
148
- get b() {
149
- return this.buffer[2];
150
- }
151
- set b(v) {
152
- this.buffer[2] = v;
153
- }
154
- get a() {
155
- return this.buffer[3];
156
- }
157
- set a(v) {
158
- this.buffer[3] = v;
159
- }
160
- toInts() {
161
- return [
162
- Math.round(this.buffer[0] * 255),
163
- Math.round(this.buffer[1] * 255),
164
- Math.round(this.buffer[2] * 255),
165
- Math.round(this.buffer[3] * 255)
166
- ];
167
- }
168
- map(fn) {
169
- return [fn(this.buffer[0]), fn(this.buffer[1]), fn(this.buffer[2]), fn(this.buffer[3])];
170
- }
171
- toString() {
172
- const [r, g, b, a] = this.toInts();
173
- return `rgba(${r}, ${g}, ${b}, ${a / 255})`;
174
- }
175
- equals(other) {
176
- if (!other) return false;
177
- return this.buffer[0] === other.buffer[0] && this.buffer[1] === other.buffer[1] && this.buffer[2] === other.buffer[2] && this.buffer[3] === other.buffer[3];
178
- }
179
- };
180
- var CSS_COLOR_NAMES = {
181
- black: "#000000",
182
- white: "#ffffff",
183
- red: "#ff0000",
184
- green: "#008000",
185
- blue: "#0000ff",
186
- yellow: "#ffff00",
187
- cyan: "#00ffff",
188
- magenta: "#ff00ff",
189
- silver: "#c0c0c0",
190
- gray: "#808080",
191
- grey: "#808080",
192
- maroon: "#800000",
193
- olive: "#808000",
194
- lime: "#00ff00",
195
- aqua: "#00ffff",
196
- teal: "#008080",
197
- navy: "#000080",
198
- fuchsia: "#ff00ff",
199
- purple: "#800080",
200
- orange: "#ffa500",
201
- transparent: "#00000000",
202
- brightblack: "#808080",
203
- brightred: "#ff5555",
204
- brightgreen: "#55ff55",
205
- brightyellow: "#ffff55",
206
- brightblue: "#5555ff",
207
- brightmagenta: "#ff55ff",
208
- brightcyan: "#55ffff",
209
- brightwhite: "#ffffff"
210
- };
211
- function hexToRgb(hex) {
212
- hex = hex.replace(/^#/, "");
213
- if (hex.length === 3) {
214
- hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
215
- }
216
- if (hex.length === 6) {
217
- hex = hex + "ff";
218
- }
219
- const r = parseInt(hex.substring(0, 2), 16) / 255;
220
- const g = parseInt(hex.substring(2, 4), 16) / 255;
221
- const b = parseInt(hex.substring(4, 6), 16) / 255;
222
- const a = parseInt(hex.substring(6, 8), 16) / 255;
223
- return RGBA.fromValues(r, g, b, a);
224
- }
225
- function rgbToHex(rgb) {
226
- const [r, g, b] = rgb.toInts();
227
- return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
228
- }
229
- function hsvToRgb(h, s, v) {
230
- const c = v * s;
231
- const x = c * (1 - Math.abs(h / 60 % 2 - 1));
232
- const m = v - c;
233
- let r = 0, g = 0, b = 0;
234
- if (h < 60) {
235
- r = c;
236
- g = x;
237
- b = 0;
238
- } else if (h < 120) {
239
- r = x;
240
- g = c;
241
- b = 0;
242
- } else if (h < 180) {
243
- r = 0;
244
- g = c;
245
- b = x;
246
- } else if (h < 240) {
247
- r = 0;
248
- g = x;
249
- b = c;
250
- } else if (h < 300) {
251
- r = x;
252
- g = 0;
253
- b = c;
254
- } else {
255
- r = c;
256
- g = 0;
257
- b = x;
258
- }
259
- return RGBA.fromValues(r + m, g + m, b + m, 1);
260
- }
261
- function parseColor(color) {
262
- if (color instanceof RGBA) return color;
263
- if (typeof color !== "string") return RGBA.fromValues(1, 1, 1, 1);
264
- const lower = color.toLowerCase().trim();
265
- if (CSS_COLOR_NAMES[lower]) {
266
- return hexToRgb(CSS_COLOR_NAMES[lower]);
267
- }
268
- if (lower.startsWith("#")) {
269
- return hexToRgb(lower);
270
- }
271
- const rgbaMatch = lower.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)$/);
272
- if (rgbaMatch) {
273
- return RGBA.fromInts(
274
- parseInt(rgbaMatch[1]),
275
- parseInt(rgbaMatch[2]),
276
- parseInt(rgbaMatch[3]),
277
- rgbaMatch[4] ? Math.round(parseFloat(rgbaMatch[4]) * 255) : 255
278
- );
279
- }
280
- return RGBA.fromValues(1, 1, 1, 1);
281
- }
282
-
283
- // src/core-shims/types.ts
284
- var TextAttributes = {
285
- NONE: 0,
286
- BOLD: 1 << 0,
287
- DIM: 1 << 1,
288
- ITALIC: 1 << 2,
289
- UNDERLINE: 1 << 3,
290
- BLINK: 1 << 4,
291
- INVERSE: 1 << 5,
292
- HIDDEN: 1 << 6,
293
- STRIKETHROUGH: 1 << 7
294
- };
295
- var ATTRIBUTE_BASE_BITS = 8;
296
- var ATTRIBUTE_BASE_MASK = 255;
297
- function getBaseAttributes(attr) {
298
- return attr & 255;
299
- }
300
- var DebugOverlayCorner = /* @__PURE__ */ ((DebugOverlayCorner2) => {
301
- DebugOverlayCorner2[DebugOverlayCorner2["topLeft"] = 0] = "topLeft";
302
- DebugOverlayCorner2[DebugOverlayCorner2["topRight"] = 1] = "topRight";
303
- DebugOverlayCorner2[DebugOverlayCorner2["bottomLeft"] = 2] = "bottomLeft";
304
- DebugOverlayCorner2[DebugOverlayCorner2["bottomRight"] = 3] = "bottomRight";
305
- return DebugOverlayCorner2;
306
- })(DebugOverlayCorner || {});
307
-
308
- // src/core-shims/index.ts
309
- __reExport(core_shims_exports, Box_star);
310
- __reExport(core_shims_exports, Text_star);
311
- __reExport(core_shims_exports, TextNode_star);
312
- __reExport(core_shims_exports, Code_star);
313
- __reExport(core_shims_exports, Diff_star);
314
- __reExport(core_shims_exports, Input_star);
315
- __reExport(core_shims_exports, Select_star);
316
- __reExport(core_shims_exports, TabSelect_star);
317
- __reExport(core_shims_exports, Textarea_star);
318
- __reExport(core_shims_exports, ScrollBox_star);
319
- __reExport(core_shims_exports, ScrollBar_star);
320
- __reExport(core_shims_exports, Slider_star);
321
- __reExport(core_shims_exports, ASCIIFont_star);
322
- __reExport(core_shims_exports, LineNumberRenderable_star);
323
- __reExport(core_shims_exports, Markdown_star);
324
- __reExport(core_shims_exports, FrameBuffer_star);
325
- __reExport(core_shims_exports, TextBufferRenderable_star);
326
- import {
327
- Renderable,
328
- BaseRenderable,
329
- RootRenderable,
330
- LayoutEvents,
331
- RenderableEvents,
332
- isRenderable
333
- } from "../../../../opentui/packages/core/src/Renderable";
334
- import {
335
- BorderChars,
336
- BorderCharArrays,
337
- isValidBorderStyle,
338
- parseBorderStyle,
339
- getBorderFromSides,
340
- getBorderSides,
341
- borderCharsToArray
342
- } from "../../../../opentui/packages/core/src/lib/border";
343
- import {
344
- StyledText,
345
- isStyledText,
346
- stringToStyledText,
347
- t,
348
- bold,
349
- italic,
350
- underline,
351
- strikethrough,
352
- dim,
353
- reverse,
354
- blink,
355
- fg,
356
- bg,
357
- black,
358
- red,
359
- green,
360
- yellow,
361
- blue,
362
- magenta,
363
- cyan,
364
- white
365
- } from "../../../../opentui/packages/core/src/lib/styled-text";
366
- import {
367
- parseAlign,
368
- parseAlignItems,
369
- parseFlexDirection,
370
- parseJustify,
371
- parseOverflow,
372
- parsePositionType,
373
- parseWrap
374
- } from "../../../../opentui/packages/core/src/lib/yoga.options";
375
- import {
376
- validateOptions,
377
- isPositionType,
378
- isDimensionType,
379
- isFlexBasisType,
380
- isSizeType,
381
- isMarginType,
382
- isPaddingType,
383
- isPositionTypeType,
384
- isOverflowType
385
- } from "../../../../opentui/packages/core/src/lib/renderable.validations";
386
- import {
387
- Selection,
388
- convertGlobalToLocalSelection
389
- } from "../../../../opentui/packages/core/src/lib/selection";
390
- import { maybeMakeRenderable } from "../../../../opentui/packages/core/src/renderables/composition/vnode";
391
- import * as Box_star from "../../../../opentui/packages/core/src/renderables/Box";
392
- import * as Text_star from "../../../../opentui/packages/core/src/renderables/Text";
393
- import * as TextNode_star from "../../../../opentui/packages/core/src/renderables/TextNode";
394
- import * as Code_star from "../../../../opentui/packages/core/src/renderables/Code";
395
- import * as Diff_star from "../../../../opentui/packages/core/src/renderables/Diff";
396
- import * as Input_star from "../../../../opentui/packages/core/src/renderables/Input";
397
- import * as Select_star from "../../../../opentui/packages/core/src/renderables/Select";
398
- import * as TabSelect_star from "../../../../opentui/packages/core/src/renderables/TabSelect";
399
- import * as Textarea_star from "../../../../opentui/packages/core/src/renderables/Textarea";
400
- import * as ScrollBox_star from "../../../../opentui/packages/core/src/renderables/ScrollBox";
401
- import * as ScrollBar_star from "../../../../opentui/packages/core/src/renderables/ScrollBar";
402
- import * as Slider_star from "../../../../opentui/packages/core/src/renderables/Slider";
403
- import * as ASCIIFont_star from "../../../../opentui/packages/core/src/renderables/ASCIIFont";
404
- import * as LineNumberRenderable_star from "../../../../opentui/packages/core/src/renderables/LineNumberRenderable";
405
- import * as Markdown_star from "../../../../opentui/packages/core/src/renderables/Markdown";
406
- import * as FrameBuffer_star from "../../../../opentui/packages/core/src/renderables/FrameBuffer";
407
- import * as TextBufferRenderable_star from "../../../../opentui/packages/core/src/renderables/TextBufferRenderable";
408
- import { TextBufferRenderable } from "../../../../opentui/packages/core/src/renderables/TextBufferRenderable";
409
-
410
- // src/browser-text-buffer.ts
411
- var BrowserTextBuffer = class _BrowserTextBuffer {
412
- _text = "";
413
- _chunks = [];
414
- _defaultFg = null;
415
- _defaultBg = null;
416
- _defaultAttributes = 0;
417
- _syntaxStyle = null;
418
- _tabWidth = 4;
419
- _widthMethod;
420
- constructor(widthMethod) {
421
- this._widthMethod = widthMethod;
422
- }
423
- static create(widthMethod) {
424
- return new _BrowserTextBuffer(widthMethod);
425
- }
426
- // Compat property
427
- get ptr() {
428
- return 1;
429
- }
430
- get length() {
431
- return this._text.length;
432
- }
433
- get byteSize() {
434
- return new TextEncoder().encode(this._text).length;
435
- }
436
- setText(text) {
437
- this._text = text;
438
- this._chunks = [
439
- {
440
- __isChunk: true,
441
- text,
442
- fg: this._defaultFg ?? void 0,
443
- bg: this._defaultBg ?? void 0,
444
- attributes: this._defaultAttributes
445
- }
446
- ];
447
- }
448
- append(text) {
449
- this._text += text;
450
- this._chunks.push({
451
- __isChunk: true,
452
- text,
453
- fg: this._defaultFg ?? void 0,
454
- bg: this._defaultBg ?? void 0,
455
- attributes: this._defaultAttributes
456
- });
457
- }
458
- setStyledText(styledText) {
459
- this._chunks = styledText.chunks.map((chunk) => ({
460
- __isChunk: true,
461
- text: chunk.text,
462
- fg: chunk.fg ?? this._defaultFg ?? void 0,
463
- bg: chunk.bg ?? this._defaultBg ?? void 0,
464
- attributes: chunk.attributes ?? this._defaultAttributes,
465
- link: chunk.link
466
- }));
467
- this._text = this._chunks.map((c) => c.text).join("");
468
- }
469
- getPlainText() {
470
- return this._text;
471
- }
472
- getTextRange(startOffset, endOffset) {
473
- return this._text.slice(startOffset, endOffset);
474
- }
475
- getLineCount() {
476
- if (this._text.length === 0) return 0;
477
- return this._text.split("\n").length;
478
- }
479
- getChunks() {
480
- return this._chunks;
481
- }
482
- setDefaultFg(fg2) {
483
- this._defaultFg = fg2;
484
- }
485
- setDefaultBg(bg2) {
486
- this._defaultBg = bg2;
487
- }
488
- setDefaultAttributes(attributes) {
489
- this._defaultAttributes = attributes ?? 0;
490
- }
491
- resetDefaults() {
492
- this._defaultFg = null;
493
- this._defaultBg = null;
494
- this._defaultAttributes = 0;
495
- }
496
- get defaultFg() {
497
- return this._defaultFg;
498
- }
499
- get defaultBg() {
500
- return this._defaultBg;
501
- }
502
- get defaultAttributes() {
503
- return this._defaultAttributes;
504
- }
505
- setSyntaxStyle(style) {
506
- this._syntaxStyle = style;
507
- }
508
- getSyntaxStyle() {
509
- return this._syntaxStyle;
510
- }
511
- setTabWidth(width) {
512
- this._tabWidth = width;
513
- }
514
- getTabWidth() {
515
- return this._tabWidth;
516
- }
517
- addHighlightByCharRange(_highlight) {
518
- }
519
- addHighlight(_lineIdx, _highlight) {
520
- }
521
- removeHighlightsByRef(_hlRef) {
522
- }
523
- clearLineHighlights(_lineIdx) {
524
- }
525
- clearAllHighlights() {
526
- }
527
- getLineHighlights(_lineIdx) {
528
- return [];
529
- }
530
- getHighlightCount() {
531
- return 0;
532
- }
533
- loadFile(_path) {
534
- }
535
- clear() {
536
- this._text = "";
537
- this._chunks = [];
538
- }
539
- reset() {
540
- this.clear();
541
- this.resetDefaults();
542
- }
543
- destroy() {
544
- this._text = "";
545
- this._chunks = [];
546
- }
547
- };
548
-
549
- // src/browser-text-buffer-view.ts
550
- var defaultFg = {
551
- r: 1,
552
- g: 1,
553
- b: 1,
554
- a: 1,
555
- buffer: new Float32Array([1, 1, 1, 1])
556
- };
557
- var defaultBg = {
558
- r: 0,
559
- g: 0,
560
- b: 0,
561
- a: 0,
562
- buffer: new Float32Array([0, 0, 0, 0])
563
- };
564
- var BrowserTextBufferView = class _BrowserTextBufferView {
565
- textBuffer;
566
- _wrapWidth = null;
567
- _wrapMode = "word";
568
- _viewportX = 0;
569
- _viewportY = 0;
570
- _viewportWidth = 0;
571
- _viewportHeight = 0;
572
- _truncate = false;
573
- textAlign = "left";
574
- _selection = null;
575
- _selectionBg;
576
- _selectionFg;
577
- constructor(textBuffer) {
578
- this.textBuffer = textBuffer;
579
- }
580
- static create(textBuffer) {
581
- return new _BrowserTextBufferView(textBuffer);
582
- }
583
- // Compat
584
- get ptr() {
585
- return 1;
586
- }
587
- get lineInfo() {
588
- const lines = this.getAllWrappedLines();
589
- const lineStarts = [];
590
- const lineWidths = [];
591
- const lineSources = [];
592
- const lineWraps = [];
593
- let maxWidth = 0;
594
- let offset = 0;
595
- for (let i = 0; i < lines.length; i++) {
596
- lineStarts.push(offset);
597
- const lineText = lines[i].map((c) => c.text).join("");
598
- const w = lineText.length;
599
- lineWidths.push(w);
600
- lineSources.push(0);
601
- lineWraps.push(0);
602
- if (w > maxWidth) maxWidth = w;
603
- offset += lineText.length + 1;
604
- }
605
- return { lineStarts, lineWidths, maxLineWidth: maxWidth, lineSources, lineWraps };
606
- }
607
- get logicalLineInfo() {
608
- return this.lineInfo;
609
- }
610
- setSelection(start, end, bgColor, fgColor) {
611
- this._selection = { start, end };
612
- this._selectionBg = bgColor;
613
- this._selectionFg = fgColor;
614
- }
615
- updateSelection(end, bgColor, fgColor) {
616
- if (this._selection) {
617
- this._selection.end = end;
618
- }
619
- if (bgColor) this._selectionBg = bgColor;
620
- if (fgColor) this._selectionFg = fgColor;
621
- }
622
- resetSelection() {
623
- this._selection = null;
624
- }
625
- getSelection() {
626
- return this._selection;
627
- }
628
- hasSelection() {
629
- return this._selection !== null;
630
- }
631
- setLocalSelection(_anchorX, _anchorY, _focusX, _focusY, _bgColor, _fgColor) {
632
- return false;
633
- }
634
- updateLocalSelection(_anchorX, _anchorY, _focusX, _focusY, _bgColor, _fgColor) {
635
- return false;
636
- }
637
- resetLocalSelection() {
638
- }
639
- getSelectedText() {
640
- if (!this._selection) return "";
641
- const text = this.textBuffer.getPlainText();
642
- const start = Math.min(this._selection.start, this._selection.end);
643
- const end = Math.max(this._selection.start, this._selection.end);
644
- return text.slice(start, end);
645
- }
646
- getPlainText() {
647
- return this.textBuffer.getPlainText();
648
- }
649
- setWrapWidth(width) {
650
- this._wrapWidth = width;
651
- }
652
- setWrapMode(mode) {
653
- this._wrapMode = mode;
654
- }
655
- setViewportSize(width, height) {
656
- this._viewportWidth = width;
657
- this._viewportHeight = height;
658
- if (this._wrapMode !== "none" && width > 0 && this._wrapWidth !== width) {
659
- this._wrapWidth = width;
660
- }
661
- }
662
- setViewport(x, y, width, height) {
663
- this._viewportX = x;
664
- this._viewportY = y;
665
- this._viewportWidth = width;
666
- this._viewportHeight = height;
667
- if (this._wrapMode !== "none" && width > 0 && this._wrapWidth !== width) {
668
- this._wrapWidth = width;
669
- }
670
- }
671
- setTabIndicator(_indicator) {
672
- }
673
- setTabIndicatorColor(_color) {
674
- }
675
- setTruncate(truncate) {
676
- this._truncate = truncate;
677
- }
678
- getAllWrappedLines() {
679
- const chunks = this.textBuffer.getChunks();
680
- if (chunks.length === 0) return [];
681
- const dfg = this.textBuffer.defaultFg ?? defaultFg;
682
- const dbg = this.textBuffer.defaultBg ?? defaultBg;
683
- const logicalLines = [[]];
684
- for (const chunk of chunks) {
685
- const parts = chunk.text.split("\n");
686
- for (let i = 0; i < parts.length; i++) {
687
- if (i > 0) {
688
- logicalLines.push([]);
689
- }
690
- if (parts[i].length > 0) {
691
- const vlc = {
692
- text: parts[i],
693
- fg: chunk.fg ?? dfg,
694
- bg: chunk.bg ?? dbg,
695
- attributes: chunk.attributes ?? 0
696
- };
697
- if (chunk.link) vlc.link = chunk.link;
698
- logicalLines[logicalLines.length - 1].push(vlc);
699
- }
700
- }
701
- }
702
- if (this._wrapMode === "none" || !this._wrapWidth || this._wrapWidth <= 0) {
703
- return logicalLines;
704
- }
705
- const wrappedLines = [];
706
- const wrapWidth = this._wrapWidth;
707
- for (const logicalLine of logicalLines) {
708
- const lineText = logicalLine.map((c) => c.text).join("");
709
- if (lineText.length <= wrapWidth) {
710
- wrappedLines.push(logicalLine);
711
- continue;
712
- }
713
- if (this._wrapMode === "char") {
714
- let pos = 0;
715
- while (pos < lineText.length) {
716
- const end = Math.min(pos + wrapWidth, lineText.length);
717
- const sliceChunks = this.sliceChunks(logicalLine, pos, end);
718
- wrappedLines.push(sliceChunks);
719
- pos = end;
720
- }
721
- } else {
722
- let pos = 0;
723
- while (pos < lineText.length) {
724
- let end = Math.min(pos + wrapWidth, lineText.length);
725
- if (end < lineText.length) {
726
- let breakPos = end;
727
- while (breakPos > pos && lineText[breakPos] !== " ") {
728
- breakPos--;
729
- }
730
- if (breakPos > pos) {
731
- end = breakPos + 1;
732
- }
733
- }
734
- const sliceChunks = this.sliceChunks(logicalLine, pos, end);
735
- wrappedLines.push(sliceChunks);
736
- pos = end;
737
- }
738
- }
739
- }
740
- return wrappedLines;
741
- }
742
- sliceChunks(chunks, start, end) {
743
- const result = [];
744
- let offset = 0;
745
- for (const chunk of chunks) {
746
- const chunkStart = offset;
747
- const chunkEnd = offset + chunk.text.length;
748
- if (chunkEnd <= start || chunkStart >= end) {
749
- offset = chunkEnd;
750
- continue;
751
- }
752
- const sliceStart = Math.max(0, start - chunkStart);
753
- const sliceEnd = Math.min(chunk.text.length, end - chunkStart);
754
- const slicedText = chunk.text.slice(sliceStart, sliceEnd);
755
- if (slicedText.length > 0) {
756
- const sliced = {
757
- text: slicedText,
758
- fg: chunk.fg,
759
- bg: chunk.bg,
760
- attributes: chunk.attributes
761
- };
762
- if (chunk.link) sliced.link = chunk.link;
763
- result.push(sliced);
764
- }
765
- offset = chunkEnd;
766
- }
767
- return result;
768
- }
769
- measureForDimensions(width, height) {
770
- const oldWrapWidth = this._wrapWidth;
771
- if (width > 0 && this._wrapMode !== "none") {
772
- this._wrapWidth = width;
773
- } else if (width === 0) {
774
- this._wrapWidth = null;
775
- }
776
- const lines = this.getAllWrappedLines();
777
- this._wrapWidth = oldWrapWidth;
778
- let maxWidth = 0;
779
- for (const line of lines) {
780
- const lineWidth = line.reduce((sum, c) => sum + c.text.length, 0);
781
- if (lineWidth > maxWidth) maxWidth = lineWidth;
782
- }
783
- return {
784
- lineCount: lines.length,
785
- maxWidth
786
- };
787
- }
788
- getVirtualLineCount() {
789
- return this.getAllWrappedLines().length;
790
- }
791
- getVisibleLines() {
792
- const allLines = this.getAllWrappedLines();
793
- if (allLines.length === 0) return null;
794
- const startLine = this._viewportY;
795
- const endLine = this._viewportHeight > 0 ? Math.min(startLine + this._viewportHeight, allLines.length) : allLines.length;
796
- const visibleLines = [];
797
- for (let i = startLine; i < endLine; i++) {
798
- if (i >= 0 && i < allLines.length) {
799
- visibleLines.push({ chunks: allLines[i] });
800
- }
801
- }
802
- return visibleLines;
803
- }
804
- destroy() {
805
- }
806
- };
807
-
808
- // src/browser-syntax-style.ts
809
- var BrowserSyntaxStyle = class _BrowserSyntaxStyle {
810
- _destroyed = false;
811
- static create() {
812
- return new _BrowserSyntaxStyle();
813
- }
814
- static fromTheme(_theme) {
815
- return new _BrowserSyntaxStyle();
816
- }
817
- static fromStyles(_styles) {
818
- return new _BrowserSyntaxStyle();
819
- }
820
- get ptr() {
821
- return 0;
822
- }
823
- registerStyle(_name, _style) {
824
- return 0;
825
- }
826
- resolveStyleId(_name) {
827
- return null;
828
- }
829
- getStyleId(_name) {
830
- return null;
831
- }
832
- getStyleCount() {
833
- return 0;
834
- }
835
- clearNameCache() {
836
- }
837
- getStyle(_name) {
838
- return void 0;
839
- }
840
- mergeStyles(..._styleNames) {
841
- return { attributes: 0 };
842
- }
843
- clearCache() {
844
- }
845
- getCacheSize() {
846
- return 0;
847
- }
848
- getAllStyles() {
849
- return /* @__PURE__ */ new Map();
850
- }
851
- getRegisteredNames() {
852
- return [];
853
- }
854
- destroy() {
855
- this._destroyed = true;
856
- }
857
- };
858
-
859
- // src/browser-render-context.ts
860
- import { EventEmitter } from "events";
861
- var BrowserKeyHandler = class extends EventEmitter {
862
- constructor() {
863
- super();
864
- }
865
- processInput(_data) {
866
- return false;
867
- }
868
- };
869
- var BrowserInternalKeyHandler = class extends BrowserKeyHandler {
870
- renderableHandlers = /* @__PURE__ */ new Map();
871
- onInternal(event, handler) {
872
- if (!this.renderableHandlers.has(event)) {
873
- this.renderableHandlers.set(event, /* @__PURE__ */ new Set());
874
- }
875
- this.renderableHandlers.get(event).add(handler);
876
- }
877
- offInternal(event, handler) {
878
- const handlers = this.renderableHandlers.get(event);
879
- if (handlers) {
880
- handlers.delete(handler);
881
- }
882
- }
883
- emit(event, ...args) {
884
- const hasGlobal = super.emit(event, ...args);
885
- const renderableSet = this.renderableHandlers.get(event);
886
- if (renderableSet && renderableSet.size > 0) {
887
- for (const handler of [...renderableSet]) {
888
- try {
889
- handler(...args);
890
- } catch (e) {
891
- console.error(`[BrowserInternalKeyHandler] Error in ${event} handler:`, e);
892
- }
893
- if (args[0] && args[0].propagationStopped) break;
894
- }
895
- return true;
896
- }
897
- return hasGlobal;
898
- }
899
- };
900
- var BrowserRenderContext = class extends EventEmitter {
901
- _width;
902
- _height;
903
- _widthMethod;
904
- _renderRequested = false;
905
- _onRenderRequest = null;
906
- _lifecyclePasses = /* @__PURE__ */ new Set();
907
- _focusedRenderable = null;
908
- keyInput;
909
- _internalKeyInput;
910
- constructor(width, height, widthMethod = "wcwidth") {
911
- super();
912
- this._width = width;
913
- this._height = height;
914
- this._widthMethod = widthMethod;
915
- this.keyInput = new BrowserKeyHandler();
916
- this._internalKeyInput = new BrowserInternalKeyHandler();
917
- }
918
- get width() {
919
- return this._width;
920
- }
921
- get height() {
922
- return this._height;
923
- }
924
- get widthMethod() {
925
- return this._widthMethod;
926
- }
927
- get capabilities() {
928
- return null;
929
- }
930
- get hasSelection() {
931
- return false;
932
- }
933
- get currentFocusedRenderable() {
934
- return this._focusedRenderable;
935
- }
936
- setOnRenderRequest(callback) {
937
- this._onRenderRequest = callback;
938
- }
939
- resize(width, height) {
940
- this._width = width;
941
- this._height = height;
942
- this.emit("resize", width, height);
943
- }
944
- // RenderContext interface methods
945
- addToHitGrid(_x, _y, _width, _height, _id) {
946
- }
947
- pushHitGridScissorRect(_x, _y, _width, _height) {
948
- }
949
- popHitGridScissorRect() {
950
- }
951
- clearHitGridScissorRects() {
952
- }
953
- requestRender() {
954
- if (this._renderRequested) return;
955
- this._renderRequested = true;
956
- this._onRenderRequest?.();
957
- this._renderRequested = false;
958
- }
959
- setCursorPosition(_x, _y, _visible) {
960
- }
961
- setCursorStyle(_options) {
962
- }
963
- setCursorColor(_color) {
964
- }
965
- setMousePointer(_shape) {
966
- }
967
- requestLive() {
968
- }
969
- dropLive() {
970
- }
971
- getSelection() {
972
- return null;
973
- }
974
- requestSelectionUpdate() {
975
- }
976
- focusRenderable(renderable) {
977
- if (this._focusedRenderable && this._focusedRenderable !== renderable) {
978
- this._focusedRenderable.blur?.();
979
- }
980
- this._focusedRenderable = renderable;
981
- }
982
- registerLifecyclePass(renderable) {
983
- this._lifecyclePasses.add(renderable);
984
- }
985
- unregisterLifecyclePass(renderable) {
986
- this._lifecyclePasses.delete(renderable);
987
- }
988
- getLifecyclePasses() {
989
- return this._lifecyclePasses;
990
- }
991
- clearSelection() {
992
- }
993
- startSelection(_renderable, _x, _y) {
994
- }
995
- updateSelection(_currentRenderable, _x, _y, _options) {
996
- }
997
- };
998
-
999
- // src/core-shims/index.ts
1000
- import {
1001
- CliRenderer,
1002
- CliRenderEvents,
1003
- createCliRenderer
1004
- } from "../../../../opentui/packages/core/src/renderer";
1005
- import {
1006
- Timeline,
1007
- engine,
1008
- createTimeline
1009
- } from "../../../../opentui/packages/core/src/animation/Timeline";
1010
- import * as Yoga from "yoga-layout";
1011
- Object.defineProperty(TextBufferRenderable.prototype, "textAlign", {
1012
- get() {
1013
- return this.textBufferView?.textAlign ?? "left";
1014
- },
1015
- set(value) {
1016
- if (this.textBufferView) {
1017
- this.textBufferView.textAlign = value;
1018
- this.requestRender();
1019
- }
1020
- },
1021
- enumerable: true,
1022
- configurable: true
1023
- });
1024
- function resolveRenderLib() {
1025
- return null;
1026
- }
1027
- function createTextAttributes(opts = {}) {
1028
- const TA = {
1029
- NONE: 0,
1030
- BOLD: 1,
1031
- DIM: 2,
1032
- ITALIC: 4,
1033
- UNDERLINE: 8,
1034
- BLINK: 16,
1035
- INVERSE: 32,
1036
- HIDDEN: 64,
1037
- STRIKETHROUGH: 128
1038
- };
1039
- let attr = TA.NONE;
1040
- if (opts.bold) attr |= TA.BOLD;
1041
- if (opts.italic) attr |= TA.ITALIC;
1042
- if (opts.underline) attr |= TA.UNDERLINE;
1043
- if (opts.dim) attr |= TA.DIM;
1044
- if (opts.blink) attr |= TA.BLINK;
1045
- if (opts.inverse) attr |= TA.INVERSE;
1046
- if (opts.hidden) attr |= TA.HIDDEN;
1047
- if (opts.strikethrough) attr |= TA.STRIKETHROUGH;
1048
- return attr;
1049
- }
1050
- function attributesWithLink(baseAttributes, linkId) {
1051
- return baseAttributes & 255 | (linkId & 16777215) << 8;
1052
- }
1053
- function getLinkId(attributes) {
1054
- return attributes >>> 8 & 16777215;
1055
- }
1056
- function visualizeRenderableTree(..._args) {
1057
- }
1058
-
1059
- // src/browser-buffer.ts
1060
- var CONTINUATION = 3221225472;
1061
- var BrowserBuffer = class _BrowserBuffer {
1062
- id;
1063
- respectAlpha;
1064
- _width;
1065
- _height;
1066
- _widthMethod;
1067
- // Cell data - same layout as native OptimizedBuffer
1068
- char;
1069
- fg;
1070
- bg;
1071
- attributes;
1072
- scissorStack = [];
1073
- opacityStack = [];
1074
- // Link registry for clickable links
1075
- linkRegistry = /* @__PURE__ */ new Map();
1076
- nextLinkId = 1;
1077
- constructor(width, height, options = {}) {
1078
- this._width = width;
1079
- this._height = height;
1080
- this._widthMethod = options.widthMethod ?? "wcwidth";
1081
- this.respectAlpha = options.respectAlpha ?? false;
1082
- this.id = options.id ?? `browser-buffer-${Math.random().toString(36).slice(2, 8)}`;
1083
- const size = width * height;
1084
- this.char = new Uint32Array(size);
1085
- this.fg = new Float32Array(size * 4);
1086
- this.bg = new Float32Array(size * 4);
1087
- this.attributes = new Uint32Array(size);
1088
- this.char.fill(32);
1089
- }
1090
- static create(width, height, widthMethod, options) {
1091
- return new _BrowserBuffer(width, height, { ...options, widthMethod });
1092
- }
1093
- get width() {
1094
- return this._width;
1095
- }
1096
- get height() {
1097
- return this._height;
1098
- }
1099
- get widthMethod() {
1100
- return this._widthMethod;
1101
- }
1102
- get ptr() {
1103
- return 0;
1104
- }
1105
- get buffers() {
1106
- return {
1107
- char: this.char,
1108
- fg: this.fg,
1109
- bg: this.bg,
1110
- attributes: this.attributes
1111
- };
1112
- }
1113
- setRespectAlpha(respectAlpha) {
1114
- this.respectAlpha = respectAlpha;
1115
- }
1116
- getNativeId() {
1117
- return this.id;
1118
- }
1119
- registerLink(url) {
1120
- const id = this.nextLinkId++;
1121
- this.linkRegistry.set(id, url);
1122
- return id;
1123
- }
1124
- getLinkUrl(linkId) {
1125
- return this.linkRegistry.get(linkId);
1126
- }
1127
- isInScissor(x, y) {
1128
- if (this.scissorStack.length === 0) return true;
1129
- const rect = this.scissorStack[this.scissorStack.length - 1];
1130
- return x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height;
1131
- }
1132
- getCurrentOpacityMultiplier() {
1133
- if (this.opacityStack.length === 0) return 1;
1134
- return this.opacityStack[this.opacityStack.length - 1];
1135
- }
1136
- applyOpacity(color) {
1137
- const multiplier = this.getCurrentOpacityMultiplier();
1138
- if (multiplier >= 1) return color;
1139
- return {
1140
- r: color.r,
1141
- g: color.g,
1142
- b: color.b,
1143
- a: color.a * multiplier,
1144
- buffer: new Float32Array([color.r, color.g, color.b, color.a * multiplier]),
1145
- toInts: color.toInts,
1146
- equals: color.equals,
1147
- map: color.map,
1148
- toString: color.toString
1149
- };
1150
- }
1151
- clear(bg2) {
1152
- const size = this._width * this._height;
1153
- this.char.fill(32);
1154
- this.attributes.fill(0);
1155
- this.linkRegistry.clear();
1156
- this.nextLinkId = 1;
1157
- if (bg2) {
1158
- for (let i = 0; i < size; i++) {
1159
- const offset = i * 4;
1160
- this.bg[offset] = bg2.r;
1161
- this.bg[offset + 1] = bg2.g;
1162
- this.bg[offset + 2] = bg2.b;
1163
- this.bg[offset + 3] = bg2.a;
1164
- this.fg[offset] = 0;
1165
- this.fg[offset + 1] = 0;
1166
- this.fg[offset + 2] = 0;
1167
- this.fg[offset + 3] = 0;
1168
- }
1169
- } else {
1170
- this.fg.fill(0);
1171
- this.bg.fill(0);
1172
- }
1173
- }
1174
- setCell(x, y, char, fgColor, bgColor, attr = 0) {
1175
- if (x < 0 || x >= this._width || y < 0 || y >= this._height) return;
1176
- if (!this.isInScissor(x, y)) return;
1177
- const idx = y * this._width + x;
1178
- const offset = idx * 4;
1179
- const effectiveBg = this.applyOpacity(bgColor);
1180
- const effectiveFg = this.applyOpacity(fgColor);
1181
- this.char[idx] = char.codePointAt(0) ?? 32;
1182
- this.attributes[idx] = attr;
1183
- this.fg[offset] = effectiveFg.r;
1184
- this.fg[offset + 1] = effectiveFg.g;
1185
- this.fg[offset + 2] = effectiveFg.b;
1186
- this.fg[offset + 3] = effectiveFg.a;
1187
- this.bg[offset] = effectiveBg.r;
1188
- this.bg[offset + 1] = effectiveBg.g;
1189
- this.bg[offset + 2] = effectiveBg.b;
1190
- this.bg[offset + 3] = effectiveBg.a;
1191
- }
1192
- setCellWithAlphaBlending(x, y, char, fgColor, bgColor, attr = 0) {
1193
- this.setCell(x, y, char, fgColor, bgColor, attr);
1194
- }
1195
- drawChar(charCode, x, y, fgColor, bgColor, attr = 0) {
1196
- if (x < 0 || x >= this._width || y < 0 || y >= this._height) return;
1197
- if (!this.isInScissor(x, y)) return;
1198
- const idx = y * this._width + x;
1199
- const offset = idx * 4;
1200
- const effectiveBg = this.applyOpacity(bgColor);
1201
- const effectiveFg = this.applyOpacity(fgColor);
1202
- this.char[idx] = charCode;
1203
- this.attributes[idx] = attr;
1204
- this.fg[offset] = effectiveFg.r;
1205
- this.fg[offset + 1] = effectiveFg.g;
1206
- this.fg[offset + 2] = effectiveFg.b;
1207
- this.fg[offset + 3] = effectiveFg.a;
1208
- this.bg[offset] = effectiveBg.r;
1209
- this.bg[offset + 1] = effectiveBg.g;
1210
- this.bg[offset + 2] = effectiveBg.b;
1211
- this.bg[offset + 3] = effectiveBg.a;
1212
- }
1213
- drawText(text, x, y, fgColor, bgColor, attr = 0, _selection) {
1214
- const transparentBg = {
1215
- r: 0,
1216
- g: 0,
1217
- b: 0,
1218
- a: 0,
1219
- buffer: new Float32Array([0, 0, 0, 0])
1220
- };
1221
- const bg2 = bgColor ?? transparentBg;
1222
- let curX = x;
1223
- for (const ch of text) {
1224
- if (curX >= this._width) break;
1225
- if (curX >= 0) {
1226
- this.setCell(curX, y, ch, fgColor, bg2, attr);
1227
- }
1228
- curX++;
1229
- }
1230
- }
1231
- fillRect(x, y, width, height, bgColor) {
1232
- for (let row = y; row < y + height && row < this._height; row++) {
1233
- for (let col = x; col < x + width && col < this._width; col++) {
1234
- if (col < 0 || row < 0) continue;
1235
- if (!this.isInScissor(col, row)) continue;
1236
- const idx = row * this._width + col;
1237
- const offset = idx * 4;
1238
- const effectiveBg = this.applyOpacity(bgColor);
1239
- this.char[idx] = 32;
1240
- this.bg[offset] = effectiveBg.r;
1241
- this.bg[offset + 1] = effectiveBg.g;
1242
- this.bg[offset + 2] = effectiveBg.b;
1243
- this.bg[offset + 3] = effectiveBg.a;
1244
- }
1245
- }
1246
- }
1247
- drawBox(options) {
1248
- const {
1249
- x,
1250
- y,
1251
- width,
1252
- height,
1253
- border,
1254
- borderColor,
1255
- backgroundColor,
1256
- shouldFill = true,
1257
- title,
1258
- titleAlignment = "left"
1259
- } = options;
1260
- if (width <= 0 || height <= 0) return;
1261
- const sides = {
1262
- top: border === true || Array.isArray(border) && border.includes("top"),
1263
- right: border === true || Array.isArray(border) && border.includes("right"),
1264
- bottom: border === true || Array.isArray(border) && border.includes("bottom"),
1265
- left: border === true || Array.isArray(border) && border.includes("left")
1266
- };
1267
- const borderChars = options.customBorderChars ?? this.getDefaultBorderChars(options.borderStyle);
1268
- if (shouldFill) {
1269
- const fillStartX = x + (sides.left ? 1 : 0);
1270
- const fillStartY = y + (sides.top ? 1 : 0);
1271
- const fillWidth = width - (sides.left ? 1 : 0) - (sides.right ? 1 : 0);
1272
- const fillHeight = height - (sides.top ? 1 : 0) - (sides.bottom ? 1 : 0);
1273
- if (fillWidth > 0 && fillHeight > 0) {
1274
- this.fillRect(fillStartX, fillStartY, fillWidth, fillHeight, backgroundColor);
1275
- }
1276
- }
1277
- if (!border) return;
1278
- const transparent = { r: 0, g: 0, b: 0, a: 0, buffer: new Float32Array([0, 0, 0, 0]) };
1279
- const topLeft = borderChars[0];
1280
- const topRight = borderChars[1];
1281
- const bottomLeft = borderChars[2];
1282
- const bottomRight = borderChars[3];
1283
- const horizontal = borderChars[4];
1284
- const vertical = borderChars[5];
1285
- if (sides.top) {
1286
- if (sides.left) this.drawChar(topLeft, x, y, borderColor, transparent);
1287
- for (let col = 1; col < width - 1; col++) {
1288
- this.drawChar(horizontal, x + col, y, borderColor, transparent);
1289
- }
1290
- if (sides.right && width > 1) this.drawChar(topRight, x + width - 1, y, borderColor, transparent);
1291
- }
1292
- if (sides.bottom && height > 1) {
1293
- if (sides.left) this.drawChar(bottomLeft, x, y + height - 1, borderColor, transparent);
1294
- for (let col = 1; col < width - 1; col++) {
1295
- this.drawChar(horizontal, x + col, y + height - 1, borderColor, transparent);
1296
- }
1297
- if (sides.right && width > 1)
1298
- this.drawChar(bottomRight, x + width - 1, y + height - 1, borderColor, transparent);
1299
- }
1300
- if (sides.left) {
1301
- for (let row = 1; row < height - 1; row++) {
1302
- this.drawChar(vertical, x, y + row, borderColor, transparent);
1303
- }
1304
- }
1305
- if (sides.right && width > 1) {
1306
- for (let row = 1; row < height - 1; row++) {
1307
- this.drawChar(vertical, x + width - 1, y + row, borderColor, transparent);
1308
- }
1309
- }
1310
- if (title && sides.top && width > 4) {
1311
- const maxTitleLen = width - 4;
1312
- const truncatedTitle = title.length > maxTitleLen ? title.slice(0, maxTitleLen) : title;
1313
- let titleX;
1314
- if (titleAlignment === "center") {
1315
- titleX = x + Math.floor((width - truncatedTitle.length) / 2);
1316
- } else if (titleAlignment === "right") {
1317
- titleX = x + width - truncatedTitle.length - 2;
1318
- } else {
1319
- titleX = x + 2;
1320
- }
1321
- this.drawText(truncatedTitle, titleX, y, borderColor, transparent);
1322
- }
1323
- }
1324
- getDefaultBorderChars(borderStyle) {
1325
- const styles = {
1326
- rounded: [9581, 9582, 9584, 9583, 9472, 9474, 9516, 9524, 9500, 9508, 9532],
1327
- single: [9484, 9488, 9492, 9496, 9472, 9474, 9516, 9524, 9500, 9508, 9532],
1328
- double: [9556, 9559, 9562, 9565, 9552, 9553, 9574, 9577, 9568, 9571, 9580],
1329
- heavy: [9487, 9491, 9495, 9499, 9473, 9475, 9523, 9531, 9507, 9515, 9547]
1330
- };
1331
- const chars = styles[borderStyle ?? "rounded"] ?? styles.rounded;
1332
- return new Uint32Array(chars);
1333
- }
1334
- pushScissorRect(x, y, width, height) {
1335
- if (this.scissorStack.length > 0) {
1336
- const current = this.scissorStack[this.scissorStack.length - 1];
1337
- const nx = Math.max(x, current.x);
1338
- const ny = Math.max(y, current.y);
1339
- const nw = Math.min(x + width, current.x + current.width) - nx;
1340
- const nh = Math.min(y + height, current.y + current.height) - ny;
1341
- this.scissorStack.push({ x: nx, y: ny, width: Math.max(0, nw), height: Math.max(0, nh) });
1342
- } else {
1343
- this.scissorStack.push({ x, y, width, height });
1344
- }
1345
- }
1346
- popScissorRect() {
1347
- this.scissorStack.pop();
1348
- }
1349
- clearScissorRects() {
1350
- this.scissorStack = [];
1351
- }
1352
- pushOpacity(opacity) {
1353
- const current = this.getCurrentOpacityMultiplier();
1354
- this.opacityStack.push(current * opacity);
1355
- }
1356
- popOpacity() {
1357
- this.opacityStack.pop();
1358
- }
1359
- getCurrentOpacity() {
1360
- return this.getCurrentOpacityMultiplier();
1361
- }
1362
- clearOpacity() {
1363
- this.opacityStack = [];
1364
- }
1365
- resize(width, height) {
1366
- this._width = width;
1367
- this._height = height;
1368
- const size = width * height;
1369
- this.char = new Uint32Array(size);
1370
- this.fg = new Float32Array(size * 4);
1371
- this.bg = new Float32Array(size * 4);
1372
- this.attributes = new Uint32Array(size);
1373
- this.char.fill(32);
1374
- }
1375
- // Read buffer into CapturedLine[] for testing
1376
- getSpanLines() {
1377
- const lines = [];
1378
- for (let row = 0; row < this._height; row++) {
1379
- const spans = [];
1380
- let currentSpan = null;
1381
- for (let col = 0; col < this._width; col++) {
1382
- const idx = row * this._width + col;
1383
- const offset = idx * 4;
1384
- if (this.attributes[idx] & CONTINUATION) continue;
1385
- const charCode = this.char[idx];
1386
- const ch = charCode === 0 ? " " : String.fromCodePoint(charCode);
1387
- const fgR = this.fg[offset];
1388
- const fgG = this.fg[offset + 1];
1389
- const fgB = this.fg[offset + 2];
1390
- const fgA = this.fg[offset + 3];
1391
- const bgR = this.bg[offset];
1392
- const bgG = this.bg[offset + 1];
1393
- const bgB = this.bg[offset + 2];
1394
- const bgA = this.bg[offset + 3];
1395
- const attr = this.attributes[idx] & 255;
1396
- const fg2 = {
1397
- r: fgR,
1398
- g: fgG,
1399
- b: fgB,
1400
- a: fgA,
1401
- buffer: new Float32Array([fgR, fgG, fgB, fgA])
1402
- };
1403
- const bg2 = {
1404
- r: bgR,
1405
- g: bgG,
1406
- b: bgB,
1407
- a: bgA,
1408
- buffer: new Float32Array([bgR, bgG, bgB, bgA])
1409
- };
1410
- if (currentSpan && currentSpan.fg.r === fgR && currentSpan.fg.g === fgG && currentSpan.fg.b === fgB && currentSpan.fg.a === fgA && currentSpan.bg.r === bgR && currentSpan.bg.g === bgG && currentSpan.bg.b === bgB && currentSpan.bg.a === bgA && currentSpan.attributes === attr) {
1411
- currentSpan.text += ch;
1412
- currentSpan.width += 1;
1413
- } else {
1414
- if (currentSpan) spans.push(currentSpan);
1415
- currentSpan = { text: ch, fg: fg2, bg: bg2, attributes: attr, width: 1 };
1416
- }
1417
- }
1418
- if (currentSpan) spans.push(currentSpan);
1419
- lines.push({ spans });
1420
- }
1421
- return lines;
1422
- }
1423
- // Draw a text buffer view into the buffer
1424
- drawTextBufferView(view, x, y) {
1425
- if (!view || !view.getVisibleLines) return;
1426
- const lines = view.getVisibleLines();
1427
- if (!lines) return;
1428
- const textAlign = view.textAlign;
1429
- const viewWidth = view._viewportWidth;
1430
- for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
1431
- const line = lines[lineIdx];
1432
- if (!line) continue;
1433
- let curX = x;
1434
- if (textAlign && textAlign !== "left" && viewWidth) {
1435
- const lineWidth = line.chunks.reduce((sum, c) => sum + c.text.length, 0);
1436
- if (textAlign === "center") {
1437
- curX = x + Math.floor((viewWidth - lineWidth) / 2);
1438
- } else if (textAlign === "right") {
1439
- curX = x + viewWidth - lineWidth;
1440
- }
1441
- }
1442
- for (const chunk of line.chunks) {
1443
- const text = chunk.text;
1444
- const fgColor = chunk.fg;
1445
- const bgColor = chunk.bg;
1446
- let attr = chunk.attributes ?? 0;
1447
- if (chunk.link && chunk.link.url) {
1448
- const linkId = this.registerLink(chunk.link.url);
1449
- attr = attributesWithLink(attr, linkId);
1450
- }
1451
- for (const ch of text) {
1452
- if (curX >= this._width) break;
1453
- if (curX >= 0 && y + lineIdx >= 0 && y + lineIdx < this._height) {
1454
- this.setCell(curX, y + lineIdx, ch, fgColor, bgColor, attr);
1455
- }
1456
- curX++;
1457
- }
1458
- }
1459
- }
1460
- }
1461
- drawTextBuffer(textBufferView, x, y) {
1462
- this.drawTextBufferView(textBufferView, x, y);
1463
- }
1464
- drawFrameBuffer(destX, destY, frameBuffer, sourceX = 0, sourceY = 0, sourceWidth, sourceHeight) {
1465
- const sw = sourceWidth ?? frameBuffer.width;
1466
- const sh = sourceHeight ?? frameBuffer.height;
1467
- const srcChar = frameBuffer.char;
1468
- const srcFg = frameBuffer.fg;
1469
- const srcBg = frameBuffer.bg;
1470
- const srcAttr = frameBuffer.attributes;
1471
- const srcCols = frameBuffer.width;
1472
- for (let row = 0; row < sh; row++) {
1473
- const srcRow = sourceY + row;
1474
- const dstRow = destY + row;
1475
- if (srcRow < 0 || srcRow >= frameBuffer.height) continue;
1476
- if (dstRow < 0 || dstRow >= this._height) continue;
1477
- for (let col = 0; col < sw; col++) {
1478
- const srcCol = sourceX + col;
1479
- const dstCol = destX + col;
1480
- if (srcCol < 0 || srcCol >= frameBuffer.width) continue;
1481
- if (dstCol < 0 || dstCol >= this._width) continue;
1482
- if (!this.isInScissor(dstCol, dstRow)) continue;
1483
- const srcIdx = srcRow * srcCols + srcCol;
1484
- const dstIdx = dstRow * this._width + dstCol;
1485
- const srcOffset = srcIdx * 4;
1486
- const dstOffset = dstIdx * 4;
1487
- this.char[dstIdx] = srcChar[srcIdx];
1488
- this.attributes[dstIdx] = srcAttr[srcIdx];
1489
- const fgA = srcFg[srcOffset + 3];
1490
- const opacityMul = this.getCurrentOpacityMultiplier();
1491
- this.fg[dstOffset] = srcFg[srcOffset];
1492
- this.fg[dstOffset + 1] = srcFg[srcOffset + 1];
1493
- this.fg[dstOffset + 2] = srcFg[srcOffset + 2];
1494
- this.fg[dstOffset + 3] = fgA * opacityMul;
1495
- const bgA = srcBg[srcOffset + 3];
1496
- this.bg[dstOffset] = srcBg[srcOffset];
1497
- this.bg[dstOffset + 1] = srcBg[srcOffset + 1];
1498
- this.bg[dstOffset + 2] = srcBg[srcOffset + 2];
1499
- this.bg[dstOffset + 3] = bgA * opacityMul;
1500
- }
1501
- }
1502
- }
1503
- drawEditorView(editorView, x, y) {
1504
- if (!editorView) return;
1505
- const viewport = editorView.getViewport();
1506
- const text = editorView.getText();
1507
- const lines = text.split("\n");
1508
- const dfg = editorView.editBuffer?._defaultFg ?? {
1509
- r: 1,
1510
- g: 1,
1511
- b: 1,
1512
- a: 1,
1513
- buffer: new Float32Array([1, 1, 1, 1])
1514
- };
1515
- const dbg = editorView.editBuffer?._defaultBg ?? {
1516
- r: 0,
1517
- g: 0,
1518
- b: 0,
1519
- a: 0,
1520
- buffer: new Float32Array([0, 0, 0, 0])
1521
- };
1522
- const visibleRows = viewport.height > 0 ? viewport.height : this._height - y;
1523
- if (text === "" && editorView._placeholderChunks && editorView._placeholderChunks.length > 0) {
1524
- let curX = x;
1525
- for (const chunk of editorView._placeholderChunks) {
1526
- const chunkFg = chunk.fg ?? dfg;
1527
- const chunkBg = chunk.bg ?? dbg;
1528
- const attr = (chunk.attributes ?? 0) | 2;
1529
- for (const ch of chunk.text) {
1530
- if (curX >= this._width) break;
1531
- if (curX >= 0 && y >= 0 && y < this._height) {
1532
- this.setCell(curX, y, ch, chunkFg, chunkBg, attr);
1533
- }
1534
- curX++;
1535
- }
1536
- }
1537
- } else {
1538
- for (let row = 0; row < visibleRows; row++) {
1539
- const lineIdx = viewport.offsetY + row;
1540
- if (lineIdx < 0 || lineIdx >= lines.length) continue;
1541
- const dstRow = y + row;
1542
- if (dstRow < 0 || dstRow >= this._height) continue;
1543
- const line = lines[lineIdx];
1544
- for (let col = 0; col < line.length; col++) {
1545
- const srcCol = viewport.offsetX + col;
1546
- if (srcCol < 0 || srcCol >= line.length) continue;
1547
- const dstCol = x + col;
1548
- if (dstCol < 0 || dstCol >= this._width) break;
1549
- this.setCell(dstCol, dstRow, line[srcCol], dfg, dbg, 0);
1550
- }
1551
- }
1552
- }
1553
- const cursor = editorView.getVisualCursor();
1554
- if (cursor) {
1555
- const cursorX = x + cursor.visualCol;
1556
- const cursorY = y + cursor.visualRow;
1557
- if (cursorX >= 0 && cursorX < this._width && cursorY >= 0 && cursorY < this._height) {
1558
- const idx = cursorY * this._width + cursorX;
1559
- const charCode = this.char[idx];
1560
- const ch = charCode === 0 || charCode === 32 ? " " : String.fromCodePoint(charCode);
1561
- this.setCell(cursorX, cursorY, ch, dfg, dbg, 32);
1562
- }
1563
- }
1564
- }
1565
- drawSuperSampleBuffer() {
1566
- }
1567
- drawPackedBuffer() {
1568
- }
1569
- drawGrayscaleBuffer() {
1570
- }
1571
- drawGrayscaleBufferSupersampled() {
1572
- }
1573
- drawGrid() {
1574
- }
1575
- encodeUnicode(_text) {
1576
- return null;
1577
- }
1578
- freeUnicode() {
1579
- }
1580
- getRealCharBytes() {
1581
- return new Uint8Array(0);
1582
- }
1583
- destroy() {
1584
- }
1585
- };
1586
-
1587
- // src/canvas-painter.ts
1588
- var TextAttributes2 = {
1589
- BOLD: 1 << 0,
1590
- DIM: 1 << 1,
1591
- ITALIC: 1 << 2,
1592
- UNDERLINE: 1 << 3,
1593
- UNDERLINE_DASHED: 1 << 4,
1594
- INVERSE: 1 << 5,
1595
- UNDERLINE_DOTTED: 1 << 6
1596
- };
1597
- var CONTINUATION2 = 3221225472;
1598
- var BOX_DRAWING_MAP = {
1599
- // Light lines
1600
- 9472: { left: true, right: true, type: "light" },
1601
- // ─
1602
- 9474: { up: true, down: true, type: "light" },
1603
- // │
1604
- 9484: { right: true, down: true, type: "light" },
1605
- // ┌
1606
- 9488: { left: true, down: true, type: "light" },
1607
- // ┐
1608
- 9492: { right: true, up: true, type: "light" },
1609
- // └
1610
- 9496: { left: true, up: true, type: "light" },
1611
- // ┘
1612
- 9500: { up: true, down: true, right: true, type: "light" },
1613
- // ├
1614
- 9508: { up: true, down: true, left: true, type: "light" },
1615
- // ┤
1616
- 9516: { left: true, right: true, down: true, type: "light" },
1617
- // ┬
1618
- 9524: { left: true, right: true, up: true, type: "light" },
1619
- // ┴
1620
- 9532: { left: true, right: true, up: true, down: true, type: "light" },
1621
- // ┼
1622
- // Heavy lines
1623
- 9473: { left: true, right: true, type: "heavy" },
1624
- // ━
1625
- 9475: { up: true, down: true, type: "heavy" },
1626
- // ┃
1627
- 9487: { right: true, down: true, type: "heavy" },
1628
- // ┏
1629
- 9491: { left: true, down: true, type: "heavy" },
1630
- // ┓
1631
- 9495: { right: true, up: true, type: "heavy" },
1632
- // ┗
1633
- 9499: { left: true, up: true, type: "heavy" },
1634
- // ┛
1635
- 9507: { up: true, down: true, right: true, type: "heavy" },
1636
- // ┣
1637
- 9515: { up: true, down: true, left: true, type: "heavy" },
1638
- // ┫
1639
- 9523: { left: true, right: true, down: true, type: "heavy" },
1640
- // ┳
1641
- 9531: { left: true, right: true, up: true, type: "heavy" },
1642
- // ┻
1643
- 9547: { left: true, right: true, up: true, down: true, type: "heavy" },
1644
- // ╋
1645
- // Light/heavy mixed (treat as light for simplicity of joins)
1646
- 9485: { right: true, down: true, type: "heavy" },
1647
- // ┍
1648
- 9486: { right: true, down: true, type: "heavy" },
1649
- // ┎
1650
- 9489: { left: true, down: true, type: "heavy" },
1651
- // ┑
1652
- 9490: { left: true, down: true, type: "heavy" },
1653
- // ┒
1654
- 9493: { right: true, up: true, type: "heavy" },
1655
- // ┕
1656
- 9494: { right: true, up: true, type: "heavy" },
1657
- // ┖
1658
- 9497: { left: true, up: true, type: "heavy" },
1659
- // ┙
1660
- 9498: { left: true, up: true, type: "heavy" },
1661
- // ┚
1662
- 9501: { up: true, down: true, right: true, type: "heavy" },
1663
- // ┝
1664
- 9502: { up: true, down: true, right: true, type: "light" },
1665
- // ┞
1666
- 9503: { up: true, down: true, right: true, type: "light" },
1667
- // ┟
1668
- 9504: { up: true, down: true, right: true, type: "heavy" },
1669
- // ┠
1670
- 9505: { up: true, down: true, right: true, type: "heavy" },
1671
- // ┡
1672
- 9506: { up: true, down: true, right: true, type: "heavy" },
1673
- // ┢
1674
- 9509: { up: true, down: true, left: true, type: "heavy" },
1675
- // ┥
1676
- 9510: { up: true, down: true, left: true, type: "light" },
1677
- // ┦
1678
- 9511: { up: true, down: true, left: true, type: "light" },
1679
- // ┧
1680
- 9512: { up: true, down: true, left: true, type: "heavy" },
1681
- // ┨
1682
- 9513: { up: true, down: true, left: true, type: "heavy" },
1683
- // ┩
1684
- 9514: { up: true, down: true, left: true, type: "heavy" },
1685
- // ┪
1686
- 9517: { left: true, right: true, down: true, type: "heavy" },
1687
- // ┭
1688
- 9518: { left: true, right: true, down: true, type: "heavy" },
1689
- // ┮
1690
- 9519: { left: true, right: true, down: true, type: "heavy" },
1691
- // ┯
1692
- 9520: { left: true, right: true, down: true, type: "heavy" },
1693
- // ┰
1694
- 9521: { left: true, right: true, down: true, type: "heavy" },
1695
- // ┱
1696
- 9522: { left: true, right: true, down: true, type: "heavy" },
1697
- // ┲
1698
- 9525: { left: true, right: true, up: true, type: "heavy" },
1699
- // ┵
1700
- 9526: { left: true, right: true, up: true, type: "heavy" },
1701
- // ┶
1702
- 9527: { left: true, right: true, up: true, type: "heavy" },
1703
- // ┷
1704
- 9528: { left: true, right: true, up: true, type: "heavy" },
1705
- // ┸
1706
- 9529: { left: true, right: true, up: true, type: "heavy" },
1707
- // ┹
1708
- 9530: { left: true, right: true, up: true, type: "heavy" },
1709
- // ┺
1710
- 9533: { left: true, right: true, up: true, down: true, type: "heavy" },
1711
- // ┽
1712
- 9534: { left: true, right: true, up: true, down: true, type: "heavy" },
1713
- // ┾
1714
- 9535: { left: true, right: true, up: true, down: true, type: "heavy" },
1715
- // ┿
1716
- 9536: { left: true, right: true, up: true, down: true, type: "heavy" },
1717
- // ╀
1718
- 9537: { left: true, right: true, up: true, down: true, type: "heavy" },
1719
- // ╁
1720
- 9538: { left: true, right: true, up: true, down: true, type: "heavy" },
1721
- // ╂
1722
- 9539: { left: true, right: true, up: true, down: true, type: "heavy" },
1723
- // ╃
1724
- 9540: { left: true, right: true, up: true, down: true, type: "heavy" },
1725
- // ╄
1726
- 9541: { left: true, right: true, up: true, down: true, type: "heavy" },
1727
- // ╅
1728
- 9542: { left: true, right: true, up: true, down: true, type: "heavy" },
1729
- // ╆
1730
- 9543: { left: true, right: true, up: true, down: true, type: "heavy" },
1731
- // ╇
1732
- 9544: { left: true, right: true, up: true, down: true, type: "heavy" },
1733
- // ╈
1734
- 9545: { left: true, right: true, up: true, down: true, type: "heavy" },
1735
- // ╉
1736
- 9546: { left: true, right: true, up: true, down: true, type: "heavy" },
1737
- // ╊
1738
- // Dashed lines (render as solid light/heavy)
1739
- 9476: { left: true, right: true, type: "light" },
1740
- // ┄
1741
- 9477: { left: true, right: true, type: "heavy" },
1742
- // ┅
1743
- 9478: { up: true, down: true, type: "light" },
1744
- // ┆
1745
- 9479: { up: true, down: true, type: "heavy" },
1746
- // ┇
1747
- 9480: { left: true, right: true, type: "light" },
1748
- // ┈
1749
- 9481: { left: true, right: true, type: "heavy" },
1750
- // ┉
1751
- 9482: { up: true, down: true, type: "light" },
1752
- // ┊
1753
- 9483: { up: true, down: true, type: "heavy" },
1754
- // ┋
1755
- // Double lines
1756
- 9552: { left: true, right: true, type: "double" },
1757
- // ═
1758
- 9553: { up: true, down: true, type: "double" },
1759
- // ║
1760
- 9554: { right: true, down: true, type: "double" },
1761
- // ╒
1762
- 9555: { right: true, down: true, type: "double" },
1763
- // ╓
1764
- 9556: { right: true, down: true, type: "double" },
1765
- // ╔
1766
- 9557: { left: true, down: true, type: "double" },
1767
- // ╕
1768
- 9558: { left: true, down: true, type: "double" },
1769
- // ╖
1770
- 9559: { left: true, down: true, type: "double" },
1771
- // ╗
1772
- 9560: { right: true, up: true, type: "double" },
1773
- // ╘
1774
- 9561: { right: true, up: true, type: "double" },
1775
- // ╙
1776
- 9562: { right: true, up: true, type: "double" },
1777
- // ╚
1778
- 9563: { left: true, up: true, type: "double" },
1779
- // ╛
1780
- 9564: { left: true, up: true, type: "double" },
1781
- // ╜
1782
- 9565: { left: true, up: true, type: "double" },
1783
- // ╝
1784
- 9566: { up: true, down: true, right: true, type: "double" },
1785
- // ╞
1786
- 9567: { up: true, down: true, right: true, type: "double" },
1787
- // ╟
1788
- 9568: { up: true, down: true, right: true, type: "double" },
1789
- // ╠
1790
- 9569: { up: true, down: true, left: true, type: "double" },
1791
- // ╡
1792
- 9570: { up: true, down: true, left: true, type: "double" },
1793
- // ╢
1794
- 9571: { up: true, down: true, left: true, type: "double" },
1795
- // ╣
1796
- 9572: { left: true, right: true, down: true, type: "double" },
1797
- // ╤
1798
- 9573: { left: true, right: true, down: true, type: "double" },
1799
- // ╥
1800
- 9574: { left: true, right: true, down: true, type: "double" },
1801
- // ╦
1802
- 9575: { left: true, right: true, up: true, type: "double" },
1803
- // ╧
1804
- 9576: { left: true, right: true, up: true, type: "double" },
1805
- // ╨
1806
- 9577: { left: true, right: true, up: true, type: "double" },
1807
- // ╩
1808
- 9578: { left: true, right: true, up: true, down: true, type: "double" },
1809
- // ╪
1810
- 9579: { left: true, right: true, up: true, down: true, type: "double" },
1811
- // ╫
1812
- 9580: { left: true, right: true, up: true, down: true, type: "double" },
1813
- // ╬
1814
- // Rounded corners
1815
- 9581: { right: true, down: true, type: "light", arc: "tl" },
1816
- // ╭
1817
- 9582: { left: true, down: true, type: "light", arc: "tr" },
1818
- // ╮
1819
- 9583: { left: true, up: true, type: "light", arc: "br" },
1820
- // ╯
1821
- 9584: { right: true, up: true, type: "light", arc: "bl" },
1822
- // ╰
1823
- // Diagonal lines (render as simple connecting lines)
1824
- 9585: { type: "light" },
1825
- // ╱ (forward slash)
1826
- 9586: { type: "light" },
1827
- // ╲ (backslash)
1828
- 9587: { type: "light" },
1829
- // ╳ (cross)
1830
- // Half lines
1831
- 9588: { left: true, type: "light" },
1832
- // ╴
1833
- 9589: { up: true, type: "light" },
1834
- // ╵
1835
- 9590: { right: true, type: "light" },
1836
- // ╶
1837
- 9591: { down: true, type: "light" },
1838
- // ╷
1839
- 9592: { left: true, type: "heavy" },
1840
- // ╸
1841
- 9593: { up: true, type: "heavy" },
1842
- // ╹
1843
- 9594: { right: true, type: "heavy" },
1844
- // ╺
1845
- 9595: { down: true, type: "heavy" },
1846
- // ╻
1847
- 9596: { left: true, right: true, type: "light" },
1848
- // ╼ (light left, heavy right)
1849
- 9597: { up: true, down: true, type: "light" },
1850
- // ╽ (light up, heavy down)
1851
- 9598: { left: true, right: true, type: "heavy" },
1852
- // ╾ (heavy left, light right)
1853
- 9599: { up: true, down: true, type: "heavy" }
1854
- // ╿ (heavy up, light down)
1855
- };
1856
- var CanvasPainter = class {
1857
- cellWidth = 0;
1858
- cellHeight = 0;
1859
- fontFamily;
1860
- fontSize;
1861
- baselineOffset = 0;
1862
- constructor(options = {}) {
1863
- this.fontFamily = options.fontFamily ?? "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'Consolas', monospace";
1864
- this.fontSize = options.fontSize ?? 14;
1865
- }
1866
- measureCell(ctx) {
1867
- ctx.font = `${this.fontSize}px ${this.fontFamily}`;
1868
- const metrics = ctx.measureText("M");
1869
- this.cellWidth = metrics.width;
1870
- this.cellHeight = Math.ceil(this.fontSize * 1.4);
1871
- this.baselineOffset = Math.ceil(this.fontSize * 1.1);
1872
- return { width: this.cellWidth, height: this.cellHeight };
1873
- }
1874
- getCellSize() {
1875
- return { width: this.cellWidth, height: this.cellHeight };
1876
- }
1877
- paint(ctx, buffer, selection) {
1878
- const { char, fg: fg2, bg: bg2, attributes } = buffer;
1879
- const cols = buffer.width;
1880
- const rows = buffer.height;
1881
- const cw = this.cellWidth;
1882
- const ch = this.cellHeight;
1883
- for (let row = 0; row < rows; row++) {
1884
- let runStartCol = 0;
1885
- let runR = -1, runG = -1, runB = -1, runA = -1;
1886
- for (let col = 0; col <= cols; col++) {
1887
- let r = 0, g = 0, b = 0, a = 0;
1888
- if (col < cols) {
1889
- const idx = row * cols + col;
1890
- const offset = idx * 4;
1891
- const attr = attributes[idx] & 255;
1892
- const isInverse = !!(attr & TextAttributes2.INVERSE);
1893
- if (isInverse) {
1894
- r = fg2[offset];
1895
- g = fg2[offset + 1];
1896
- b = fg2[offset + 2];
1897
- a = fg2[offset + 3];
1898
- } else {
1899
- r = bg2[offset];
1900
- g = bg2[offset + 1];
1901
- b = bg2[offset + 2];
1902
- a = bg2[offset + 3];
1903
- }
1904
- }
1905
- if (col < cols && r === runR && g === runG && b === runB && a === runA) {
1906
- continue;
1907
- }
1908
- if (runA > 0 && col > runStartCol) {
1909
- ctx.fillStyle = rgbaToCSS(runR, runG, runB, runA);
1910
- ctx.fillRect(runStartCol * cw, row * ch, (col - runStartCol) * cw, ch);
1911
- }
1912
- runStartCol = col;
1913
- runR = r;
1914
- runG = g;
1915
- runB = b;
1916
- runA = a;
1917
- }
1918
- }
1919
- for (let row = 0; row < rows; row++) {
1920
- for (let col = 0; col < cols; col++) {
1921
- const idx = row * cols + col;
1922
- if (attributes[idx] & CONTINUATION2) continue;
1923
- const charCode = char[idx];
1924
- if (charCode === 0 || charCode === 32) continue;
1925
- const offset = idx * 4;
1926
- const attr = attributes[idx] & 255;
1927
- const isInverse = !!(attr & TextAttributes2.INVERSE);
1928
- let fgR, fgG, fgB, fgA;
1929
- if (isInverse) {
1930
- fgR = bg2[offset];
1931
- fgG = bg2[offset + 1];
1932
- fgB = bg2[offset + 2];
1933
- fgA = bg2[offset + 3];
1934
- if (fgA === 0) {
1935
- fgR = 0;
1936
- fgG = 0;
1937
- fgB = 0;
1938
- fgA = 1;
1939
- }
1940
- } else {
1941
- fgR = fg2[offset];
1942
- fgG = fg2[offset + 1];
1943
- fgB = fg2[offset + 2];
1944
- fgA = fg2[offset + 3];
1945
- }
1946
- if (fgA === 0) continue;
1947
- const isBold = !!(attr & TextAttributes2.BOLD);
1948
- const isItalic = !!(attr & TextAttributes2.ITALIC);
1949
- const isDim = !!(attr & TextAttributes2.DIM);
1950
- const isUnderline = !!(attr & TextAttributes2.UNDERLINE);
1951
- const effectiveA = isDim ? fgA * 0.5 : fgA;
1952
- if (charCode >= 9472 && charCode <= 9599) {
1953
- const def = BOX_DRAWING_MAP[charCode];
1954
- if (def) {
1955
- const cellX = col * cw;
1956
- const cellY = row * ch;
1957
- const cx = cellX + cw / 2;
1958
- const cy = cellY + ch / 2;
1959
- ctx.strokeStyle = rgbaToCSS(fgR, fgG, fgB, effectiveA);
1960
- ctx.lineCap = "square";
1961
- if (def.type === "double") {
1962
- const gap = Math.max(2, Math.round(cw / 5));
1963
- ctx.lineWidth = 1;
1964
- ctx.beginPath();
1965
- if (def.left) {
1966
- ctx.moveTo(cellX, cy - gap / 2);
1967
- ctx.lineTo(cx, cy - gap / 2);
1968
- ctx.moveTo(cellX, cy + gap / 2);
1969
- ctx.lineTo(cx, cy + gap / 2);
1970
- }
1971
- if (def.right) {
1972
- ctx.moveTo(cx, cy - gap / 2);
1973
- ctx.lineTo(cellX + cw, cy - gap / 2);
1974
- ctx.moveTo(cx, cy + gap / 2);
1975
- ctx.lineTo(cellX + cw, cy + gap / 2);
1976
- }
1977
- if (def.up) {
1978
- ctx.moveTo(cx - gap / 2, cellY);
1979
- ctx.lineTo(cx - gap / 2, cy);
1980
- ctx.moveTo(cx + gap / 2, cellY);
1981
- ctx.lineTo(cx + gap / 2, cy);
1982
- }
1983
- if (def.down) {
1984
- ctx.moveTo(cx - gap / 2, cy);
1985
- ctx.lineTo(cx - gap / 2, cellY + ch);
1986
- ctx.moveTo(cx + gap / 2, cy);
1987
- ctx.lineTo(cx + gap / 2, cellY + ch);
1988
- }
1989
- ctx.stroke();
1990
- } else if (def.arc) {
1991
- const rx = cw / 2;
1992
- const ry = ch / 2;
1993
- ctx.lineWidth = 1;
1994
- ctx.beginPath();
1995
- if (def.arc === "tl") {
1996
- ctx.ellipse(cellX + cw, cellY + ch, rx, ry, 0, Math.PI, Math.PI * 1.5);
1997
- } else if (def.arc === "tr") {
1998
- ctx.ellipse(cellX, cellY + ch, rx, ry, 0, Math.PI * 1.5, Math.PI * 2);
1999
- } else if (def.arc === "bl") {
2000
- ctx.ellipse(cellX + cw, cellY, rx, ry, 0, Math.PI * 0.5, Math.PI);
2001
- } else if (def.arc === "br") {
2002
- ctx.ellipse(cellX, cellY, rx, ry, 0, 0, Math.PI * 0.5);
2003
- }
2004
- ctx.stroke();
2005
- } else if (charCode === 9585 || charCode === 9586 || charCode === 9587) {
2006
- ctx.lineWidth = 1;
2007
- ctx.beginPath();
2008
- if (charCode === 9585 || charCode === 9587) {
2009
- ctx.moveTo(cellX + cw, cellY);
2010
- ctx.lineTo(cellX, cellY + ch);
2011
- }
2012
- if (charCode === 9586 || charCode === 9587) {
2013
- ctx.moveTo(cellX, cellY);
2014
- ctx.lineTo(cellX + cw, cellY + ch);
2015
- }
2016
- ctx.stroke();
2017
- } else {
2018
- ctx.lineWidth = def.type === "heavy" ? 2 : 1;
2019
- ctx.beginPath();
2020
- if (def.left) {
2021
- ctx.moveTo(cellX, cy);
2022
- ctx.lineTo(cx, cy);
2023
- }
2024
- if (def.right) {
2025
- ctx.moveTo(cx, cy);
2026
- ctx.lineTo(cellX + cw, cy);
2027
- }
2028
- if (def.up) {
2029
- ctx.moveTo(cx, cellY);
2030
- ctx.lineTo(cx, cy);
2031
- }
2032
- if (def.down) {
2033
- ctx.moveTo(cx, cy);
2034
- ctx.lineTo(cx, cellY + ch);
2035
- }
2036
- ctx.stroke();
2037
- }
2038
- continue;
2039
- }
2040
- }
2041
- ctx.fillStyle = rgbaToCSS(fgR, fgG, fgB, effectiveA);
2042
- let fontStyle = "";
2043
- if (isItalic) fontStyle += "italic ";
2044
- if (isBold) fontStyle += "bold ";
2045
- fontStyle += `${this.fontSize}px ${this.fontFamily}`;
2046
- ctx.font = fontStyle;
2047
- const character = String.fromCodePoint(charCode);
2048
- const x = col * cw;
2049
- const y = row * this.cellHeight + this.baselineOffset;
2050
- ctx.fillText(character, x, y);
2051
- if (isUnderline) {
2052
- const isDashed = !!(attr & TextAttributes2.UNDERLINE_DASHED);
2053
- const isDotted = !!(attr & TextAttributes2.UNDERLINE_DOTTED);
2054
- ctx.strokeStyle = ctx.fillStyle;
2055
- ctx.lineWidth = 1;
2056
- const underlineY = row * this.cellHeight + this.baselineOffset + 2;
2057
- if (isDotted) {
2058
- ctx.lineCap = "round";
2059
- ctx.setLineDash([0.5, 2.5]);
2060
- } else if (isDashed) {
2061
- ctx.setLineDash([2, 2]);
2062
- }
2063
- ctx.beginPath();
2064
- ctx.moveTo(x, underlineY);
2065
- ctx.lineTo(x + cw, underlineY);
2066
- ctx.stroke();
2067
- if (isDotted || isDashed) {
2068
- ctx.setLineDash([]);
2069
- ctx.lineCap = "butt";
2070
- }
2071
- }
2072
- }
2073
- }
2074
- if (selection?.active) {
2075
- ctx.fillStyle = "rgba(51, 153, 255, 0.3)";
2076
- for (let row = 0; row < rows; row++) {
2077
- let runStart = -1;
2078
- for (let col = 0; col <= cols; col++) {
2079
- const selected = col < cols && selection.isSelected(col, row);
2080
- if (selected && runStart === -1) {
2081
- runStart = col;
2082
- } else if (!selected && runStart !== -1) {
2083
- ctx.fillRect(runStart * cw, row * ch, (col - runStart) * cw, ch);
2084
- runStart = -1;
2085
- }
2086
- }
2087
- }
2088
- }
2089
- }
2090
- };
2091
- function rgbaToCSS(r, g, b, a) {
2092
- const ri = Math.round(r * 255);
2093
- const gi = Math.round(g * 255);
2094
- const bi = Math.round(b * 255);
2095
- return `rgba(${ri},${gi},${bi},${a})`;
2096
- }
2097
-
2098
- // src/selection-manager.ts
2099
- var SelectionManager = class {
2100
- _startCol = 0;
2101
- _startRow = 0;
2102
- _endCol = 0;
2103
- _endRow = 0;
2104
- _active = false;
2105
- _selecting = false;
2106
- startSelection(col, row) {
2107
- this._startCol = col;
2108
- this._startRow = row;
2109
- this._endCol = col;
2110
- this._endRow = row;
2111
- this._selecting = true;
2112
- this._active = true;
2113
- }
2114
- updateSelection(col, row) {
2115
- if (!this._selecting) return;
2116
- this._endCol = col;
2117
- this._endRow = row;
2118
- }
2119
- endSelection() {
2120
- this._selecting = false;
2121
- if (this._startCol === this._endCol && this._startRow === this._endRow) {
2122
- this._active = false;
2123
- }
2124
- }
2125
- clearSelection() {
2126
- this._active = false;
2127
- this._selecting = false;
2128
- }
2129
- get active() {
2130
- return this._active;
2131
- }
2132
- get selecting() {
2133
- return this._selecting;
2134
- }
2135
- /** Returns the selection range normalized to reading order (top-left to bottom-right) */
2136
- getSelectedRange() {
2137
- if (!this._active) return null;
2138
- let startCol = this._startCol;
2139
- let startRow = this._startRow;
2140
- let endCol = this._endCol;
2141
- let endRow = this._endRow;
2142
- if (startRow > endRow || startRow === endRow && startCol > endCol) {
2143
- const tmpCol = startCol;
2144
- const tmpRow = startRow;
2145
- startCol = endCol;
2146
- startRow = endRow;
2147
- endCol = tmpCol;
2148
- endRow = tmpRow;
2149
- }
2150
- return { startCol, startRow, endCol, endRow };
2151
- }
2152
- isSelected(col, row) {
2153
- const range = this.getSelectedRange();
2154
- if (!range) return false;
2155
- const { startCol, startRow, endCol, endRow } = range;
2156
- if (row < startRow || row > endRow) return false;
2157
- if (startRow === endRow) {
2158
- return col >= startCol && col < endCol;
2159
- }
2160
- if (row === startRow) return col >= startCol;
2161
- if (row === endRow) return col < endCol;
2162
- return true;
2163
- }
2164
- getSelectedText(buffer) {
2165
- const range = this.getSelectedRange();
2166
- if (!range) return "";
2167
- const { startCol, startRow, endCol, endRow } = range;
2168
- const lines = [];
2169
- for (let row = startRow; row <= endRow; row++) {
2170
- let lineStart = row === startRow ? startCol : 0;
2171
- let lineEnd = row === endRow ? endCol : buffer.width;
2172
- let line = "";
2173
- for (let col = lineStart; col < lineEnd && col < buffer.width; col++) {
2174
- const idx = row * buffer.width + col;
2175
- const charCode = buffer.char[idx];
2176
- line += charCode === 0 ? " " : String.fromCodePoint(charCode);
2177
- }
2178
- lines.push(line.trimEnd());
2179
- }
2180
- return lines.join("\n");
2181
- }
2182
- };
2183
-
2184
- // src/render-pipeline.ts
2185
- function executeRenderPipeline(buffer, renderContext, root, deltaTime) {
2186
- buffer.clear();
2187
- const lifecyclePasses = renderContext.getLifecyclePasses();
2188
- for (const renderable of lifecyclePasses) {
2189
- if (renderable.onLifecyclePass) {
2190
- renderable.onLifecyclePass();
2191
- }
2192
- }
2193
- root.calculateLayout();
2194
- const renderList = [];
2195
- root.updateLayout(deltaTime, renderList);
2196
- for (const cmd of renderList) {
2197
- switch (cmd.action) {
2198
- case "pushScissorRect":
2199
- buffer.pushScissorRect(cmd.x, cmd.y, cmd.width, cmd.height);
2200
- break;
2201
- case "popScissorRect":
2202
- buffer.popScissorRect();
2203
- break;
2204
- case "pushOpacity":
2205
- buffer.pushOpacity(cmd.opacity);
2206
- break;
2207
- case "popOpacity":
2208
- buffer.popOpacity();
2209
- break;
2210
- case "render":
2211
- cmd.renderable.render(buffer, deltaTime);
2212
- break;
2213
- }
2214
- }
2215
- buffer.clearScissorRects();
2216
- buffer.clearOpacity();
2217
- }
2218
-
2219
- // src/browser-renderer.ts
2220
- var RootRenderableClass = null;
2221
- function setRootRenderableClass(cls) {
2222
- RootRenderableClass = cls;
2223
- }
2224
- var BrowserRenderer = class _BrowserRenderer {
2225
- canvas;
2226
- ctx2d;
2227
- buffer;
2228
- renderContext;
2229
- root;
2230
- // RootRenderable
2231
- painter;
2232
- selection;
2233
- cols;
2234
- rows;
2235
- cellWidth = 0;
2236
- cellHeight = 0;
2237
- rafId = null;
2238
- lastTime = 0;
2239
- needsRender = true;
2240
- isDragOver = false;
2241
- cleanupListeners = [];
2242
- mouseDownCell = null;
2243
- backgroundColor = null;
2244
- constructor(canvas, cols, rows, options) {
2245
- this.canvas = canvas;
2246
- this.cols = cols;
2247
- this.rows = rows;
2248
- const ctx2d = canvas.getContext("2d");
2249
- if (!ctx2d) throw new Error("Could not get 2d context");
2250
- this.ctx2d = ctx2d;
2251
- this.painter = new CanvasPainter();
2252
- const cellSize = this.painter.measureCell(this.ctx2d);
2253
- this.cellWidth = cellSize.width;
2254
- this.cellHeight = cellSize.height;
2255
- const dpr = window.devicePixelRatio || 1;
2256
- canvas.width = Math.ceil(cols * this.cellWidth * dpr);
2257
- canvas.height = Math.ceil(rows * this.cellHeight * dpr);
2258
- canvas.style.width = `${cols * this.cellWidth}px`;
2259
- canvas.style.height = `${rows * this.cellHeight}px`;
2260
- this.ctx2d.scale(dpr, dpr);
2261
- this.backgroundColor = options?.backgroundColor ?? null;
2262
- canvas.style.cursor = "text";
2263
- this.buffer = BrowserBuffer.create(cols, rows, "wcwidth");
2264
- this.renderContext = new BrowserRenderContext(cols, rows);
2265
- this.renderContext.setOnRenderRequest(() => {
2266
- this.needsRender = true;
2267
- });
2268
- this.selection = new SelectionManager();
2269
- if (!RootRenderableClass) {
2270
- throw new Error("RootRenderableClass not set. Call setRootRenderableClass before creating BrowserRenderer.");
2271
- }
2272
- this.root = new RootRenderableClass(this.renderContext);
2273
- this.setupDomListeners();
2274
- }
2275
- pixelToCell(clientX, clientY) {
2276
- const rect = this.canvas.getBoundingClientRect();
2277
- const x = clientX - rect.left;
2278
- const y = clientY - rect.top;
2279
- const col = Math.floor(x / this.cellWidth);
2280
- const row = Math.floor(y / this.cellHeight);
2281
- return {
2282
- col: Math.max(0, Math.min(col, this.cols - 1)),
2283
- row: Math.max(0, Math.min(row, this.rows - 1))
2284
- };
2285
- }
2286
- setupDomListeners() {
2287
- const onDragOver = (e) => {
2288
- e.preventDefault();
2289
- };
2290
- this.canvas.addEventListener("dragover", onDragOver);
2291
- this.cleanupListeners.push(() => this.canvas.removeEventListener("dragover", onDragOver));
2292
- const onDrop = (e) => {
2293
- e.preventDefault();
2294
- const files = e.dataTransfer?.files;
2295
- if (!files || files.length === 0) return;
2296
- const file = files[0];
2297
- this.isDragOver = false;
2298
- this.renderContext.emit("drag-leave", void 0);
2299
- file.text().then((content) => {
2300
- this.renderContext.emit("file-drop", {
2301
- name: file.name,
2302
- content,
2303
- type: file.type,
2304
- size: file.size
2305
- });
2306
- });
2307
- };
2308
- this.canvas.addEventListener("drop", onDrop);
2309
- this.cleanupListeners.push(() => this.canvas.removeEventListener("drop", onDrop));
2310
- const onMouseDown = (e) => {
2311
- if (e.button !== 0) return;
2312
- this.canvas.focus();
2313
- const { col, row } = this.pixelToCell(e.clientX, e.clientY);
2314
- this.mouseDownCell = { col, row };
2315
- this.selection.startSelection(col, row);
2316
- this.needsRender = true;
2317
- };
2318
- this.canvas.addEventListener("mousedown", onMouseDown);
2319
- this.cleanupListeners.push(() => this.canvas.removeEventListener("mousedown", onMouseDown));
2320
- const onMouseMove = (e) => {
2321
- const { col, row } = this.pixelToCell(e.clientX, e.clientY);
2322
- const idx = row * this.buffer.width + col;
2323
- if (idx >= 0 && idx < this.buffer.attributes.length) {
2324
- const linkId = getLinkId(this.buffer.attributes[idx]);
2325
- this.canvas.style.cursor = linkId > 0 ? "pointer" : "text";
2326
- }
2327
- if (!this.selection.selecting) return;
2328
- this.selection.updateSelection(col, row);
2329
- this.needsRender = true;
2330
- };
2331
- this.canvas.addEventListener("mousemove", onMouseMove);
2332
- this.cleanupListeners.push(() => this.canvas.removeEventListener("mousemove", onMouseMove));
2333
- const onMouseUp = (e) => {
2334
- const wasSelecting = this.selection.selecting;
2335
- if (wasSelecting) {
2336
- this.selection.endSelection();
2337
- this.needsRender = true;
2338
- }
2339
- if (e.button === 0 && this.mouseDownCell) {
2340
- const { col, row } = this.pixelToCell(e.clientX, e.clientY);
2341
- if (col === this.mouseDownCell.col && row === this.mouseDownCell.row) {
2342
- const idx = row * this.buffer.width + col;
2343
- if (idx >= 0 && idx < this.buffer.attributes.length) {
2344
- const attr = this.buffer.attributes[idx];
2345
- const linkId = getLinkId(attr);
2346
- if (linkId > 0) {
2347
- const url = this.buffer.getLinkUrl(linkId);
2348
- if (url) {
2349
- window.open(url, "_blank");
2350
- }
2351
- }
2352
- }
2353
- }
2354
- this.mouseDownCell = null;
2355
- }
2356
- };
2357
- window.addEventListener("mouseup", onMouseUp);
2358
- this.cleanupListeners.push(() => window.removeEventListener("mouseup", onMouseUp));
2359
- const onDragEnter = (e) => {
2360
- e.preventDefault();
2361
- if (this.isDragOver) return;
2362
- this.isDragOver = true;
2363
- this.renderContext.emit("drag-enter", void 0);
2364
- this.needsRender = true;
2365
- };
2366
- this.canvas.addEventListener("dragenter", onDragEnter);
2367
- this.cleanupListeners.push(() => this.canvas.removeEventListener("dragenter", onDragEnter));
2368
- const onDragLeave = (e) => {
2369
- if (e.relatedTarget && this.canvas.contains(e.relatedTarget)) return;
2370
- this.isDragOver = false;
2371
- this.renderContext.emit("drag-leave", void 0);
2372
- this.needsRender = true;
2373
- };
2374
- this.canvas.addEventListener("dragleave", onDragLeave);
2375
- this.cleanupListeners.push(() => this.canvas.removeEventListener("dragleave", onDragLeave));
2376
- const onPaste = (e) => {
2377
- if (document.activeElement !== this.canvas) return;
2378
- const text = e.clipboardData?.getData("text/plain");
2379
- if (text) {
2380
- e.preventDefault();
2381
- this.renderContext.emit("paste", text);
2382
- }
2383
- };
2384
- document.addEventListener("paste", onPaste);
2385
- this.cleanupListeners.push(() => document.removeEventListener("paste", onPaste));
2386
- }
2387
- start() {
2388
- this.lastTime = performance.now();
2389
- this.loop();
2390
- }
2391
- stop() {
2392
- if (this.rafId !== null) {
2393
- cancelAnimationFrame(this.rafId);
2394
- this.rafId = null;
2395
- }
2396
- for (const cleanup of this.cleanupListeners) {
2397
- cleanup();
2398
- }
2399
- this.cleanupListeners = [];
2400
- }
2401
- loop = () => {
2402
- this.rafId = requestAnimationFrame(this.loop);
2403
- const now = performance.now();
2404
- const deltaTime = now - this.lastTime;
2405
- this.lastTime = now;
2406
- if (!this.needsRender) return;
2407
- this.needsRender = false;
2408
- executeRenderPipeline(this.buffer, this.renderContext, this.root, deltaTime);
2409
- const dpr = window.devicePixelRatio || 1;
2410
- this.ctx2d.setTransform(dpr, 0, 0, dpr, 0, 0);
2411
- if (this.backgroundColor) {
2412
- this.ctx2d.fillStyle = this.backgroundColor;
2413
- this.ctx2d.fillRect(0, 0, this.canvas.width, this.canvas.height);
2414
- } else {
2415
- this.ctx2d.clearRect(0, 0, this.canvas.width, this.canvas.height);
2416
- }
2417
- this.painter.paint(this.ctx2d, this.buffer, this.selection);
2418
- };
2419
- resize(cols, rows) {
2420
- this.cols = cols;
2421
- this.rows = rows;
2422
- const dpr = window.devicePixelRatio || 1;
2423
- this.canvas.width = Math.ceil(cols * this.cellWidth * dpr);
2424
- this.canvas.height = Math.ceil(rows * this.cellHeight * dpr);
2425
- this.canvas.style.width = `${cols * this.cellWidth}px`;
2426
- this.canvas.style.height = `${rows * this.cellHeight}px`;
2427
- this.ctx2d.scale(dpr, dpr);
2428
- this.buffer.resize(cols, rows);
2429
- this.renderContext.resize(cols, rows);
2430
- this.root.resize(cols, rows);
2431
- this.needsRender = true;
2432
- }
2433
- static PREVENT_DEFAULT_KEYS = /* @__PURE__ */ new Set([
2434
- "ArrowUp",
2435
- "ArrowDown",
2436
- "ArrowLeft",
2437
- "ArrowRight",
2438
- " ",
2439
- "PageUp",
2440
- "PageDown",
2441
- "Tab",
2442
- "Home",
2443
- "End"
2444
- ]);
2445
- static MODIFIER_KEYS = /* @__PURE__ */ new Set(["Alt", "Control", "Meta", "Shift"]);
2446
- static KEY_MAP = {
2447
- ArrowUp: "up",
2448
- ArrowDown: "down",
2449
- ArrowLeft: "left",
2450
- ArrowRight: "right",
2451
- Enter: "return",
2452
- Backspace: "backspace",
2453
- Delete: "delete",
2454
- Tab: "tab",
2455
- Escape: "escape",
2456
- Home: "home",
2457
- End: "end",
2458
- " ": "space",
2459
- PageUp: "pageup",
2460
- PageDown: "pagedown"
2461
- };
2462
- handleKeyDown(event) {
2463
- if (_BrowserRenderer.PREVENT_DEFAULT_KEYS.has(event.key)) {
2464
- event.preventDefault();
2465
- }
2466
- if (this.selection.active && (event.key === "c" || event.key === "\xE7") && (event.metaKey || event.ctrlKey || event.altKey)) {
2467
- const text = this.selection.getSelectedText(this.buffer);
2468
- if (text) {
2469
- navigator.clipboard.writeText(text).catch(() => {
2470
- const ta = document.createElement("textarea");
2471
- ta.value = text;
2472
- ta.style.position = "fixed";
2473
- ta.style.left = "-9999px";
2474
- document.body.appendChild(ta);
2475
- ta.select();
2476
- document.execCommand("copy");
2477
- document.body.removeChild(ta);
2478
- });
2479
- }
2480
- event.preventDefault();
2481
- return;
2482
- }
2483
- const modifierOnly = _BrowserRenderer.MODIFIER_KEYS.has(event.key);
2484
- if (this.selection.active && !modifierOnly) {
2485
- this.selection.clearSelection();
2486
- this.needsRender = true;
2487
- }
2488
- const keyEvent = {
2489
- name: _BrowserRenderer.KEY_MAP[event.key] ?? (event.key.length === 1 ? event.key : event.key.toLowerCase()),
2490
- ctrl: event.ctrlKey,
2491
- meta: event.metaKey,
2492
- shift: event.shiftKey,
2493
- option: event.altKey,
2494
- sequence: event.key,
2495
- number: false,
2496
- raw: event.key,
2497
- eventType: "press",
2498
- source: "raw",
2499
- _defaultPrevented: false,
2500
- _propagationStopped: false,
2501
- get defaultPrevented() {
2502
- return this._defaultPrevented;
2503
- },
2504
- get propagationStopped() {
2505
- return this._propagationStopped;
2506
- },
2507
- preventDefault() {
2508
- this._defaultPrevented = true;
2509
- },
2510
- stopPropagation() {
2511
- this._propagationStopped = true;
2512
- }
2513
- };
2514
- this.renderContext._internalKeyInput.emit("keypress", keyEvent);
2515
- this.renderContext.keyInput.emit("keypress", keyEvent);
2516
- }
2517
- };
2518
-
2519
- // src/browser-context.ts
2520
- import { createContext, useContext } from "react";
2521
- var BrowserContext = createContext(null);
2522
- function useBrowserContext() {
2523
- const ctx = useContext(BrowserContext);
2524
- if (!ctx) {
2525
- throw new Error("useBrowserContext must be used within a BrowserContext.Provider");
2526
- }
2527
- return ctx;
2528
- }
2529
-
2530
- // src/create-browser-root.tsx
2531
- import { _render } from "../../../opentui/packages/react/src/reconciler/reconciler";
2532
- import { AppContext } from "../../../opentui/packages/react/src/components/app";
2533
- import { ErrorBoundary as _ErrorBoundary } from "../../../opentui/packages/react/src/components/error-boundary";
2534
- import { jsx } from "react/jsx-runtime";
2535
- var ErrorBoundary = _ErrorBoundary;
2536
- function createBrowserRoot(renderer) {
2537
- let unmountFn = null;
2538
- return {
2539
- render(node) {
2540
- const element = /* @__PURE__ */ jsx(BrowserContext.Provider, { value: { renderContext: renderer.renderContext }, children: /* @__PURE__ */ jsx(AppContext.Provider, { value: { keyHandler: renderer.renderContext.keyInput, renderer: renderer.renderContext }, children: /* @__PURE__ */ jsx(ErrorBoundary, { children: node }) }) });
2541
- unmountFn = _render(element, renderer.root);
2542
- },
2543
- unmount() {
2544
- if (unmountFn) {
2545
- }
2546
- }
2547
- };
2548
- }
2549
-
2550
- // src/TUI.tsx
2551
- import { RootRenderable as RootRenderable2 } from "@opentui/core";
2552
-
2553
- // src/buffer-to-text.ts
2554
- function bufferToText(buffer) {
2555
- const lines = [];
2556
- for (let row = 0; row < buffer.height; row++) {
2557
- let line = "";
2558
- for (let col = 0; col < buffer.width; col++) {
2559
- const idx = row * buffer.width + col;
2560
- const charCode = buffer.char[idx];
2561
- line += charCode === 0 ? " " : String.fromCodePoint(charCode);
2562
- }
2563
- lines.push(line.trimEnd());
2564
- }
2565
- while (lines.length > 0 && lines[lines.length - 1] === "") {
2566
- lines.pop();
2567
- }
2568
- return lines.join("\n");
2569
- }
2570
-
2571
- // src/headless-renderer.ts
2572
- var RootRenderableClass2 = null;
2573
- function setHeadlessRootRenderableClass(cls) {
2574
- RootRenderableClass2 = cls;
2575
- }
2576
- var HeadlessRenderer = class {
2577
- buffer;
2578
- renderContext;
2579
- root;
2580
- // RootRenderable
2581
- constructor(options) {
2582
- const { cols, rows } = options;
2583
- this.buffer = BrowserBuffer.create(cols, rows, "wcwidth");
2584
- this.renderContext = new BrowserRenderContext(cols, rows);
2585
- this.renderContext.setOnRenderRequest(() => {
2586
- });
2587
- if (!RootRenderableClass2) {
2588
- throw new Error(
2589
- "RootRenderableClass not set. Call setHeadlessRootRenderableClass before creating HeadlessRenderer."
2590
- );
2591
- }
2592
- this.root = new RootRenderableClass2(this.renderContext);
2593
- }
2594
- renderOnce() {
2595
- executeRenderPipeline(this.buffer, this.renderContext, this.root, 0);
2596
- }
2597
- toText() {
2598
- return bufferToText(this.buffer);
2599
- }
2600
- resize(cols, rows) {
2601
- this.buffer.resize(cols, rows);
2602
- this.renderContext.resize(cols, rows);
2603
- this.root.resize(cols, rows);
2604
- }
2605
- };
2606
-
2607
- // src/create-headless-root.tsx
2608
- import { _render as _render2, reconciler } from "../../../opentui/packages/react/src/reconciler/reconciler";
2609
- import { AppContext as AppContext2 } from "../../../opentui/packages/react/src/components/app";
2610
- import { ErrorBoundary as _ErrorBoundary2 } from "../../../opentui/packages/react/src/components/error-boundary";
2611
- import { jsx as jsx2 } from "react/jsx-runtime";
2612
- var ErrorBoundary2 = _ErrorBoundary2;
2613
- var _r = reconciler;
2614
- var flushSync = _r.flushSyncFromReconciler ?? _r.flushSync;
2615
- function createHeadlessRoot(renderer) {
2616
- let container = null;
2617
- return {
2618
- render(node) {
2619
- const element = /* @__PURE__ */ jsx2(BrowserContext.Provider, { value: { renderContext: renderer.renderContext }, children: /* @__PURE__ */ jsx2(
2620
- AppContext2.Provider,
2621
- {
2622
- value: {
2623
- keyHandler: renderer.renderContext.keyInput,
2624
- renderer: renderer.renderContext
2625
- },
2626
- children: /* @__PURE__ */ jsx2(ErrorBoundary2, { children: node })
2627
- }
2628
- ) });
2629
- container = _render2(element, renderer.root);
2630
- },
2631
- renderToText(node) {
2632
- flushSync(() => {
2633
- this.render(node);
2634
- });
2635
- renderer.renderOnce();
2636
- return renderer.toText();
2637
- },
2638
- unmount() {
2639
- if (container) {
2640
- reconciler.updateContainer(null, container, null, () => {
2641
- });
2642
- reconciler.flushSyncWork();
2643
- container = null;
2644
- }
2645
- }
2646
- };
2647
- }
2648
-
2649
- // src/TUI.tsx
2650
- import { jsx as jsx3 } from "react/jsx-runtime";
2651
- function TUI({
2652
- children,
2653
- style,
2654
- className,
2655
- fontSize = 14,
2656
- fontFamily = "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'Consolas', monospace",
2657
- autoFocus = true,
2658
- backgroundColor,
2659
- onReady,
2660
- fallbackCols = 80,
2661
- fallbackRows = 24
2662
- }) {
2663
- const containerRef = useRef(null);
2664
- const canvasRef = useRef(null);
2665
- const rendererRef = useRef(null);
2666
- const rootRef = useRef(null);
2667
- const [isClient, setIsClient] = useState(false);
2668
- useEffect(() => {
2669
- setIsClient(true);
2670
- }, []);
2671
- useEffect(() => {
2672
- if (!isClient) return;
2673
- const canvas = canvasRef.current;
2674
- const container = containerRef.current;
2675
- if (!canvas || !container) return;
2676
- setRootRenderableClass(RootRenderable2);
2677
- const painter = new CanvasPainter({ fontSize, fontFamily });
2678
- const tempCtx = canvas.getContext("2d");
2679
- const cellSize = painter.measureCell(tempCtx);
2680
- const containerRect = container.getBoundingClientRect();
2681
- const cols = Math.max(1, Math.floor(containerRect.width / cellSize.width));
2682
- const rows = Math.max(1, Math.floor(containerRect.height / cellSize.height));
2683
- const renderer = new BrowserRenderer(canvas, cols, rows, { backgroundColor });
2684
- rendererRef.current = renderer;
2685
- const root = createBrowserRoot(renderer);
2686
- rootRef.current = root;
2687
- root.render(children);
2688
- renderer.start();
2689
- canvas.tabIndex = 0;
2690
- if (autoFocus) {
2691
- canvas.focus();
2692
- }
2693
- onReady?.(renderer);
2694
- const onKeyDown = (e) => {
2695
- renderer.handleKeyDown(e);
2696
- };
2697
- canvas.addEventListener("keydown", onKeyDown);
2698
- const resizeObserver = new ResizeObserver((entries) => {
2699
- for (const entry of entries) {
2700
- const { width, height } = entry.contentRect;
2701
- const newCols = Math.max(1, Math.floor(width / cellSize.width));
2702
- const newRows = Math.max(1, Math.floor(height / cellSize.height));
2703
- renderer.resize(newCols, newRows);
2704
- }
2705
- });
2706
- resizeObserver.observe(container);
2707
- return () => {
2708
- canvas.removeEventListener("keydown", onKeyDown);
2709
- resizeObserver.disconnect();
2710
- renderer.stop();
2711
- root.unmount();
2712
- rendererRef.current = null;
2713
- rootRef.current = null;
2714
- };
2715
- }, [isClient, fontSize, fontFamily, backgroundColor]);
2716
- useEffect(() => {
2717
- if (rootRef.current) {
2718
- rootRef.current.render(children);
2719
- }
2720
- }, [children]);
2721
- if (!isClient) {
2722
- const isServer = typeof window === "undefined";
2723
- let text = "";
2724
- if (isServer) {
2725
- setHeadlessRootRenderableClass(RootRenderable2);
2726
- const renderer = new HeadlessRenderer({ cols: fallbackCols, rows: fallbackRows });
2727
- const root = createHeadlessRoot(renderer);
2728
- text = root.renderToText(children);
2729
- root.unmount();
2730
- }
2731
- return /* @__PURE__ */ jsx3("div", { style, className, children: /* @__PURE__ */ jsx3(
2732
- "pre",
2733
- {
2734
- suppressHydrationWarning: true,
2735
- "aria-hidden": true,
2736
- style: {
2737
- fontFamily,
2738
- fontSize,
2739
- margin: 0,
2740
- position: "absolute",
2741
- width: "1px",
2742
- height: "1px",
2743
- overflow: "hidden",
2744
- clip: "rect(0, 0, 0, 0)",
2745
- whiteSpace: "pre"
2746
- },
2747
- children: text
2748
- }
2749
- ) });
2750
- }
2751
- return /* @__PURE__ */ jsx3(
2752
- "div",
2753
- {
2754
- ref: containerRef,
2755
- style: {
2756
- position: "relative",
2757
- overflow: "hidden",
2758
- ...style
2759
- },
2760
- className,
2761
- children: /* @__PURE__ */ jsx3(
2762
- "canvas",
2763
- {
2764
- ref: canvasRef,
2765
- style: {
2766
- display: "block",
2767
- width: "100%",
2768
- height: "100%",
2769
- outline: "none"
2770
- }
2771
- }
2772
- )
2773
- }
2774
- );
2775
- }
2776
-
2777
- // src/mount.ts
2778
- function mountGridland(canvas, element, options = {}) {
2779
- const {
2780
- fontSize = 14,
2781
- fontFamily = "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'Consolas', monospace",
2782
- keyboard = true,
2783
- autoResize = true
2784
- } = options;
2785
- const { RootRenderable: RootRenderable3 } = __require("@opentui/core");
2786
- setRootRenderableClass(RootRenderable3);
2787
- const painter = new CanvasPainter({ fontSize, fontFamily });
2788
- const tempCtx = canvas.getContext("2d");
2789
- const cellSize = painter.measureCell(tempCtx);
2790
- let cols = options.cols ?? Math.max(1, Math.floor(canvas.clientWidth / cellSize.width));
2791
- let rows = options.rows ?? Math.max(1, Math.floor(canvas.clientHeight / cellSize.height));
2792
- const renderer = new BrowserRenderer(canvas, cols, rows);
2793
- const root = createBrowserRoot(renderer);
2794
- root.render(element);
2795
- renderer.start();
2796
- canvas.tabIndex = 0;
2797
- let keydownHandler = null;
2798
- if (keyboard) {
2799
- keydownHandler = (e) => renderer.handleKeyDown(e);
2800
- canvas.addEventListener("keydown", keydownHandler);
2801
- }
2802
- let resizeObserver = null;
2803
- if (autoResize) {
2804
- resizeObserver = new ResizeObserver((entries) => {
2805
- for (const entry of entries) {
2806
- const { width, height } = entry.contentRect;
2807
- const newCols = Math.max(1, Math.floor(width / cellSize.width));
2808
- const newRows = Math.max(1, Math.floor(height / cellSize.height));
2809
- if (newCols !== cols || newRows !== rows) {
2810
- cols = newCols;
2811
- rows = newRows;
2812
- renderer.resize(newCols, newRows);
2813
- }
2814
- }
2815
- });
2816
- resizeObserver.observe(canvas);
2817
- }
2818
- return {
2819
- renderer,
2820
- unmount() {
2821
- if (keydownHandler) {
2822
- canvas.removeEventListener("keydown", keydownHandler);
2823
- }
2824
- resizeObserver?.disconnect();
2825
- renderer.stop();
2826
- root.unmount();
2827
- },
2828
- resize(newCols, newRows) {
2829
- cols = newCols;
2830
- rows = newRows;
2831
- renderer.resize(newCols, newRows);
2832
- }
2833
- };
2834
- }
2835
-
2836
- // src/file-drop.ts
2837
- import { useEffect as useEffect2, useState as useState2 } from "react";
2838
- function useFileDrop(callback) {
2839
- const { renderContext } = useBrowserContext();
2840
- const [isDragOver, setIsDragOver] = useState2(false);
2841
- useEffect2(() => {
2842
- const handler = (file) => {
2843
- callback(file);
2844
- };
2845
- const onDragEnter = () => setIsDragOver(true);
2846
- const onDragLeave = () => setIsDragOver(false);
2847
- renderContext.on("file-drop", handler);
2848
- renderContext.on("drag-enter", onDragEnter);
2849
- renderContext.on("drag-leave", onDragLeave);
2850
- return () => {
2851
- renderContext.off("file-drop", handler);
2852
- renderContext.off("drag-enter", onDragEnter);
2853
- renderContext.off("drag-leave", onDragLeave);
2854
- };
2855
- }, [renderContext, callback]);
2856
- return { isDragOver };
2857
- }
2858
-
2859
- // src/paste.ts
2860
- import { useEffect as useEffect3 } from "react";
2861
- function usePaste(callback) {
2862
- const { renderContext } = useBrowserContext();
2863
- useEffect3(() => {
2864
- const handler = (text) => {
2865
- callback(text);
2866
- };
2867
- renderContext.on("paste", handler);
2868
- return () => {
2869
- renderContext.off("paste", handler);
2870
- };
2871
- }, [renderContext, callback]);
2872
- }
2873
-
2874
- // src/utils.ts
2875
- function isBrowser() {
2876
- return typeof window !== "undefined" && typeof document !== "undefined";
2877
- }
2878
- function isCanvasSupported() {
2879
- if (!isBrowser()) return false;
2880
- const canvas = document.createElement("canvas");
2881
- return !!canvas.getContext("2d");
2882
- }
2883
- function calculateGridSize(widthPx, heightPx, cellWidth, cellHeight) {
2884
- return {
2885
- cols: Math.max(1, Math.floor(widthPx / cellWidth)),
2886
- rows: Math.max(1, Math.floor(heightPx / cellHeight))
2887
- };
2888
- }
2889
- export {
2890
- BrowserBuffer,
2891
- BrowserContext,
2892
- BrowserRenderContext,
2893
- BrowserRenderer,
2894
- BrowserTextBuffer,
2895
- BrowserTextBufferView,
2896
- CanvasPainter,
2897
- HeadlessRenderer,
2898
- SelectionManager,
2899
- TUI,
2900
- bufferToText,
2901
- calculateGridSize,
2902
- createBrowserRoot,
2903
- createHeadlessRoot,
2904
- isBrowser,
2905
- isCanvasSupported,
2906
- mountGridland,
2907
- setHeadlessRootRenderableClass,
2908
- useBrowserContext,
2909
- useFileDrop,
2910
- usePaste
2911
- };
2912
- //# sourceMappingURL=core.js.map