@scenoco-three/editor 0.5.0 → 0.6.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 (112) hide show
  1. package/README.md +31 -93
  2. package/dist/cli/index.js +49 -115
  3. package/dist-app/assets/EffectComposer-lekj9UUq.js +1 -0
  4. package/dist-app/assets/GLTFLoader-C2lUhzim.js +1 -0
  5. package/dist-app/assets/OutputPass-DZtd342R.js +72 -0
  6. package/dist-app/assets/Pass-BP3b3hlf.js +1 -0
  7. package/dist-app/assets/RenderPass-BjVchlYg.js +1 -0
  8. package/dist-app/assets/ShaderPass-DMXLcV9X.js +24 -0
  9. package/dist-app/assets/SkeletonUtils-Cxy_owx9.js +1 -0
  10. package/dist-app/assets/index-AxOpglZ-.css +1 -0
  11. package/dist-app/assets/index-D-Iq7tix.js +4135 -0
  12. package/dist-app/assets/inter-CaiW00F8.js +1 -0
  13. package/dist-app/assets/registry-BOM35tSr.js +1433 -0
  14. package/dist-app/assets/worker-_Lh8Vt-i.js +1270 -0
  15. package/dist-app/examples/breakout/assets/geometry/ball.geometry.xml +1 -0
  16. package/dist-app/examples/breakout/assets/geometry/brick.geometry.xml +1 -0
  17. package/dist-app/examples/breakout/assets/geometry/floor-sensor.geometry.xml +1 -0
  18. package/dist-app/examples/breakout/assets/geometry/paddle.geometry.xml +1 -0
  19. package/dist-app/examples/breakout/assets/geometry/wall-side.geometry.xml +1 -0
  20. package/dist-app/examples/breakout/assets/geometry/wall-top.geometry.xml +1 -0
  21. package/dist-app/examples/breakout/assets/materials/ball.material.xml +1 -0
  22. package/dist-app/examples/breakout/assets/materials/brick-firm.material.xml +1 -0
  23. package/dist-app/examples/breakout/assets/materials/brick-hard.material.xml +1 -0
  24. package/dist-app/examples/breakout/assets/materials/brick-soft.material.xml +1 -0
  25. package/dist-app/examples/breakout/assets/materials/paddle.material.xml +10 -0
  26. package/dist-app/examples/breakout/assets/materials/wall.material.xml +1 -0
  27. package/dist-app/examples/breakout/assets/render/neon.render.xml +14 -0
  28. package/dist-app/examples/breakout/assets/scenes/arena.prefab.xml +68 -0
  29. package/dist-app/examples/breakout/assets/scenes/brick.prefab.xml +15 -0
  30. package/dist-app/examples/breakout/assets/scenes/hud.scene.xml +56 -0
  31. package/dist-app/examples/breakout/assets/scenes/level-1.scene.xml +36 -0
  32. package/dist-app/examples/breakout/assets/scenes/level-2.scene.xml +36 -0
  33. package/dist-app/examples/breakout/assets/scenes/level-3.scene.xml +36 -0
  34. package/dist-app/examples/castle/assets/geometry/capsule.geometry.xml +3 -0
  35. package/dist-app/examples/castle/assets/geometry/cone.geometry.xml +2 -0
  36. package/dist-app/examples/castle/assets/geometry/cube.geometry.xml +3 -0
  37. package/dist-app/examples/castle/assets/geometry/cylinder.geometry.xml +3 -0
  38. package/dist-app/examples/castle/assets/geometry/plane.geometry.xml +4 -0
  39. package/dist-app/examples/castle/assets/geometry/quad.geometry.xml +3 -0
  40. package/dist-app/examples/castle/assets/geometry/sphere.geometry.xml +2 -0
  41. package/dist-app/examples/castle/assets/materials/grass.material.xml +1 -0
  42. package/dist-app/examples/castle/assets/materials/roof.material.xml +1 -0
  43. package/dist-app/examples/castle/assets/materials/stone.material.xml +1 -0
  44. package/dist-app/examples/castle/assets/materials/wood.material.xml +1 -0
  45. package/dist-app/examples/castle/assets/scenes/main.scene.xml +25 -0
  46. package/dist-app/examples/collector/assets/geometry/capsule.geometry.xml +3 -0
  47. package/dist-app/examples/collector/assets/geometry/cone.geometry.xml +2 -0
  48. package/dist-app/examples/collector/assets/geometry/cube.geometry.xml +3 -0
  49. package/dist-app/examples/collector/assets/geometry/cylinder.geometry.xml +3 -0
  50. package/dist-app/examples/collector/assets/geometry/plane.geometry.xml +4 -0
  51. package/dist-app/examples/collector/assets/geometry/quad.geometry.xml +3 -0
  52. package/dist-app/examples/collector/assets/geometry/sphere.geometry.xml +2 -0
  53. package/dist-app/examples/collector/assets/materials/crate.material.xml +1 -0
  54. package/dist-app/examples/collector/assets/materials/depot.material.xml +1 -0
  55. package/dist-app/examples/collector/assets/materials/ground.material.xml +1 -0
  56. package/dist-app/examples/collector/assets/materials/player.material.xml +1 -0
  57. package/dist-app/examples/collector/assets/scenes/crate.prefab.xml +9 -0
  58. package/dist-app/examples/collector/assets/scenes/main.scene.xml +83 -0
  59. package/dist-app/examples/playable/assets/geometry/capsule.geometry.xml +3 -0
  60. package/dist-app/examples/playable/assets/geometry/cone.geometry.xml +2 -0
  61. package/dist-app/examples/playable/assets/geometry/cube.geometry.xml +3 -0
  62. package/dist-app/examples/playable/assets/geometry/cylinder.geometry.xml +3 -0
  63. package/dist-app/examples/playable/assets/geometry/plane.geometry.xml +4 -0
  64. package/dist-app/examples/playable/assets/geometry/quad.geometry.xml +3 -0
  65. package/dist-app/examples/playable/assets/geometry/sphere.geometry.xml +2 -0
  66. package/dist-app/examples/playable/assets/materials/ground.material.xml +1 -0
  67. package/dist-app/examples/playable/assets/materials/target.material.xml +2 -0
  68. package/dist-app/examples/playable/assets/scenes/main.scene.xml +53 -0
  69. package/dist-app/examples/playable/assets/scenes/target.prefab.xml +9 -0
  70. package/dist-app/examples.json +68 -0
  71. package/{app → dist-app}/index.html +2 -2
  72. package/dist-app/model.json +1 -0
  73. package/package.json +26 -30
  74. package/app/cameraRig.ts +0 -118
  75. package/app/commands.ts +0 -78
  76. package/app/desktopControls.ts +0 -47
  77. package/app/document.ts +0 -547
  78. package/app/editLog.ts +0 -127
  79. package/app/editor.css +0 -51
  80. package/app/gizmo.ts +0 -169
  81. package/app/main.ts +0 -543
  82. package/app/meta.ts +0 -24
  83. package/app/picking.ts +0 -47
  84. package/app/session.ts +0 -572
  85. package/app/touchControls.ts +0 -127
  86. package/app/ui-react/AddMemberPopup.tsx +0 -114
  87. package/app/ui-react/BottomLauncher.tsx +0 -15
  88. package/app/ui-react/EditorUI.tsx +0 -22
  89. package/app/ui-react/FilePicker.tsx +0 -80
  90. package/app/ui-react/HeadBtn.tsx +0 -21
  91. package/app/ui-react/InspectorSheet.tsx +0 -475
  92. package/app/ui-react/NodeListSheet.tsx +0 -56
  93. package/app/ui-react/TopBar.tsx +0 -137
  94. package/app/ui-react/Tree.tsx +0 -106
  95. package/app/ui-react/cx.ts +0 -4
  96. package/app/ui-react/fields.tsx +0 -407
  97. package/app/ui-react/format.ts +0 -30
  98. package/app/ui-react/mount.tsx +0 -31
  99. package/app/ui-react/store.ts +0 -80
  100. package/app/ui-react/types.ts +0 -88
  101. package/app/ui-react/ui.module.css +0 -250
  102. package/app/ui-react/useSize.ts +0 -20
  103. package/app/values.ts +0 -137
  104. package/app/vite-env.d.ts +0 -7
  105. package/dist/index.d.ts +0 -10
  106. package/dist/index.js +0 -10
  107. package/dist/mcp.d.ts +0 -32
  108. package/dist/mcp.js +0 -191
  109. package/dist/protocol.d.ts +0 -170
  110. package/dist/protocol.js +0 -22
  111. package/dist/server.d.ts +0 -124
  112. package/dist/server.js +0 -437
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@scenoco-three/editor",
3
- "version": "0.5.0",
4
- "description": "Local visual editor for SceNoCo scenes, with an observation-only MCP server so an agent can see and drive the same live scene.",
3
+ "version": "0.6.0",
4
+ "description": "The standalone SceNoCo editor: a static site that opens any .scene.xml from a folder or a URL, builds the scene live, and saves back \u2014 a tree, an inspector, gizmos. `scenoco-editor` serves it locally.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -14,25 +14,20 @@
14
14
  "bin": {
15
15
  "scenoco-editor": "./dist/cli/index.js"
16
16
  },
17
- "main": "./dist/index.js",
18
- "types": "./dist/index.d.ts",
19
- "exports": {
20
- ".": {
21
- "types": "./dist/index.d.ts",
22
- "import": "./dist/index.js"
23
- }
24
- },
17
+ "main": "./dist/cli/index.js",
25
18
  "files": [
26
19
  "dist",
27
- "app"
20
+ "dist-app"
28
21
  ],
29
22
  "keywords": [
30
23
  "three",
31
24
  "threejs",
32
25
  "scenoco",
33
26
  "editor",
34
- "preview",
35
- "render",
27
+ "scene",
28
+ "xml",
29
+ "gizmo",
30
+ "inspector",
36
31
  "gamedev"
37
32
  ],
38
33
  "publishConfig": {
@@ -40,29 +35,30 @@
40
35
  },
41
36
  "scripts": {
42
37
  "prebuild": "rm -rf dist",
43
- "build": "tsc -p tsconfig.build.json && chmod +x dist/cli/index.js",
38
+ "build": "tsc -p tsconfig.build.json && chmod +x dist/cli/index.js && npm run build:app",
39
+ "build:app": "node scripts/static-model.mjs && node scripts/static-assets.mjs && vite build --config vite.static.config.ts",
40
+ "preview:app": "vite preview --config vite.static.config.ts --port 5195 --strictPort",
44
41
  "typecheck": "npm --prefix ../core run build && npm --prefix ../compiler run build && tsc --noEmit -p tsconfig.json"
45
42
  },
46
- "dependencies": {
47
- "@modelcontextprotocol/sdk": "^1.12.0",
48
- "@scenoco-three/compiler": "^0.5.0",
49
- "@scenoco-three/core": "^0.5.0",
43
+ "dependencies": {},
44
+ "peerDependencies": {},
45
+ "devDependencies": {
46
+ "@scenoco-three/audio": "^0.6.0",
47
+ "@scenoco-three/box2d": "^0.6.0",
48
+ "@scenoco-three/collision": "^0.6.0",
49
+ "@scenoco-three/compiler": "^0.6.0",
50
+ "@scenoco-three/core": "^0.6.0",
51
+ "@scenoco-three/particles": "^0.6.0",
52
+ "@scenoco-three/postfx": "^0.6.0",
53
+ "@scenoco-three/ui": "^0.6.0",
54
+ "@types/react": "^18.3.31",
55
+ "@types/react-dom": "^18.3.7",
50
56
  "@vitejs/plugin-react": "^6.0.3",
51
57
  "lucide-react": "^0.468.0",
52
58
  "react": "^18.3.1",
53
59
  "react-arborist": "^3.4.0",
54
60
  "react-dom": "^18.3.1",
55
- "ws": "^8.18.0",
56
- "zod": "^4.0.0",
61
+ "vite": "^8.0.0",
57
62
  "zustand": "^5.0.2"
58
- },
59
- "peerDependencies": {
60
- "three": ">=0.160.0",
61
- "vite": ">=5"
62
- },
63
- "devDependencies": {
64
- "@types/react": "^18.3.31",
65
- "@types/react-dom": "^18.3.7",
66
- "@types/ws": "^8.5.13"
67
63
  }
68
- }
64
+ }
package/app/cameraRig.ts DELETED
@@ -1,118 +0,0 @@
1
- import { Euler, Quaternion, Vector3, type PerspectiveCamera } from 'three';
2
-
3
- /**
4
- * Owns the editor camera's orientation (yaw/pitch, roll-free) and exposes simple
5
- * verbs that both the desktop (mouse/keyboard) and touch input adapters call, so
6
- * the two never fight over the camera transform. Call `sync()` once per frame.
7
- */
8
- export class CameraRig {
9
- readonly euler = new Euler(0, 0, 0, 'YXZ');
10
- /** Base movement speed (world units/sec), tuned by the wheel / scene size. */
11
- speed: number;
12
- /**
13
- * Distance to the focus point (Unity scene-camera pivot). Touch pan/dolly scale
14
- * by this, so motion stays consistent on screen at any zoom — fine near objects,
15
- * fast when far. Set by `frameOn`; dolly shrinks/grows it.
16
- */
17
- focusDistance: number;
18
-
19
- private readonly q = new Quaternion();
20
- private readonly v = new Vector3();
21
-
22
- /** Where a focus glide is taking the camera, while one is running. */
23
- private glideTo: Vector3 | null = null;
24
-
25
- readonly camera: PerspectiveCamera;
26
-
27
- constructor(camera: PerspectiveCamera, target: Vector3) {
28
- this.camera = camera;
29
- camera.lookAt(target);
30
- this.euler.setFromQuaternion(camera.quaternion);
31
- this.speed = Math.max(1, camera.position.distanceTo(target) * 0.5);
32
- this.focusDistance = Math.max(0.1, camera.position.distanceTo(target));
33
- }
34
-
35
- /**
36
- * Frame a point: keep looking exactly the way you are, and move until it is centred
37
- * `distance` away.
38
- *
39
- * This is Unity's F, and the angle is the whole of it. Turning to face the selection instead
40
- * throws away the view the person spent their time arranging — they lined the scene up from
41
- * one side, pressed F to get closer to a node, and now they are looking at it from wherever
42
- * the pivot maths decided. Keeping the orientation means F only ever answers "put that in
43
- * front of me", which is the question it was asked.
44
- *
45
- * The camera glides rather than teleports: a cut leaves you working out where you ended up,
46
- * and the motion is what tells you the scene did not change — only your seat.
47
- */
48
- frameOn(point: Vector3, distance: number): void {
49
- this.focusDistance = Math.max(0.1, distance);
50
- // Straight back along the current view direction — `-Z` in camera space, which is where the
51
- // camera already looks, so nothing about the orientation moves.
52
- this.glideTo = point.clone().addScaledVector(this.forward(this.v), -this.focusDistance);
53
- this.speed = Math.max(1, this.focusDistance * 0.5); // fly speed scales with how far out we sit
54
- }
55
-
56
- /** The unit vector the camera currently looks along. */
57
- private forward(into: Vector3): Vector3 {
58
- return into.set(0, 0, -1).applyQuaternion(this.q.setFromEuler(this.euler));
59
- }
60
-
61
- /** Add yaw/pitch in radians (pitch clamped to avoid flipping). */
62
- rotate(dYaw: number, dPitch: number): void {
63
- this.glideTo = null; // the person is driving now
64
- this.euler.y += dYaw;
65
- this.euler.x = clamp(this.euler.x + dPitch, -Math.PI / 2 + 0.01, Math.PI / 2 - 0.01);
66
- }
67
-
68
- /**
69
- * Translate: `right` and `forward` in camera-local space, `up` along **world** up.
70
- *
71
- * Local up is the tempting reading and the wrong one. With the camera pitched down at the
72
- * scene — which is where an editor camera spends its life — a local "up" also carries you
73
- * backwards, so Q and E slide the view around instead of raising and lowering it. Unity's scene
74
- * camera uses world up for the same reason.
75
- */
76
- move(right: number, up: number, forward: number): void {
77
- this.glideTo = null; // any input cancels a focus glide — the camera has one owner at a time
78
- this.v.set(right, 0, -forward).applyQuaternion(this.q.setFromEuler(this.euler));
79
- this.v.y += up;
80
- this.camera.position.add(this.v);
81
- }
82
-
83
- /**
84
- * Apply the orientation, and advance a focus glide. Call once per frame, after input.
85
- *
86
- * Frame-rate independent easing: the *fraction of the remaining distance* covered per second
87
- * is fixed, so the same glide takes the same time at 60Hz and at 144Hz. `1 - k ** dt` is the
88
- * exponential form of that — the naive `lerp(a, b, 0.1)` per frame is twice as fast on a
89
- * 120Hz display, which is how a smooth camera on one machine becomes a snap on another.
90
- */
91
- sync(dt = 0): void {
92
- this.camera.quaternion.setFromEuler(this.euler);
93
- if (!this.glideTo) return;
94
- const remaining = this.v.subVectors(this.glideTo, this.camera.position);
95
- // Close enough that another frame of easing would be invisible: land exactly and stop, so
96
- // the camera is never left a hair off with a live tween attached to it.
97
- if (remaining.lengthSq() < 1e-8) {
98
- this.camera.position.copy(this.glideTo);
99
- this.glideTo = null;
100
- return;
101
- }
102
- this.camera.position.addScaledVector(remaining, 1 - GLIDE_LEFT_PER_SECOND ** dt);
103
- }
104
- }
105
-
106
- /**
107
- * The fraction of the distance to the focus target still left after one second.
108
- *
109
- * Measured rather than guessed: at 1e-6 the camera is 94% of the way there in 0.2s and 98.5% in
110
- * 0.3s, so the move reads as quick and decisive and is visually over in about a third of a
111
- * second. Ten times gentler (1e-5) drifts long enough to feel laggy on a phone; ten times
112
- * sharper is indistinguishable from a cut.
113
- */
114
- const GLIDE_LEFT_PER_SECOND = 1e-6;
115
-
116
- function clamp(v: number, lo: number, hi: number): number {
117
- return Math.max(lo, Math.min(hi, v));
118
- }
package/app/commands.ts DELETED
@@ -1,78 +0,0 @@
1
- /**
2
- * The stateful edit primitive. Every mutation in the editor is a `Command` so it
3
- * can be undone and (because each one also mutates the compiled scene) saved as a
4
- * diff. Commands are deliberately tiny — `apply` redoes, `invert` undoes.
5
- */
6
- export interface Command {
7
- /** Human label (undo menu / debugging). */
8
- readonly label: string;
9
- apply(): void;
10
- invert(): void;
11
- /**
12
- * Edits sharing a key, applied back-to-back, collapse into a single undo step.
13
- * A gizmo drag or a numeric scrub emits one command per frame; undo should
14
- * rewind the whole gesture, not each intermediate value.
15
- */
16
- readonly coalesceKey?: string;
17
- }
18
-
19
- /** Undo/redo history with same-key coalescing. `onChange` fires after every mutation. */
20
- export class UndoStack {
21
- private readonly undoables: Command[] = [];
22
- private readonly redoables: Command[] = [];
23
-
24
- private readonly onChange: (() => void) | undefined;
25
-
26
- constructor(onChange?: () => void) {
27
- this.onChange = onChange;
28
- }
29
-
30
- /** Apply a command and record it, merging into the previous one if keys match. */
31
- run(cmd: Command): void {
32
- cmd.apply();
33
- const prev = this.undoables[this.undoables.length - 1];
34
- if (cmd.coalesceKey !== undefined && prev?.coalesceKey === cmd.coalesceKey) {
35
- // Adopt the latest `apply` (current value) but keep the earliest `invert`
36
- // (the pre-gesture state), so one undo rewinds the entire gesture.
37
- this.undoables[this.undoables.length - 1] = {
38
- label: prev.label,
39
- coalesceKey: prev.coalesceKey,
40
- apply: cmd.apply,
41
- invert: prev.invert,
42
- };
43
- } else {
44
- this.undoables.push(cmd);
45
- }
46
- this.redoables.length = 0;
47
- this.onChange?.();
48
- }
49
-
50
- undo(): void {
51
- const cmd = this.undoables.pop();
52
- if (!cmd) return;
53
- cmd.invert();
54
- this.redoables.push(cmd);
55
- this.onChange?.();
56
- }
57
-
58
- redo(): void {
59
- const cmd = this.redoables.pop();
60
- if (!cmd) return;
61
- cmd.apply();
62
- this.undoables.push(cmd);
63
- this.onChange?.();
64
- }
65
-
66
- get canUndo(): boolean {
67
- return this.undoables.length > 0;
68
- }
69
- get canRedo(): boolean {
70
- return this.redoables.length > 0;
71
- }
72
-
73
- clear(): void {
74
- this.undoables.length = 0;
75
- this.redoables.length = 0;
76
- this.onChange?.();
77
- }
78
- }
@@ -1,47 +0,0 @@
1
- import { Input, KeyCode, MouseButton } from '@scenoco-three/core';
2
- import type { CameraRig } from './cameraRig.js';
3
-
4
- /**
5
- * Desktop fly navigation via `engine.input` (Unity scene-view style):
6
- * • hold RIGHT mouse to look · WASD move · Q/E down/up · Shift faster · wheel speed.
7
- */
8
- export class DesktopControls {
9
- private readonly look = 0.0025; // radians per pixel
10
-
11
- private readonly rig: CameraRig;
12
- /** v2's input is one singleton attached to the canvas, not a per-engine manager. */
13
- private readonly input = Input;
14
-
15
- constructor(rig: CameraRig) {
16
- this.rig = rig;
17
- }
18
-
19
- /** Advance one frame (call after `engine.tick`). */
20
- update(dt: number): void {
21
- const { input, rig } = this;
22
-
23
- const wheel = input.mouseScrollDelta.y;
24
- if (wheel !== 0) rig.speed = clamp(rig.speed * (wheel < 0 ? 1.1 : 1 / 1.1), 0.05, 1000);
25
-
26
- if (input.getMouseButton(MouseButton.Right)) {
27
- const d = input.mouseDelta;
28
- rig.rotate(-d.x * this.look, -d.y * this.look);
29
- }
30
-
31
- const right = axis(input.getKey(KeyCode.D), input.getKey(KeyCode.A));
32
- const up = axis(input.getKey(KeyCode.E), input.getKey(KeyCode.Q));
33
- const forward = axis(input.getKey(KeyCode.W), input.getKey(KeyCode.S));
34
- if (right || up || forward) {
35
- const len = Math.hypot(right, up, forward);
36
- const step = (rig.speed * (input.getKey(KeyCode.LeftShift) ? 4 : 1) * dt) / len;
37
- rig.move(right * step, up * step, forward * step);
38
- }
39
- }
40
- }
41
-
42
- function axis(pos: boolean, neg: boolean): number {
43
- return (pos ? 1 : 0) - (neg ? 1 : 0);
44
- }
45
- function clamp(v: number, lo: number, hi: number): number {
46
- return Math.max(lo, Math.min(hi, v));
47
- }