@textmode/runner-client 0.1.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 ADDED
@@ -0,0 +1,133 @@
1
+ # @textmode/runner-client
2
+
3
+ Browser iframe runtime client for the hosted textmode runner.
4
+
5
+ This package gives any browser host app a typed runtime API for mounting the
6
+ runner iframe, performing the current generic protocol handshake, routing
7
+ request/response messages, monitoring heartbeat status, loading fonts,
8
+ controlling playback, running code, exporting output, reconnecting, and
9
+ disposing the transport.
10
+
11
+ It does not execute textmode.js sketches directly. It controls a runner app at
12
+ the `runnerUrl` you provide.
13
+
14
+ ## Install
15
+
16
+ ```sh
17
+ npm install @textmode/runner-client
18
+ ```
19
+
20
+ `@textmode/runner-protocol` is installed as a dependency and is also available
21
+ for consumers that need the protocol types directly.
22
+
23
+ ## Usage
24
+
25
+ ```ts
26
+ import {
27
+ IframeTextmodeRuntime,
28
+ RunnerRequestError,
29
+ type RunnerRuntimeStatus,
30
+ } from '@textmode/runner-client';
31
+
32
+ const container = document.querySelector<HTMLElement>('#runner');
33
+
34
+ if (!container) {
35
+ throw new Error('missing runner container');
36
+ }
37
+
38
+ const runtime = new IframeTextmodeRuntime({
39
+ runnerUrl: 'https://runner.textmode.art/',
40
+ onStatusChange(status: RunnerRuntimeStatus, reason) {
41
+ console.info('runner status changed', status, reason);
42
+ },
43
+ onExportProgress(requestId, format, progress) {
44
+ console.info('export progress', requestId, format, progress);
45
+ },
46
+ });
47
+
48
+ await runtime.init(container, {
49
+ width: 640,
50
+ height: 640,
51
+ fontSize: 16,
52
+ frameRate: 60,
53
+ });
54
+
55
+ try {
56
+ await runtime.runCode(`
57
+ t.draw(() => {
58
+ t.print('textmode', 0, 0);
59
+ });
60
+ `);
61
+ } catch (error) {
62
+ if (error instanceof RunnerRequestError) {
63
+ console.error(error.message, error.line, error.column);
64
+ }
65
+ }
66
+
67
+ const image = await runtime.export('image', {
68
+ format: 'png',
69
+ scale: 2,
70
+ });
71
+
72
+ console.info(image.filename, image.mimeType, image.blob);
73
+ runtime.dispose();
74
+ ```
75
+
76
+ ## Public API
77
+
78
+ Import from the package root only:
79
+
80
+ ```ts
81
+ import { IframeTextmodeRuntime } from '@textmode/runner-client';
82
+ ```
83
+
84
+ Public subpath imports are intentionally not supported. Internal modules such
85
+ as request routing, heartbeat control, iframe mounting, and sandbox policy may
86
+ change without a semver-major release.
87
+
88
+ The main exports are:
89
+
90
+ - `IframeTextmodeRuntime`
91
+ - `RunnerRuntimeStatus`
92
+ - `RunnerExecutionError`
93
+ - `RunnerRequestError`
94
+ - `FontLoadResult`
95
+ - `IframeTextmodeRuntimeOptions`
96
+ - `IframeMountMode`
97
+ - `IframeSandboxToken`
98
+ - `DEFAULT_IFRAME_SANDBOX_TOKENS`
99
+
100
+ ## Runtime Lifecycle
101
+
102
+ Typical host apps follow this lifecycle:
103
+
104
+ 1. Create an `IframeTextmodeRuntime` with a trusted `runnerUrl`.
105
+ 2. Call `init(container, settings)` from a browser context.
106
+ 3. Use `runCode`, `configure`, `setSettings`, `export`, `loadFont`, and
107
+ `playback` as needed.
108
+ 4. Call `reconnect` after a recoverable runner failure.
109
+ 5. Call `dispose` when the host view is unmounted.
110
+
111
+ The runtime exposes `status`, `isReady`, and `frame` getters for host UI state.
112
+
113
+ ## Sandbox Policy
114
+
115
+ The default iframe sandbox tokens are:
116
+
117
+ ```ts
118
+ ['allow-scripts', 'allow-same-origin']
119
+ ```
120
+
121
+ `allow-downloads` is not included by default. Export downloads should be
122
+ initiated by the host app after it receives an export result from the runner.
123
+
124
+ The runtime refuses to start a runner that combines `allow-scripts` and
125
+ `allow-same-origin` on the same origin as the parent page.
126
+
127
+ ## API Docs
128
+
129
+ Generated TypeDoc Markdown lives in [`api/runner-client`](./api/runner-client/index.md).
130
+
131
+ ## License
132
+
133
+ AGPL-3.0-or-later. See [LICENSE](./LICENSE).
@@ -0,0 +1,177 @@
1
+ import { type ExportMessage, type ExportResultMessage, type GifExportOptions, type ImageExportOptions, type PlaybackAction, type PlaybackState, type RunnerCapabilities, type RuntimeSettings, type SvgExportOptions, type TxtExportOptions, type WebmExportOptions } from '@textmode/runner-protocol';
2
+ import type { FontLoadResult } from './font';
3
+ import { type IframeTextmodeRuntimeOptions } from './options';
4
+ import type { RunnerRuntimeStatus } from './status';
5
+ /**
6
+ * Browser iframe runtime for communicating with the hosted textmode runner.
7
+ *
8
+ * @category Runtime
9
+ */
10
+ export declare class IframeTextmodeRuntime {
11
+ private readonly runnerHref;
12
+ private readonly runnerOrigin;
13
+ private readonly sandboxTokens;
14
+ private readonly handshakeTimeoutMs;
15
+ private readonly requestTimeoutMs;
16
+ private readonly options;
17
+ private readonly mountMode;
18
+ private readonly pending;
19
+ private readonly heartbeat;
20
+ private iframe;
21
+ private channel;
22
+ private port;
23
+ private container;
24
+ private settings;
25
+ private capabilities;
26
+ private ready;
27
+ private currentStatus;
28
+ private readyResolver;
29
+ private readyRejecter;
30
+ private readyTimeoutId;
31
+ private lastRequestedCode;
32
+ constructor(options: IframeTextmodeRuntimeOptions);
33
+ /**
34
+ * Whether the runner iframe is ready to accept requests.
35
+ *
36
+ * @category Runtime
37
+ */
38
+ get isReady(): boolean;
39
+ /**
40
+ * Current runner iframe element, when mounted.
41
+ *
42
+ * @category Runtime
43
+ */
44
+ get frame(): HTMLIFrameElement | null;
45
+ /**
46
+ * Current runner lifecycle status.
47
+ *
48
+ * @category Runtime
49
+ */
50
+ get status(): RunnerRuntimeStatus;
51
+ /**
52
+ * Alias for {@link IframeTextmodeRuntime.status}.
53
+ *
54
+ * @category Runtime
55
+ */
56
+ get runnerStatus(): RunnerRuntimeStatus;
57
+ /**
58
+ * Capabilities advertised by the connected runner.
59
+ *
60
+ * @category Runtime
61
+ */
62
+ get advertisedCapabilities(): RunnerCapabilities | null;
63
+ /**
64
+ * Mounts the runner iframe and performs the current protocol handshake.
65
+ *
66
+ * @param container - DOM element that should contain the runner iframe.
67
+ * @param settings - Optional fixed runtime settings to configure after ready.
68
+ * @returns `true` when the runner is ready.
69
+ * @category Runtime
70
+ */
71
+ init(container: HTMLElement, settings?: RuntimeSettings): Promise<boolean>;
72
+ /**
73
+ * Disposes the iframe connection and rejects pending requests.
74
+ *
75
+ * @category Runtime
76
+ */
77
+ dispose(): void;
78
+ /**
79
+ * Recreates the iframe and reruns the last requested code when available.
80
+ *
81
+ * @returns `true` when reconnection succeeds.
82
+ * @category Runtime
83
+ */
84
+ reconnect(): Promise<boolean>;
85
+ /**
86
+ * Focuses the iframe from a host user gesture.
87
+ *
88
+ * Some browsers use this to unlock normal iframe animation cadence.
89
+ *
90
+ * @category Runtime
91
+ */
92
+ activateFromUserGesture(): void;
93
+ /**
94
+ * Configures complete fixed runtime settings.
95
+ *
96
+ * @category Runtime
97
+ */
98
+ configure(settings: RuntimeSettings): Promise<PlaybackState | null>;
99
+ /**
100
+ * Applies a partial runtime settings update.
101
+ *
102
+ * @category Runtime
103
+ */
104
+ setSettings(settings: Partial<RuntimeSettings>): Promise<PlaybackState | null>;
105
+ /**
106
+ * Executes code in the runner.
107
+ *
108
+ * @category Runtime
109
+ */
110
+ runCode(code: string, options?: {
111
+ softReset?: boolean;
112
+ }): Promise<boolean>;
113
+ /**
114
+ * Exports the current runner output in any supported format.
115
+ *
116
+ * @category Exports
117
+ */
118
+ export(format: ExportMessage['format'], options?: ImageExportOptions | SvgExportOptions | TxtExportOptions | GifExportOptions | WebmExportOptions, timeoutMs?: number): Promise<ExportResultMessage>;
119
+ /**
120
+ * Exports the current runner output as a raster image.
121
+ *
122
+ * @category Exports
123
+ */
124
+ exportImage(options: ImageExportOptions): Promise<ExportResultMessage>;
125
+ /**
126
+ * Exports the current runner output as SVG.
127
+ *
128
+ * @category Exports
129
+ */
130
+ exportSvg(options: SvgExportOptions): Promise<ExportResultMessage>;
131
+ /**
132
+ * Exports the current runner output as plain text.
133
+ *
134
+ * @category Exports
135
+ */
136
+ exportTxt(options: TxtExportOptions): Promise<ExportResultMessage>;
137
+ /**
138
+ * Records an animated GIF export.
139
+ *
140
+ * @category Exports
141
+ */
142
+ exportGif(options: GifExportOptions): Promise<ExportResultMessage>;
143
+ /**
144
+ * Records a WebM export.
145
+ *
146
+ * @category Exports
147
+ */
148
+ exportWebm(options: WebmExportOptions): Promise<ExportResultMessage>;
149
+ /**
150
+ * Loads a font file into the runner.
151
+ *
152
+ * @category Fonts
153
+ */
154
+ loadFont(file: File): Promise<FontLoadResult>;
155
+ /**
156
+ * Sends a playback command and resolves with the resulting playback state.
157
+ *
158
+ * @category Playback
159
+ */
160
+ playback(action: PlaybackAction, options?: {
161
+ frame?: number;
162
+ maxFrames?: number;
163
+ }): Promise<PlaybackState>;
164
+ private connectPort;
165
+ private handlePortMessage;
166
+ private handleReady;
167
+ private markReady;
168
+ private handleRunError;
169
+ private request;
170
+ private postMessage;
171
+ private handleUnavailable;
172
+ private validateReadyMessage;
173
+ private assertSandboxOriginPolicy;
174
+ private setStatus;
175
+ private disposeFrame;
176
+ private createRequestId;
177
+ }