@aurigax-ai/ghostty-web 0.1.0 → 0.2.0
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/README.md +5 -1
- package/lib/box-glyphs.ts +278 -0
- package/lib/ghostty.ts +244 -126
- package/lib/index.ts +2 -1
- package/lib/input-handler.ts +13 -1
- package/lib/interfaces.ts +6 -0
- package/lib/renderer.ts +550 -688
- package/lib/selection-manager.ts +8 -3
- package/lib/terminal.ts +58 -30
- package/lib/webgl-renderer.ts +870 -0
- package/package.json +2 -2
package/lib/selection-manager.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { EventEmitter } from './event-emitter';
|
|
14
14
|
import type { GhosttyTerminal } from './ghostty';
|
|
15
15
|
import type { IEvent } from './interfaces';
|
|
16
|
-
import type {
|
|
16
|
+
import type { TerminalRenderer } from './renderer';
|
|
17
17
|
import type { Terminal } from './terminal';
|
|
18
18
|
import type { GhosttyCell } from './types';
|
|
19
19
|
|
|
@@ -34,7 +34,7 @@ export interface SelectionCoordinates {
|
|
|
34
34
|
|
|
35
35
|
export class SelectionManager {
|
|
36
36
|
private terminal: Terminal;
|
|
37
|
-
private renderer:
|
|
37
|
+
private renderer: TerminalRenderer;
|
|
38
38
|
private wasmTerm: GhosttyTerminal;
|
|
39
39
|
private textarea: HTMLTextAreaElement;
|
|
40
40
|
|
|
@@ -101,7 +101,7 @@ export class SelectionManager {
|
|
|
101
101
|
|
|
102
102
|
constructor(
|
|
103
103
|
terminal: Terminal,
|
|
104
|
-
renderer:
|
|
104
|
+
renderer: TerminalRenderer,
|
|
105
105
|
wasmTerm: GhosttyTerminal,
|
|
106
106
|
textarea: HTMLTextAreaElement
|
|
107
107
|
) {
|
|
@@ -114,6 +114,11 @@ export class SelectionManager {
|
|
|
114
114
|
this.attachEventListeners();
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
/** Points the selection at a new renderer drawing the same canvas. */
|
|
118
|
+
setRenderer(renderer: TerminalRenderer): void {
|
|
119
|
+
this.renderer = renderer;
|
|
120
|
+
}
|
|
121
|
+
|
|
117
122
|
// ==========================================================================
|
|
118
123
|
// Public API
|
|
119
124
|
// ==========================================================================
|
package/lib/terminal.ts
CHANGED
|
@@ -44,9 +44,10 @@ import { LinkDetector } from './link-detector';
|
|
|
44
44
|
import { type IMarker, Marker } from './marker';
|
|
45
45
|
import { OSC8LinkProvider } from './providers/osc8-link-provider';
|
|
46
46
|
import { UrlRegexProvider } from './providers/url-regex-provider';
|
|
47
|
-
import { CanvasRenderer } from './renderer';
|
|
47
|
+
import { CanvasRenderer, type RendererOptions, type TerminalRenderer } from './renderer';
|
|
48
48
|
import { SelectionManager } from './selection-manager';
|
|
49
49
|
import type { ILink, ILinkProvider } from './types';
|
|
50
|
+
import { WebglRenderer } from './webgl-renderer';
|
|
50
51
|
|
|
51
52
|
// ============================================================================
|
|
52
53
|
// Terminal Class
|
|
@@ -75,7 +76,7 @@ export class Terminal implements ITerminalCore {
|
|
|
75
76
|
// Components (created on open())
|
|
76
77
|
private ghostty?: Ghostty;
|
|
77
78
|
public wasmTerm?: GhosttyTerminal; // Made public for link providers
|
|
78
|
-
public renderer?:
|
|
79
|
+
public renderer?: TerminalRenderer; // Made public for FitAddon
|
|
79
80
|
private inputHandler?: InputHandler;
|
|
80
81
|
private selectionManager?: SelectionManager;
|
|
81
82
|
private canvas?: HTMLCanvasElement;
|
|
@@ -187,6 +188,7 @@ export class Terminal implements ITerminalCore {
|
|
|
187
188
|
fontSize: options.fontSize ?? 15,
|
|
188
189
|
fontFamily: options.fontFamily ?? 'monospace',
|
|
189
190
|
allowTransparency: options.allowTransparency ?? false,
|
|
191
|
+
renderer: options.renderer ?? 'webgl',
|
|
190
192
|
convertEol: options.convertEol ?? false,
|
|
191
193
|
disableStdin: options.disableStdin ?? false,
|
|
192
194
|
smoothScrollDuration: options.smoothScrollDuration ?? 100, // Default: 100ms smooth scroll
|
|
@@ -283,13 +285,6 @@ export class Terminal implements ITerminalCore {
|
|
|
283
285
|
// Resize canvas to match new font metrics
|
|
284
286
|
this.renderer.resize(this.cols, this.rows);
|
|
285
287
|
|
|
286
|
-
// Update canvas element dimensions to match renderer
|
|
287
|
-
const metrics = this.renderer.getMetrics();
|
|
288
|
-
this.canvas.width = metrics.width * this.cols;
|
|
289
|
-
this.canvas.height = metrics.height * this.rows;
|
|
290
|
-
this.canvas.style.width = `${metrics.width * this.cols}px`;
|
|
291
|
-
this.canvas.style.height = `${metrics.height * this.rows}px`;
|
|
292
|
-
|
|
293
288
|
// Force full re-render with new font
|
|
294
289
|
this.renderer.render(this.wasmTerm, true, this.viewportY, this);
|
|
295
290
|
}
|
|
@@ -457,29 +452,18 @@ export class Terminal implements ITerminalCore {
|
|
|
457
452
|
textarea.focus();
|
|
458
453
|
});
|
|
459
454
|
|
|
460
|
-
|
|
461
|
-
this.renderer = new CanvasRenderer(this.canvas, {
|
|
462
|
-
fontSize: this.options.fontSize,
|
|
463
|
-
fontFamily: this.options.fontFamily,
|
|
464
|
-
cursorStyle: this.options.cursorStyle,
|
|
465
|
-
cursorBlink: this.options.cursorBlink,
|
|
466
|
-
theme: this.options.theme,
|
|
467
|
-
});
|
|
468
|
-
this.renderer.onNeedsFrame = () => this.wake();
|
|
469
|
-
|
|
470
|
-
// Size canvas to terminal dimensions (use renderer.resize for proper DPI scaling)
|
|
455
|
+
this.renderer = this.createRenderer();
|
|
471
456
|
this.renderer.resize(this.cols, this.rows);
|
|
472
457
|
|
|
473
458
|
// Create mouse tracking configuration
|
|
474
459
|
const canvas = this.canvas;
|
|
475
|
-
const renderer = this.renderer;
|
|
476
460
|
const wasmTerm = this.wasmTerm;
|
|
477
461
|
const mouseConfig: MouseTrackingConfig = {
|
|
478
462
|
hasMouseTracking: () => wasmTerm?.hasMouseTracking() ?? false,
|
|
479
463
|
hasSgrMouseMode: () => wasmTerm?.getMode(1006, false) ?? true, // SGR extended mode
|
|
480
464
|
getCellDimensions: () => ({
|
|
481
|
-
width: renderer
|
|
482
|
-
height: renderer
|
|
465
|
+
width: this.renderer!.charWidth,
|
|
466
|
+
height: this.renderer!.charHeight,
|
|
483
467
|
}),
|
|
484
468
|
getCanvasOffset: () => {
|
|
485
469
|
const rect = canvas.getBoundingClientRect();
|
|
@@ -578,6 +562,57 @@ export class Terminal implements ITerminalCore {
|
|
|
578
562
|
}
|
|
579
563
|
}
|
|
580
564
|
|
|
565
|
+
/** Which renderer draws the terminal now: 'webgl', or 'canvas' when asked for or fallen back to. */
|
|
566
|
+
get rendererType(): 'webgl' | 'canvas' {
|
|
567
|
+
return this.renderer instanceof WebglRenderer ? 'webgl' : 'canvas';
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
private rendererOptions(): RendererOptions {
|
|
571
|
+
return {
|
|
572
|
+
fontSize: this.options.fontSize,
|
|
573
|
+
fontFamily: this.options.fontFamily,
|
|
574
|
+
cursorStyle: this.options.cursorStyle,
|
|
575
|
+
cursorBlink: this.options.cursorBlink,
|
|
576
|
+
theme: this.options.theme,
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
private createRenderer(): TerminalRenderer {
|
|
581
|
+
let renderer: TerminalRenderer | null = null;
|
|
582
|
+
if (this.options.renderer === 'webgl') {
|
|
583
|
+
try {
|
|
584
|
+
const webgl = new WebglRenderer(this.canvas!, this.rendererOptions());
|
|
585
|
+
webgl.onContextLost = () => this.fallBackToCanvas();
|
|
586
|
+
renderer = webgl;
|
|
587
|
+
} catch {
|
|
588
|
+
renderer = null;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
renderer ??= new CanvasRenderer(this.canvas!, this.rendererOptions());
|
|
592
|
+
renderer.onNeedsFrame = () => this.wake();
|
|
593
|
+
return renderer;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/** Replaces a WebGL renderer whose context was lost with a canvas renderer on the same canvas. */
|
|
597
|
+
private fallBackToCanvas(): void {
|
|
598
|
+
if (!this.renderer || !this.canvas || !(this.renderer instanceof WebglRenderer)) return;
|
|
599
|
+
const hoveredLink = this.renderer.getHoveredHyperlinkId();
|
|
600
|
+
this.renderer.dispose();
|
|
601
|
+
const renderer = new CanvasRenderer(this.canvas, this.rendererOptions());
|
|
602
|
+
renderer.onNeedsFrame = () => this.wake();
|
|
603
|
+
renderer.setHoveredHyperlinkId(hoveredLink);
|
|
604
|
+
if (this.selectionManager) {
|
|
605
|
+
renderer.setSelectionManager(this.selectionManager);
|
|
606
|
+
this.selectionManager.setRenderer(renderer);
|
|
607
|
+
}
|
|
608
|
+
this.renderer = renderer;
|
|
609
|
+
renderer.resize(this.cols, this.rows);
|
|
610
|
+
if (this.wasmTerm) {
|
|
611
|
+
renderer.render(this.wasmTerm, true, this.viewportY, this, this.scrollbarOpacity);
|
|
612
|
+
}
|
|
613
|
+
this.wake();
|
|
614
|
+
}
|
|
615
|
+
|
|
581
616
|
/**
|
|
582
617
|
* Write data to terminal
|
|
583
618
|
*/
|
|
@@ -723,13 +758,6 @@ export class Terminal implements ITerminalCore {
|
|
|
723
758
|
// Resize renderer
|
|
724
759
|
this.renderer!.resize(cols, rows);
|
|
725
760
|
|
|
726
|
-
// Update canvas dimensions
|
|
727
|
-
const metrics = this.renderer!.getMetrics();
|
|
728
|
-
this.canvas!.width = metrics.width * cols;
|
|
729
|
-
this.canvas!.height = metrics.height * rows;
|
|
730
|
-
this.canvas!.style.width = `${metrics.width * cols}px`;
|
|
731
|
-
this.canvas!.style.height = `${metrics.height * rows}px`;
|
|
732
|
-
|
|
733
761
|
// Fire resize event
|
|
734
762
|
this.resizeEmitter.fire({ cols, rows });
|
|
735
763
|
|