@scenoco-three/editor 0.5.4 → 0.7.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.
Files changed (47) hide show
  1. package/README.md +31 -106
  2. package/bin/serve.js +37 -0
  3. package/dist/editor.js +12521 -0
  4. package/package.json +29 -41
  5. package/app/cameraRig.ts +0 -118
  6. package/app/commands.ts +0 -78
  7. package/app/desktopControls.ts +0 -47
  8. package/app/document.ts +0 -547
  9. package/app/editLog.ts +0 -127
  10. package/app/editor.css +0 -51
  11. package/app/gizmo.ts +0 -169
  12. package/app/globals.d.ts +0 -9
  13. package/app/index.html +0 -14
  14. package/app/main.ts +0 -546
  15. package/app/meta.ts +0 -24
  16. package/app/picking.ts +0 -47
  17. package/app/session.ts +0 -612
  18. package/app/touchControls.ts +0 -127
  19. package/app/ui-react/AddMemberPopup.tsx +0 -114
  20. package/app/ui-react/BottomLauncher.tsx +0 -15
  21. package/app/ui-react/EditorUI.tsx +0 -22
  22. package/app/ui-react/FilePicker.tsx +0 -80
  23. package/app/ui-react/HeadBtn.tsx +0 -21
  24. package/app/ui-react/InspectorSheet.tsx +0 -475
  25. package/app/ui-react/NodeListSheet.tsx +0 -56
  26. package/app/ui-react/TopBar.tsx +0 -137
  27. package/app/ui-react/Tree.tsx +0 -106
  28. package/app/ui-react/cx.ts +0 -4
  29. package/app/ui-react/fields.tsx +0 -407
  30. package/app/ui-react/format.ts +0 -30
  31. package/app/ui-react/mount.tsx +0 -31
  32. package/app/ui-react/store.ts +0 -80
  33. package/app/ui-react/types.ts +0 -88
  34. package/app/ui-react/ui.module.css +0 -250
  35. package/app/ui-react/useSize.ts +0 -20
  36. package/app/values.ts +0 -137
  37. package/app/vite-env.d.ts +0 -7
  38. package/dist/cli/index.d.ts +0 -2
  39. package/dist/cli/index.js +0 -122
  40. package/dist/index.d.ts +0 -10
  41. package/dist/index.js +0 -10
  42. package/dist/mcp.d.ts +0 -32
  43. package/dist/mcp.js +0 -191
  44. package/dist/protocol.d.ts +0 -170
  45. package/dist/protocol.js +0 -22
  46. package/dist/server.d.ts +0 -124
  47. package/dist/server.js +0 -440
package/dist/mcp.d.ts DELETED
@@ -1,32 +0,0 @@
1
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import type { EditorBridge } from './server.js';
3
- /**
4
- * The MCP surface: **eyes, not hands.**
5
- *
6
- * Open a document, render it, drive it, read what happened. There is no tool here that writes to
7
- * the user's source, and that is a decision rather than an omission. An agent already has file
8
- * tools, and the editor's watcher rebuilds whatever it writes — so an edit tool would have been
9
- * a second write path into someone's project that never passed through their editor, their diff
10
- * or their undo. An agent holding one can reshape a scene while believing it is only looking at
11
- * it.
12
- *
13
- * v1 shipped `patch`, `structural` and `save` tools alongside these. They are deleted.
14
- *
15
- * The one thing worth insisting on: `simulate` exists because a screenshot of frame zero says
16
- * almost nothing about whether a game works. Drive it to the interesting moment, *then* look.
17
- */
18
- export declare function createMcpServer(bridge: EditorBridge, editorUrl: () => string): McpServer;
19
- /**
20
- * Serve the MCP over HTTP on a fixed port.
21
- *
22
- * Fixed on purpose: an MCP client is configured once with a URL, and a port that moved on every
23
- * restart would need reconfiguring every time.
24
- *
25
- * **One transport per session**, created when a client initializes and kept until it goes away.
26
- * A single shared transport looked simpler and allowed exactly one client ever: the first
27
- * `initialize` claimed it, and every later one — a reconnect, a second client, a restarted
28
- * agent — got a bare `400`, which reads like a crashed server rather than a design limit.
29
- */
30
- export declare function startMcpHttp(makeServer: () => McpServer, port: number): Promise<{
31
- close(): Promise<void>;
32
- }>;
package/dist/mcp.js DELETED
@@ -1,191 +0,0 @@
1
- import { randomUUID } from 'node:crypto';
2
- import { createServer as createHttpServer } from 'node:http';
3
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
- import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
5
- import { z } from 'zod';
6
- import { commandId } from './protocol.js';
7
- /**
8
- * The MCP surface: **eyes, not hands.**
9
- *
10
- * Open a document, render it, drive it, read what happened. There is no tool here that writes to
11
- * the user's source, and that is a decision rather than an omission. An agent already has file
12
- * tools, and the editor's watcher rebuilds whatever it writes — so an edit tool would have been
13
- * a second write path into someone's project that never passed through their editor, their diff
14
- * or their undo. An agent holding one can reshape a scene while believing it is only looking at
15
- * it.
16
- *
17
- * v1 shipped `patch`, `structural` and `save` tools alongside these. They are deleted.
18
- *
19
- * The one thing worth insisting on: `simulate` exists because a screenshot of frame zero says
20
- * almost nothing about whether a game works. Drive it to the interesting moment, *then* look.
21
- */
22
- export function createMcpServer(bridge, editorUrl) {
23
- const mcp = new McpServer({ name: 'scenoco-editor', version: '2.0.0' });
24
- /** Every tool needs a live page; saying so plainly beats a timeout. */
25
- const send = async (command) => {
26
- if (!bridge.connected()) {
27
- throw new Error(`no editor page is open — visit ${editorUrl()} first, then retry`);
28
- }
29
- const response = await bridge.send(command);
30
- if (!response.ok)
31
- throw new Error(response.error);
32
- return response.result;
33
- };
34
- const frameSchema = {
35
- width: z.number().int().positive().describe('Captured image width in pixels'),
36
- height: z.number().int().positive().describe('Captured image height in pixels'),
37
- ui: z
38
- .boolean()
39
- .describe('Include the screen-space UI layer, framing the shot through the camera that hosts it'),
40
- };
41
- const POINT = '"y: 6; z: 12" — the document\'s own vector grammar; an omitted axis is 0';
42
- mcp.registerTool('open', {
43
- description: 'Open a scene or prefab document in the editor page.',
44
- inputSchema: { path: z.string().describe('Project-relative path, e.g. assets/scenes/main.scene.xml') },
45
- }, async ({ path }) => text(await send({ id: commandId(), type: 'open', path })));
46
- mcp.registerTool('screenshot', {
47
- description: 'Render one PNG of the open scene from a given viewpoint. Does not move the editor camera.',
48
- inputSchema: {
49
- camera: z.string().describe(`Where to look from: ${POINT}, or "fit" to frame the whole scene`),
50
- lookAt: z.string().optional().describe(`What to look at: ${POINT}. Defaults to the origin`),
51
- ...frameSchema,
52
- },
53
- }, async ({ camera, lookAt, width, height, ui }) => image(await send({
54
- id: commandId(),
55
- type: 'preview',
56
- camera,
57
- ...(lookAt ? { lookAt } : {}),
58
- frame: { width, height, ui },
59
- })));
60
- mcp.registerTool('telemetry', {
61
- description: 'Read the live state at an id the document declares — transform, visibility, and each component’s public values.',
62
- inputSchema: { id: z.string().describe('An id= from the open document') },
63
- }, async ({ id }) => text(await send({ id: commandId(), type: 'telemetry', targetId: id })));
64
- for (const [name, type] of [
65
- ['play', 'run'],
66
- ['pause', 'pause'],
67
- ['stop', 'stop'],
68
- ]) {
69
- mcp.registerTool(name, { description: `${name[0].toUpperCase()}${name.slice(1)} the open scene.`, inputSchema: {} }, async () => text(await send({ id: commandId(), type })));
70
- }
71
- mcp.registerTool('simulate', {
72
- description: 'Run a batch of steps against the live scene and collect what each produced. This is how ' +
73
- 'to find out whether a game works: drive it to the moment that matters, then capture. ' +
74
- 'Steps run in order.',
75
- inputSchema: {
76
- steps: z
77
- .array(z.record(z.string(), z.unknown()))
78
- .describe('Ordered steps. {"do":"play"} · {"do":"step","frames":30,"dt":0.0167} · ' +
79
- '{"do":"press","key":"Space"} (a KeyCode member name) · {"do":"keyDown"/"keyUp","key":…} · ' +
80
- '{"do":"click","x":100,"y":80} · {"do":"move","x":…,"y":…} · ' +
81
- `{"do":"preview","camera":${JSON.stringify(POINT)}} · {"do":"telemetry","id":"ball"}`),
82
- ...frameSchema,
83
- },
84
- }, async ({ steps, width, height, ui }) => {
85
- const result = await send({
86
- id: commandId(),
87
- type: 'simulate',
88
- steps: steps,
89
- frame: { width, height, ui },
90
- });
91
- if (result.kind !== 'multi')
92
- return text(result);
93
- // Images and readings interleaved in the order they were captured, so a caller can line a
94
- // frame up with the numbers that went with it.
95
- return {
96
- content: result.results.map((one) => one.kind === 'image'
97
- ? { type: 'image', data: one.dataUrl.split(',')[1] ?? '', mimeType: 'image/png' }
98
- : { type: 'text', text: JSON.stringify(one.kind === 'json' ? one.data : one, null, 2) }),
99
- };
100
- });
101
- return mcp;
102
- }
103
- function text(result) {
104
- const body = result.kind === 'json'
105
- ? JSON.stringify(result.data, null, 2)
106
- : result.kind === 'ok'
107
- ? (result.info ?? 'ok')
108
- : JSON.stringify(result);
109
- return { content: [{ type: 'text', text: body }] };
110
- }
111
- function image(result) {
112
- if (result.kind !== 'image')
113
- throw new Error('the editor did not return an image');
114
- return {
115
- content: [{ type: 'image', data: result.dataUrl.split(',')[1] ?? '', mimeType: 'image/png' }],
116
- };
117
- }
118
- /**
119
- * Serve the MCP over HTTP on a fixed port.
120
- *
121
- * Fixed on purpose: an MCP client is configured once with a URL, and a port that moved on every
122
- * restart would need reconfiguring every time.
123
- *
124
- * **One transport per session**, created when a client initializes and kept until it goes away.
125
- * A single shared transport looked simpler and allowed exactly one client ever: the first
126
- * `initialize` claimed it, and every later one — a reconnect, a second client, a restarted
127
- * agent — got a bare `400`, which reads like a crashed server rather than a design limit.
128
- */
129
- export async function startMcpHttp(makeServer, port) {
130
- const sessions = new Map();
131
- const http = createHttpServer((req, res) => {
132
- void handle(req, res).catch((error) => {
133
- res.statusCode = 500;
134
- res.end(String(error instanceof Error ? error.message : error));
135
- });
136
- });
137
- async function handle(req, res) {
138
- const id = req.headers['mcp-session-id'];
139
- const existing = typeof id === 'string' ? sessions.get(id) : undefined;
140
- if (existing)
141
- return existing.handleRequest(req, res, await body(req));
142
- const parsed = await body(req);
143
- if (!isInitialize(parsed)) {
144
- res.statusCode = 400;
145
- res.setHeader('Content-Type', 'application/json');
146
- res.end(JSON.stringify({
147
- jsonrpc: '2.0',
148
- id: null,
149
- error: { code: -32000, message: 'unknown session — send `initialize` first' },
150
- }));
151
- return;
152
- }
153
- const transport = new StreamableHTTPServerTransport({
154
- sessionIdGenerator: () => randomUUID(),
155
- onsessioninitialized: (session) => {
156
- sessions.set(session, transport);
157
- },
158
- });
159
- transport.onclose = () => {
160
- if (transport.sessionId)
161
- sessions.delete(transport.sessionId);
162
- };
163
- await makeServer().connect(transport);
164
- return transport.handleRequest(req, res, parsed);
165
- }
166
- await new Promise((done) => http.listen(port, done));
167
- return {
168
- close: async () => {
169
- for (const transport of sessions.values())
170
- await transport.close().catch(() => { });
171
- sessions.clear();
172
- await new Promise((done) => http.close(() => done()));
173
- },
174
- };
175
- }
176
- function isInitialize(parsed) {
177
- return typeof parsed === 'object' && parsed !== null && parsed.method === 'initialize';
178
- }
179
- /** The request body, read once — the transport is handed the parsed value rather than the stream. */
180
- async function body(req) {
181
- const chunks = [];
182
- for await (const chunk of req)
183
- chunks.push(chunk);
184
- const raw = Buffer.concat(chunks).toString('utf8');
185
- try {
186
- return raw ? JSON.parse(raw) : undefined;
187
- }
188
- catch {
189
- return undefined;
190
- }
191
- }
@@ -1,170 +0,0 @@
1
- /**
2
- * The contract between the Node side and the editor page.
3
- *
4
- * Pure types plus one correlation helper, so both ends agree and the relay is testable on its
5
- * own. Nothing here reaches the compiler: the browser holds no compiler in v2 either.
6
- *
7
- * ### What is deliberately absent
8
- *
9
- * v1 carried an editing half — `EditTarget`, `EditOp`, `StructuralOp`, and `CompiledScene`
10
- * fragments travelling over the wire so a remote caller could reshape a document. All of it is
11
- * gone. An agent has file tools and the watcher rebuilds either way, so a second write path into
12
- * the user's source bought nothing and cost the guarantee that every change to their scene went
13
- * through their editor, their diff and their undo.
14
- *
15
- * What remains is observation: open, render, drive, sample.
16
- */
17
- /** WebSocket path the editor page connects to. */
18
- export declare const WS_PATH = "/scenoco-ws";
19
- /**
20
- * How a capture is rendered — everything except *where the camera is*.
21
- *
22
- * Framing belongs to the individual shot and changes between them; this is the output format,
23
- * and it is the same for every capture in one command. So it sits on the command rather than on
24
- * each step: one simulation renders one way throughout, and two shots of the same run stay
25
- * comparable because nothing but the camera moved between them.
26
- */
27
- export interface PreviewFrame {
28
- width: number;
29
- height: number;
30
- /**
31
- * Include the screen-space UI layer — a `<Canvas>` HUD and anything else parented to a camera.
32
- * The shot is then framed *through* that camera, which is what puts the HUD where the player
33
- * sees it rather than floating in world space in front of a camera nobody is looking through.
34
- */
35
- ui: boolean;
36
- }
37
- /** A command sent Node → editor page, correlated with its reply by `id`. */
38
- export type EditorCommand = {
39
- id: string;
40
- type: 'open';
41
- path: string;
42
- } | {
43
- id: string;
44
- type: 'preview';
45
- camera: string;
46
- lookAt?: string;
47
- frame: PreviewFrame;
48
- } | {
49
- id: string;
50
- type: 'run';
51
- } | {
52
- id: string;
53
- type: 'pause';
54
- } | {
55
- id: string;
56
- type: 'stop';
57
- } | {
58
- id: string;
59
- type: 'telemetry';
60
- targetId: string;
61
- } | {
62
- id: string;
63
- type: 'simulate';
64
- steps: SimStep[];
65
- frame: PreviewFrame;
66
- };
67
- /**
68
- * One step in a `simulate` batch — transport, frame stepping, synthetic input, or a capture.
69
- *
70
- * Run in order against the live engine, so a caller can drive the game to a precise condition
71
- * and sample it there. This is the whole reason the editor is worth having for an agent: a
72
- * screenshot of frame zero says almost nothing about whether a game works.
73
- */
74
- export type SimStep = {
75
- do: 'play' | 'pause' | 'stop';
76
- }
77
- /** Advance by `frames` ticks (default 1) of `dt` seconds (default 1/60). */
78
- | {
79
- do: 'step';
80
- frames?: number;
81
- dt?: number;
82
- }
83
- /**
84
- * Keyboard. `key` is a `KeyCode` member name — `Space`, `W`, `LeftArrow`; an unknown one fails
85
- * the batch with the valid list rather than doing nothing. Each action is its own verb.
86
- */
87
- | {
88
- do: 'press';
89
- key: string;
90
- } | {
91
- do: 'keyDown';
92
- key: string;
93
- } | {
94
- do: 'keyUp';
95
- key: string;
96
- }
97
- /** Mouse. `x,y` are canvas-relative pixels from the top left; `click` is down plus up. */
98
- | {
99
- do: 'click';
100
- x?: number;
101
- y?: number;
102
- button?: 'left' | 'right' | 'middle';
103
- } | {
104
- do: 'move';
105
- x?: number;
106
- y?: number;
107
- dx?: number;
108
- dy?: number;
109
- }
110
- /**
111
- * Capture a PNG. `camera` is a position in the document's own vector grammar
112
- * (`"y: 6; z: 12"`), or `"fit"` to frame the whole scene. Size and UI come from the batch's
113
- * `frame` — see {@link PreviewFrame} for why they are not repeated per step.
114
- */
115
- | {
116
- do: 'preview';
117
- camera: string;
118
- lookAt?: string;
119
- }
120
- /** Sample state at an `id` the document declares — a node and its components. */
121
- | {
122
- do: 'telemetry';
123
- id: string;
124
- };
125
- /** The page's reply, keyed by the command id. */
126
- export type EditorResponse = {
127
- id: string;
128
- ok: true;
129
- result: EditorResult;
130
- } | {
131
- id: string;
132
- ok: false;
133
- error: string;
134
- };
135
- /**
136
- * An unsolicited Node → page notification.
137
- *
138
- * - `documentChanged` — the open document, or something it references, changed on disk. The page
139
- * reloads the scene.
140
- * - `buildFailed` — a document no longer compiles. The page shows the diagnostics rather than
141
- * reloading into a broken scene.
142
- * - `shutdown` — the server is stopping, so the page closes itself instead of being left stale.
143
- */
144
- export type EditorPush = {
145
- push: 'documentChanged';
146
- file: string;
147
- } | {
148
- push: 'buildFailed';
149
- message: string;
150
- } | {
151
- push: 'shutdown';
152
- };
153
- /** Result payloads, discriminated by `kind`. */
154
- export type EditorResult = {
155
- kind: 'ok';
156
- info?: string;
157
- } | {
158
- kind: 'image';
159
- dataUrl: string;
160
- } | {
161
- kind: 'json';
162
- data: unknown;
163
- }
164
- /** `simulate` — one entry per capturing step, in order. */
165
- | {
166
- kind: 'multi';
167
- results: EditorResult[];
168
- };
169
- /** Random correlation id for a command. */
170
- export declare function commandId(): string;
package/dist/protocol.js DELETED
@@ -1,22 +0,0 @@
1
- /**
2
- * The contract between the Node side and the editor page.
3
- *
4
- * Pure types plus one correlation helper, so both ends agree and the relay is testable on its
5
- * own. Nothing here reaches the compiler: the browser holds no compiler in v2 either.
6
- *
7
- * ### What is deliberately absent
8
- *
9
- * v1 carried an editing half — `EditTarget`, `EditOp`, `StructuralOp`, and `CompiledScene`
10
- * fragments travelling over the wire so a remote caller could reshape a document. All of it is
11
- * gone. An agent has file tools and the watcher rebuilds either way, so a second write path into
12
- * the user's source bought nothing and cost the guarantee that every change to their scene went
13
- * through their editor, their diff and their undo.
14
- *
15
- * What remains is observation: open, render, drive, sample.
16
- */
17
- /** WebSocket path the editor page connects to. */
18
- export const WS_PATH = '/scenoco-ws';
19
- /** Random correlation id for a command. */
20
- export function commandId() {
21
- return Math.random().toString(36).slice(2, 10);
22
- }
package/dist/server.d.ts DELETED
@@ -1,124 +0,0 @@
1
- import { type ElementAt, type ProjectModel } from '@scenoco-three/compiler';
2
- import { type EditorCommand, type EditorPush, type EditorResponse } from './protocol.js';
3
- export interface ServerOptions {
4
- /** Project root — the directory holding `tsconfig.json` and `assets/`. Defaults to cwd. */
5
- root?: string;
6
- /** Packages whose tags the documents may use, beyond `three`. */
7
- modules?: readonly string[];
8
- /** Fixed port. Defaults to a free one the OS picks. */
9
- port?: number;
10
- /**
11
- * Called when no editor page has been connected for a while — none ever connected within
12
- * `initialGraceMs`, or the last one disconnected and none came back within `idleMs`.
13
- *
14
- * Opt-in, so an embedder or a test stays long-lived. The CLI wires it to a graceful shutdown,
15
- * which is what keeps a closed tab from leaving an editor process running for the rest of the
16
- * day.
17
- */
18
- onIdle?: () => void;
19
- idleMs?: number;
20
- initialGraceMs?: number;
21
- }
22
- /** Relays a command to the connected editor page and awaits its reply. */
23
- export interface EditorBridge {
24
- connected(): boolean;
25
- send(command: EditorCommand): Promise<EditorResponse>;
26
- push(message: EditorPush): void;
27
- /**
28
- * Resolve as soon as an editor page is connected, or `false` after `ms`.
29
- *
30
- * The CLI waits on this after reclaiming the previous session's port: a tab stranded by an
31
- * ungraceful exit is still retrying its socket every second, so giving it a moment to come
32
- * back is the difference between reusing the tab and stacking up a new one.
33
- */
34
- waitForConnection(ms: number): Promise<boolean>;
35
- }
36
- export interface EditorServer {
37
- url: string;
38
- /**
39
- * The same editor, reachable from another device on the network — a phone, for testing touch.
40
- *
41
- * Absent when the host has no LAN interface to offer.
42
- */
43
- networkUrl?: string;
44
- port: number;
45
- /**
46
- * True when this server bound the **previous** session's port rather than a fresh one, so any
47
- * tab left over from that session is about to reattach on its own retry loop.
48
- */
49
- reusedPort: boolean;
50
- bridge: EditorBridge;
51
- /** What the project's TypeScript declares — the inspector's schema. */
52
- model(): ProjectModel;
53
- close(): Promise<void>;
54
- }
55
- /**
56
- * The editor's own dev server.
57
- *
58
- * It serves the editor page from this package and lets it import the **project's** generated
59
- * modules over `/@fs`, which is the arrangement decided when this was ported: riding the
60
- * project's own Vite would put a React app inside the game's build.
61
- *
62
- * Its jobs are few. Compile the project **with provenance** so the page can map a mesh back to
63
- * the element that built it. Serve the model and the document text the inspector needs. Take a
64
- * save and splice it into the source. Watch, rebuild, and tell the page.
65
- */
66
- export declare function startEditorServer(options?: ServerOptions): Promise<EditorServer>;
67
- /** One change to a document, addressed the way provenance addresses everything. */
68
- export type Edit = {
69
- op: 'setAttribute';
70
- at: ElementAt;
71
- name: string;
72
- value: string | null;
73
- } | {
74
- op: 'removeElement';
75
- at: ElementAt;
76
- } | {
77
- op: 'insertChild';
78
- at: ElementAt;
79
- xml: string;
80
- index?: number;
81
- } | {
82
- op: 'moveElement';
83
- at: ElementAt;
84
- parent: ElementAt;
85
- index?: number;
86
- };
87
- /**
88
- * Apply a save.
89
- *
90
- * One at a time against the running text, because each edit's addresses are computed by the page
91
- * from the document it is looking at — so a batch has to be built as a sequence of
92
- * already-consistent steps, not as parallel edits into one snapshot.
93
- */
94
- export declare function applyEdits(source: string, edits: readonly Edit[]): {
95
- text: string;
96
- problems: string[];
97
- };
98
- /**
99
- * The half of an editor socket this file uses — `ws`'s `WebSocket`, and anything a test can fake.
100
- */
101
- export interface BridgeSocket {
102
- readonly readyState: number;
103
- readonly OPEN: number;
104
- send(data: string): void;
105
- on(event: 'message', listener: (data: unknown) => void): void;
106
- on(event: 'close', listener: () => void): void;
107
- }
108
- /** How the bridge decides the process has nothing left to serve. */
109
- export interface BridgeOptions {
110
- onIdle?: () => void;
111
- idleMs?: number;
112
- initialGraceMs?: number;
113
- }
114
- /**
115
- * Command routing, failover and the idle self-halt, with no server attached.
116
- *
117
- * Separate from `attachBridge` because these are the rules worth pinning down — which tab a
118
- * command goes to, what happens to a command when that tab closes, when the process gives up —
119
- * and none of them need a port, a browser, or a compiled project to be true.
120
- */
121
- export declare function createBridgeCore(options?: BridgeOptions): {
122
- bridge: EditorBridge;
123
- register: (ws: BridgeSocket) => void;
124
- };