@flyingrobots/bijou-node 7.2.0 → 8.0.0-rc.1
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/chalk-token-styler.d.ts +4 -0
- package/dist/chalk-token-styler.d.ts.map +1 -0
- package/dist/chalk-token-styler.js +79 -0
- package/dist/chalk-token-styler.js.map +1 -0
- package/dist/index.d.ts +1 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -108
- package/dist/index.js.map +1 -1
- package/dist/node-context-selection.d.ts +11 -0
- package/dist/node-context-selection.d.ts.map +1 -0
- package/dist/node-context-selection.js +77 -0
- package/dist/node-context-selection.js.map +1 -0
- package/dist/node-context.d.ts +14 -0
- package/dist/node-context.d.ts.map +1 -0
- package/dist/node-context.js +30 -0
- package/dist/node-context.js.map +1 -0
- package/dist/recorder.d.ts +2 -40
- package/dist/recorder.d.ts.map +1 -1
- package/dist/recorder.js +1 -239
- package/dist/recorder.js.map +1 -1
- package/dist/recorder.part01.d.ts +56 -0
- package/dist/recorder.part01.d.ts.map +1 -0
- package/dist/recorder.part01.js +76 -0
- package/dist/recorder.part01.js.map +1 -0
- package/dist/recorder.part02.d.ts +5 -0
- package/dist/recorder.part02.d.ts.map +1 -0
- package/dist/recorder.part02.js +54 -0
- package/dist/recorder.part02.js.map +1 -0
- package/dist/recorder.part03.d.ts +6 -0
- package/dist/recorder.part03.d.ts.map +1 -0
- package/dist/recorder.part03.js +118 -0
- package/dist/recorder.part03.js.map +1 -0
- package/dist/style.d.ts.map +1 -1
- package/dist/style.js +4 -97
- package/dist/style.js.map +1 -1
- package/dist/worker/worker.d.ts +4 -76
- package/dist/worker/worker.d.ts.map +1 -1
- package/dist/worker/worker.js +3 -240
- package/dist/worker/worker.js.map +1 -1
- package/dist/worker/worker.part01.d.ts +59 -0
- package/dist/worker/worker.part01.d.ts.map +1 -0
- package/dist/worker/worker.part01.js +40 -0
- package/dist/worker/worker.part01.js.map +1 -0
- package/dist/worker/worker.part02.d.ts +12 -0
- package/dist/worker/worker.part02.d.ts.map +1 -0
- package/dist/worker/worker.part02.js +121 -0
- package/dist/worker/worker.part02.js.map +1 -0
- package/dist/worker/worker.part03.d.ts +13 -0
- package/dist/worker/worker.part03.d.ts.map +1 -0
- package/dist/worker/worker.part03.js +106 -0
- package/dist/worker/worker.part03.js.map +1 -0
- package/package.json +3 -3
package/dist/style.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import chalk, { Chalk } from 'chalk';
|
|
2
|
+
import { createChalkTokenStyler } from './chalk-token-styler.js';
|
|
2
3
|
/**
|
|
3
4
|
* Create a chalk-backed {@link StylePort} with optional color suppression.
|
|
4
5
|
*
|
|
@@ -8,96 +9,10 @@ import chalk, { Chalk } from 'chalk';
|
|
|
8
9
|
export function chalkStyle(arg) {
|
|
9
10
|
const opts = typeof arg === 'boolean' ? { noColor: arg } : (arg ?? {});
|
|
10
11
|
const isNoColor = opts.noColor ?? false;
|
|
11
|
-
const instance = opts.level !== undefined
|
|
12
|
-
? new Chalk({ level: opts.level })
|
|
13
|
-
: chalk;
|
|
12
|
+
const instance = opts.level !== undefined ? new Chalk({ level: opts.level }) : chalk;
|
|
14
13
|
/** Whether ANSI styling is active (respects both noColor flag and chalk level). */
|
|
15
14
|
const ansiEnabled = !isNoColor && instance.level > 0;
|
|
16
|
-
const
|
|
17
|
-
/** SGR codes for underline variants (not supported by chalk natively). */
|
|
18
|
-
const UNDERLINE_VARIANT_SGR = {
|
|
19
|
-
'curly-underline': '\x1b[4:3m',
|
|
20
|
-
'dotted-underline': '\x1b[4:4m',
|
|
21
|
-
'dashed-underline': '\x1b[4:5m',
|
|
22
|
-
};
|
|
23
|
-
/** SGR 24 resets underline. */
|
|
24
|
-
const UNDERLINE_RESET = '\x1b[24m';
|
|
25
|
-
/**
|
|
26
|
-
* Chain text-decoration modifiers onto a chalk instance.
|
|
27
|
-
*
|
|
28
|
-
* @param c - Base chalk instance (already color-configured).
|
|
29
|
-
* @param modifiers - Optional array of modifier names from a {@link TokenValue}.
|
|
30
|
-
* @returns The chalk instance with all modifiers applied.
|
|
31
|
-
*/
|
|
32
|
-
function applyModifiers(c, modifiers) {
|
|
33
|
-
if (modifiers === undefined)
|
|
34
|
-
return c;
|
|
35
|
-
let result = c;
|
|
36
|
-
for (const mod of modifiers) {
|
|
37
|
-
switch (mod) {
|
|
38
|
-
case 'bold':
|
|
39
|
-
result = result.bold;
|
|
40
|
-
break;
|
|
41
|
-
case 'dim':
|
|
42
|
-
result = result.dim;
|
|
43
|
-
break;
|
|
44
|
-
case 'strikethrough':
|
|
45
|
-
result = result.strikethrough;
|
|
46
|
-
break;
|
|
47
|
-
case 'inverse':
|
|
48
|
-
result = result.inverse;
|
|
49
|
-
break;
|
|
50
|
-
case 'underline':
|
|
51
|
-
result = result.underline;
|
|
52
|
-
break;
|
|
53
|
-
// Underline variants are handled after chalk styling via raw SGR wrapping
|
|
54
|
-
case 'curly-underline':
|
|
55
|
-
case 'dotted-underline':
|
|
56
|
-
case 'dashed-underline': break;
|
|
57
|
-
default: {
|
|
58
|
-
const _exhaustive = mod;
|
|
59
|
-
void _exhaustive;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return result;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Wrap text with raw SGR sequences for underline variants that chalk
|
|
67
|
-
* doesn't support natively. Called after chalk styling is applied.
|
|
68
|
-
*/
|
|
69
|
-
function applyUnderlineVariants(text, modifiers) {
|
|
70
|
-
if (!ansiEnabled || modifiers === undefined)
|
|
71
|
-
return text;
|
|
72
|
-
let result = text;
|
|
73
|
-
for (const mod of modifiers) {
|
|
74
|
-
const sgr = UNDERLINE_VARIANT_SGR[mod];
|
|
75
|
-
if (sgr) {
|
|
76
|
-
result = sgr + result + UNDERLINE_RESET;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return result;
|
|
80
|
-
}
|
|
81
|
-
function styleCacheKey(token) {
|
|
82
|
-
return [
|
|
83
|
-
token.hex,
|
|
84
|
-
token.bg,
|
|
85
|
-
token.modifiers?.join(',') ?? '',
|
|
86
|
-
].join('|');
|
|
87
|
-
}
|
|
88
|
-
function compileStyled(token) {
|
|
89
|
-
const base = applyModifiers(token.hex ? instance.hex(token.hex) : instance, token.modifiers);
|
|
90
|
-
const background = token.bg ? instance.bgHex(token.bg) : null;
|
|
91
|
-
const modifiers = token.modifiers;
|
|
92
|
-
return (text) => {
|
|
93
|
-
let result = base(text);
|
|
94
|
-
result = applyUnderlineVariants(result, modifiers);
|
|
95
|
-
if (background) {
|
|
96
|
-
result = background(result);
|
|
97
|
-
}
|
|
98
|
-
return result;
|
|
99
|
-
};
|
|
100
|
-
}
|
|
15
|
+
const styleToken = createChalkTokenStyler(instance, ansiEnabled);
|
|
101
16
|
return {
|
|
102
17
|
/**
|
|
103
18
|
* Apply a resolved design-token's hex color and modifiers to text.
|
|
@@ -107,15 +22,7 @@ export function chalkStyle(arg) {
|
|
|
107
22
|
* @returns Styled text, or unmodified text when ANSI output is disabled via `noColor` or `level: 0`.
|
|
108
23
|
*/
|
|
109
24
|
styled(token, text) {
|
|
110
|
-
|
|
111
|
-
return text;
|
|
112
|
-
const key = styleCacheKey(token);
|
|
113
|
-
let styler = styledCache.get(key);
|
|
114
|
-
if (styler == null) {
|
|
115
|
-
styler = compileStyled(token);
|
|
116
|
-
styledCache.set(key, styler);
|
|
117
|
-
}
|
|
118
|
-
return styler(text);
|
|
25
|
+
return styleToken(token, text);
|
|
119
26
|
},
|
|
120
27
|
/**
|
|
121
28
|
* Apply a 24-bit RGB foreground color to text.
|
package/dist/style.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"style.js","sourceRoot":"","sources":["../src/style.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,KAAK,EAAsB,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"style.js","sourceRoot":"","sources":["../src/style.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,KAAK,EAAsB,MAAM,OAAO,CAAC;AAEzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAYjE;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,GAAiC;IAC1D,MAAM,IAAI,GAAG,OAAO,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;IACvE,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC;IACxC,MAAM,QAAQ,GACZ,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACtE,mFAAmF;IACnF,MAAM,WAAW,GAAG,CAAC,SAAS,IAAI,QAAQ,CAAC,KAAK,GAAG,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,sBAAsB,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAEjE,OAAO;QACL;;;;;;WAMG;QACH,MAAM,CAAC,KAAiB,EAAE,IAAY;YACpC,OAAO,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACjC,CAAC;QACD;;;;;;;;WAQG;QACH,GAAG,CAAC,CAAS,EAAE,CAAS,EAAE,CAAS,EAAE,IAAY;YAC/C,IAAI,SAAS;gBAAE,OAAO,IAAI,CAAC;YAC3B,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACrC,CAAC;QAED;;;;;;WAMG;QACH,GAAG,CAAC,KAAa,EAAE,IAAY;YAC7B,IAAI,SAAS;gBAAE,OAAO,IAAI,CAAC;YAC3B,OAAO,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;QACnC,CAAC;QAED;;;;;;;;WAQG;QACH,KAAK,CAAC,CAAS,EAAE,CAAS,EAAE,CAAS,EAAE,IAAY;YACjD,IAAI,SAAS;gBAAE,OAAO,IAAI,CAAC;YAC3B,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACvC,CAAC;QAED;;;;;;WAMG;QACH,KAAK,CAAC,KAAa,EAAE,IAAY;YAC/B,IAAI,SAAS;gBAAE,OAAO,IAAI,CAAC;YAC3B,OAAO,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;QACrC,CAAC;QAED;;;;;WAKG;QACH,IAAI,CAAC,IAAY;YACf,IAAI,SAAS;gBAAE,OAAO,IAAI,CAAC;YAC3B,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/worker/worker.d.ts
CHANGED
|
@@ -1,78 +1,6 @@
|
|
|
1
|
-
import { type BijouContext } from '@flyingrobots/bijou';
|
|
2
|
-
import type { App, RunOptions } from '@flyingrobots/bijou-tui';
|
|
3
|
-
import type { RunWorkerOptions, WorkerHandle } from './worker-options.js';
|
|
4
1
|
export type { RunWorkerOptions, WorkerHandle } from './worker-options.js';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
on(event: 'exit', handler: (value: number) => void): void;
|
|
10
|
-
terminate(): Promise<number>;
|
|
11
|
-
}
|
|
12
|
-
interface WorkerParentPort {
|
|
13
|
-
on(event: 'message', listener: (msg: unknown) => void): void;
|
|
14
|
-
off(event: 'message', listener: (msg: unknown) => void): void;
|
|
15
|
-
postMessage(message: unknown): void;
|
|
16
|
-
}
|
|
17
|
-
interface WorkerThreadBindings {
|
|
18
|
-
isMainThread: boolean;
|
|
19
|
-
parentPort: WorkerParentPort | null;
|
|
20
|
-
workerData: unknown;
|
|
21
|
-
createWorker(entry: string, options: Record<string, unknown>): WorkerInstance;
|
|
22
|
-
createNodeContext(): BijouContext;
|
|
23
|
-
runApp<Model, M>(app: App<Model, M>, options: RunOptions<M>): Promise<void>;
|
|
24
|
-
scheduleTimeout(callback: () => void, ms: number): ReturnType<typeof setTimeout>;
|
|
25
|
-
}
|
|
26
|
-
export type WorkerMessage = {
|
|
27
|
-
type: 'io:data';
|
|
28
|
-
data: string;
|
|
29
|
-
} | {
|
|
30
|
-
type: 'io:resize';
|
|
31
|
-
columns: number;
|
|
32
|
-
rows: number;
|
|
33
|
-
} | {
|
|
34
|
-
type: 'data';
|
|
35
|
-
payload: unknown;
|
|
36
|
-
} | {
|
|
37
|
-
type: 'quit';
|
|
38
|
-
};
|
|
39
|
-
export type MainMessage = {
|
|
40
|
-
type: 'render:frame';
|
|
41
|
-
output: string;
|
|
42
|
-
} | {
|
|
43
|
-
type: 'error';
|
|
44
|
-
message: string;
|
|
45
|
-
} | {
|
|
46
|
-
type: 'data';
|
|
47
|
-
payload: unknown;
|
|
48
|
-
} | {
|
|
49
|
-
type: 'quit';
|
|
50
|
-
};
|
|
51
|
-
/**
|
|
52
|
-
* Checks if the current environment is running inside a Bijou Worker.
|
|
53
|
-
*/
|
|
54
|
-
export declare function isBijouWorker(): boolean;
|
|
55
|
-
/**
|
|
56
|
-
* Sends a custom data message from the worker to the main thread.
|
|
57
|
-
* This will be received via the `onMessage` callback in `runInWorker`.
|
|
58
|
-
*/
|
|
59
|
-
export declare function sendToMain(payload: unknown): void;
|
|
60
|
-
/**
|
|
61
|
-
* Spawns a background worker thread to run the TEA application.
|
|
62
|
-
* The main thread delegates all logic to the worker, only handling raw I/O
|
|
63
|
-
* and frame rendering (to keep the TTY responsive).
|
|
64
|
-
*
|
|
65
|
-
* @param options - Configuration including the path to the worker entry file.
|
|
66
|
-
* @returns A handle to the running worker.
|
|
67
|
-
*/
|
|
68
|
-
export declare function runInWorker(options: RunWorkerOptions, bindings?: WorkerThreadBindings): WorkerHandle;
|
|
69
|
-
/**
|
|
70
|
-
* The entry point for the worker thread. Must be called in the file
|
|
71
|
-
* specified by `options.entry` in `runInWorker()`.
|
|
72
|
-
*
|
|
73
|
-
* Intercepts the normal `run()` execution, redirecting I/O to the parent port.
|
|
74
|
-
*
|
|
75
|
-
* @param app - The TEA app to run.
|
|
76
|
-
*/
|
|
77
|
-
export declare function startWorkerApp<Model, M>(app: App<Model, M>, bindings?: WorkerThreadBindings): Promise<void>;
|
|
2
|
+
export type { WorkerMessage, MainMessage } from './worker.part01.js';
|
|
3
|
+
export { isBijouWorker, sendToMain } from './worker.part01.js';
|
|
4
|
+
export { runInWorker } from './worker.part02.js';
|
|
5
|
+
export { startWorkerApp } from './worker.part03.js';
|
|
78
6
|
//# sourceMappingURL=worker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/worker/worker.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/worker/worker.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAC1E,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/worker/worker.js
CHANGED
|
@@ -1,241 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import { run } from '@flyingrobots/bijou-tui';
|
|
5
|
-
import { createNodeContext } from '../index.js';
|
|
6
|
-
import { hasBijouWorkerFlag, isObjectRecord, readBijouWorkerData, } from './worker-data.js';
|
|
7
|
-
import { DISABLE_MOUSE, mouseModeEnableSequence, resolveWorkerMouseMode, } from './worker-mouse-mode.js';
|
|
8
|
-
function defaultWorkerThreadBindings() {
|
|
9
|
-
return {
|
|
10
|
-
isMainThread,
|
|
11
|
-
parentPort,
|
|
12
|
-
workerData,
|
|
13
|
-
createWorker(entry, options) {
|
|
14
|
-
return new Worker(entry, options);
|
|
15
|
-
},
|
|
16
|
-
createNodeContext,
|
|
17
|
-
runApp: run,
|
|
18
|
-
scheduleTimeout(callback, ms) {
|
|
19
|
-
return setTimeout(callback, ms);
|
|
20
|
-
},
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Checks if the current environment is running inside a Bijou Worker.
|
|
25
|
-
*/
|
|
26
|
-
export function isBijouWorker() {
|
|
27
|
-
const bindings = defaultWorkerThreadBindings();
|
|
28
|
-
return !bindings.isMainThread && hasBijouWorkerFlag(bindings.workerData);
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Sends a custom data message from the worker to the main thread.
|
|
32
|
-
* This will be received via the `onMessage` callback in `runInWorker`.
|
|
33
|
-
*/
|
|
34
|
-
export function sendToMain(payload) {
|
|
35
|
-
const bindings = defaultWorkerThreadBindings();
|
|
36
|
-
if (bindings.parentPort) {
|
|
37
|
-
bindings.parentPort.postMessage({ type: 'data', payload });
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Spawns a background worker thread to run the TEA application.
|
|
42
|
-
* The main thread delegates all logic to the worker, only handling raw I/O
|
|
43
|
-
* and frame rendering (to keep the TTY responsive).
|
|
44
|
-
*
|
|
45
|
-
* @param options - Configuration including the path to the worker entry file.
|
|
46
|
-
* @returns A handle to the running worker.
|
|
47
|
-
*/
|
|
48
|
-
export function runInWorker(options, bindings = defaultWorkerThreadBindings()) {
|
|
49
|
-
if (!bindings.isMainThread) {
|
|
50
|
-
throw new Error('runInWorker must be called from the main thread.');
|
|
51
|
-
}
|
|
52
|
-
const ctx = options.ctx ?? bindings.createNodeContext();
|
|
53
|
-
installRuntimeViewportOverlay(ctx);
|
|
54
|
-
const useAltScreen = options.altScreen ?? true;
|
|
55
|
-
const useHideCursor = options.hideCursor ?? true;
|
|
56
|
-
const mouseMode = resolveWorkerMouseMode(options);
|
|
57
|
-
const useMouse = mouseMode !== undefined;
|
|
58
|
-
if (useAltScreen || useHideCursor) {
|
|
59
|
-
ctx.io.write('\x1b[?1049h'); // ENTER_ALT_SCREEN
|
|
60
|
-
if (useHideCursor)
|
|
61
|
-
ctx.io.write('\x1b[?25l'); // HIDE_CURSOR
|
|
62
|
-
}
|
|
63
|
-
if (useMouse) {
|
|
64
|
-
ctx.io.write(mouseModeEnableSequence(mouseMode)); // ENABLE_MOUSE
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
ctx.io.write(DISABLE_MOUSE);
|
|
68
|
-
}
|
|
69
|
-
const serializableOptions = {
|
|
70
|
-
altScreen: options.altScreen,
|
|
71
|
-
hideCursor: options.hideCursor,
|
|
72
|
-
mouse: options.mouse,
|
|
73
|
-
mouseMode: options.mouseMode,
|
|
74
|
-
css: options.css,
|
|
75
|
-
};
|
|
76
|
-
const worker = bindings.createWorker(resolvePath(options.entry), {
|
|
77
|
-
workerData: {
|
|
78
|
-
isBijouWorker: true,
|
|
79
|
-
options: serializableOptions,
|
|
80
|
-
runtime: readRuntimeViewport(ctx.runtime),
|
|
81
|
-
},
|
|
82
|
-
execArgv: options.execArgv,
|
|
83
|
-
// Pipe stdout/stderr so we can capture logs if needed, but primarily use IPC
|
|
84
|
-
});
|
|
85
|
-
const inputHandle = ctx.io.rawInput((data) => {
|
|
86
|
-
worker.postMessage({ type: 'io:data', data });
|
|
87
|
-
});
|
|
88
|
-
const resizeHandle = ctx.io.onResize((columns, rows) => {
|
|
89
|
-
const nextViewport = updateRuntimeViewport(ctx.runtime, columns, rows);
|
|
90
|
-
worker.postMessage({ type: 'io:resize', ...nextViewport });
|
|
91
|
-
});
|
|
92
|
-
const onExit = new Promise((resolve, reject) => {
|
|
93
|
-
let requestedQuit = false;
|
|
94
|
-
let forcedTerminate = false;
|
|
95
|
-
worker.on('message', (msg) => {
|
|
96
|
-
if (msg.type === 'render:frame') {
|
|
97
|
-
ctx.io.write(msg.output);
|
|
98
|
-
}
|
|
99
|
-
else if (msg.type === 'error') {
|
|
100
|
-
ctx.io.writeError(msg.message);
|
|
101
|
-
}
|
|
102
|
-
else if (msg.type === 'data') {
|
|
103
|
-
options.onMessage?.(msg.payload);
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
requestedQuit = true;
|
|
107
|
-
bindings.scheduleTimeout(() => {
|
|
108
|
-
if (requestedQuit) {
|
|
109
|
-
forcedTerminate = true;
|
|
110
|
-
void worker.terminate();
|
|
111
|
-
}
|
|
112
|
-
}, 50);
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
worker.on('error', (err) => {
|
|
116
|
-
cleanup();
|
|
117
|
-
reject(err);
|
|
118
|
-
});
|
|
119
|
-
worker.on('exit', (code) => {
|
|
120
|
-
cleanup();
|
|
121
|
-
if (requestedQuit && (code === 0 || forcedTerminate)) {
|
|
122
|
-
resolve();
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
if (code !== 0)
|
|
126
|
-
reject(new Error(`Worker stopped with exit code ${String(code)}`));
|
|
127
|
-
else
|
|
128
|
-
resolve();
|
|
129
|
-
});
|
|
130
|
-
function cleanup() {
|
|
131
|
-
inputHandle.dispose();
|
|
132
|
-
resizeHandle.dispose();
|
|
133
|
-
if (useMouse) {
|
|
134
|
-
ctx.io.write(DISABLE_MOUSE);
|
|
135
|
-
}
|
|
136
|
-
if (useAltScreen || useHideCursor) {
|
|
137
|
-
ctx.io.write('\x1b[?1049l'); // EXIT_ALT_SCREEN
|
|
138
|
-
if (useHideCursor)
|
|
139
|
-
ctx.io.write('\x1b[?25h'); // SHOW_CURSOR
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
return {
|
|
144
|
-
send: (payload) => { worker.postMessage({ type: 'data', payload }); },
|
|
145
|
-
terminate: async () => { await worker.terminate(); },
|
|
146
|
-
onExit
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* The entry point for the worker thread. Must be called in the file
|
|
151
|
-
* specified by `options.entry` in `runInWorker()`.
|
|
152
|
-
*
|
|
153
|
-
* Intercepts the normal `run()` execution, redirecting I/O to the parent port.
|
|
154
|
-
*
|
|
155
|
-
* @param app - The TEA app to run.
|
|
156
|
-
*/
|
|
157
|
-
export async function startWorkerApp(app, bindings = defaultWorkerThreadBindings()) {
|
|
158
|
-
const port = bindings.parentPort;
|
|
159
|
-
if (bindings.isMainThread || port === null) {
|
|
160
|
-
throw new Error('startWorkerApp must be called from within a worker thread.');
|
|
161
|
-
}
|
|
162
|
-
const initData = readBijouWorkerData(bindings.workerData);
|
|
163
|
-
const proxyCtx = bindings.createNodeContext();
|
|
164
|
-
installRuntimeViewportOverlay(proxyCtx);
|
|
165
|
-
const { setDefaultContext } = await import('@flyingrobots/bijou');
|
|
166
|
-
setDefaultContext(proxyCtx);
|
|
167
|
-
updateRuntimeViewport(proxyCtx.runtime, initData.runtime.columns, initData.runtime.rows);
|
|
168
|
-
const proxyMouseMode = resolveWorkerMouseMode(initData.options);
|
|
169
|
-
const ignoredProxyWrites = new Set([DISABLE_MOUSE]);
|
|
170
|
-
if (proxyMouseMode !== undefined) {
|
|
171
|
-
ignoredProxyWrites.add(mouseModeEnableSequence(proxyMouseMode));
|
|
172
|
-
}
|
|
173
|
-
proxyCtx.io.write = (data) => {
|
|
174
|
-
if (ignoredProxyWrites.has(data))
|
|
175
|
-
return;
|
|
176
|
-
port.postMessage({ type: 'render:frame', output: data });
|
|
177
|
-
};
|
|
178
|
-
proxyCtx.io.writeError = (data) => {
|
|
179
|
-
port.postMessage({ type: 'error', message: data });
|
|
180
|
-
};
|
|
181
|
-
// The main thread owns terminal mode; the worker still parses forwarded input.
|
|
182
|
-
const proxyOptions = {
|
|
183
|
-
css: initData.options.css,
|
|
184
|
-
ctx: proxyCtx,
|
|
185
|
-
altScreen: false,
|
|
186
|
-
hideCursor: false,
|
|
187
|
-
mouse: initData.options.mouse,
|
|
188
|
-
mouseMode: initData.options.mouseMode,
|
|
189
|
-
};
|
|
190
|
-
proxyCtx.io.rawInput = (handler) => {
|
|
191
|
-
const listener = (msg) => {
|
|
192
|
-
if (!isWorkerMessage(msg))
|
|
193
|
-
return;
|
|
194
|
-
if (msg.type === 'io:data')
|
|
195
|
-
handler(msg.data);
|
|
196
|
-
};
|
|
197
|
-
port.on('message', listener);
|
|
198
|
-
return {
|
|
199
|
-
dispose: () => { port.off('message', listener); }
|
|
200
|
-
};
|
|
201
|
-
};
|
|
202
|
-
proxyCtx.io.onResize = (handler) => {
|
|
203
|
-
const listener = (msg) => {
|
|
204
|
-
if (!isWorkerMessage(msg))
|
|
205
|
-
return;
|
|
206
|
-
if (msg.type === 'io:resize') {
|
|
207
|
-
const nextViewport = updateRuntimeViewport(proxyCtx.runtime, msg.columns, msg.rows);
|
|
208
|
-
handler(nextViewport.columns, nextViewport.rows);
|
|
209
|
-
}
|
|
210
|
-
};
|
|
211
|
-
port.on('message', listener);
|
|
212
|
-
return {
|
|
213
|
-
dispose: () => { port.off('message', listener); }
|
|
214
|
-
};
|
|
215
|
-
};
|
|
216
|
-
proxyCtx.io.onData = (handler) => {
|
|
217
|
-
const listener = (msg) => {
|
|
218
|
-
if (!isWorkerMessage(msg))
|
|
219
|
-
return;
|
|
220
|
-
if (msg.type === 'data')
|
|
221
|
-
handler(msg.payload);
|
|
222
|
-
};
|
|
223
|
-
port.on('message', listener);
|
|
224
|
-
return {
|
|
225
|
-
dispose: () => { port.off('message', listener); }
|
|
226
|
-
};
|
|
227
|
-
};
|
|
228
|
-
await bindings.runApp(app, proxyOptions);
|
|
229
|
-
port.postMessage({ type: 'quit' });
|
|
230
|
-
}
|
|
231
|
-
function isWorkerMessage(msg) {
|
|
232
|
-
if (!isObjectRecord(msg))
|
|
233
|
-
return false;
|
|
234
|
-
if (msg['type'] === 'io:data')
|
|
235
|
-
return typeof msg['data'] === 'string';
|
|
236
|
-
if (msg['type'] === 'io:resize') {
|
|
237
|
-
return typeof msg['columns'] === 'number' && typeof msg['rows'] === 'number';
|
|
238
|
-
}
|
|
239
|
-
return msg['type'] === 'data' || msg['type'] === 'quit';
|
|
240
|
-
}
|
|
1
|
+
export { isBijouWorker, sendToMain } from './worker.part01.js';
|
|
2
|
+
export { runInWorker } from './worker.part02.js';
|
|
3
|
+
export { startWorkerApp } from './worker.part03.js';
|
|
241
4
|
//# sourceMappingURL=worker.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/worker/worker.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/worker/worker.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type BijouContext } from '@flyingrobots/bijou';
|
|
2
|
+
import type { App, RunOptions } from '@flyingrobots/bijou-tui';
|
|
3
|
+
export interface WorkerInstance {
|
|
4
|
+
postMessage(message: unknown): void;
|
|
5
|
+
on(event: 'message', handler: (value: MainMessage) => void): void;
|
|
6
|
+
on(event: 'error', handler: (value: Error) => void): void;
|
|
7
|
+
on(event: 'exit', handler: (value: number) => void): void;
|
|
8
|
+
terminate(): Promise<number>;
|
|
9
|
+
}
|
|
10
|
+
export interface WorkerParentPort {
|
|
11
|
+
on(event: 'message', listener: (msg: unknown) => void): void;
|
|
12
|
+
off(event: 'message', listener: (msg: unknown) => void): void;
|
|
13
|
+
postMessage(message: unknown): void;
|
|
14
|
+
}
|
|
15
|
+
export interface WorkerThreadBindings {
|
|
16
|
+
isMainThread: boolean;
|
|
17
|
+
parentPort: WorkerParentPort | null;
|
|
18
|
+
workerData: unknown;
|
|
19
|
+
createWorker(entry: string, options: Record<string, unknown>): WorkerInstance;
|
|
20
|
+
createNodeContext(): BijouContext;
|
|
21
|
+
runApp<Model, M>(app: App<Model, M>, options: RunOptions<M>): Promise<void>;
|
|
22
|
+
scheduleTimeout(callback: () => void, ms: number): ReturnType<typeof setTimeout>;
|
|
23
|
+
}
|
|
24
|
+
export declare function defaultWorkerThreadBindings(): WorkerThreadBindings;
|
|
25
|
+
export type WorkerMessage = {
|
|
26
|
+
type: 'io:data';
|
|
27
|
+
data: string;
|
|
28
|
+
} | {
|
|
29
|
+
type: 'io:resize';
|
|
30
|
+
columns: number;
|
|
31
|
+
rows: number;
|
|
32
|
+
} | {
|
|
33
|
+
type: 'data';
|
|
34
|
+
payload: unknown;
|
|
35
|
+
} | {
|
|
36
|
+
type: 'quit';
|
|
37
|
+
};
|
|
38
|
+
export type MainMessage = {
|
|
39
|
+
type: 'render:frame';
|
|
40
|
+
output: string;
|
|
41
|
+
} | {
|
|
42
|
+
type: 'error';
|
|
43
|
+
message: string;
|
|
44
|
+
} | {
|
|
45
|
+
type: 'data';
|
|
46
|
+
payload: unknown;
|
|
47
|
+
} | {
|
|
48
|
+
type: 'quit';
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Checks if the current environment is running inside a Bijou Worker.
|
|
52
|
+
*/
|
|
53
|
+
export declare function isBijouWorker(): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Sends a custom data message from the worker to the main thread.
|
|
56
|
+
* This will be received via the `onMessage` callback in `runInWorker`.
|
|
57
|
+
*/
|
|
58
|
+
export declare function sendToMain(payload: unknown): void;
|
|
59
|
+
//# sourceMappingURL=worker.part01.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.part01.d.ts","sourceRoot":"","sources":["../../src/worker/worker.part01.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAK/D,MAAM,WAAW,cAAc;IAC7B,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACpC,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,IAAI,CAAC;IAClE,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1D,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1D,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAC9B;AACD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI,CAAC;IAC7D,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,GAAG,IAAI,CAAC;IAC9D,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CACrC;AACD,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACpC,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,cAAc,CAAC;IAC9E,iBAAiB,IAAI,YAAY,CAAC;IAClC,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5E,eAAe,CACb,QAAQ,EAAE,MAAM,IAAI,EACpB,EAAE,EAAE,MAAM,GACT,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;CAClC;AACD,wBAAgB,2BAA2B,IAAI,oBAAoB,CAclE;AAKD,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACpD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AACrB,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AACrB;;GAEG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAGvC;AACD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAQjD"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Worker, isMainThread, parentPort, workerData, } from 'node:worker_threads';
|
|
2
|
+
import { run } from '@flyingrobots/bijou-tui';
|
|
3
|
+
import { createNodeContext } from '../node-context.js';
|
|
4
|
+
import { hasBijouWorkerFlag } from './worker-data.js';
|
|
5
|
+
export function defaultWorkerThreadBindings() {
|
|
6
|
+
return {
|
|
7
|
+
isMainThread,
|
|
8
|
+
parentPort,
|
|
9
|
+
workerData,
|
|
10
|
+
createWorker(entry, options) {
|
|
11
|
+
return new Worker(entry, options);
|
|
12
|
+
},
|
|
13
|
+
createNodeContext,
|
|
14
|
+
runApp: run,
|
|
15
|
+
scheduleTimeout(callback, ms) {
|
|
16
|
+
return setTimeout(callback, ms);
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Checks if the current environment is running inside a Bijou Worker.
|
|
22
|
+
*/
|
|
23
|
+
export function isBijouWorker() {
|
|
24
|
+
const bindings = defaultWorkerThreadBindings();
|
|
25
|
+
return !bindings.isMainThread && hasBijouWorkerFlag(bindings.workerData);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Sends a custom data message from the worker to the main thread.
|
|
29
|
+
* This will be received via the `onMessage` callback in `runInWorker`.
|
|
30
|
+
*/
|
|
31
|
+
export function sendToMain(payload) {
|
|
32
|
+
const bindings = defaultWorkerThreadBindings();
|
|
33
|
+
if (bindings.parentPort) {
|
|
34
|
+
bindings.parentPort.postMessage({
|
|
35
|
+
type: 'data',
|
|
36
|
+
payload,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=worker.part01.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.part01.js","sourceRoot":"","sources":["../../src/worker/worker.part01.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,YAAY,EACZ,UAAU,EACV,UAAU,GACX,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AA0BtD,MAAM,UAAU,2BAA2B;IACzC,OAAO;QACL,YAAY;QACZ,UAAU;QACV,UAAU;QACV,YAAY,CAAC,KAAK,EAAE,OAAO;YACzB,OAAO,IAAI,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACpC,CAAC;QACD,iBAAiB;QACjB,MAAM,EAAE,GAAG;QACX,eAAe,CAAC,QAAQ,EAAE,EAAE;YAC1B,OAAO,UAAU,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAClC,CAAC;KACF,CAAC;AACJ,CAAC;AAeD;;GAEG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,QAAQ,GAAG,2BAA2B,EAAE,CAAC;IAC/C,OAAO,CAAC,QAAQ,CAAC,YAAY,IAAI,kBAAkB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AAC3E,CAAC;AACD;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,OAAgB;IACzC,MAAM,QAAQ,GAAG,2BAA2B,EAAE,CAAC;IAC/C,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;QACxB,QAAQ,CAAC,UAAU,CAAC,WAAW,CAAC;YAC9B,IAAI,EAAE,MAAM;YACZ,OAAO;SACc,CAAC,CAAC;IAC3B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RunWorkerOptions, WorkerHandle } from './worker-options.js';
|
|
2
|
+
import { type WorkerThreadBindings } from './worker.part01.js';
|
|
3
|
+
/**
|
|
4
|
+
* Spawns a background worker thread to run the TEA application.
|
|
5
|
+
* The main thread delegates all logic to the worker, only handling raw I/O
|
|
6
|
+
* and frame rendering (to keep the TTY responsive).
|
|
7
|
+
*
|
|
8
|
+
* @param options - Configuration including the path to the worker entry file.
|
|
9
|
+
* @returns A handle to the running worker.
|
|
10
|
+
*/
|
|
11
|
+
export declare function runInWorker(options: RunWorkerOptions, bindings?: WorkerThreadBindings): WorkerHandle;
|
|
12
|
+
//# sourceMappingURL=worker.part02.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.part02.d.ts","sourceRoot":"","sources":["../../src/worker/worker.part02.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAGL,KAAK,oBAAoB,EAE1B,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,gBAAgB,EACzB,QAAQ,GAAE,oBAAoD,GAC7D,YAAY,CAuGd"}
|