@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.
- package/README.md +31 -106
- package/bin/serve.js +37 -0
- package/dist/editor.js +12521 -0
- package/package.json +29 -41
- package/app/cameraRig.ts +0 -118
- package/app/commands.ts +0 -78
- package/app/desktopControls.ts +0 -47
- package/app/document.ts +0 -547
- package/app/editLog.ts +0 -127
- package/app/editor.css +0 -51
- package/app/gizmo.ts +0 -169
- package/app/globals.d.ts +0 -9
- package/app/index.html +0 -14
- package/app/main.ts +0 -546
- package/app/meta.ts +0 -24
- package/app/picking.ts +0 -47
- package/app/session.ts +0 -612
- package/app/touchControls.ts +0 -127
- package/app/ui-react/AddMemberPopup.tsx +0 -114
- package/app/ui-react/BottomLauncher.tsx +0 -15
- package/app/ui-react/EditorUI.tsx +0 -22
- package/app/ui-react/FilePicker.tsx +0 -80
- package/app/ui-react/HeadBtn.tsx +0 -21
- package/app/ui-react/InspectorSheet.tsx +0 -475
- package/app/ui-react/NodeListSheet.tsx +0 -56
- package/app/ui-react/TopBar.tsx +0 -137
- package/app/ui-react/Tree.tsx +0 -106
- package/app/ui-react/cx.ts +0 -4
- package/app/ui-react/fields.tsx +0 -407
- package/app/ui-react/format.ts +0 -30
- package/app/ui-react/mount.tsx +0 -31
- package/app/ui-react/store.ts +0 -80
- package/app/ui-react/types.ts +0 -88
- package/app/ui-react/ui.module.css +0 -250
- package/app/ui-react/useSize.ts +0 -20
- package/app/values.ts +0 -137
- package/app/vite-env.d.ts +0 -7
- package/dist/cli/index.d.ts +0 -2
- package/dist/cli/index.js +0 -122
- package/dist/index.d.ts +0 -10
- package/dist/index.js +0 -10
- package/dist/mcp.d.ts +0 -32
- package/dist/mcp.js +0 -191
- package/dist/protocol.d.ts +0 -170
- package/dist/protocol.js +0 -22
- package/dist/server.d.ts +0 -124
- package/dist/server.js +0 -440
package/README.md
CHANGED
|
@@ -1,112 +1,37 @@
|
|
|
1
1
|
# @scenoco-three/editor
|
|
2
2
|
|
|
3
|
-
A
|
|
4
|
-
|
|
5
|
-
read back telemetry — the "eyes and hands" for an agent that authors scenes with
|
|
6
|
-
`@scenoco-three/compiler`. Humans and agents share the **same live browser tab**: the MCP
|
|
7
|
-
tools drive it over a WebSocket bridge, there's no separate headless render path.
|
|
3
|
+
A visual editor for SceNoCo scenes that is never part of the project. Part of
|
|
4
|
+
[SceNoCo](https://github.com/RomaRogov/scenoco-three).
|
|
8
5
|
|
|
9
6
|
```bash
|
|
10
|
-
npm
|
|
7
|
+
npm run dev # the game's own dev server, as always
|
|
8
|
+
npx scenoco-editor # the editor's interface, on http://localhost:5185
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
| `open_file` | `path` | Opens a `.scene.xml` / `.prefab.xml` in edit mode. |
|
|
41
|
-
| `screenshot` | `camera`, `lookAt?`, `width`, `height`, `fit`, `ui` | A **PNG** of the current view, at exactly the size and camera fit you state, with or without the HUD. |
|
|
42
|
-
| `playback` | `state` | `play` / `pause` / `stop` — enter play mode (components, systems, physics) / freeze / return to the authored scene. |
|
|
43
|
-
| `inspect` | `id` | **JSON** of current property values at a scene `id` — a **node** (its transform + every component on it) or a **component** (its authored field values + its host node's transform). Nodes and components share one id namespace. |
|
|
44
|
-
|
|
45
|
-
Values read back in the same vocabulary the scene XML is written in: vectors are
|
|
46
|
-
named by axis (`{"x": 0, "y": 1.5, "z": 0}`), eulers are **degrees**, colours are
|
|
47
|
-
`"#rrggbb"`, and a reference is `"#id"`. Points you pass *in* (`camera`, `lookAt`)
|
|
48
|
-
take the DSL's own vector grammar — `"y: 6; z: 12"`, an omitted axis being 0, with
|
|
49
|
-
the positional `"0 6 12"` still accepted.
|
|
50
|
-
| `simulate` | `commands: SimStep[]` + the same `width`/`height`/`fit`/`ui` | Ordered **images + JSON** — the workhorse below. The capture format is stated once for the batch, not per shot. |
|
|
51
|
-
|
|
52
|
-
### `simulate` — closed-loop driving
|
|
53
|
-
|
|
54
|
-
One call injects input, steps the sim, and captures — *see a game move, not a static scene*.
|
|
55
|
-
Each command is a self-contained verb `{ do, … }`:
|
|
56
|
-
|
|
57
|
-
```ts
|
|
58
|
-
type SimStep =
|
|
59
|
-
| { do: 'play' | 'pause' | 'stop' }
|
|
60
|
-
| { do: 'step'; frames?: number; dt?: number } // advance `frames` ticks (default 1)
|
|
61
|
-
| { do: 'press'; key: string } // key press + release
|
|
62
|
-
| { do: 'keyDown'; key: string }
|
|
63
|
-
| { do: 'keyUp'; key: string }
|
|
64
|
-
| { do: 'click'; x?: number; y?: number; button?: 'left' | 'right' | 'middle' }
|
|
65
|
-
| { do: 'move'; x?: number; y?: number; dx?: number; dy?: number }
|
|
66
|
-
| { do: 'gamepad'; index?: number; buttons?: boolean[]; axes?: number[] }
|
|
67
|
-
| { do: 'preview'; camera: string; lookAt?: string } // size/fit/ui come from the call
|
|
68
|
-
| { do: 'telemetry'; id: string };
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
```jsonc
|
|
72
|
-
// "press Space, advance 90 frames, read the ball, screenshot" — in one tool call
|
|
73
|
-
{ "width": 960, "height": 540, "fit": "contain", "ui": true,
|
|
74
|
-
"commands": [ { "do": "play" },
|
|
75
|
-
{ "do": "press", "key": "Space" },
|
|
76
|
-
{ "do": "step", "frames": 90 },
|
|
77
|
-
{ "do": "telemetry", "id": "ball" },
|
|
78
|
-
{ "do": "preview", "camera": "fit" } ] }
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
The result is a `multi` payload: each capture's image/JSON in step order.
|
|
82
|
-
|
|
83
|
-
## Programmatic API
|
|
84
|
-
|
|
85
|
-
```ts
|
|
86
|
-
import { startEditorServer, startMcpServer } from '@scenoco-three/editor';
|
|
87
|
-
|
|
88
|
-
const editor = await startEditorServer({ cwd }); // defaults: assets/ roots, packages from package.json
|
|
89
|
-
const mcp = await startMcpServer(editor.bridge, { port: 55470 }); // editor.bridge.send(EditorCommand)
|
|
90
|
-
console.log(editor.url, mcp.url);
|
|
91
|
-
// … later
|
|
92
|
-
await mcp.close(); await editor.close();
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
Also exported: `buildMcpServer`, the WebSocket `protocol` types (`EditorCommand`,
|
|
96
|
-
`EditorResponse`, `EditorResult`, `WS_PATH`, `commandId`).
|
|
97
|
-
|
|
98
|
-
## Standalone & dependencies
|
|
99
|
-
|
|
100
|
-
Build-time / Node-side tool — **not** part of the browser runtime; nothing here ships to
|
|
101
|
-
production. It depends on `@scenoco-three/compiler` and `@scenoco-three/vite` (compile +
|
|
102
|
-
serve) and the MCP SDK; `@scenoco-three/core`, `three`, and `vite` are **peers** (the editor
|
|
103
|
-
renders your scene with the same runtime your app uses). You can run it purely for the **human
|
|
104
|
-
browser UI** without ever touching MCP, or wire **only** the MCP server to an agent — the two
|
|
105
|
-
share one live session but neither requires the other.
|
|
106
|
-
|
|
107
|
-
See the [repository](../../README.md), [ARCHITECTURE.md](../../ARCHITECTURE.md), and the
|
|
108
|
-
[measurements](../../bench/README.md).
|
|
109
|
-
|
|
110
|
-
## License
|
|
111
|
-
|
|
112
|
-
[MIT](./LICENSE)
|
|
11
|
+
Then open the game with `?edit` — `http://localhost:5173/?edit` — or any scene or prefab function
|
|
12
|
+
with `?edit&scene=/src/scenes/levels.tsx%23Level2`.
|
|
13
|
+
|
|
14
|
+
- **The game builds the scene.** In development, `play()` sees `?edit`, builds the scene with the
|
|
15
|
+
game's own code on the game's own server, and holds it still: no behaviour wakes. Then it loads
|
|
16
|
+
this package's interface from its URL (`?edit=<url>` to load it from elsewhere).
|
|
17
|
+
- **What you see is live.** A tree of the scene, an inspector — transform, the element's own
|
|
18
|
+
attributes, the geometry's and material's, every behaviour's fields — and a move / rotate / scale
|
|
19
|
+
gizmo, with Global or Local handles. A change is applied to the live object the way the scene sets it.
|
|
20
|
+
- **Made for a finger as much as a mouse.** On a phone held upright the node list and the inspector
|
|
21
|
+
are bottom sheets, one at a time; turned sideways, or on a screen, they are side columns that can
|
|
22
|
+
both be open. Drag the letter beside a number to scrub it: the scene follows the drag, and the
|
|
23
|
+
file is written once, when it ends.
|
|
24
|
+
- **What you change is written back, and nothing else is.** The project's `scenoco()` Vite plugin
|
|
25
|
+
writes one attribute into the `.tsx` file the element came from, splicing its text; comments,
|
|
26
|
+
layout and every other attribute stay byte for byte. A value it cannot see through — a
|
|
27
|
+
`material={paint.wall}`, a ref — is shown, not edited. Rotations show in degrees and are written
|
|
28
|
+
in radians; numbers are rounded to four decimals.
|
|
29
|
+
|
|
30
|
+
- **Undo puts the text back.** ⌘Z / Ctrl+Z and ⇧⌘Z / Ctrl+Y restore an attribute exactly as it
|
|
31
|
+
was written — `-Math.PI / 2` too — and survive the reload that removing an attribute needs.
|
|
32
|
+
- **A file changed behind its back is refused.** Every write names the element it means as the
|
|
33
|
+
editor last read it; if the file was edited elsewhere since, nothing is written and the editor
|
|
34
|
+
says to reload.
|
|
35
|
+
|
|
36
|
+
What it does not do yet: add or remove elements, or show what behaviours spawn at runtime
|
|
37
|
+
(nothing runs while editing — `BrickField`'s bricks are not there).
|
package/bin/serve.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `scenoco-editor [--port 5185]` — serves the editor's interface for a game's page to load.
|
|
4
|
+
*
|
|
5
|
+
* Start the game's own dev server as usual, run this beside it, and open the game with `?edit`:
|
|
6
|
+
* the page builds the scene, holds it still, and imports the interface from here. Nothing about
|
|
7
|
+
* the editor is installed in the project.
|
|
8
|
+
*/
|
|
9
|
+
import { createReadStream, existsSync } from 'node:fs';
|
|
10
|
+
import { createServer } from 'node:http';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
|
|
13
|
+
const argv = process.argv.slice(2);
|
|
14
|
+
if (argv.includes('-h') || argv.includes('--help')) {
|
|
15
|
+
console.log('scenoco-editor [--port <n>]\n\nServes the editor for a game page opened with ?edit (port 5185 by default).');
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
const port = Number(argv[argv.indexOf('--port') + 1] || 0) || 5185;
|
|
19
|
+
const file = fileURLToPath(new URL('../dist/editor.js', import.meta.url));
|
|
20
|
+
if (!existsSync(file)) {
|
|
21
|
+
console.error('scenoco-editor: this package has no dist/editor.js — run `npm run build` in packages/editor');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
createServer((req, res) => {
|
|
26
|
+
// The game's page is on another port, so the module needs CORS to load at all.
|
|
27
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
28
|
+
if (new URL(req.url ?? '/', 'http://x').pathname !== '/editor.js') {
|
|
29
|
+
res.statusCode = 404;
|
|
30
|
+
return res.end('Open your game with ?edit — it loads /editor.js from here.');
|
|
31
|
+
}
|
|
32
|
+
res.setHeader('Content-Type', 'text/javascript');
|
|
33
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
34
|
+
createReadStream(file).pipe(res);
|
|
35
|
+
}).listen(port, () => {
|
|
36
|
+
console.log(`scenoco-editor: serving on http://localhost:${port}/editor.js — open your game with ?edit`);
|
|
37
|
+
});
|