File size: 15,017 Bytes
970fc3e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
import { roomAlignmentAction } from "./workflow.mjs";
import { transparencyActions } from "./scene-appearance.mjs";
const AUTHORING_PAGES = ["global", "main", "createTools", "createLight", "lightSettings", "drawTools", "curveTools", "actorSettings", "sceneTools", "sceneMore", "roomTools", "sceneTransparency", "doorSettings", "brushSettings", "suggestions", "recordingTools"];
const item = (id, label, disabled = false) => ({ id, label, disabled });
function authoringMenu(c, page = "main") {
  if (!c.authoring || c.phase !== "explore") return null;
  if (!AUTHORING_PAGES.includes(page) && page !== "interaction") return null;
  const back = item("authoringBack", "Back"), voice = item("voice", c.actorSelected ? "Describe a basic motion" : c.targetName ? "Describe an edit" : "Describe your idea");
  const target = c.actorName || c.targetName || (c.objectSelection?.length ? `${c.objectSelection.length} objects selected` : "Current scene");
  const door = c.objectSelection?.length === 1 && c.door?.id === c.objectSelection[0];
  const regionTarget = c.regionTarget ?? (c.objectSelection?.length === 1 && !c.actorSelected);
  const result = (title, entries, hint = "X Voice · Y Menu · A Confirm · B Cancel") => ({ title, entries, hint });
  if (c.voiceDraft) return result("Review speech · Not sent", [item("voiceConfirm", "Send · A"), item("voiceRetry", "Speak again · X"), item("voiceCancel", "Cancel speech · B")], "Right stick: pages · A Send · X Retry · B Cancel");
  if (c.recording && c.recording !== "idle") return result(c.recording === "recording" ? "Listening" : "Processing speech", [item("voice", c.recording === "recording" ? "Stop listening" : "Transcribing…", c.recording !== "recording"), item("voiceCancel", "Cancel speech · B")], "Release X to review · B Cancel");
  const p = c.production;
  if (p?.transitioning || p?.saving) return result("Saving changes", [item("cinemaSave", "Please wait…", true)]);
  if (p?.requesting) return result("Camera / Lighting Agent", [item("cinemaCancel", "Cancel request")], "B Cancel");
  if (p?.placing) return result("Choose a placement point", [item("cinemaCancel", "Cancel placement · B")], p.placement?.visible ? "Place at the ring · A Create · B Cancel" : "Point at visible ground · A Create");
  if (p?.dirty) return result(p.tool === "transform" ? "Transform" : p.tool === "curve" ? "Edit curve" : "Preview changes", [item("cinemaSave", "Save · A"), item("cinemaCancel", "Revert · B")], p.tool === "transform" ? "L stick: Move · L grip + stick: Height\nR stick: Rotate · R grip: Grab" : p.tool === "curve" ? "Right grip: pull control points · Release, then A Save" : "A Save · B Discard");
  if (page === "sceneTransparency") return result(`Scene transparency · ${Math.round((1 - (c.roomOpacity ?? 0.5)) * 100)}%`, [...transparencyActions(c.roomOpacity), back], "70% is the most transparent · Saved in this browser");
  if (page === "roomTools" && c.roomMode && c.hasScene) {
    const align = roomAlignmentAction(c), blocked = !!(c.editingBusy || c.job || c.saving || c.finishing || c.applying || c.actorPlacing || c.draft?.active || p?.monitor || p?.playing) || (c.videoState ?? "idle") !== "idle";
    return result(
      "Room / Alignment",
      [align, item("sceneTransparency", "Scene transparency"), item("overview", "Scene overview", blocked), back],
      blocked ? "Finish the current operation before adjusting the room." : !c.roomAlignmentAvailable ? "Build or load a reconstructed room to align it." : !c.xr || c.xrMode !== "immersive-ar" ? "Enter Quest MR to align the room with your real space." : "Align room opens a preview, even when a saved offset exists. · A Confirm · B Back"
    );
  }
  if (p && !c.editing) {
    if (page === "recordingTools") return result("Recording", [item("record", c.videoState === "recording" ? "Stop and save" : "Start recording", !c.productionReady && c.videoState !== "recording"), item("videoFiles", "View recordings"), back]);
    return result(p.monitor ? `Camera monitor · ${p.cameraName || ""}` : p.previewLabel ? `Rehearse · ${p.previewLabel}` : "Explore mode", [
      item(p.monitor ? "cinemaNext" : "cinemaPreview", p.monitor ? "Next camera" : "Camera Agent · View camera"),
      item(p.playing ? "cinemaPause" : "cinemaPlay", p.playing ? "Pause rehearsal · Left grip" : p.activePreviewIds?.length ? "Resume rehearsal · Left grip" : "Rehearse selection · Left grip", !p.canPreview),
      ...p.monitor ? [item("cinemaClose", "Close monitor")] : [item("recordingTools", "Recording and export", !c.productionReady)],
      ...c.script?.agentText || c.script?.userText ? [item("agentReply", "View Agent reply")] : [],
      ...c.roomMode && c.hasScene ? [item("roomTools", "Room / Alignment")] : [],
      item("edit", "Enter edit mode")
    ], p.previewIds?.length ? "Rehearse selection and linked interactions · Right stick click: edit" : "Right stick click: edit and select a target · X Voice");
  }
  if (c.saving || c.finishing || c.applying) return result("Saving", [item("save", "Saving…", true)]);
  if (c.job === "ready") return result("Preview result", [
    item("apply", "Apply changes"),
    item("discard", "Discard preview"),
    ...c.script?.agentText || c.script?.userText ? [item("agentReply", "View Agent reply")] : [],
    ...c.generatedPreview ? [item("motionReplay", "Replay motion")] : c.floodPreview ? [item("floodPreview", "Replay interaction")] : []
  ], "A Apply · B Discard · X Voice");
  if (c.job === "running") return result("Agent working", [item("cancel", "Stop request"), ...c.script?.agentText || c.script?.userText ? [item("agentReply", "View Agent reply")] : []], "B Stop · X Voice");
  if (c.actorPlacing) return result("Place actor", [item("cancelOperation", "Cancel placement")], "Point at ground · A Place · B Cancel");
  if (c.draft?.active || c.objectInteraction?.mode === "draft") {
    const regions = c.draft?.kind === "regions", drawing = !!c.draft?.drawing;
    if (page === "brushSettings") return result("Brush settings", [
      item("draftSmooth", "Smoothing: " + (c.draft?.smoothingLabel || "Standard"), drawing),
      ...c.draft?.mode === "space3d" ? [item("brushNear", "Bring tip closer", drawing), item("brushFar", "Move tip farther", drawing)] : [],
      item("draftClear", "Clear draft", drawing),
      back
    ]);
    return result(regions ? "Draw interaction regions" : c.draft?.mode === "space3d" ? "Spatial path · 3D" : "Ground path · 2D", [
      item("draftApply", regions ? "Finish regions" : "Save curve", drawing || !(regions ? c.draft?.regionCount : c.draft?.pointCount)),
      item("draftUndo", regions ? "Undo last region" : "Undo stroke", !c.draft?.canUndo || drawing),
      ...regions ? [item("regionSurface", c.draft?.forceFloor ? "Draw object sources" : "Draw floor trigger", drawing)] : [],
      ...!regions ? [item("brushSettings", "Brush settings")] : [],
      item("draftCancel", "Cancel drawing")
    ], "Right trigger: draw · Release, then A Save · B Cancel");
  }
  if (p && (page === "global" || page === "main" && !c.objectSelection?.length)) return result("Edit mode", [
    item("createTools", "Create"),
    item("drawTools", "Sketches and paths"),
    ...c.roomMode && c.hasScene ? [item("roomTools", "Room / Alignment")] : [],
    ...c.script?.agentText || c.script?.userText ? [item("agentReply", "View Agent reply")] : [],
    ...c.suggestions?.length ? [item("suggestions", "Agent suggestions")] : [],
    item("cinemaComplete", "Finish setup and explore"),
    item("edit", "Return to explore")
  ], "Right stick: select · A Confirm · B / Y Hide menu");
  if (p && page === "createTools") return result("Create", [item("createLight", "Light"), item("cinemaCamera", "Camera"), item("cinemaBox", "Object"), item("actorPlace", "Actor", !c.actorReady), item("cinemaCreateOther", "Other")], "Choose a category · B Back · Edit after placement");
  if (p && page === "createLight") return result("Light type", [item("cinemaPoint", "Point · All directions"), item("cinemaSpot", "Spot · Directional"), back], "Choose a type, then a ground point · Type stays editable");
  if (p && page === "lightSettings") return result("Light settings", [item("cinemaLightPoint", "Switch to point", c.selectedLightType === "point"), item("cinemaLightSpot", "Switch to spot", c.selectedLightType === "spot"), item("cinemaOther", "Voice: brightness and color"), back], "Review changes · A Save · B Revert");
  if (p?.monitor && c.editing) return result(`Camera monitor · ${p.cameraName || ""}`, [item("cinemaAdjustCamera", "Adjust this camera"), item("cinemaNext", "Next camera"), item("cinemaClose", "Close monitor")], "B Close · After saving, say “preview camera”");
  if (p && c.objectInteraction && c.selectedLightType && !["drawTools", "curveTools"].includes(page)) return result("Light · Interaction", [item("lightSettings", "Type and light settings"), item("cinemaBind", "Bind selected curve", !c.curveCount), item("cinemaPlay", "Rehearse this light", !p.canPreview), item("interactionClose", "Back")]);
  if (p && c.objectInteraction && c.selectedCameraId && !["drawTools", "curveTools"].includes(page)) return result("Camera · Interaction", [item("cinemaPreview", "View this camera"), item("cinemaBind", "Bind selected curve", !c.curveCount), item("cinemaOther", "Voice: camera and movement"), item("interactionClose", "Back")]);
  if (p && c.objectInteraction && page !== "drawTools" && page !== "curveTools") return result("Interaction", [
    item("voice", "Describe an interaction"),
    item("drawTools", "Draw a path or region"),
    item("cinemaBind", "Bind selected curve", !c.curveCount),
    item("cinemaPlay", "Rehearse selection · Left grip", !p.canPreview),
    item("interactionClose", "Back")
  ]);
  if (p && page === "main" && c.objectSelection?.length) return result(target, [item("cinemaTransform", "Transform"), item("objectInteraction", "Interaction"), item("cinemaOther", "Other")], "Right stick: select · A Confirm · Other starts voice");
  if (page === "suggestions") return result("Agent suggestions", [
    ...(c.suggestions || []).map((s, i) => item("suggestion:" + i, s.label)),
    back
  ], "Right stick: select · A Confirm · B Back");
  if (page === "drawTools") return result("Spatial sketches", [
    item("drawFloor", "Draw ground path · 2D"),
    item("drawSpace", "Draw spatial path · 3D"),
    ...regionTarget ? [item("regionDraft", "Draw surface sources and trigger region")] : [],
    ...c.curveCount ? [item("curveTools", `Saved curves · ${c.curveCount}`)] : [],
    back
  ]);
  if (page === "curveTools") return result("Saved curves", [
    item("curveNext", "Next curve · " + c.curveName, !c.curveCount),
    ...p ? [item("cinemaCurveEdit", "Pull control points", !c.curveCount), item("cinemaStraight", "Extreme · Straight endpoints", !c.curveCount), item("cinemaSmooth", "Smooth control points", !c.curveCount)] : [],
    item("curveRemove", "Delete selected curve", !c.curveCount),
    back
  ]);
  if (page === "recordingTools") return result("Recording and export", [item("record", c.videoState === "recording" ? "Stop and save recording" : c.videoRetry ? "Retry recording save" : "Start recording", ["starting", "saving"].includes(c.videoState)), item("videoFiles", "Saved recordings"), back]);
  if (page === "actorSettings") return result(target, [
    item("objectTransform", "Move and rotate"),
    item("actors", "Motion assets and entrance"),
    item("actorClearMotion", "Clear motion"),
    item("actorRemove", "Delete actor"),
    item("sceneTools", "Scene tools"),
    back
  ]);
  if (page === "doorSettings") return result(target, [
    item("doorToggle", c.door?.effect?.open ? "Close door" : "Open door"),
    item("doorHinge", "Switch hinge side"),
    item("doorDirection", "Change opening direction"),
    item("objectTransform", "Move and rotate"),
    item("sceneTools", "Scene tools"),
    back
  ]);
  if (page === "sceneTools") return result("Scene tools", [
    item("actorPlace", "Add actor", !c.actorReady),
    ...c.actorCount ? [item("actorTransport", p?.playing ? "Pause rehearsal" : "Rehearse selection", p ? !p.canPreview : !c.actorMotionCount)] : [],
    item("save", "Save scene"),
    item("sceneMore", "Recording, display and more"),
    back
  ]);
  if (page === "sceneMore") return result("Scene settings", [
    item("recordingTools", c.videoState === "recording" ? "Recording · Stop and save" : "Recording and export"),
    item("display", "Display and alignment"),
    item("overview", "Scene overview"),
    item("authoringHelp", "Controls"),
    item("selectionSettings", "Selection and materials"),
    back
  ]);
  if (c.suggestions?.length) return result("Agent suggestions", [
    ...c.suggestions.map((s, i) => item("suggestion:" + i, s.label)),
    item("dismissSuggestions", "Dismiss suggestions")
  ], "Choose a suggestion to preview · Right stick: select · A Confirm");
  if (c.actorSelected) return result(target, [item("actorAgent", "Generate a basic motion"), item("drawTools", "Draw a motion path"), item("actorPreview", "Rehearse this actor"), item("actorSettings", "Actor settings")]);
  if (door) return result(target, [item("voice", "Describe an interaction"), item("regionDraft", "Draw surface sources and trigger region"), ...c.floodAvailable ? [item("floodPreview", "Rehearse interaction")] : [], item("doorSettings", "Door settings")]);
  return result(target, [
    voice,
    item("drawTools", "Draw a spatial sketch"),
    ...c.objectSelection?.length ? [item("objectTransform", "Move and rotate")] : [item("edit", c.editing ? "Finish selection" : "Select objects")],
    item("sceneTools", "Scene tools")
  ]);
}
function authoringParent(page) {
  return { global: "global", main: "global", createTools: "global", sceneTools: "global", roomTools: "global", sceneTransparency: "roomTools", suggestions: "global", createLight: "createTools", lightSettings: "interaction", curveTools: "drawTools", sceneMore: "sceneTools", recordingTools: "sceneMore" }[page] || "main";
}
function createMenuNavigation() {
  const states = /* @__PURE__ */ new WeakMap();
  return { sample(source, active) {
    const y = source.gamepad?.axes?.[3] ?? 0, old = states.get(source);
    if (!active || source.handedness !== "right") {
      states.set(source, { active: false, ready: false });
      return 0;
    }
    const neutral = Math.abs(y) < 0.3;
    if (!old?.active) {
      states.set(source, { active: true, ready: neutral });
      return 0;
    }
    if (neutral) {
      old.ready = true;
      return 0;
    }
    if (!old.ready || Math.abs(y) < 0.65) return 0;
    old.ready = false;
    return Math.sign(y);
  } };
}
const suggestionScope = (c) => JSON.stringify([
  c.revision,
  c.spatialContext?.spatialKey || c.spatialKey || "",
  [...c.targetIds || c.objectSelection || []].sort(),
  c.curveId || null
]);
export {
  AUTHORING_PAGES,
  authoringMenu,
  authoringParent,
  createMenuNavigation,
  suggestionScope
};