@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.
- package/README.md +31 -93
- package/dist/cli/index.js +49 -115
- package/dist-app/assets/EffectComposer-lekj9UUq.js +1 -0
- package/dist-app/assets/GLTFLoader-C2lUhzim.js +1 -0
- package/dist-app/assets/OutputPass-DZtd342R.js +72 -0
- package/dist-app/assets/Pass-BP3b3hlf.js +1 -0
- package/dist-app/assets/RenderPass-BjVchlYg.js +1 -0
- package/dist-app/assets/ShaderPass-DMXLcV9X.js +24 -0
- package/dist-app/assets/SkeletonUtils-Cxy_owx9.js +1 -0
- package/dist-app/assets/index-AxOpglZ-.css +1 -0
- package/dist-app/assets/index-D-Iq7tix.js +4135 -0
- package/dist-app/assets/inter-CaiW00F8.js +1 -0
- package/dist-app/assets/registry-BOM35tSr.js +1433 -0
- package/dist-app/assets/worker-_Lh8Vt-i.js +1270 -0
- package/dist-app/examples/breakout/assets/geometry/ball.geometry.xml +1 -0
- package/dist-app/examples/breakout/assets/geometry/brick.geometry.xml +1 -0
- package/dist-app/examples/breakout/assets/geometry/floor-sensor.geometry.xml +1 -0
- package/dist-app/examples/breakout/assets/geometry/paddle.geometry.xml +1 -0
- package/dist-app/examples/breakout/assets/geometry/wall-side.geometry.xml +1 -0
- package/dist-app/examples/breakout/assets/geometry/wall-top.geometry.xml +1 -0
- package/dist-app/examples/breakout/assets/materials/ball.material.xml +1 -0
- package/dist-app/examples/breakout/assets/materials/brick-firm.material.xml +1 -0
- package/dist-app/examples/breakout/assets/materials/brick-hard.material.xml +1 -0
- package/dist-app/examples/breakout/assets/materials/brick-soft.material.xml +1 -0
- package/dist-app/examples/breakout/assets/materials/paddle.material.xml +10 -0
- package/dist-app/examples/breakout/assets/materials/wall.material.xml +1 -0
- package/dist-app/examples/breakout/assets/render/neon.render.xml +14 -0
- package/dist-app/examples/breakout/assets/scenes/arena.prefab.xml +68 -0
- package/dist-app/examples/breakout/assets/scenes/brick.prefab.xml +15 -0
- package/dist-app/examples/breakout/assets/scenes/hud.scene.xml +56 -0
- package/dist-app/examples/breakout/assets/scenes/level-1.scene.xml +36 -0
- package/dist-app/examples/breakout/assets/scenes/level-2.scene.xml +36 -0
- package/dist-app/examples/breakout/assets/scenes/level-3.scene.xml +36 -0
- package/dist-app/examples/castle/assets/geometry/capsule.geometry.xml +3 -0
- package/dist-app/examples/castle/assets/geometry/cone.geometry.xml +2 -0
- package/dist-app/examples/castle/assets/geometry/cube.geometry.xml +3 -0
- package/dist-app/examples/castle/assets/geometry/cylinder.geometry.xml +3 -0
- package/dist-app/examples/castle/assets/geometry/plane.geometry.xml +4 -0
- package/dist-app/examples/castle/assets/geometry/quad.geometry.xml +3 -0
- package/dist-app/examples/castle/assets/geometry/sphere.geometry.xml +2 -0
- package/dist-app/examples/castle/assets/materials/grass.material.xml +1 -0
- package/dist-app/examples/castle/assets/materials/roof.material.xml +1 -0
- package/dist-app/examples/castle/assets/materials/stone.material.xml +1 -0
- package/dist-app/examples/castle/assets/materials/wood.material.xml +1 -0
- package/dist-app/examples/castle/assets/scenes/main.scene.xml +25 -0
- package/dist-app/examples/collector/assets/geometry/capsule.geometry.xml +3 -0
- package/dist-app/examples/collector/assets/geometry/cone.geometry.xml +2 -0
- package/dist-app/examples/collector/assets/geometry/cube.geometry.xml +3 -0
- package/dist-app/examples/collector/assets/geometry/cylinder.geometry.xml +3 -0
- package/dist-app/examples/collector/assets/geometry/plane.geometry.xml +4 -0
- package/dist-app/examples/collector/assets/geometry/quad.geometry.xml +3 -0
- package/dist-app/examples/collector/assets/geometry/sphere.geometry.xml +2 -0
- package/dist-app/examples/collector/assets/materials/crate.material.xml +1 -0
- package/dist-app/examples/collector/assets/materials/depot.material.xml +1 -0
- package/dist-app/examples/collector/assets/materials/ground.material.xml +1 -0
- package/dist-app/examples/collector/assets/materials/player.material.xml +1 -0
- package/dist-app/examples/collector/assets/scenes/crate.prefab.xml +9 -0
- package/dist-app/examples/collector/assets/scenes/main.scene.xml +83 -0
- package/dist-app/examples/playable/assets/geometry/capsule.geometry.xml +3 -0
- package/dist-app/examples/playable/assets/geometry/cone.geometry.xml +2 -0
- package/dist-app/examples/playable/assets/geometry/cube.geometry.xml +3 -0
- package/dist-app/examples/playable/assets/geometry/cylinder.geometry.xml +3 -0
- package/dist-app/examples/playable/assets/geometry/plane.geometry.xml +4 -0
- package/dist-app/examples/playable/assets/geometry/quad.geometry.xml +3 -0
- package/dist-app/examples/playable/assets/geometry/sphere.geometry.xml +2 -0
- package/dist-app/examples/playable/assets/materials/ground.material.xml +1 -0
- package/dist-app/examples/playable/assets/materials/target.material.xml +2 -0
- package/dist-app/examples/playable/assets/scenes/main.scene.xml +53 -0
- package/dist-app/examples/playable/assets/scenes/target.prefab.xml +9 -0
- package/dist-app/examples.json +68 -0
- package/{app → dist-app}/index.html +2 -2
- package/dist-app/model.json +1 -0
- package/package.json +26 -30
- 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/main.ts +0 -543
- package/app/meta.ts +0 -24
- package/app/picking.ts +0 -47
- package/app/session.ts +0 -572
- 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/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 -437
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
-
import { X } from 'lucide-react';
|
|
3
|
-
import { getDeps, useUI } from './store.js';
|
|
4
|
-
import { cx } from './cx.js';
|
|
5
|
-
import styles from './ui.module.css';
|
|
6
|
-
|
|
7
|
-
/** One offerable tag with the registry description shown beside it. */
|
|
8
|
-
interface Candidate {
|
|
9
|
-
tag: string;
|
|
10
|
-
description: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* The "add component" picker: a centred modal with a case-insensitive filter over name and
|
|
15
|
-
* description.
|
|
16
|
-
*
|
|
17
|
-
* What it offers is every component class the project declares, read from `ProjectModel` — which
|
|
18
|
-
* is read from the source. v1 asked a runtime registry that decorators populated, so a component
|
|
19
|
-
* only appeared here if it had been registered; a class is the registration now.
|
|
20
|
-
*
|
|
21
|
-
* There is no attachment half any more: a geometry and a material are assets a mesh points at,
|
|
22
|
-
* so they are attributes rather than members to add.
|
|
23
|
-
*/
|
|
24
|
-
export function AddMemberPopup(): React.JSX.Element | null {
|
|
25
|
-
const adding = useUI((s) => s.adding);
|
|
26
|
-
const closeAdd = useUI((s) => s.closeAdd);
|
|
27
|
-
useUI((s) => s.rev);
|
|
28
|
-
const [query, setQuery] = useState('');
|
|
29
|
-
const input = useRef<HTMLInputElement>(null);
|
|
30
|
-
|
|
31
|
-
// Reopening for a different target starts from an empty filter.
|
|
32
|
-
useEffect(() => {
|
|
33
|
-
if (adding) {
|
|
34
|
-
setQuery('');
|
|
35
|
-
input.current?.focus();
|
|
36
|
-
}
|
|
37
|
-
}, [adding]);
|
|
38
|
-
|
|
39
|
-
const candidates = useMemo(() => (adding ? offerings() : []), [adding]);
|
|
40
|
-
if (!adding) return null;
|
|
41
|
-
|
|
42
|
-
const needle = query.trim().toLowerCase();
|
|
43
|
-
const matches = needle
|
|
44
|
-
? candidates.filter((c) => c.tag.toLowerCase().includes(needle) || c.description.toLowerCase().includes(needle))
|
|
45
|
-
: candidates;
|
|
46
|
-
|
|
47
|
-
const { getDoc } = getDeps();
|
|
48
|
-
const add = (tag: string): void => {
|
|
49
|
-
getDoc()?.addComponent(adding, tag);
|
|
50
|
-
closeAdd();
|
|
51
|
-
useUI.getState().bump(); // re-read the inspector against the new member
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const label = 'Add component';
|
|
55
|
-
return (
|
|
56
|
-
// Clicking the backdrop dismisses; clicks inside must not bubble to it.
|
|
57
|
-
<div className={styles.modalBackdrop} onClick={closeAdd}>
|
|
58
|
-
<div className={styles.modal} onClick={(e) => e.stopPropagation()}>
|
|
59
|
-
<div className={styles.modalHead}>
|
|
60
|
-
<span className={styles.title}>{label}</span>
|
|
61
|
-
<button type="button" className={styles.clearBtn} title="Close" aria-label="Close" onClick={closeAdd}>
|
|
62
|
-
<X />
|
|
63
|
-
</button>
|
|
64
|
-
</div>
|
|
65
|
-
<input
|
|
66
|
-
ref={input}
|
|
67
|
-
type="text"
|
|
68
|
-
className={cx(styles.input, styles.modalFilter)}
|
|
69
|
-
placeholder="Filter…"
|
|
70
|
-
value={query}
|
|
71
|
-
autoFocus
|
|
72
|
-
onChange={(e) => setQuery(e.target.value)}
|
|
73
|
-
onKeyDown={(e) => {
|
|
74
|
-
if (e.key === 'Escape') closeAdd();
|
|
75
|
-
// Enter commits the only remaining match — the fast path once you've typed.
|
|
76
|
-
if (e.key === 'Enter' && matches.length === 1) add(matches[0]!.tag);
|
|
77
|
-
}}
|
|
78
|
-
/>
|
|
79
|
-
<div className={styles.modalList}>
|
|
80
|
-
{matches.length === 0 ? (
|
|
81
|
-
<div className={styles.empty}>
|
|
82
|
-
{candidates.length === 0
|
|
83
|
-
? 'This project declares no components yet.'
|
|
84
|
-
: 'Nothing matches that filter.'}
|
|
85
|
-
</div>
|
|
86
|
-
) : (
|
|
87
|
-
matches.map((c) => (
|
|
88
|
-
<button key={c.tag} type="button" className={styles.modalItem} onClick={() => add(c.tag)}>
|
|
89
|
-
<span className={styles.modalItemTag}>{c.tag}</span>
|
|
90
|
-
<span className={styles.modalItemDesc}>{c.description}</span>
|
|
91
|
-
</button>
|
|
92
|
-
))
|
|
93
|
-
)}
|
|
94
|
-
</div>
|
|
95
|
-
</div>
|
|
96
|
-
</div>
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/** What this node may still receive, straight from the registry. */
|
|
101
|
-
/**
|
|
102
|
-
* Every component the project declares.
|
|
103
|
-
*
|
|
104
|
-
* A component with a `requiresNode` — `class Flicker extends Component<PointLight>` — is only
|
|
105
|
-
* offered on a node it can actually work on. Almost none declare one: like a MonoBehaviour on a
|
|
106
|
-
* GameObject, a component goes on anything, and the constraint exists for the rare one that
|
|
107
|
-
* reads a host-specific member.
|
|
108
|
-
*/
|
|
109
|
-
function offerings(): Candidate[] {
|
|
110
|
-
const model = getDeps().getModel();
|
|
111
|
-
return (model?.components ?? [])
|
|
112
|
-
.map((c) => ({ tag: c.name, description: c.description ?? '' }))
|
|
113
|
-
.sort((a, b) => a.tag.localeCompare(b.tag));
|
|
114
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { ListTree } from 'lucide-react';
|
|
2
|
-
import { useUI } from './store.js';
|
|
3
|
-
import styles from './ui.module.css';
|
|
4
|
-
|
|
5
|
-
/** The small bottom-right button that opens the node list. Hidden while it's open. */
|
|
6
|
-
export function BottomLauncher(): React.JSX.Element | null {
|
|
7
|
-
const nodesOpen = useUI((s) => s.nodesOpen);
|
|
8
|
-
const openNodes = useUI((s) => s.openNodes);
|
|
9
|
-
if (nodesOpen) return null;
|
|
10
|
-
return (
|
|
11
|
-
<button className={styles.launcher} title="Node list" aria-label="Node list" onClick={openNodes}>
|
|
12
|
-
<ListTree />
|
|
13
|
-
</button>
|
|
14
|
-
);
|
|
15
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { TopBar } from './TopBar.js';
|
|
2
|
-
import { BottomLauncher } from './BottomLauncher.js';
|
|
3
|
-
import { NodeListSheet } from './NodeListSheet.js';
|
|
4
|
-
import { InspectorSheet } from './InspectorSheet.js';
|
|
5
|
-
import { AddMemberPopup } from './AddMemberPopup.js';
|
|
6
|
-
import { FilePicker } from './FilePicker.js';
|
|
7
|
-
|
|
8
|
-
/** The whole editor chrome: a responsive top bar, a bottom launcher, the two bottom sheets
|
|
9
|
-
* (node list + inspector), and the open dialog that stands in for all of it until a document
|
|
10
|
-
* is open. */
|
|
11
|
-
export function EditorUI(): React.JSX.Element {
|
|
12
|
-
return (
|
|
13
|
-
<>
|
|
14
|
-
<TopBar />
|
|
15
|
-
<BottomLauncher />
|
|
16
|
-
<NodeListSheet />
|
|
17
|
-
<InspectorSheet />
|
|
18
|
-
<AddMemberPopup />
|
|
19
|
-
<FilePicker />
|
|
20
|
-
</>
|
|
21
|
-
);
|
|
22
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
2
|
-
import { getDeps, useUI } from './store.js';
|
|
3
|
-
import styles from './ui.module.css';
|
|
4
|
-
|
|
5
|
-
/** A document the picker offers — the scenes and prefabs the server found in the project. */
|
|
6
|
-
interface Pickable {
|
|
7
|
-
kind: string;
|
|
8
|
-
/** Project-relative path, which is what `open` takes. */
|
|
9
|
-
path: string;
|
|
10
|
-
/** File name without its kind suffix — `main` from `main.scene.xml`. */
|
|
11
|
-
stem: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/** Only these two are a thing to *open*: the rest are referenced by path from inside one. */
|
|
15
|
-
const OPENABLE = new Set(['scene', 'prefab']);
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* The start-up open dialog, shown whenever no document is open — which is what a bare
|
|
19
|
-
* `scenoco-editor` means.
|
|
20
|
-
*
|
|
21
|
-
* Opening the first scene it happened to find instead would be a guess, and a wrong one in any
|
|
22
|
-
* project with more than one scene: the editor would come up on something the person has to
|
|
23
|
-
* navigate away from, with no way to see what else there is.
|
|
24
|
-
*/
|
|
25
|
-
export function FilePicker(): React.JSX.Element | null {
|
|
26
|
-
useUI((s) => s.rev); // an open (or a failed one) changes whether this shows at all
|
|
27
|
-
const { getDoc, listDocuments, openDocument } = getDeps();
|
|
28
|
-
const [files, setFiles] = useState<Pickable[] | null>(null);
|
|
29
|
-
const [failed, setFailed] = useState<string | null>(null);
|
|
30
|
-
const open = getDoc() !== undefined;
|
|
31
|
-
|
|
32
|
-
useEffect(() => {
|
|
33
|
-
if (open) return;
|
|
34
|
-
let live = true;
|
|
35
|
-
void listDocuments().then(
|
|
36
|
-
(all) => live && setFiles(all.filter((f) => OPENABLE.has(f.kind))),
|
|
37
|
-
() => live && setFiles([]),
|
|
38
|
-
);
|
|
39
|
-
return () => {
|
|
40
|
-
live = false;
|
|
41
|
-
};
|
|
42
|
-
}, [open, listDocuments]);
|
|
43
|
-
|
|
44
|
-
if (open) return null;
|
|
45
|
-
|
|
46
|
-
const pick = (file: Pickable): void => {
|
|
47
|
-
setFailed(null);
|
|
48
|
-
void openDocument(file.path).catch((error: unknown) =>
|
|
49
|
-
setFailed(error instanceof Error ? error.message : String(error)),
|
|
50
|
-
);
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<div className={styles.modalBackdrop}>
|
|
55
|
-
<div className={styles.modal}>
|
|
56
|
-
<div className={styles.modalHead}>
|
|
57
|
-
<span className={styles.title}>Open a document</span>
|
|
58
|
-
</div>
|
|
59
|
-
<div className={styles.modalList}>
|
|
60
|
-
{files === null ? (
|
|
61
|
-
<div className={styles.empty}>Scanning the project…</div>
|
|
62
|
-
) : files.length === 0 ? (
|
|
63
|
-
<div className={styles.empty}>No .scene.xml or .prefab.xml documents in this project.</div>
|
|
64
|
-
) : (
|
|
65
|
-
files.map((file) => (
|
|
66
|
-
<button key={file.path} type="button" className={styles.modalItem} onClick={() => pick(file)}>
|
|
67
|
-
<span className={styles.modalItemTag}>
|
|
68
|
-
{file.stem}
|
|
69
|
-
<span className={styles.pickKind}>{file.kind}</span>
|
|
70
|
-
</span>
|
|
71
|
-
<span className={styles.modalItemDesc}>{file.path}</span>
|
|
72
|
-
</button>
|
|
73
|
-
))
|
|
74
|
-
)}
|
|
75
|
-
{failed !== null && <div className={styles.empty}>{failed}</div>}
|
|
76
|
-
</div>
|
|
77
|
-
</div>
|
|
78
|
-
</div>
|
|
79
|
-
);
|
|
80
|
-
}
|
package/app/ui-react/HeadBtn.tsx
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
|
-
import styles from './ui.module.css';
|
|
3
|
-
|
|
4
|
-
/** An icon button in a sheet header (actions row / close). */
|
|
5
|
-
export function HeadBtn({
|
|
6
|
-
label,
|
|
7
|
-
onClick,
|
|
8
|
-
disabled,
|
|
9
|
-
children,
|
|
10
|
-
}: {
|
|
11
|
-
label: string;
|
|
12
|
-
onClick: () => void;
|
|
13
|
-
disabled?: boolean;
|
|
14
|
-
children: ReactNode;
|
|
15
|
-
}): React.JSX.Element {
|
|
16
|
-
return (
|
|
17
|
-
<button className={styles.headBtn} title={label} aria-label={label} onClick={onClick} disabled={disabled}>
|
|
18
|
-
{children}
|
|
19
|
-
</button>
|
|
20
|
-
);
|
|
21
|
-
}
|