create-aura3d 1.3.1 → 1.3.2

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 (49) hide show
  1. package/dist/cli.js +0 -0
  2. package/dist/index.js +1 -1
  3. package/package.json +2 -2
  4. package/templates/animation-channel/package.json +1 -1
  5. package/templates/animation-studio/dist/episodes/scene/episode-3d.webm +0 -0
  6. package/templates/animation-studio/dist/episodes/scene/frames/action.png +0 -0
  7. package/templates/animation-studio/dist/episodes/scene/frames/dialogue.png +0 -0
  8. package/templates/animation-studio/dist/episodes/scene/frames/final.png +0 -0
  9. package/templates/animation-studio/dist/episodes/scene/frames/first.png +0 -0
  10. package/templates/animation-studio/dist/episodes/scene/frames/mouth-closed.png +0 -0
  11. package/templates/animation-studio/dist/episodes/scene/frames/mouth-open.png +0 -0
  12. package/templates/animation-studio/dist/episodes/scene/render-live-summary.json +2086 -6754
  13. package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/broken.png +0 -0
  14. package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/chefs.png +0 -0
  15. package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/customer.png +0 -0
  16. package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/worker-1.png +0 -0
  17. package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/worker-2.png +0 -0
  18. package/templates/animation-studio/dist/scene/working.document.json +219 -348
  19. package/templates/animation-studio/package-lock.json +1307 -55
  20. package/templates/animation-studio/package.json +11 -5
  21. package/templates/animation-studio/studio/dist/assets/index-i8URxOOt.css +1 -0
  22. package/templates/animation-studio/studio/dist/assets/index-pC9hdZ-B.js +11 -0
  23. package/templates/animation-studio/studio/dist/index.html +19 -0
  24. package/templates/animation-studio/studio/index.html +18 -0
  25. package/templates/animation-studio/studio/src/App.tsx +430 -0
  26. package/templates/animation-studio/studio/src/components/Console.tsx +366 -0
  27. package/templates/animation-studio/studio/src/components/Icon.tsx +90 -0
  28. package/templates/animation-studio/studio/src/components/Inspector.tsx +209 -0
  29. package/templates/animation-studio/studio/src/components/Outliner.tsx +210 -0
  30. package/templates/animation-studio/studio/src/components/Palette.tsx +204 -0
  31. package/templates/animation-studio/studio/src/components/Stage.tsx +339 -0
  32. package/templates/animation-studio/studio/src/components/Timeline.tsx +183 -0
  33. package/templates/animation-studio/studio/src/components/Topbar.tsx +66 -0
  34. package/templates/animation-studio/studio/src/main.tsx +10 -0
  35. package/templates/animation-studio/studio/src/state/backend.ts +108 -0
  36. package/templates/animation-studio/studio/src/state/fidelity.ts +110 -0
  37. package/templates/animation-studio/studio/src/state/mapDocument.ts +419 -0
  38. package/templates/animation-studio/studio/src/state/sceneTool.ts +55 -0
  39. package/templates/animation-studio/studio/src/state/types.ts +165 -0
  40. package/templates/animation-studio/studio/src/state/util.ts +46 -0
  41. package/templates/animation-studio/studio/src/styles.css +528 -0
  42. package/templates/animation-studio/studio/vite.config.ts +175 -0
  43. package/templates/character-controller/package.json +3 -3
  44. package/templates/cinematic-scene/package.json +1 -1
  45. package/templates/episode-builder/package.json +1 -1
  46. package/templates/fighting-game/package.json +1 -1
  47. package/templates/mini-game/package.json +1 -1
  48. package/templates/product-viewer/package.json +1 -1
  49. package/templates/prompt-animation-channel/package.json +1 -1
@@ -0,0 +1,210 @@
1
+ /* Scene Outliner — collapsible Shots / Cast / Sets / Props with counts + visibility. */
2
+ import { useState, type ReactNode } from "react";
3
+ import { Icon } from "./Icon";
4
+ import type { CastMember, CastSource, EpisodeDocument, PropEntity, Selection, SelectionType, SetEntity } from "../state/types";
5
+ import { fidelityLabel, type CharacterFidelity } from "../state/fidelity";
6
+
7
+ interface RowProps {
8
+ name: string;
9
+ sel: boolean;
10
+ onSelect: () => void;
11
+ hidden: boolean;
12
+ onToggle: () => void;
13
+ meta?: string;
14
+ accent?: string;
15
+ glyph?: string;
16
+ icon?: string;
17
+ /** Provenance badge (Phase E3): authored-fallback vs catalog-resolved vs user-uploaded. */
18
+ source?: CastSource;
19
+ sourceLabel?: string;
20
+ /** M7 — fidelity tier badge (A/B/C); C renders as "Previz". */
21
+ fidelity?: CharacterFidelity;
22
+ }
23
+
24
+ /** Short, honest badge text + class per cast provenance class (E3). */
25
+ const SOURCE_BADGE: Record<CastSource, { text: string; cls: string }> = {
26
+ "authored-fallback": { text: "fallback", cls: "src-fallback" },
27
+ "catalog-resolved": { text: "catalog", cls: "src-catalog" },
28
+ "user-uploaded": { text: "uploaded", cls: "src-uploaded" }
29
+ };
30
+
31
+ function Row({ name, sel, onSelect, hidden, onToggle, meta, accent, glyph, icon, source, sourceLabel, fidelity }: RowProps) {
32
+ const badge = source ? SOURCE_BADGE[source] : undefined;
33
+ return (
34
+ <div className={"ol-item" + (sel ? " sel" : "")} onClick={onSelect}>
35
+ {glyph ? (
36
+ <div className="av" style={{ background: accent }}>
37
+ {glyph}
38
+ </div>
39
+ ) : (
40
+ <div className="ic">
41
+ <Icon name={icon || "cube"} size={13} />
42
+ </div>
43
+ )}
44
+ <span className="nm">{name}</span>
45
+ {fidelity && (
46
+ <span
47
+ className={"fid-badge fid-" + fidelity.grade.toLowerCase() + (fidelity.previz ? " fid-previz" : "")}
48
+ title={fidelity.reason}
49
+ data-grade={fidelity.grade}
50
+ data-previz={fidelity.previz ? "true" : "false"}
51
+ >
52
+ {fidelity.previz ? "Previz" : fidelityLabel(fidelity.grade)}
53
+ </span>
54
+ )}
55
+ {badge && (
56
+ <span className={"src-badge " + badge.cls} title={sourceLabel ?? badge.text} data-source={source}>
57
+ {badge.text}
58
+ </span>
59
+ )}
60
+ {meta && <span className="meta">{meta}</span>}
61
+ <button
62
+ className={"vis" + (hidden ? " off" : "")}
63
+ title={hidden ? "Show" : "Hide"}
64
+ onClick={(e) => {
65
+ e.stopPropagation();
66
+ onToggle();
67
+ }}
68
+ >
69
+ <Icon name={hidden ? "eyeOff" : "eye"} size={14} />
70
+ </button>
71
+ </div>
72
+ );
73
+ }
74
+
75
+ function Group({
76
+ title,
77
+ count,
78
+ children,
79
+ onAdd
80
+ }: {
81
+ title: string;
82
+ count: number;
83
+ children: ReactNode;
84
+ onAdd?: () => void;
85
+ }) {
86
+ const [open, setOpen] = useState(true);
87
+ return (
88
+ <div className="ol-group">
89
+ <div className={"ol-gh" + (open ? "" : " closed")} onClick={() => setOpen(!open)}>
90
+ <span className="tw">
91
+ <Icon name="chevD" size={13} />
92
+ </span>
93
+ {title}
94
+ <span className="cnt">{count}</span>
95
+ {onAdd && (
96
+ <button
97
+ className="add"
98
+ onClick={(e) => {
99
+ e.stopPropagation();
100
+ onAdd();
101
+ }}
102
+ >
103
+ <Icon name="plus" size={13} />
104
+ </button>
105
+ )}
106
+ </div>
107
+ {open && <div>{children}</div>}
108
+ </div>
109
+ );
110
+ }
111
+
112
+ export interface OutlinerProps {
113
+ data: EpisodeDocument;
114
+ sel: Selection | null;
115
+ onSelect: (type: SelectionType, id: string) => void;
116
+ hidden: Set<string>;
117
+ onToggle: (id: string) => void;
118
+ onAddCast: () => void;
119
+ onOpenPalette: () => void;
120
+ }
121
+
122
+ export function Outliner({ data, sel, onSelect, hidden, onToggle, onAddCast, onOpenPalette }: OutlinerProps) {
123
+ return (
124
+ <section className="panel col">
125
+ <div className="ol-search" onClick={onOpenPalette} style={{ cursor: "text" }}>
126
+ <Icon name="search" size={14} />
127
+ <input placeholder="Search scene…" readOnly style={{ cursor: "text" }} />
128
+ <kbd>⌘K</kbd>
129
+ </div>
130
+ <div className="ol-scroll">
131
+ {/* M7 — scene fidelity tier. A grade-C scene is labeled "previz" and never sold as finished. */}
132
+ <div
133
+ className={"scene-fidelity fid-" + data.fidelity.grade.toLowerCase() + (data.fidelity.previz ? " fid-previz" : "")}
134
+ title={data.fidelity.reason}
135
+ data-grade={data.fidelity.grade}
136
+ data-previz={data.fidelity.previz ? "true" : "false"}
137
+ >
138
+ <span className="fid-tier">{data.fidelity.previz ? "Previz" : fidelityLabel(data.fidelity.grade)}</span>
139
+ <span className="fid-note">
140
+ {data.fidelity.previz ? "preview-quality — not a finished render" : "scene fidelity"}
141
+ </span>
142
+ </div>
143
+ <Group title="Shots" count={data.shots.length}>
144
+ {data.shots.map((s) => (
145
+ <div
146
+ key={s.id}
147
+ className={"ol-item" + (sel?.type === "shot" && sel.id === s.id ? " sel" : "")}
148
+ onClick={() => onSelect("shot", s.id)}
149
+ >
150
+ <div className="ic" style={{ background: s.color + "33", color: s.color }}>
151
+ <Icon name="film" size={13} />
152
+ </div>
153
+ <span className="nm">{s.name}</span>
154
+ <span className="meta">{s.dur + "s"}</span>
155
+ </div>
156
+ ))}
157
+ </Group>
158
+
159
+ <Group title="Cast" count={data.cast.length} onAdd={onAddCast}>
160
+ {data.cast.map((c: CastMember) => (
161
+ <Row
162
+ key={c.id}
163
+ name={c.name}
164
+ glyph={c.glyph}
165
+ accent={c.color}
166
+ meta={c.lines ? c.lines + " lines" : "—"}
167
+ source={c.source}
168
+ sourceLabel={c.sourceLabel}
169
+ fidelity={c.fidelity}
170
+ sel={sel?.type === "cast" && sel.id === c.id}
171
+ onSelect={() => onSelect("cast", c.id)}
172
+ hidden={hidden.has(c.id)}
173
+ onToggle={() => onToggle(c.id)}
174
+ />
175
+ ))}
176
+ </Group>
177
+
178
+ <Group title="Sets" count={data.sets.length}>
179
+ {data.sets.map((s: SetEntity) => (
180
+ <Row
181
+ key={s.id}
182
+ name={s.name}
183
+ icon={s.icon}
184
+ meta={s.meta}
185
+ sel={sel?.type === "set" && sel.id === s.id}
186
+ onSelect={() => onSelect("set", s.id)}
187
+ hidden={hidden.has(s.id)}
188
+ onToggle={() => onToggle(s.id)}
189
+ />
190
+ ))}
191
+ </Group>
192
+
193
+ <Group title="Props" count={data.props.length}>
194
+ {data.props.map((p: PropEntity) => (
195
+ <Row
196
+ key={p.id}
197
+ name={p.name}
198
+ icon={p.icon}
199
+ meta={p.meta}
200
+ sel={sel?.type === "prop" && sel.id === p.id}
201
+ onSelect={() => onSelect("prop", p.id)}
202
+ hidden={hidden.has(p.id)}
203
+ onToggle={() => onToggle(p.id)}
204
+ />
205
+ ))}
206
+ </Group>
207
+ </div>
208
+ </section>
209
+ );
210
+ }
@@ -0,0 +1,204 @@
1
+ /* Command palette (⌘K) — jump to shots/cast, run Scene-Tool commands, switch views. */
2
+ import { useEffect, useMemo, useRef, useState, type MutableRefObject } from "react";
3
+ import { Icon } from "./Icon";
4
+ import { fmt } from "../state/util";
5
+ import type { ConsoleApi } from "./Console";
6
+ import type { EpisodeDocument, SelectionType, ViewMode } from "../state/types";
7
+
8
+ interface PaletteItem {
9
+ g: string;
10
+ nm: string;
11
+ ds?: string;
12
+ ic?: string;
13
+ av?: string;
14
+ glyph?: string;
15
+ tag?: string;
16
+ mono?: boolean;
17
+ kw?: string;
18
+ run: () => void;
19
+ }
20
+
21
+ export interface PaletteProps {
22
+ open: boolean;
23
+ setOpen: (o: boolean) => void;
24
+ data: EpisodeDocument;
25
+ api: MutableRefObject<ConsoleApi>;
26
+ onJumpShot: (id: string) => void;
27
+ onSelectEntity: (type: SelectionType, id: string) => void;
28
+ onRender: (scope: "shot" | "sequence") => void;
29
+ onView: (m: ViewMode) => void;
30
+ }
31
+
32
+ export function Palette({ open, setOpen, data, api, onJumpShot, onSelectEntity, onRender, onView }: PaletteProps) {
33
+ const [q, setQ] = useState("");
34
+ const [idx, setIdx] = useState(0);
35
+ const inputRef = useRef<HTMLInputElement>(null);
36
+ const listRef = useRef<HTMLDivElement>(null);
37
+
38
+ useEffect(() => {
39
+ if (open) {
40
+ setQ("");
41
+ setIdx(0);
42
+ window.setTimeout(() => inputRef.current?.focus(), 30);
43
+ }
44
+ }, [open]);
45
+
46
+ const items = useMemo<PaletteItem[]>(() => {
47
+ const out: PaletteItem[] = [];
48
+ out.push({ g: "Actions", nm: "Render full sequence", ds: "Low-fi preview of all shots", ic: "play2", kw: "render export preview", run: () => onRender("sequence") });
49
+ out.push({ g: "Actions", nm: "Render current shot", ds: "Preview just the active shot", ic: "film", kw: "render shot preview", run: () => onRender("shot") });
50
+ (["Render", "Wireframe", "Storyboard"] as ViewMode[]).forEach((m) =>
51
+ out.push({
52
+ g: "Actions",
53
+ nm: "View: " + m,
54
+ ds: "Switch viewport mode",
55
+ ic: m === "Render" ? "film" : m === "Wireframe" ? "cube" : "grid",
56
+ kw: "view mode " + m,
57
+ run: () => onView(m)
58
+ })
59
+ );
60
+ data.shots.forEach((s) =>
61
+ out.push({ g: "Go to shot", nm: s.name, ds: s.cam, ic: "film", tag: fmt(s.start), kw: "shot " + s.name + " " + s.cam, run: () => onJumpShot(s.id) })
62
+ );
63
+ data.cast.forEach((c) =>
64
+ out.push({ g: "Cast", av: c.color, glyph: c.glyph, nm: c.name, ds: c.kind, kw: "cast character " + c.name, run: () => onSelectEntity("cast", c.id) })
65
+ );
66
+ (
67
+ [
68
+ ["cast add Pip", "Cast a new character"],
69
+ ["shot retime --id shot-2 --duration 30", "Retime a shot + ripple"],
70
+ ["cam orbit", "Set active shot camera"],
71
+ ["fx add rim light", "Add an effect to the timeline"],
72
+ ["set station --hdr dawn", "Re-light the active set"],
73
+ ["shot add --after shot-2", "Block a new shot"]
74
+ ] as Array<[string, string]>
75
+ ).forEach(([cmd, ds]) =>
76
+ out.push({ g: "Run command", nm: cmd, ds, mono: true, ic: "bolt", kw: "command " + cmd, run: () => api.current.run?.(cmd, true) })
77
+ );
78
+ return out;
79
+ }, [data, api, onJumpShot, onRender, onSelectEntity, onView]);
80
+
81
+ const filtered = useMemo(() => {
82
+ const s = q.trim().toLowerCase();
83
+ if (!s) return items;
84
+ return items.filter((it) => (it.nm + " " + (it.ds || "") + " " + (it.kw || "")).toLowerCase().includes(s));
85
+ }, [q, items]);
86
+
87
+ useEffect(() => {
88
+ setIdx(0);
89
+ }, [q]);
90
+ useEffect(() => {
91
+ const el = listRef.current;
92
+ if (!el) return;
93
+ const on = el.querySelector(".cmdk-item.on");
94
+ if (on) on.scrollIntoView({ block: "nearest" });
95
+ }, [idx]);
96
+
97
+ if (!open) return null;
98
+
99
+ const exec = (it?: PaletteItem) => {
100
+ if (!it) return;
101
+ setOpen(false);
102
+ window.setTimeout(() => it.run(), 10);
103
+ };
104
+ const onKey = (e: React.KeyboardEvent) => {
105
+ if (e.key === "Escape") {
106
+ e.preventDefault();
107
+ setOpen(false);
108
+ } else if (e.key === "ArrowDown") {
109
+ e.preventDefault();
110
+ setIdx((i) => Math.min(filtered.length - 1, i + 1));
111
+ } else if (e.key === "ArrowUp") {
112
+ e.preventDefault();
113
+ setIdx((i) => Math.max(0, i - 1));
114
+ } else if (e.key === "Enter") {
115
+ e.preventDefault();
116
+ exec(filtered[idx]);
117
+ }
118
+ };
119
+
120
+ // group while keeping a flat index for keyboard navigation
121
+ let flat = -1;
122
+ let lastG: string | null = null;
123
+ const rows: Array<{ grp?: string; key: string; it?: PaletteItem; myIdx?: number }> = [];
124
+ filtered.forEach((it) => {
125
+ if (it.g !== lastG) {
126
+ rows.push({ grp: it.g, key: "g" + it.g });
127
+ lastG = it.g;
128
+ }
129
+ flat++;
130
+ rows.push({ it, myIdx: flat, key: it.g + it.nm });
131
+ });
132
+
133
+ return (
134
+ <div className="cmdk-ov" onMouseDown={() => setOpen(false)}>
135
+ <div className="cmdk" onMouseDown={(e) => e.stopPropagation()}>
136
+ <div className="cmdk-in">
137
+ <span className="si">
138
+ <Icon name="search" size={18} />
139
+ </span>
140
+ <input
141
+ ref={inputRef}
142
+ value={q}
143
+ placeholder="Search shots, cast, commands…"
144
+ onChange={(e) => setQ(e.target.value)}
145
+ onKeyDown={onKey}
146
+ />
147
+ <span className="esc">ESC</span>
148
+ </div>
149
+ <div className="cmdk-list" ref={listRef}>
150
+ {filtered.length === 0 ? (
151
+ <div className="cmdk-empty">{"No matches for “" + q + "”"}</div>
152
+ ) : (
153
+ rows.map((r) =>
154
+ r.grp ? (
155
+ <div className="cmdk-grp" key={r.key}>
156
+ {r.grp}
157
+ </div>
158
+ ) : (
159
+ <div
160
+ key={r.key}
161
+ className={"cmdk-item" + (r.myIdx === idx ? " on" : "")}
162
+ onMouseEnter={() => setIdx(r.myIdx!)}
163
+ onClick={() => exec(r.it)}
164
+ >
165
+ {r.it!.av ? (
166
+ <div className="av" style={{ background: r.it!.av }}>
167
+ {r.it!.glyph}
168
+ </div>
169
+ ) : (
170
+ <div className="ic">
171
+ <Icon name={r.it!.ic || "bolt"} size={15} />
172
+ </div>
173
+ )}
174
+ <div className="tx">
175
+ <div className="nm">{r.it!.nm}</div>
176
+ {r.it!.ds && <div className={"ds" + (r.it!.mono ? " mono" : "")}>{r.it!.ds}</div>}
177
+ </div>
178
+ {r.it!.tag ? (
179
+ <span className="tag">{r.it!.tag}</span>
180
+ ) : (
181
+ <span className="ent">
182
+ <Icon name="send" size={14} />
183
+ </span>
184
+ )}
185
+ </div>
186
+ )
187
+ )
188
+ )}
189
+ </div>
190
+ <div className="cmdk-foot">
191
+ <span>
192
+ <span className="kk">↑↓</span>navigate
193
+ </span>
194
+ <span>
195
+ <span className="kk">↵</span>run
196
+ </span>
197
+ <span>
198
+ <span className="kk">⌘K</span>toggle
199
+ </span>
200
+ </div>
201
+ </div>
202
+ </div>
203
+ );
204
+ }