@react-x11/components 0.2.1 → 0.4.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 +6 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/heights.d.ts +26 -1
- package/dist/internal/heights.d.ts.map +1 -1
- package/dist/internal/heights.js +57 -6
- package/dist/internal/heights.js.map +1 -1
- package/dist/internal/scroll.d.ts +11 -0
- package/dist/internal/scroll.d.ts.map +1 -1
- package/dist/internal/scroll.js +43 -3
- package/dist/internal/scroll.js.map +1 -1
- package/dist/internal/timers.d.ts +8 -0
- package/dist/internal/timers.d.ts.map +1 -1
- package/dist/internal/timers.js +10 -0
- package/dist/internal/timers.js.map +1 -1
- package/dist/internal/window.d.ts +161 -0
- package/dist/internal/window.d.ts.map +1 -0
- package/dist/internal/window.js +417 -0
- package/dist/internal/window.js.map +1 -0
- package/dist/qml/containers.d.ts +3 -0
- package/dist/qml/containers.d.ts.map +1 -0
- package/dist/qml/containers.js +331 -0
- package/dist/qml/containers.js.map +1 -0
- package/dist/qml/controls.d.ts +12 -0
- package/dist/qml/controls.d.ts.map +1 -0
- package/dist/qml/controls.js +21 -0
- package/dist/qml/controls.js.map +1 -0
- package/dist/qml/globals.d.ts +6 -0
- package/dist/qml/globals.d.ts.map +1 -0
- package/dist/qml/globals.js +25 -0
- package/dist/qml/globals.js.map +1 -0
- package/dist/qml/index.d.ts +15 -0
- package/dist/qml/index.d.ts.map +1 -0
- package/dist/qml/index.js +25 -0
- package/dist/qml/index.js.map +1 -0
- package/dist/qml/interaction.d.ts +12 -0
- package/dist/qml/interaction.d.ts.map +1 -0
- package/dist/qml/interaction.js +246 -0
- package/dist/qml/interaction.js.map +1 -0
- package/dist/qml/ir.d.ts +106 -0
- package/dist/qml/ir.d.ts.map +1 -0
- package/dist/qml/ir.js +7 -0
- package/dist/qml/ir.js.map +1 -0
- package/dist/qml/layouts.d.ts +3 -0
- package/dist/qml/layouts.d.ts.map +1 -0
- package/dist/qml/layouts.js +122 -0
- package/dist/qml/layouts.js.map +1 -0
- package/dist/qml/models.d.ts +14 -0
- package/dist/qml/models.d.ts.map +1 -0
- package/dist/qml/models.js +80 -0
- package/dist/qml/models.js.map +1 -0
- package/dist/qml/objects.d.ts +228 -0
- package/dist/qml/objects.d.ts.map +1 -0
- package/dist/qml/objects.js +1103 -0
- package/dist/qml/objects.js.map +1 -0
- package/dist/qml/parse.d.ts +10 -0
- package/dist/qml/parse.d.ts.map +1 -0
- package/dist/qml/parse.js +607 -0
- package/dist/qml/parse.js.map +1 -0
- package/dist/qml/qtquick.d.ts +7 -0
- package/dist/qml/qtquick.d.ts.map +1 -0
- package/dist/qml/qtquick.js +347 -0
- package/dist/qml/qtquick.js.map +1 -0
- package/dist/qml/react.d.ts +83 -0
- package/dist/qml/react.d.ts.map +1 -0
- package/dist/qml/react.js +323 -0
- package/dist/qml/react.js.map +1 -0
- package/dist/qml/resolver.d.ts +38 -0
- package/dist/qml/resolver.d.ts.map +1 -0
- package/dist/qml/resolver.js +64 -0
- package/dist/qml/resolver.js.map +1 -0
- package/dist/qml/slots.d.ts +65 -0
- package/dist/qml/slots.d.ts.map +1 -0
- package/dist/qml/slots.js +230 -0
- package/dist/qml/slots.js.map +1 -0
- package/dist/qml/states.d.ts +5 -0
- package/dist/qml/states.d.ts.map +1 -0
- package/dist/qml/states.js +276 -0
- package/dist/qml/states.js.map +1 -0
- package/dist/qml/view-utils.d.ts +42 -0
- package/dist/qml/view-utils.d.ts.map +1 -0
- package/dist/qml/view-utils.js +121 -0
- package/dist/qml/view-utils.js.map +1 -0
- package/dist/table/index.d.ts +73 -2
- package/dist/table/index.d.ts.map +1 -1
- package/dist/table/index.js +405 -161
- package/dist/table/index.js.map +1 -1
- package/dist/tabs/hx.d.ts +18 -0
- package/dist/tabs/hx.d.ts.map +1 -0
- package/dist/tabs/hx.js +10 -0
- package/dist/tabs/hx.js.map +1 -0
- package/dist/tabs/index.d.ts +143 -0
- package/dist/tabs/index.d.ts.map +1 -0
- package/dist/tabs/index.js +602 -0
- package/dist/tabs/index.js.map +1 -0
- package/dist/terminal/vt/index.d.ts +1 -1
- package/dist/terminal/vt/index.d.ts.map +1 -1
- package/dist/terminal/vt/index.js +3 -3
- package/dist/terminal/vt/index.js.map +1 -1
- package/dist/terminal/vt/pty.d.ts +16 -0
- package/dist/terminal/vt/pty.d.ts.map +1 -1
- package/dist/terminal/vt/pty.js +186 -0
- package/dist/terminal/vt/pty.js.map +1 -1
- package/dist/tree/index.d.ts +65 -5
- package/dist/tree/index.d.ts.map +1 -1
- package/dist/tree/index.js +446 -189
- package/dist/tree/index.js.map +1 -1
- package/package.json +11 -1
- package/src/index.ts +44 -0
- package/src/internal/heights.ts +57 -6
- package/src/internal/scroll.ts +59 -3
- package/src/internal/timers.ts +13 -0
- package/src/internal/window.ts +570 -0
- package/src/qml/containers.tsx +380 -0
- package/src/qml/controls.tsx +47 -0
- package/src/qml/globals.ts +33 -0
- package/src/qml/index.ts +67 -0
- package/src/qml/interaction.tsx +317 -0
- package/src/qml/ir.ts +80 -0
- package/src/qml/layouts.tsx +141 -0
- package/src/qml/models.ts +101 -0
- package/src/qml/objects.ts +1431 -0
- package/src/qml/parse.ts +624 -0
- package/src/qml/qtquick.tsx +413 -0
- package/src/qml/react.tsx +454 -0
- package/src/qml/resolver.ts +99 -0
- package/src/qml/slots.ts +249 -0
- package/src/qml/states.ts +318 -0
- package/src/qml/view-utils.ts +140 -0
- package/src/table/index.ts +599 -178
- package/src/tabs/hx.ts +31 -0
- package/src/tabs/index.ts +850 -0
- package/src/terminal/vt/index.ts +4 -2
- package/src/terminal/vt/pty.ts +243 -0
- package/src/tree/index.ts +687 -260
package/src/terminal/vt/index.ts
CHANGED
|
@@ -33,7 +33,7 @@ import type { EmbedStatus, ExitInfo } from '../../embed/index.js';
|
|
|
33
33
|
import type { TerminalColors } from '../backends.js';
|
|
34
34
|
import { ELEMENT, VtTermNode } from './node.js';
|
|
35
35
|
import type { VtTermProps } from './node.js';
|
|
36
|
-
import { PtyUnavailableError,
|
|
36
|
+
import { PtyUnavailableError, defaultPtyHost } from './pty.js';
|
|
37
37
|
import type { PtyHost, PtySession } from './pty.js';
|
|
38
38
|
import { startTimeout, stopTimeout } from '../../embed/timers.js';
|
|
39
39
|
import type { TimerId } from '../../embed/timers.js';
|
|
@@ -45,6 +45,8 @@ export type { VtTermProps };
|
|
|
45
45
|
export {
|
|
46
46
|
PTY_MODULES,
|
|
47
47
|
PtyUnavailableError,
|
|
48
|
+
bunPtyHost,
|
|
49
|
+
defaultPtyHost,
|
|
48
50
|
defaultShell,
|
|
49
51
|
nodePtyHost,
|
|
50
52
|
ptyLoadError,
|
|
@@ -203,7 +205,7 @@ export function VtTerminal(props: VtTerminalProps): ReactElement {
|
|
|
203
205
|
);
|
|
204
206
|
return;
|
|
205
207
|
}
|
|
206
|
-
const host = handlers.current.pty ??
|
|
208
|
+
const host = handlers.current.pty ?? defaultPtyHost();
|
|
207
209
|
if (!(await host.available())) {
|
|
208
210
|
// The message separates "nothing installed" from "installed but it
|
|
209
211
|
// would not load" — a native module built for another Node ABI looks
|
package/src/terminal/vt/pty.ts
CHANGED
|
@@ -332,3 +332,246 @@ export function nodePtyHost(): PtyHost {
|
|
|
332
332
|
sharedHost = host;
|
|
333
333
|
return host;
|
|
334
334
|
}
|
|
335
|
+
|
|
336
|
+
// --- the Bun implementation ------------------------------------------------
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Bun's built-in pty, as of Bun 1.4: `Bun.spawn(argv, { terminal })` hands
|
|
340
|
+
* back a `Terminal` on the subprocess.
|
|
341
|
+
*
|
|
342
|
+
* This exists for the reason the whole file exists in the first place — a pty
|
|
343
|
+
* should not cost 64 MB and a native build. Under Bun it costs nothing: the
|
|
344
|
+
* pty is the runtime's, so `node-pty` is not installed, not probed, and not
|
|
345
|
+
* loaded, and the vt backend works out of the box on a machine with no C
|
|
346
|
+
* toolchain. Same argument as `@xterm/headless` being lazy: an app should not
|
|
347
|
+
* pay for what its runtime already provides.
|
|
348
|
+
*
|
|
349
|
+
* Written structurally, like `NodePty` above and for the same two reasons:
|
|
350
|
+
* `src/` may not name another runtime's types, and `@types/bun` is not
|
|
351
|
+
* installed here (nor in a Node app that merely imports this package).
|
|
352
|
+
*/
|
|
353
|
+
interface BunTerminalHandle {
|
|
354
|
+
write(data: string | Uint8Array): void;
|
|
355
|
+
resize(cols: number, rows: number): void;
|
|
356
|
+
close(): void;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
interface BunSubprocess {
|
|
360
|
+
readonly pid: number;
|
|
361
|
+
readonly exitCode: number | null;
|
|
362
|
+
readonly signalCode: string | null;
|
|
363
|
+
readonly exited: Promise<number>;
|
|
364
|
+
readonly terminal?: BunTerminalHandle | null;
|
|
365
|
+
kill(signal?: string | number): void;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
interface BunRuntime {
|
|
369
|
+
/** The pty class. Present from 1.4 — this is the feature detector. */
|
|
370
|
+
Terminal?: unknown;
|
|
371
|
+
spawn(
|
|
372
|
+
argv: readonly string[],
|
|
373
|
+
options: {
|
|
374
|
+
cwd?: string;
|
|
375
|
+
env?: Record<string, string | undefined>;
|
|
376
|
+
terminal?: {
|
|
377
|
+
cols?: number;
|
|
378
|
+
rows?: number;
|
|
379
|
+
data?(terminal: BunTerminalHandle, chunk: Uint8Array): void;
|
|
380
|
+
};
|
|
381
|
+
},
|
|
382
|
+
): BunSubprocess;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* The Bun runtime, if this is Bun *and* its Bun is new enough to have a pty.
|
|
387
|
+
*
|
|
388
|
+
* `Bun.Terminal` rather than a parse of `Bun.version`: the `terminal` option
|
|
389
|
+
* is ignored rather than rejected by a Bun that predates it, so a version
|
|
390
|
+
* comparison is the difference between falling through to node-pty and
|
|
391
|
+
* spawning a child whose output goes nowhere.
|
|
392
|
+
*/
|
|
393
|
+
function bunRuntime(): BunRuntime | null {
|
|
394
|
+
const bun = (globalThis as { Bun?: BunRuntime }).Bun;
|
|
395
|
+
if (!bun || typeof bun.spawn !== 'function') return null;
|
|
396
|
+
return typeof bun.Terminal === 'function' ? bun : null;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
class BunPtySession implements PtySession {
|
|
400
|
+
#proc: BunSubprocess;
|
|
401
|
+
#term: BunTerminalHandle;
|
|
402
|
+
#alive = true;
|
|
403
|
+
// Bun takes the output callback as a *spawn option*, so bytes can land
|
|
404
|
+
// before `openPty` has even returned — let alone before the caller has
|
|
405
|
+
// called `onData`. Anything that arrives in that window is held here and
|
|
406
|
+
// flushed on attach; without it the shell's first prompt is a coin flip.
|
|
407
|
+
#backlog: Uint8Array[] = [];
|
|
408
|
+
#data: ((chunk: Uint8Array) => void) | null = null;
|
|
409
|
+
// Same race, one step later: a program that exits immediately settles
|
|
410
|
+
// `exited` before `onExit` is attached.
|
|
411
|
+
#exit: ExitInfo | null = null;
|
|
412
|
+
#onExit: ((info: ExitInfo) => void) | null = null;
|
|
413
|
+
|
|
414
|
+
constructor(proc: BunSubprocess, term: BunTerminalHandle) {
|
|
415
|
+
this.#proc = proc;
|
|
416
|
+
this.#term = term;
|
|
417
|
+
void proc.exited.then(() => {
|
|
418
|
+
this.#alive = false;
|
|
419
|
+
// Bun reports the signal by *name* (`'SIGTERM'`) and nulls `exitCode`
|
|
420
|
+
// when one ended the child — which is `ExitInfo` exactly, so unlike the
|
|
421
|
+
// node-pty path there is no number to stringify and no name table to
|
|
422
|
+
// get wrong per platform.
|
|
423
|
+
const info: ExitInfo = {
|
|
424
|
+
code: proc.signalCode ? null : (proc.exitCode ?? 0),
|
|
425
|
+
signal: proc.signalCode ?? null,
|
|
426
|
+
};
|
|
427
|
+
if (this.#onExit) this.#onExit(info);
|
|
428
|
+
else this.#exit = info;
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/** Called from the spawn-time `data` callback. */
|
|
433
|
+
receive(chunk: Uint8Array): void {
|
|
434
|
+
if (this.#data) this.#data(chunk);
|
|
435
|
+
else this.#backlog.push(chunk);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
get pid(): number | null {
|
|
439
|
+
return this.#alive ? this.#proc.pid : null;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
write(data: string): void {
|
|
443
|
+
if (!this.#alive) return;
|
|
444
|
+
try {
|
|
445
|
+
this.#term.write(data);
|
|
446
|
+
} catch {
|
|
447
|
+
// the child exited between the keystroke and this write
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
resize(cols: number, rows: number): void {
|
|
452
|
+
if (!this.#alive || cols <= 0 || rows <= 0) return;
|
|
453
|
+
try {
|
|
454
|
+
this.#term.resize(cols, rows);
|
|
455
|
+
} catch {
|
|
456
|
+
// same race as write
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
kill(signal = 'SIGTERM'): boolean {
|
|
461
|
+
if (!this.#alive) return false;
|
|
462
|
+
try {
|
|
463
|
+
this.#proc.kill(signal);
|
|
464
|
+
return true;
|
|
465
|
+
} catch {
|
|
466
|
+
return false;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
onData(listener: (chunk: Uint8Array) => void): void {
|
|
471
|
+
this.#data = listener;
|
|
472
|
+
// Bytes, not a string: Bun hands over a `Buffer`, and the emulator's
|
|
473
|
+
// decoder is stateful across chunks. Decoding here would split a
|
|
474
|
+
// multi-byte character on whatever boundary the pty read returned.
|
|
475
|
+
const held = this.#backlog;
|
|
476
|
+
this.#backlog = [];
|
|
477
|
+
for (const chunk of held) listener(chunk);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
onExit(listener: (info: ExitInfo) => void): void {
|
|
481
|
+
this.#onExit = listener;
|
|
482
|
+
if (this.#exit) {
|
|
483
|
+
const info = this.#exit;
|
|
484
|
+
this.#exit = null;
|
|
485
|
+
listener(info);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// No `pause`/`resume`: Bun's terminal has no flow control to expose, and
|
|
490
|
+
// the interface makes them optional for exactly this case. The emulator's
|
|
491
|
+
// own write queue still bounds parse work per tick; only the pipe's
|
|
492
|
+
// buffered bytes grow.
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
let sharedBunHost: PtyHost | null = null;
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* The runtime's own pty. Null-free: callers reach it through
|
|
499
|
+
* `defaultPtyHost()`, and `available()` answers honestly under Node.
|
|
500
|
+
*/
|
|
501
|
+
export function bunPtyHost(): PtyHost {
|
|
502
|
+
if (sharedBunHost) return sharedBunHost;
|
|
503
|
+
|
|
504
|
+
const host: PtyHost = {
|
|
505
|
+
environment() {
|
|
506
|
+
return nodeProcess()?.env ?? {};
|
|
507
|
+
},
|
|
508
|
+
|
|
509
|
+
async available() {
|
|
510
|
+
return bunRuntime() !== null;
|
|
511
|
+
},
|
|
512
|
+
|
|
513
|
+
async openPty(argv, options) {
|
|
514
|
+
const bun = bunRuntime();
|
|
515
|
+
if (!bun) throw new PtyUnavailableError();
|
|
516
|
+
const ambient = host.environment?.() ?? {};
|
|
517
|
+
const merged: Record<string, string> = {};
|
|
518
|
+
for (const [key, value] of Object.entries({
|
|
519
|
+
...ambient,
|
|
520
|
+
...options.env,
|
|
521
|
+
})) {
|
|
522
|
+
// An explicit `undefined` removes a variable, as `SpawnOptions` says
|
|
523
|
+
if (value !== undefined) merged[key] = String(value);
|
|
524
|
+
}
|
|
525
|
+
// Bun sets no `TERM` of its own — the pty is a device, not a profile —
|
|
526
|
+
// so the honest advertisement is made here, as the node-pty path makes
|
|
527
|
+
// it through node-pty's `name`.
|
|
528
|
+
merged.TERM ??= 'xterm-256color';
|
|
529
|
+
const file = argv[0] ?? defaultShell(ambient);
|
|
530
|
+
let session: BunPtySession | null = null;
|
|
531
|
+
const proc = bun.spawn([file, ...argv.slice(1)], {
|
|
532
|
+
cwd: options.cwd,
|
|
533
|
+
env: merged,
|
|
534
|
+
terminal: {
|
|
535
|
+
cols: options.cols,
|
|
536
|
+
rows: options.rows,
|
|
537
|
+
data(_terminal, chunk) {
|
|
538
|
+
session?.receive(chunk);
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
});
|
|
542
|
+
const term = proc.terminal;
|
|
543
|
+
if (!term) {
|
|
544
|
+
// A Bun that has `Terminal` but did not give us one back: kill the
|
|
545
|
+
// child rather than leak it, and report it as a load failure so the
|
|
546
|
+
// caller renders `fallback` instead of an empty terminal.
|
|
547
|
+
try {
|
|
548
|
+
proc.kill();
|
|
549
|
+
} catch {
|
|
550
|
+
// already gone
|
|
551
|
+
}
|
|
552
|
+
throw new PtyUnavailableError(
|
|
553
|
+
new Error('Bun.spawn returned no terminal for a pty request'),
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
session = new BunPtySession(proc, term);
|
|
557
|
+
return session;
|
|
558
|
+
},
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
sharedBunHost = host;
|
|
562
|
+
return host;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* The pty the current runtime should use: Bun's own where there is one,
|
|
567
|
+
* `node-pty` otherwise.
|
|
568
|
+
*
|
|
569
|
+
* Bun wins on purpose even when node-pty is installed and would load (Bun's
|
|
570
|
+
* N-API support is good enough that it does). The built-in needs no native
|
|
571
|
+
* build, no ABI match and no 64 MB, and "use what the runtime provides" is
|
|
572
|
+
* the same call this package makes everywhere else. An app that wants the
|
|
573
|
+
* other one back passes `pty={nodePtyHost()}`, which is what the seam is for.
|
|
574
|
+
*/
|
|
575
|
+
export function defaultPtyHost(): PtyHost {
|
|
576
|
+
return bunRuntime() ? bunPtyHost() : nodePtyHost();
|
|
577
|
+
}
|