@floegence/floeterm-terminal-web 0.8.0 → 0.10.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 +25 -1
- package/dist/core/TerminalCore.d.ts +8 -1
- package/dist/core/TerminalCore.d.ts.map +1 -1
- package/dist/core/TerminalCore.js +141 -69
- package/dist/core/TerminalCore.js.map +1 -1
- package/dist/core/TerminalScrollbarOverlay.d.ts +63 -0
- package/dist/core/TerminalScrollbarOverlay.d.ts.map +1 -0
- package/dist/core/TerminalScrollbarOverlay.js +493 -0
- package/dist/core/TerminalScrollbarOverlay.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/GhosttyScrollbackCompat.d.ts +16 -0
- package/dist/internal/GhosttyScrollbackCompat.d.ts.map +1 -0
- package/dist/internal/GhosttyScrollbackCompat.js +43 -0
- package/dist/internal/GhosttyScrollbackCompat.js.map +1 -0
- package/dist/internal/GhosttyScrollbarCompat.d.ts +10 -0
- package/dist/internal/GhosttyScrollbarCompat.d.ts.map +1 -0
- package/dist/internal/GhosttyScrollbarCompat.js +94 -0
- package/dist/internal/GhosttyScrollbarCompat.js.map +1 -0
- package/dist/internal/TerminalResourceLoader.d.ts +27 -2
- package/dist/internal/TerminalResourceLoader.d.ts.map +1 -1
- package/dist/internal/TerminalResourceLoader.js +112 -47
- package/dist/internal/TerminalResourceLoader.js.map +1 -1
- package/dist/types.d.ts +13 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/themeColor.d.ts +12 -0
- package/dist/utils/themeColor.d.ts.map +1 -0
- package/dist/utils/themeColor.js +88 -0
- package/dist/utils/themeColor.js.map +1 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -53,6 +53,27 @@ const core = new TerminalCore(
|
|
|
53
53
|
await core.initialize();
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
+
Floeterm projects one renderer-neutral DOM scrollbar for both canvas and WebGL terminals while Ghostty remains the only scrollback and viewport owner:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
const core = new TerminalCore(container, {
|
|
60
|
+
rendererType: 'webgl',
|
|
61
|
+
fit: { scrollbarReservePx: 15 },
|
|
62
|
+
scrollbar: {
|
|
63
|
+
visibility: 'persistent', // "auto" by default, or "hidden"
|
|
64
|
+
minThumbPx: 24,
|
|
65
|
+
ariaLabel: 'Terminal history',
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Keep a long-lived Core aligned with a runtime locale change.
|
|
70
|
+
core.setScrollbarOptions({ ariaLabel: localizedTerminalHistoryLabel });
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The scrollbar uses a 12px visual and fine-pointer rail. Reserve at least 12px when the last terminal column must remain fully unobstructed; the default 15px reserve provides a stable gutter. An explicit `scrollbarReservePx: 0` remains supported as an overlay mode and may cover the rightmost 12px. The visual overlay itself never owns pointer hit testing: Floeterm captures fine-pointer track and thumb gestures at the viewport boundary, while wheel and touch events continue to target Ghostty's terminal surface exactly once. `persistent` automatically behaves as `auto` on coarse-pointer-only devices. Mixed-pointer devices keep mouse and pen interaction without creating a dead touch strip.
|
|
74
|
+
|
|
75
|
+
`auto` reveals on terminal wheel activity, keyboard focus, or fine-pointer movement into the right-edge rail, then fades after 1,200ms of inactivity. `persistent` remains visible on fine-pointer layouts and `hidden` removes the control from pointer, tab, and accessibility access. Alternate-screen programs and empty buffers also hide it. Theme-derived idle and hover/focus colors meet 3:1 and 4.5:1 contrast targets. Drag color targets 7:1; when that ratio is mathematically impossible against the configured background, Floeterm chooses whichever of black or white provides the highest available contrast. Forced-colors and reduced-motion preferences are honored.
|
|
76
|
+
|
|
56
77
|
After a host-owned layout transition or history recovery, request an explicit
|
|
57
78
|
presentation fence before declaring the terminal surface ready:
|
|
58
79
|
|
|
@@ -129,7 +150,10 @@ the foreground revision that was active at that exact byte boundary.
|
|
|
129
150
|
## Notes
|
|
130
151
|
|
|
131
152
|
- You must provide a `TerminalTransport` and `TerminalEventSource` for the managed controller.
|
|
132
|
-
- `ghostty-web`
|
|
153
|
+
- Each `TerminalCore` owns an isolated `ghostty-web` WASM runtime; consumers must not provide or share a Ghostty runtime.
|
|
154
|
+
- `scrollback` is measured in terminal buffer rows, including separately wrapped rows, and accepts integers from 1 through 10,000. Within the supported geometry, Floeterm preserves at least the requested number of buffer rows; it does not trim to an exact physical row count. Floeterm maps that contract to a bounded byte budget for the exact pinned `ghostty-web@0.4.0-next.14.g6a1a50d` release, which currently interprets the value as bytes. The 81,920,000-byte cap limits Ghostty's scrollback arena, not the total memory of a WASM runtime. The mapping is temporary and must be reviewed and removed or updated with any Ghostty upgrade.
|
|
155
|
+
- Supported terminal geometry is limited to 500 columns. Explicit dimensions above that limit fail, while automatic fitting reports at most 500 columns.
|
|
156
|
+
- Consumer dependency overrides for `ghostty-web` are unsupported. Floeterm's compatibility guard requires the exact package pin so an upstream behavior change cannot silently apply the row-to-byte mapping twice or reactivate the native canvas scrollbar beneath the renderer-neutral DOM control.
|
|
133
157
|
- `TerminalCore` bridges the hidden textarea used by `ghostty-web`, so soft-keyboard and composition input continue to work on touch devices.
|
|
134
158
|
- The hidden terminal input is mounted in the document viewport plane and anchored from rendered cursor geometry, so native IME candidate windows stay aligned without expanding transformed terminal hosts.
|
|
135
159
|
- Programmatic terminal focus uses no-scroll focus by default, keeping embedded terminals stable inside scaled, projected, or otherwise transformed host surfaces.
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { TerminalState, type Logger, type TerminalConfig, type TerminalCopySelectionResult, type TerminalCopySelectionSource, type TerminalDimensions, type TerminalEventHandlers, type TerminalFocusOptions, type TerminalAppearance, type TerminalLinkProvider, type TerminalRuntimeLineSnapshot, type TerminalRestorableSnapshot, type TerminalRestorableSnapshotOptions, type TerminalResourceEstimate, type TerminalTouchScrollRuntime, type TerminalInitializationOptions, type TerminalVisualSuspendHandle, type TerminalVisualSuspendOptions } from '../types.js';
|
|
1
|
+
import { TerminalState, type Logger, type TerminalConfig, type TerminalCopySelectionResult, type TerminalCopySelectionSource, type TerminalDimensions, type TerminalEventHandlers, type TerminalFocusOptions, type TerminalAppearance, type TerminalLinkProvider, type TerminalScrollbarOptions, type TerminalRuntimeLineSnapshot, type TerminalRestorableSnapshot, type TerminalRestorableSnapshotOptions, type TerminalResourceEstimate, type TerminalTouchScrollRuntime, type TerminalInitializationOptions, type TerminalVisualSuspendHandle, type TerminalVisualSuspendOptions } from '../types.js';
|
|
2
2
|
export { preloadTerminalResources } from '../internal/TerminalResourceLoader.js';
|
|
3
3
|
export declare class TerminalCore {
|
|
4
4
|
private container;
|
|
5
5
|
private config;
|
|
6
6
|
private terminal;
|
|
7
|
+
private ownedRuntime;
|
|
8
|
+
private ownedRuntimeMemory;
|
|
7
9
|
private fitAddon;
|
|
8
10
|
private needsFullRenderOnNextWrite;
|
|
9
11
|
private demandRenderForceAll;
|
|
@@ -22,6 +24,7 @@ export declare class TerminalCore {
|
|
|
22
24
|
private inputElement;
|
|
23
25
|
private viewportHost;
|
|
24
26
|
private renderHost;
|
|
27
|
+
private scrollbarOverlay;
|
|
25
28
|
private resizeObserver;
|
|
26
29
|
private resizeDebounceTimer;
|
|
27
30
|
private resizeRaf;
|
|
@@ -48,6 +51,8 @@ export declare class TerminalCore {
|
|
|
48
51
|
private pendingPresentationScale;
|
|
49
52
|
private suppressResizeNotifications;
|
|
50
53
|
private clearResizeSuppressionRaf;
|
|
54
|
+
private readonly scrollbackRows;
|
|
55
|
+
private scrollbarOptions;
|
|
51
56
|
private hasFocus;
|
|
52
57
|
private resizeNotifySeq;
|
|
53
58
|
private lastNotifiedSize;
|
|
@@ -76,6 +81,7 @@ export declare class TerminalCore {
|
|
|
76
81
|
private loadAddons;
|
|
77
82
|
private openTerminal;
|
|
78
83
|
private mountInputElement;
|
|
84
|
+
private mountScrollbarOverlay;
|
|
79
85
|
private installLinkDetector;
|
|
80
86
|
private installFitAddonGeometryPatch;
|
|
81
87
|
private proposeFitDimensions;
|
|
@@ -213,6 +219,7 @@ export declare class TerminalCore {
|
|
|
213
219
|
private forceResizeAndWaitForCommittedFrame;
|
|
214
220
|
setFixedDimensions(dimensions: TerminalDimensions | null): void;
|
|
215
221
|
setAppearance(appearance: TerminalAppearance): void;
|
|
222
|
+
setScrollbarOptions(options: Partial<TerminalScrollbarOptions>): void;
|
|
216
223
|
setTheme(theme: Record<string, unknown>): void;
|
|
217
224
|
setFontSize(size: number): void;
|
|
218
225
|
setPresentationScale(scale: number): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TerminalCore.d.ts","sourceRoot":"","sources":["../../src/core/TerminalCore.ts"],"names":[],"mappings":"AAYA,OAAO,EACL,aAAa,EACb,KAAK,MAAM,EAEX,KAAK,cAAc,EACnB,KAAK,2BAA2B,EAChC,KAAK,2BAA2B,EAChC,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAE1B,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,
|
|
1
|
+
{"version":3,"file":"TerminalCore.d.ts","sourceRoot":"","sources":["../../src/core/TerminalCore.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,15 +4,18 @@ import { cursorToFabricCursor, renderReasonFromForce, terminalLiveFabric, themeT
|
|
|
4
4
|
import { terminalFabricCoordinator } from '../fabric/TerminalFabricCoordinator.js';
|
|
5
5
|
import { TerminalState, } from '../types.js';
|
|
6
6
|
import { terminalInitializationScheduler, } from '../internal/TerminalInitializationScheduler.js';
|
|
7
|
-
import { getGhosttyFitAddonConstructor, getGhosttyLinkConstructors, getGhosttyTerminalConstructor, loadGhosttyModules, waitWithAbort, } from '../internal/TerminalResourceLoader.js';
|
|
7
|
+
import { acquireGhosttyRuntime, getGhosttyFitAddonConstructor, getGhosttyLinkConstructors, getGhosttyTerminalConstructor, inspectGhosttyRuntimeMemory, loadGhosttyModules, waitWithAbort, } from '../internal/TerminalResourceLoader.js';
|
|
8
|
+
import { capAutoFitTerminalColumns, mapGhosttyScrollbackRowsForPinnedVersion, validateTerminalColumns, validateTerminalScrollbackRows, } from '../internal/GhosttyScrollbackCompat.js';
|
|
9
|
+
import { installPinnedGhosttyAlternateScreenProjection, suppressPinnedGhosttyScrollbarBeforeOpen, suppressPinnedGhosttyScrollbarRenderer, } from '../internal/GhosttyScrollbarCompat.js';
|
|
8
10
|
import { createUnicodeSafeUrlProviderTerminal, } from '../internal/UnicodeSafeUrlProvider.js';
|
|
9
11
|
import { resolveTerminalInputElement, TerminalInputBridge } from './TerminalInputBridge.js';
|
|
10
12
|
import { terminalRenderScheduler } from './TerminalRenderScheduler.js';
|
|
11
13
|
import { scheduleUiTurn } from '../internal/scheduleUiTurn.js';
|
|
14
|
+
import { parseThemeColor } from '../utils/themeColor.js';
|
|
15
|
+
import { normalizeTerminalScrollbarOptions, TerminalScrollbarOverlay, } from './TerminalScrollbarOverlay.js';
|
|
12
16
|
const TERMINAL_SNAPSHOT_VERSION = 1;
|
|
13
17
|
const DEFAULT_TERMINAL_SNAPSHOT_MAX_BYTES = 2 * 1024 * 1024;
|
|
14
18
|
const ESTIMATED_RENDERER_BYTES = 4 * 1024 * 1024;
|
|
15
|
-
const ESTIMATED_CELL_BYTES = 16;
|
|
16
19
|
const SNAPSHOT_RESET_PREFIX = '\x1bc\x1b[2J\x1b[H';
|
|
17
20
|
const encodedByteLength = (value) => new TextEncoder().encode(value).byteLength;
|
|
18
21
|
const TERMINAL_SEARCH_MAX_RESULTS = 5000;
|
|
@@ -131,41 +134,6 @@ const mapThemeToGhostty = (theme) => {
|
|
|
131
134
|
}
|
|
132
135
|
return mapped;
|
|
133
136
|
};
|
|
134
|
-
function normalizeThemeColor(value) {
|
|
135
|
-
if (!value) {
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
const trimmed = value.trim();
|
|
139
|
-
const shortHex = /^#([0-9a-fA-F]{3})$/.exec(trimmed);
|
|
140
|
-
if (shortHex) {
|
|
141
|
-
const [r, g, b] = shortHex[1].split('');
|
|
142
|
-
return `#${r}${r}${g}${g}${b}${b}`.toLowerCase();
|
|
143
|
-
}
|
|
144
|
-
if (/^#[0-9a-fA-F]{6}$/.test(trimmed)) {
|
|
145
|
-
return trimmed.toLowerCase();
|
|
146
|
-
}
|
|
147
|
-
const rgb = /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/.exec(trimmed);
|
|
148
|
-
if (!rgb) {
|
|
149
|
-
return null;
|
|
150
|
-
}
|
|
151
|
-
const channels = rgb.slice(1).map(channel => Number(channel));
|
|
152
|
-
if (channels.some(channel => !Number.isInteger(channel) || channel < 0 || channel > 255)) {
|
|
153
|
-
return null;
|
|
154
|
-
}
|
|
155
|
-
return `#${channels.map(channel => channel.toString(16).padStart(2, '0')).join('')}`;
|
|
156
|
-
}
|
|
157
|
-
function parseThemeColor(value) {
|
|
158
|
-
const normalized = normalizeThemeColor(value);
|
|
159
|
-
if (!normalized) {
|
|
160
|
-
return null;
|
|
161
|
-
}
|
|
162
|
-
const hex = normalized.slice(1);
|
|
163
|
-
return {
|
|
164
|
-
r: Number.parseInt(hex.slice(0, 2), 16),
|
|
165
|
-
g: Number.parseInt(hex.slice(2, 4), 16),
|
|
166
|
-
b: Number.parseInt(hex.slice(4, 6), 16),
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
137
|
function colorKey(color) {
|
|
170
138
|
return `${color.r},${color.g},${color.b}`;
|
|
171
139
|
}
|
|
@@ -255,14 +223,14 @@ function canSkipNativeCanvasRowRender(renderer, row, fabricActive) {
|
|
|
255
223
|
function samePresentationScale(left, right) {
|
|
256
224
|
return Math.abs(left - right) <= PRESENTATION_SCALE_EPSILON;
|
|
257
225
|
}
|
|
258
|
-
function normalizeTerminalDimensions(value) {
|
|
226
|
+
function normalizeTerminalDimensions(value, columnsLabel = 'fixedDimensions.cols') {
|
|
259
227
|
const raw = (typeof value === 'object' && value) ? value : null;
|
|
260
228
|
if (!raw) {
|
|
261
229
|
return null;
|
|
262
230
|
}
|
|
263
|
-
const cols =
|
|
231
|
+
const cols = validateTerminalColumns(raw.cols, columnsLabel);
|
|
264
232
|
const rows = Math.floor(Number(raw.rows));
|
|
265
|
-
if (!Number.isFinite(
|
|
233
|
+
if (!Number.isFinite(rows) || rows <= 0) {
|
|
266
234
|
return null;
|
|
267
235
|
}
|
|
268
236
|
return { cols, rows };
|
|
@@ -341,6 +309,8 @@ export class TerminalCore {
|
|
|
341
309
|
this.container = container;
|
|
342
310
|
this.config = config;
|
|
343
311
|
this.terminal = null;
|
|
312
|
+
this.ownedRuntime = null;
|
|
313
|
+
this.ownedRuntimeMemory = null;
|
|
344
314
|
this.fitAddon = null;
|
|
345
315
|
this.needsFullRenderOnNextWrite = false;
|
|
346
316
|
this.demandRenderForceAll = false;
|
|
@@ -357,6 +327,7 @@ export class TerminalCore {
|
|
|
357
327
|
this.inputElement = null;
|
|
358
328
|
this.viewportHost = null;
|
|
359
329
|
this.renderHost = null;
|
|
330
|
+
this.scrollbarOverlay = null;
|
|
360
331
|
this.resizeObserver = null;
|
|
361
332
|
this.resizeDebounceTimer = null;
|
|
362
333
|
this.resizeRaf = null;
|
|
@@ -406,14 +377,27 @@ export class TerminalCore {
|
|
|
406
377
|
pendingResizeReason: null,
|
|
407
378
|
pendingSearchOverlayRender: false,
|
|
408
379
|
};
|
|
380
|
+
const explicitCols = config?.cols;
|
|
381
|
+
if (explicitCols !== undefined) {
|
|
382
|
+
validateTerminalColumns(explicitCols);
|
|
383
|
+
}
|
|
384
|
+
this.scrollbackRows = validateTerminalScrollbackRows(config?.scrollback === undefined ? 1000 : config.scrollback);
|
|
409
385
|
this.eventHandlers = eventHandlers;
|
|
410
386
|
this.clipboard = TerminalCore.normalizeClipboardConfig(config?.clipboard);
|
|
411
387
|
this.fit = TerminalCore.normalizeFitConfig(config?.fit);
|
|
388
|
+
this.scrollbarOptions = normalizeTerminalScrollbarOptions(config?.scrollbar);
|
|
412
389
|
this.logger = logger ?? noopLogger;
|
|
413
390
|
this.responsive = TerminalCore.normalizeResponsiveConfig(config?.responsive);
|
|
414
391
|
this.logicalFontSize = TerminalCore.normalizeFontSize(config?.fontSize);
|
|
415
392
|
this.presentationScale = TerminalCore.normalizePresentationScale(config?.presentationScale);
|
|
416
393
|
this.fixedDimensions = normalizeTerminalDimensions(config?.fixedDimensions);
|
|
394
|
+
this.config = {
|
|
395
|
+
...config,
|
|
396
|
+
...(explicitCols === undefined ? {} : { cols: explicitCols }),
|
|
397
|
+
fixedDimensions: this.fixedDimensions,
|
|
398
|
+
scrollback: this.scrollbackRows,
|
|
399
|
+
scrollbar: this.scrollbarOptions,
|
|
400
|
+
};
|
|
417
401
|
this.renderTask = {
|
|
418
402
|
id: TerminalCore.nextRenderTaskId,
|
|
419
403
|
run: forceAll => {
|
|
@@ -457,6 +441,11 @@ export class TerminalCore {
|
|
|
457
441
|
promise: Promise.resolve(),
|
|
458
442
|
};
|
|
459
443
|
operation.promise = (async () => {
|
|
444
|
+
const assertInitializationActive = () => {
|
|
445
|
+
if (this.isDisposed)
|
|
446
|
+
throw new Error('Cannot initialize a disposed TerminalCore');
|
|
447
|
+
lifecycleAbortController.signal.throwIfAborted();
|
|
448
|
+
};
|
|
460
449
|
const permit = await request.permit;
|
|
461
450
|
if (!permit) {
|
|
462
451
|
if (this.isDisposed)
|
|
@@ -467,20 +456,21 @@ export class TerminalCore {
|
|
|
467
456
|
}
|
|
468
457
|
operation.started = true;
|
|
469
458
|
try {
|
|
470
|
-
|
|
471
|
-
throw new Error('Cannot initialize a disposed TerminalCore');
|
|
459
|
+
assertInitializationActive();
|
|
472
460
|
await loadGhosttyModules(this.logger, lifecycleAbortController.signal);
|
|
473
|
-
|
|
474
|
-
|
|
461
|
+
assertInitializationActive();
|
|
462
|
+
const ownedRuntime = await acquireGhosttyRuntime(this.logger);
|
|
463
|
+
assertInitializationActive();
|
|
464
|
+
const ownedRuntimeMemory = inspectGhosttyRuntimeMemory(ownedRuntime);
|
|
465
|
+
assertInitializationActive();
|
|
466
|
+
this.ownedRuntime = ownedRuntime;
|
|
467
|
+
this.ownedRuntimeMemory = ownedRuntimeMemory;
|
|
475
468
|
await this.createTerminalInstance(lifecycleAbortController.signal);
|
|
476
|
-
|
|
477
|
-
throw new Error('Cannot initialize a disposed TerminalCore');
|
|
469
|
+
assertInitializationActive();
|
|
478
470
|
await this.loadAddons();
|
|
479
|
-
|
|
480
|
-
throw new Error('Cannot initialize a disposed TerminalCore');
|
|
471
|
+
assertInitializationActive();
|
|
481
472
|
await this.openTerminal(lifecycleAbortController.signal);
|
|
482
|
-
|
|
483
|
-
throw new Error('Cannot initialize a disposed TerminalCore');
|
|
473
|
+
assertInitializationActive();
|
|
484
474
|
this.setupEventListeners();
|
|
485
475
|
this.setupResponsiveListeners();
|
|
486
476
|
this.startSizeWatching();
|
|
@@ -517,14 +507,26 @@ export class TerminalCore {
|
|
|
517
507
|
return;
|
|
518
508
|
settled = true;
|
|
519
509
|
operation.waiters = Math.max(0, operation.waiters - 1);
|
|
520
|
-
if (operation.waiters
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
if (this.initializationOperation === operation
|
|
524
|
-
this.
|
|
525
|
-
|
|
526
|
-
|
|
510
|
+
if (operation.waiters !== 0 || operation.cancelled)
|
|
511
|
+
return;
|
|
512
|
+
if (operation.started) {
|
|
513
|
+
if (this.initializationOperation === operation
|
|
514
|
+
&& !this.isDisposed
|
|
515
|
+
&& this.state === TerminalState.INITIALIZING) {
|
|
516
|
+
operation.cancelled = true;
|
|
517
|
+
// Ghostty.load() cannot be cancelled. Abort the Core lifecycle so the
|
|
518
|
+
// owned runtime is discarded when that load settles, while the
|
|
519
|
+
// initialization permit remains held by the operation's finally path.
|
|
520
|
+
operation.lifecycleAbortController.abort();
|
|
527
521
|
}
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
operation.cancelled = true;
|
|
525
|
+
operation.request.cancel();
|
|
526
|
+
if (this.initializationOperation === operation) {
|
|
527
|
+
this.initializationOperation = null;
|
|
528
|
+
if (!this.isDisposed)
|
|
529
|
+
this.setState(TerminalState.IDLE);
|
|
528
530
|
}
|
|
529
531
|
};
|
|
530
532
|
return waitWithAbort(operation.promise, signal).finally(releaseWaiter);
|
|
@@ -536,10 +538,14 @@ export class TerminalCore {
|
|
|
536
538
|
this.resizeObserver = null;
|
|
537
539
|
this.disposeInputBridge();
|
|
538
540
|
this.disposeTerminalEventListeners();
|
|
541
|
+
this.scrollbarOverlay?.dispose();
|
|
542
|
+
this.scrollbarOverlay = null;
|
|
539
543
|
this.fabricView?.dispose();
|
|
540
544
|
this.fabricView = null;
|
|
541
545
|
this.terminal?.dispose();
|
|
542
546
|
this.terminal = null;
|
|
547
|
+
this.ownedRuntimeMemory = null;
|
|
548
|
+
this.ownedRuntime = null;
|
|
543
549
|
this.inputElement?.remove();
|
|
544
550
|
this.inputElement = null;
|
|
545
551
|
this.fitAddon = null;
|
|
@@ -552,6 +558,9 @@ export class TerminalCore {
|
|
|
552
558
|
if (!TerminalCtor) {
|
|
553
559
|
throw new Error('ghostty-web module not loaded');
|
|
554
560
|
}
|
|
561
|
+
if (!this.ownedRuntime) {
|
|
562
|
+
throw new Error('ghostty-web runtime not acquired');
|
|
563
|
+
}
|
|
555
564
|
const defaultConfig = {
|
|
556
565
|
cols: 80,
|
|
557
566
|
rows: 24,
|
|
@@ -586,13 +595,14 @@ export class TerminalCore {
|
|
|
586
595
|
cursorBlink: typeof finalConfig.cursorBlink === 'boolean' ? finalConfig.cursorBlink : undefined,
|
|
587
596
|
cursorStyle: typeof finalConfig.cursorStyle === 'string' ? finalConfig.cursorStyle : undefined,
|
|
588
597
|
theme: initialTheme,
|
|
589
|
-
scrollback:
|
|
598
|
+
scrollback: mapGhosttyScrollbackRowsForPinnedVersion(this.scrollbackRows),
|
|
590
599
|
fontSize: this.resolveEffectiveFontSize(),
|
|
591
600
|
fontFamily: typeof finalConfig.fontFamily === 'string' ? finalConfig.fontFamily : undefined,
|
|
592
601
|
allowTransparency: typeof finalConfig.allowTransparency === 'boolean' ? finalConfig.allowTransparency : undefined,
|
|
593
602
|
convertEol: typeof finalConfig.convertEol === 'boolean' ? finalConfig.convertEol : undefined,
|
|
594
603
|
disableStdin: typeof finalConfig.disableStdin === 'boolean' ? finalConfig.disableStdin : undefined,
|
|
595
|
-
smoothScrollDuration: typeof finalConfig.smoothScrollDuration === 'number' ? finalConfig.smoothScrollDuration : undefined
|
|
604
|
+
smoothScrollDuration: typeof finalConfig.smoothScrollDuration === 'number' ? finalConfig.smoothScrollDuration : undefined,
|
|
605
|
+
ghostty: this.ownedRuntime,
|
|
596
606
|
});
|
|
597
607
|
}
|
|
598
608
|
async loadAddons() {
|
|
@@ -616,7 +626,11 @@ export class TerminalCore {
|
|
|
616
626
|
const renderHost = this.ensurePresentationHosts();
|
|
617
627
|
this.applyPresentationScaleStyles();
|
|
618
628
|
this.installDemandRenderPatchBeforeOpen();
|
|
629
|
+
suppressPinnedGhosttyScrollbarBeforeOpen(this.terminal);
|
|
619
630
|
this.terminal.open(renderHost);
|
|
631
|
+
installPinnedGhosttyAlternateScreenProjection(this.terminal);
|
|
632
|
+
const renderer = this.terminal.renderer;
|
|
633
|
+
suppressPinnedGhosttyScrollbarRenderer(renderer ?? {});
|
|
620
634
|
this.installLinkDetector();
|
|
621
635
|
if (this.config.rendererType === 'webgl') {
|
|
622
636
|
const canvas = this.terminal
|
|
@@ -630,6 +644,7 @@ export class TerminalCore {
|
|
|
630
644
|
this.patchSelectionManagerClipboardBehavior();
|
|
631
645
|
this.patchSelectionManagerRenderingBehavior();
|
|
632
646
|
this.setupInputBridge(renderHost);
|
|
647
|
+
this.mountScrollbarOverlay();
|
|
633
648
|
this.applyRegisteredLinkProviders();
|
|
634
649
|
void this.refreshFontMetricsAfterLoad('open');
|
|
635
650
|
}
|
|
@@ -645,6 +660,30 @@ export class TerminalCore {
|
|
|
645
660
|
body.appendChild(input);
|
|
646
661
|
this.inputElement = input;
|
|
647
662
|
}
|
|
663
|
+
mountScrollbarOverlay() {
|
|
664
|
+
if (!this.terminal || !this.viewportHost || !this.renderHost) {
|
|
665
|
+
throw new Error('Terminal scrollbar requires an initialized presentation host');
|
|
666
|
+
}
|
|
667
|
+
this.scrollbarOverlay?.dispose();
|
|
668
|
+
const terminal = this.terminal;
|
|
669
|
+
const surfaceId = `${this.fabricViewId}-surface`;
|
|
670
|
+
this.renderHost.id = surfaceId;
|
|
671
|
+
this.scrollbarOverlay = new TerminalScrollbarOverlay(this.viewportHost, {
|
|
672
|
+
get rows() {
|
|
673
|
+
return terminal.rows;
|
|
674
|
+
},
|
|
675
|
+
getScrollbackLength: () => terminal.getScrollbackLength(),
|
|
676
|
+
getViewportY: () => terminal.getViewportY(),
|
|
677
|
+
isAlternateScreen: () => terminal.isAlternateScreen(),
|
|
678
|
+
scrollToLine: line => terminal.scrollToLine(line),
|
|
679
|
+
scrollToTop: () => terminal.scrollToTop(),
|
|
680
|
+
scrollToBottom: () => terminal.scrollToBottom(),
|
|
681
|
+
scrollLines: amount => terminal.scrollLines(amount),
|
|
682
|
+
scrollPages: amount => terminal.scrollPages(amount),
|
|
683
|
+
focusTerminal: () => this.scheduleInputSurfaceRefocus({ preventScroll: true }),
|
|
684
|
+
}, this.scrollbarOptions, surfaceId);
|
|
685
|
+
this.scrollbarOverlay.setBackgroundColor(this.terminalThemeSource.background);
|
|
686
|
+
}
|
|
648
687
|
installLinkDetector() {
|
|
649
688
|
if (!this.terminal) {
|
|
650
689
|
throw new Error('Terminal instance not created');
|
|
@@ -693,7 +732,7 @@ export class TerminalCore {
|
|
|
693
732
|
const availableWidth = Math.max(0, width - paddingLeft - paddingRight - this.fit.scrollbarReservePx);
|
|
694
733
|
const availableHeight = Math.max(0, height - paddingTop - paddingBottom);
|
|
695
734
|
return {
|
|
696
|
-
cols: Math.max(MIN_TERMINAL_COLS, Math.floor(availableWidth / cellWidth)),
|
|
735
|
+
cols: capAutoFitTerminalColumns(Math.max(MIN_TERMINAL_COLS, Math.floor(availableWidth / cellWidth))),
|
|
697
736
|
rows: Math.max(MIN_TERMINAL_ROWS, Math.floor(availableHeight / cellHeight)),
|
|
698
737
|
};
|
|
699
738
|
}
|
|
@@ -705,13 +744,19 @@ export class TerminalCore {
|
|
|
705
744
|
if (!geometry) {
|
|
706
745
|
return null;
|
|
707
746
|
}
|
|
708
|
-
const
|
|
747
|
+
const cellWidth = Number(geometry.cellWidth);
|
|
748
|
+
const hostWidth = Number(this.viewportHost?.clientWidth ?? this.container.clientWidth);
|
|
749
|
+
const visibleCellWidth = cellWidth / this.presentationScale;
|
|
750
|
+
const cols = Number.isFinite(hostWidth) && hostWidth > 0
|
|
751
|
+
&& Number.isFinite(visibleCellWidth) && visibleCellWidth > 0
|
|
752
|
+
? Math.floor(Math.max(0, hostWidth - this.fit.scrollbarReservePx) / visibleCellWidth)
|
|
753
|
+
: Math.floor(Number(geometry.cols));
|
|
709
754
|
const rows = Math.floor(Number(geometry.rows));
|
|
710
755
|
if (!Number.isFinite(cols) || !Number.isFinite(rows) || cols <= 0 || rows <= 0) {
|
|
711
756
|
return null;
|
|
712
757
|
}
|
|
713
758
|
return {
|
|
714
|
-
cols: Math.max(MIN_TERMINAL_COLS, cols),
|
|
759
|
+
cols: capAutoFitTerminalColumns(Math.max(MIN_TERMINAL_COLS, cols)),
|
|
715
760
|
rows: Math.max(MIN_TERMINAL_ROWS, rows),
|
|
716
761
|
};
|
|
717
762
|
}
|
|
@@ -1529,6 +1574,8 @@ export class TerminalCore {
|
|
|
1529
1574
|
if (typeof this.terminal.onScroll === 'function') {
|
|
1530
1575
|
const disposable = this.terminal.onScroll(() => {
|
|
1531
1576
|
this.syncImeInputAnchor();
|
|
1577
|
+
this.scrollbarOverlay?.reveal();
|
|
1578
|
+
this.scrollbarOverlay?.sync();
|
|
1532
1579
|
this.requestDemandRender(false);
|
|
1533
1580
|
});
|
|
1534
1581
|
this.trackTerminalEventDisposable(disposable);
|
|
@@ -1816,6 +1863,7 @@ export class TerminalCore {
|
|
|
1816
1863
|
else {
|
|
1817
1864
|
this.requestDemandRender(shouldForceAfterWrite);
|
|
1818
1865
|
}
|
|
1866
|
+
this.scrollbarOverlay?.sync();
|
|
1819
1867
|
callback?.();
|
|
1820
1868
|
probe?.onTerminalWriteProfile?.({
|
|
1821
1869
|
totalMs: performance.now() - profileStartedAt,
|
|
@@ -1915,6 +1963,16 @@ export class TerminalCore {
|
|
|
1915
1963
|
});
|
|
1916
1964
|
}
|
|
1917
1965
|
getResourceEstimate() {
|
|
1966
|
+
const wasmMemoryBytes = this.ownedRuntimeMemory?.buffer.byteLength ?? 0;
|
|
1967
|
+
if (wasmMemoryBytes <= 0) {
|
|
1968
|
+
return {
|
|
1969
|
+
bufferBytes: 0,
|
|
1970
|
+
cellCount: 0,
|
|
1971
|
+
wasmMemoryBytes: 0,
|
|
1972
|
+
estimatedBytes: 0,
|
|
1973
|
+
rendererType: this.config.rendererType ?? 'canvas',
|
|
1974
|
+
};
|
|
1975
|
+
}
|
|
1918
1976
|
const info = this.getTerminalInfo();
|
|
1919
1977
|
const cellCount = info ? Math.max(0, info.cols * info.bufferLength) : 0;
|
|
1920
1978
|
let bufferBytes = 0;
|
|
@@ -1926,7 +1984,8 @@ export class TerminalCore {
|
|
|
1926
1984
|
return {
|
|
1927
1985
|
bufferBytes,
|
|
1928
1986
|
cellCount,
|
|
1929
|
-
|
|
1987
|
+
wasmMemoryBytes,
|
|
1988
|
+
estimatedBytes: ESTIMATED_RENDERER_BYTES + wasmMemoryBytes,
|
|
1930
1989
|
rendererType: this.config.rendererType ?? 'canvas',
|
|
1931
1990
|
};
|
|
1932
1991
|
}
|
|
@@ -2742,11 +2801,19 @@ export class TerminalCore {
|
|
|
2742
2801
|
this.setPresentationScale(appearance.presentationScale);
|
|
2743
2802
|
}
|
|
2744
2803
|
}
|
|
2804
|
+
setScrollbarOptions(options) {
|
|
2805
|
+
normalizeTerminalScrollbarOptions(options);
|
|
2806
|
+
const next = normalizeTerminalScrollbarOptions({ ...this.scrollbarOptions, ...options });
|
|
2807
|
+
this.scrollbarOptions = next;
|
|
2808
|
+
this.config = { ...this.config, scrollbar: next };
|
|
2809
|
+
this.scrollbarOverlay?.setOptions(next);
|
|
2810
|
+
}
|
|
2745
2811
|
setTheme(theme) {
|
|
2746
2812
|
const mapped = mapThemeToGhostty(theme);
|
|
2747
2813
|
// Persist latest theme so a future re-initialization can reuse it.
|
|
2748
2814
|
this.config = { ...this.config, theme: mapped };
|
|
2749
2815
|
this.themeColorTranslator = buildThemeColorTranslator(this.terminalThemeSource, mapped);
|
|
2816
|
+
this.scrollbarOverlay?.setBackgroundColor(mapped.background);
|
|
2750
2817
|
if (!this.terminal) {
|
|
2751
2818
|
return;
|
|
2752
2819
|
}
|
|
@@ -2886,6 +2953,8 @@ export class TerminalCore {
|
|
|
2886
2953
|
this.disposeInputBridge();
|
|
2887
2954
|
this.disposeTerminalEventListeners();
|
|
2888
2955
|
this.disposeSearchOverlay();
|
|
2956
|
+
this.scrollbarOverlay?.dispose();
|
|
2957
|
+
this.scrollbarOverlay = null;
|
|
2889
2958
|
this.fabricAttachSeq += 1;
|
|
2890
2959
|
this.cancelFabricAttachSchedule?.();
|
|
2891
2960
|
this.cancelFabricAttachSchedule = null;
|
|
@@ -2895,10 +2964,14 @@ export class TerminalCore {
|
|
|
2895
2964
|
this.fabricView = null;
|
|
2896
2965
|
this.terminal?.dispose();
|
|
2897
2966
|
this.terminal = null;
|
|
2967
|
+
this.ownedRuntimeMemory = null;
|
|
2968
|
+
this.ownedRuntime = null;
|
|
2898
2969
|
this.inputElement?.remove();
|
|
2899
2970
|
this.inputElement = null;
|
|
2971
|
+
this.viewportHost?.remove();
|
|
2900
2972
|
this.viewportHost = null;
|
|
2901
2973
|
this.renderHost = null;
|
|
2974
|
+
this.fitAddon = null;
|
|
2902
2975
|
this.registeredLinkProviders.clear();
|
|
2903
2976
|
this.appliedLinkProviders.clear();
|
|
2904
2977
|
this.setState(TerminalState.DISPOSED);
|
|
@@ -3151,6 +3224,7 @@ export class TerminalCore {
|
|
|
3151
3224
|
terminalAny.cursorMoveEmitter?.fire?.();
|
|
3152
3225
|
}
|
|
3153
3226
|
this.syncImeInputAnchor();
|
|
3227
|
+
this.scrollbarOverlay?.sync();
|
|
3154
3228
|
const durationMs = performance.now() - startedAt;
|
|
3155
3229
|
getPerfProbe()?.onTerminalRender?.(durationMs);
|
|
3156
3230
|
if (this.config.rendererType !== 'webgl'
|
|
@@ -3183,12 +3257,10 @@ export class TerminalCore {
|
|
|
3183
3257
|
presentationRequest.frameHandle = requestAnimationFrame(() => {
|
|
3184
3258
|
if (!this.presentationRequests.has(presentationRequest))
|
|
3185
3259
|
return;
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
presentationRequest.resolve();
|
|
3191
|
-
});
|
|
3260
|
+
if (!this.presentationRequests.delete(presentationRequest))
|
|
3261
|
+
return;
|
|
3262
|
+
presentationRequest.frameHandle = null;
|
|
3263
|
+
presentationRequest.resolve();
|
|
3192
3264
|
});
|
|
3193
3265
|
}
|
|
3194
3266
|
}
|