@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/README.md CHANGED
@@ -1,112 +1,37 @@
1
1
  # @scenoco-three/editor
2
2
 
3
- A local **visual editor** for [SceNoCo](https://github.com/) scenes and prefabs, with an
4
- **MCP server** so a (multimodal) LLM can open a scene, render frames, drive gameplay, and
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 i -D @scenoco-three/editor
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
- ## CLI
14
-
15
- ```
16
- scenoco-editor [scene|prefab] [options] Omit the file to choose from a start-up picker
17
- --components <dirs> Comma-separated component dirs (default: "assets")
18
- --packages <pkgs> Comma-separated tag packages (default: every @scenoco-three/*
19
- dependency in the project's package.json)
20
- --port <n> Fixed MCP port — pin it so your MCP client survives restarts
21
- (the HTTP editor server always takes a free port and prints its URL)
22
- ```
23
-
24
- ```bash
25
- scenoco-editor # zero-config: file picker, auto-detected packages
26
- scenoco-editor assets/scenes/level.scene.xml --port 55470
27
- # editor: http://localhost:HTTP_PORT?file=… ← opens in your browser
28
- # mcp: http://localhost:55470/mcp ← add to your MCP client (transport: http)
29
- ```
30
-
31
- It serves the editor app through Vite + `@scenoco-three/vite` (so the open scene compiles
32
- exactly like production), bridges that browser session to an MCP server over a WebSocket, and
33
- watches the scene's source dependencies for **live reload** (with an unsaved-changes conflict
34
- prompt).
35
-
36
- ## MCP tools
37
-
38
- | Tool | Args | Returns |
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
+ });