@adminide-stack/yantra-mobile 12.0.121 → 12.0.123-alpha.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/lib/features/apps/appArt.js +1 -1
- package/lib/features/apps/appArt.js.map +1 -1
- package/lib/features/apps/mobileAppCatalog.js +15 -11
- package/lib/features/apps/mobileAppCatalog.js.map +1 -1
- package/lib/features/canvas/CanvasAgentChatPane.js +82 -20
- package/lib/features/canvas/CanvasAgentChatPane.js.map +1 -1
- package/lib/features/canvas/CanvasBoardsGrid.js +291 -100
- package/lib/features/canvas/CanvasBoardsGrid.js.map +1 -1
- package/lib/features/canvas/CanvasEditToolsPanel.js +105 -12
- package/lib/features/canvas/CanvasEditToolsPanel.js.map +1 -1
- package/lib/features/canvas/CanvasExperience.js +266 -68
- package/lib/features/canvas/CanvasExperience.js.map +1 -1
- package/lib/features/canvas/CanvasGeneratePanel.js +204 -66
- package/lib/features/canvas/CanvasGeneratePanel.js.map +1 -1
- package/lib/features/canvas/CanvasLibrarySheet.js +31 -13
- package/lib/features/canvas/CanvasLibrarySheet.js.map +1 -1
- package/lib/features/canvas/CanvasModelPicker.js +176 -0
- package/lib/features/canvas/CanvasModelPicker.js.map +1 -0
- package/lib/features/canvas/CanvasTasksPanel.js +325 -29
- package/lib/features/canvas/CanvasTasksPanel.js.map +1 -1
- package/lib/features/canvas/CanvasTemplatesPanel.js +5 -4
- package/lib/features/canvas/CanvasTemplatesPanel.js.map +1 -1
- package/lib/features/canvas/CanvasToolDock.js +137 -61
- package/lib/features/canvas/CanvasToolDock.js.map +1 -1
- package/lib/features/canvas/FrameExtractorExperience.js +33 -2
- package/lib/features/canvas/FrameExtractorExperience.js.map +1 -1
- package/lib/features/canvas/GeneratorReferenceSlots.js +197 -0
- package/lib/features/canvas/GeneratorReferenceSlots.js.map +1 -0
- package/lib/features/canvas/NativeCanvasViewer.js +89 -11
- package/lib/features/canvas/NativeCanvasViewer.js.map +1 -1
- package/lib/features/canvas/PromoContextFields.js +55 -2
- package/lib/features/canvas/PromoContextFields.js.map +1 -1
- package/lib/features/canvas/SetStudioExperience.js +323 -13
- package/lib/features/canvas/SetStudioExperience.js.map +1 -1
- package/lib/features/canvas/VideoEditExperience.js +1 -53
- package/lib/features/canvas/VideoEditExperience.js.map +1 -1
- package/lib/features/canvas/VideoEditorExperience.js +697 -506
- package/lib/features/canvas/VideoEditorExperience.js.map +1 -1
- package/lib/features/canvas/VideoGeneratorExperience.js +22 -5
- package/lib/features/canvas/VideoGeneratorExperience.js.map +1 -1
- package/lib/features/canvas/canvasAgentChat.js +125 -11
- package/lib/features/canvas/canvasAgentChat.js.map +1 -1
- package/lib/features/canvas/canvasCore.js +117 -12
- package/lib/features/canvas/canvasCore.js.map +1 -1
- package/lib/features/canvas/canvasEditOps.js +148 -111
- package/lib/features/canvas/canvasEditOps.js.map +1 -1
- package/lib/features/canvas/canvasTaskSync.js +235 -0
- package/lib/features/canvas/canvasTaskSync.js.map +1 -0
- package/lib/features/canvas/canvasTasks.js +98 -11
- package/lib/features/canvas/canvasTasks.js.map +1 -1
- package/lib/features/canvas/createTiles.js +247 -0
- package/lib/features/canvas/createTiles.js.map +1 -0
- package/lib/features/canvas/deepLinks.js +83 -0
- package/lib/features/canvas/deepLinks.js.map +1 -0
- package/lib/features/canvas/frameExtract.js +13 -1
- package/lib/features/canvas/frameExtract.js.map +1 -1
- package/lib/features/canvas/generationModels.js +36 -8
- package/lib/features/canvas/generationModels.js.map +1 -1
- package/lib/features/canvas/generatorCommands.js +106 -0
- package/lib/features/canvas/generatorCommands.js.map +1 -0
- package/lib/features/canvas/generatorReferences.js +82 -0
- package/lib/features/canvas/generatorReferences.js.map +1 -0
- package/lib/features/canvas/homeWork.js +74 -0
- package/lib/features/canvas/homeWork.js.map +1 -0
- package/lib/features/canvas/linksRpc.js +130 -0
- package/lib/features/canvas/linksRpc.js.map +1 -0
- package/lib/features/canvas/mediaRecheck.js +176 -0
- package/lib/features/canvas/mediaRecheck.js.map +1 -0
- package/lib/features/canvas/mediaUrls.js +258 -0
- package/lib/features/canvas/mediaUrls.js.map +1 -0
- package/lib/features/canvas/nativeViewerRegistry.js.map +1 -1
- package/lib/features/canvas/promoProjectImport.js +80 -13
- package/lib/features/canvas/promoProjectImport.js.map +1 -1
- package/lib/features/canvas/promoVideo.js +106 -15
- package/lib/features/canvas/promoVideo.js.map +1 -1
- package/lib/features/canvas/reframe.js +228 -0
- package/lib/features/canvas/reframe.js.map +1 -0
- package/lib/features/canvas/serverTasks.js +697 -0
- package/lib/features/canvas/serverTasks.js.map +1 -0
- package/lib/features/canvas/set/SetViewportWebView.js +207 -28
- package/lib/features/canvas/set/SetViewportWebView.js.map +1 -1
- package/lib/features/canvas/set/agent/prompt.js +66 -0
- package/lib/features/canvas/set/agent/prompt.js.map +1 -0
- package/lib/features/canvas/set/agent/setOps.js +203 -0
- package/lib/features/canvas/set/agent/setOps.js.map +1 -0
- package/lib/features/canvas/set/agent/setTurn.js +128 -0
- package/lib/features/canvas/set/agent/setTurn.js.map +1 -0
- package/lib/features/canvas/set/agent/snapshot.js +43 -0
- package/lib/features/canvas/set/agent/snapshot.js.map +1 -0
- package/lib/features/canvas/set/agent/tools.js +580 -0
- package/lib/features/canvas/set/agent/tools.js.map +1 -0
- package/lib/features/canvas/set/setProject.js +43 -4
- package/lib/features/canvas/set/setProject.js.map +1 -1
- package/lib/features/canvas/set/supplyLibrary.js +397 -0
- package/lib/features/canvas/set/supplyLibrary.js.map +1 -0
- package/lib/features/canvas/storageLibrary.js +113 -52
- package/lib/features/canvas/storageLibrary.js.map +1 -1
- package/lib/features/canvas/studioCatalog.js +15 -2
- package/lib/features/canvas/studioCatalog.js.map +1 -1
- package/lib/features/canvas/studioLooks.js +1 -8
- package/lib/features/canvas/studioLooks.js.map +1 -1
- package/lib/features/canvas/useBoardMediaRefresh.js +66 -0
- package/lib/features/canvas/useBoardMediaRefresh.js.map +1 -0
- package/lib/features/canvas/useMediaSource.js +84 -0
- package/lib/features/canvas/useMediaSource.js.map +1 -0
- package/lib/features/canvas/video3d.js +11 -10
- package/lib/features/canvas/video3d.js.map +1 -1
- package/lib/features/canvas/videoEditor/BlurBoxOverlay.js +46 -0
- package/lib/features/canvas/videoEditor/BlurBoxOverlay.js.map +1 -0
- package/lib/features/canvas/videoEditor/ExportTaskCard.js +134 -0
- package/lib/features/canvas/videoEditor/ExportTaskCard.js.map +1 -0
- package/lib/features/canvas/videoEditor/agent/AgentCutPane.js +45 -7
- package/lib/features/canvas/videoEditor/agent/AgentCutPane.js.map +1 -1
- package/lib/features/canvas/videoEditor/agent/agentLoop.js +4 -2
- package/lib/features/canvas/videoEditor/agent/agentLoop.js.map +1 -1
- package/lib/features/canvas/videoEditor/agent/attachments.js +304 -0
- package/lib/features/canvas/videoEditor/agent/attachments.js.map +1 -0
- package/lib/features/canvas/videoEditor/agent/brainClient.js +3 -2
- package/lib/features/canvas/videoEditor/agent/brainClient.js.map +1 -1
- package/lib/features/canvas/videoEditor/agent/cutTools.js +207 -7
- package/lib/features/canvas/videoEditor/agent/cutTools.js.map +1 -1
- package/lib/features/canvas/videoEditor/blurBox.js +34 -0
- package/lib/features/canvas/videoEditor/blurBox.js.map +1 -0
- package/lib/features/canvas/videoEditor/compileTimeline.js +668 -0
- package/lib/features/canvas/videoEditor/compileTimeline.js.map +1 -0
- package/lib/features/canvas/videoEditor/editorModel.js +56 -22
- package/lib/features/canvas/videoEditor/editorModel.js.map +1 -1
- package/lib/features/canvas/videoEditor/exportPipeline.js +77 -0
- package/lib/features/canvas/videoEditor/exportPipeline.js.map +1 -0
- package/lib/features/canvas/videoEditor/exportRenderer.js +19 -12
- package/lib/features/canvas/videoEditor/exportRenderer.js.map +1 -1
- package/lib/features/canvas/videoEditor/exportTask.js +216 -0
- package/lib/features/canvas/videoEditor/exportTask.js.map +1 -0
- package/lib/features/canvas/videoEditor/formWidgets.js +142 -0
- package/lib/features/canvas/videoEditor/formWidgets.js.map +1 -0
- package/lib/features/canvas/videoEditor/produce/ProduceVideoSheet.js +195 -0
- package/lib/features/canvas/videoEditor/produce/ProduceVideoSheet.js.map +1 -0
- package/lib/features/canvas/videoEditor/produce/produceScenes.js +158 -0
- package/lib/features/canvas/videoEditor/produce/produceScenes.js.map +1 -0
- package/lib/features/canvas/videoEditor/remoteMediaClient.js +17 -5
- package/lib/features/canvas/videoEditor/remoteMediaClient.js.map +1 -1
- package/lib/features/canvas/videoEditor/renderTimeline.js +207 -0
- package/lib/features/canvas/videoEditor/renderTimeline.js.map +1 -0
- package/lib/features/canvas/videoEditor/renewLinks.js +64 -0
- package/lib/features/canvas/videoEditor/renewLinks.js.map +1 -0
- package/lib/features/canvas/videoEditor/sceneLayers.js +101 -0
- package/lib/features/canvas/videoEditor/sceneLayers.js.map +1 -0
- package/lib/features/canvas/videoEditor/tools/ToolsDrawer.js +283 -0
- package/lib/features/canvas/videoEditor/tools/ToolsDrawer.js.map +1 -0
- package/lib/features/canvas/videoEditor/tools/catalog.js +576 -0
- package/lib/features/canvas/videoEditor/tools/catalog.js.map +1 -0
- package/lib/features/canvas/videoEditor/videoProjectsStore.js +27 -3
- package/lib/features/canvas/videoEditor/videoProjectsStore.js.map +1 -1
- package/lib/features/webbuilder/WebBuilderExperience.js +29 -1
- package/lib/features/webbuilder/WebBuilderExperience.js.map +1 -1
- package/lib/features/webbuilder/buildFragments.js +8 -4
- package/lib/features/webbuilder/buildFragments.js.map +1 -1
- package/lib/features/webbuilder/codezenHandoff.js +108 -0
- package/lib/features/webbuilder/codezenHandoff.js.map +1 -0
- package/lib/features/webbuilder/workflow.js.map +1 -1
- package/lib/hooks/useChatApi.js +1 -2
- package/lib/hooks/useChatApi.js.map +1 -1
- package/lib/screens/CanvasBoard/index.js +12 -1
- package/lib/screens/CanvasBoard/index.js.map +1 -1
- package/lib/screens/Home/HomeScreen.js.map +1 -1
- package/lib/screens/Home/components/HomeSuggestions.js +3 -3
- package/lib/screens/Home/components/HomeSuggestions.js.map +1 -1
- package/lib/screens/WebBuilder/index.js +4 -2
- package/lib/screens/WebBuilder/index.js.map +1 -1
- package/lib/screens/WebBuilderNative/index.js +29 -2
- package/lib/screens/WebBuilderNative/index.js.map +1 -1
- package/lib/screens/WebBuilderNative/useWebBuilderHost.js +30 -6
- package/lib/screens/WebBuilderNative/useWebBuilderHost.js.map +1 -1
- package/package.json +4 -4
- /package/lib/assets/apps/{paper2poster.webp → poster.webp} +0 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {KIT_ENTRIES,KIT_CATEGORY_LABEL}from'../supplyLibrary.js';import {SET_ASPECTS}from'../setProject.js';import {describeSet}from'./snapshot.js';function describeSupply() {
|
|
2
|
+
const categories = ["blocks", "set", "standins"];
|
|
3
|
+
return categories.map((category) => {
|
|
4
|
+
const rows = KIT_ENTRIES.filter((entry) => entry.category === category).map((entry) => ` ${entry.id} (${entry.heightM > 0 ? `${entry.heightM}m` : "flat"}) - ${entry.hint}`);
|
|
5
|
+
return [`${KIT_CATEGORY_LABEL[category]}:`, ...rows].join("\n");
|
|
6
|
+
}).join("\n");
|
|
7
|
+
}
|
|
8
|
+
function buildSetSystemPrompt(project, opts = {}) {
|
|
9
|
+
const {
|
|
10
|
+
stills = []
|
|
11
|
+
} = opts;
|
|
12
|
+
return `You are the set agent inside a 3D set studio. You build and edit the user's set by calling tools; every change shows on the stage beside them at once, and each call is one undo step. Work like a director blocking a shot: act first, then say what you built in two or three plain sentences. Ask a question only when the request cannot be carried out without an answer.
|
|
13
|
+
|
|
14
|
+
WHY A SET AND NOT A PROMPT
|
|
15
|
+
A prompt gives a different room every time; a set gives the SAME room from a second angle, with the mug still on the same table. Everything you place should serve that: real sizes, real distances, a camera a person could stand at.
|
|
16
|
+
|
|
17
|
+
HOW THE SET WORKS
|
|
18
|
+
- Metres and degrees. +Y is up, the floor is y 0, and the camera looks at its target. An object's position is where its BASE sits, so a piece standing on the floor has y 0.
|
|
19
|
+
- Stock geometry ("kit") is built in the browser: instant, free, and nothing to expire. It is most of a set.
|
|
20
|
+
- Sizes are real. Scale a kit piece with height_m (the height you want it to end up) or scale/scale_xyz. A scaled cylinder is a mug at 0.1m, a barrel at 0.9m, a column drum at 2m.
|
|
21
|
+
- Copy ids exactly from the snapshot or from a tool result. Never invent one.
|
|
22
|
+
|
|
23
|
+
THE SUPPLY LIBRARY
|
|
24
|
+
${describeSupply()}
|
|
25
|
+
|
|
26
|
+
THE ORDER THAT WORKS
|
|
27
|
+
1. Ground it: a floor, and a wall if the frame will see one. Lock both - they are not what the user will want to drag.
|
|
28
|
+
2. Put the subject at or near x 0, z 0. That is what the camera targets.
|
|
29
|
+
3. Put the camera where a person would stand, then set the target to the subject's interesting height.
|
|
30
|
+
4. Dress outward from the subject: the things that explain the place, at three depths - something near, the subject, something behind.
|
|
31
|
+
5. Generate last, and only the hero.
|
|
32
|
+
|
|
33
|
+
STANDING THINGS ON THINGS
|
|
34
|
+
Set y to the surface's height, never nudge until it looks right. Floor 0 \xB7 plank top 0.08 \xB7 steps top 0.72 \xB7 table top 0.77 \xB7 cube top 1. A figure is 1.75m, so its eye is ~1.6m and a table comes to its hip. Every object line in the snapshot ends with its own top for exactly this.
|
|
35
|
+
|
|
36
|
+
WHERE THE CAMERA GOES
|
|
37
|
+
Height decides character: 1.5-1.7m reads as a standing person, 1.1-1.3m as seated and intimate, below 0.8m makes the subject dominant, above 2.2m makes it small. Distance and lens decide the frame:
|
|
38
|
+
wide room 5-7m on 24mm \xB7 full figure 4m on 35mm \xB7 two people across a table 3.5-4.5m on 35mm \xB7 product on a table 1.2-1.8m on 50mm \xB7 portrait 1.3m on 85mm \xB7 detail 0.6-0.9m on 85mm
|
|
39
|
+
Off-centre it slightly rather than dead-on - a symmetrical frame reads as a diagram. Target a standing figure's chest (~1.2m), not its head. Never put the camera below the floor. Leave the lens a way in: two figures shoulder to shoulder between the camera and the subject will hide it.
|
|
40
|
+
|
|
41
|
+
ARRANGING PEOPLE
|
|
42
|
+
Figures talking stand 1.2-1.6m apart, each turned 30-40\xB0 toward the other - dead-on is a police line-up. Three or more go on an arc around the subject, never in a row.
|
|
43
|
+
|
|
44
|
+
FRAMING AND LIGHT
|
|
45
|
+
Aspect first, because it changes the blocking: ${SET_ASPECTS.join(", ")} - a 9:16 set stacks in depth, a 21:9 set spreads across. The key light is one direction and one intensity (set_light): 135\xB0/42\xB0 is a pleasant three-quarter key; bring the elevation down to 15-25\xB0 for long dramatic shadows, raise ambient for a soft overcast look. The light-stand piece is set dressing - it does not light anything.
|
|
46
|
+
|
|
47
|
+
GENERATING
|
|
48
|
+
- generate_object turns a prompt or a still into a real GLB and drops it into the set. It takes 45-120 seconds, so use it for the one or two pieces the shot is ABOUT, and block everything else. Say what you are generating before you call it.
|
|
49
|
+
- generate_world paints the 360 panorama behind the set in a few seconds. Worth it whenever the brief names a place or a time of day. The camera turns inside a world; it cannot travel into it, so the set still needs its own floor and walls.
|
|
50
|
+
- A generated mesh arrives at an unpredictable size. Give generate_object the height_m it should end up at; if it still looks wrong in the snapshot that comes back, update_objects it - a hero prop is usually 0.1-0.5m, a chair 0.9m, a car 4m.
|
|
51
|
+
- When a generator fails, say so in one clause and leave the stand-in block in its place rather than retrying it twice.
|
|
52
|
+
|
|
53
|
+
METHOD
|
|
54
|
+
- A first request for a scene is one block_set with mode "replace": the whole shot at once, floor to camera, rather than a dozen calls.
|
|
55
|
+
- A later request is an edit: update_objects, remove_objects, set_camera, set_light. Do not rebuild a set the user has been moving by hand.
|
|
56
|
+
- Respect what the user locked or hid; if a locked object is genuinely in the way, say so instead of unlocking it silently.
|
|
57
|
+
- If a tool reports something dropped or missing, fix it in the next call rather than repeating the same one.
|
|
58
|
+
|
|
59
|
+
REPLY STYLE
|
|
60
|
+
Plain sentences, no headings, no bullet lists, no JSON, no ids. Say what you built, the lens and the distance you chose, and any judgment call worth one sentence.
|
|
61
|
+
|
|
62
|
+
Describe only what your tool calls actually DID. If you did not call set_camera, do not say where the camera is - go and call it instead. A reply that describes a camera, a light or a prop that no call placed is worse than a short one, because the user reads it as done and it is not.
|
|
63
|
+
|
|
64
|
+
CURRENT SET
|
|
65
|
+
${describeSet(project, stills)}`;
|
|
66
|
+
}export{buildSetSystemPrompt,describeSupply};//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sources":["../../../../../src/features/canvas/set/agent/prompt.ts"],"sourcesContent":["/**\n * Twin of web `editors/set-editor/agent/prompt.ts`.\n *\n * What the set agent is told, once per turn, with the set appended.\n *\n * The craft in here is the yantra-3d-set skill's, condensed: the order that\n * works, where a camera goes for a given shot, and the surface heights that\n * stop things floating. The supply catalogue is NOT copied - it is generated\n * from `KIT_ENTRIES`, so a piece added to the library is a piece the agent can\n * reach for the same day, and a prompt that lists something the picker does\n * not have becomes impossible rather than merely unlikely.\n */\nimport { KIT_CATEGORY_LABEL, KIT_ENTRIES, type KitCategory } from '../supplyLibrary';\nimport { SET_ASPECTS, type SetProject } from '../setProject';\nimport { describeSet, type StillRef } from './snapshot';\n\n/** The supply library as the model sees it: id, real height, and what it is for. */\nexport function describeSupply(): string {\n const categories: KitCategory[] = ['blocks', 'set', 'standins'];\n return categories\n .map((category) => {\n const rows = KIT_ENTRIES.filter((entry) => entry.category === category).map(\n (entry) => ` ${entry.id} (${entry.heightM > 0 ? `${entry.heightM}m` : 'flat'}) - ${entry.hint}`,\n );\n return [`${KIT_CATEGORY_LABEL[category]}:`, ...rows].join('\\n');\n })\n .join('\\n');\n}\n\nexport interface SetPromptOptions {\n /** Stills on the canvas board, which generate_object can turn into a mesh. */\n stills?: readonly StillRef[];\n}\n\nexport function buildSetSystemPrompt(project: SetProject, opts: SetPromptOptions = {}): string {\n const { stills = [] } = opts;\n\n return `You are the set agent inside a 3D set studio. You build and edit the user's set by calling tools; every change shows on the stage beside them at once, and each call is one undo step. Work like a director blocking a shot: act first, then say what you built in two or three plain sentences. Ask a question only when the request cannot be carried out without an answer.\n\nWHY A SET AND NOT A PROMPT\nA prompt gives a different room every time; a set gives the SAME room from a second angle, with the mug still on the same table. Everything you place should serve that: real sizes, real distances, a camera a person could stand at.\n\nHOW THE SET WORKS\n- Metres and degrees. +Y is up, the floor is y 0, and the camera looks at its target. An object's position is where its BASE sits, so a piece standing on the floor has y 0.\n- Stock geometry (\"kit\") is built in the browser: instant, free, and nothing to expire. It is most of a set.\n- Sizes are real. Scale a kit piece with height_m (the height you want it to end up) or scale/scale_xyz. A scaled cylinder is a mug at 0.1m, a barrel at 0.9m, a column drum at 2m.\n- Copy ids exactly from the snapshot or from a tool result. Never invent one.\n\nTHE SUPPLY LIBRARY\n${describeSupply()}\n\nTHE ORDER THAT WORKS\n1. Ground it: a floor, and a wall if the frame will see one. Lock both - they are not what the user will want to drag.\n2. Put the subject at or near x 0, z 0. That is what the camera targets.\n3. Put the camera where a person would stand, then set the target to the subject's interesting height.\n4. Dress outward from the subject: the things that explain the place, at three depths - something near, the subject, something behind.\n5. Generate last, and only the hero.\n\nSTANDING THINGS ON THINGS\nSet y to the surface's height, never nudge until it looks right. Floor 0 · plank top 0.08 · steps top 0.72 · table top 0.77 · cube top 1. A figure is 1.75m, so its eye is ~1.6m and a table comes to its hip. Every object line in the snapshot ends with its own top for exactly this.\n\nWHERE THE CAMERA GOES\nHeight decides character: 1.5-1.7m reads as a standing person, 1.1-1.3m as seated and intimate, below 0.8m makes the subject dominant, above 2.2m makes it small. Distance and lens decide the frame:\n wide room 5-7m on 24mm · full figure 4m on 35mm · two people across a table 3.5-4.5m on 35mm · product on a table 1.2-1.8m on 50mm · portrait 1.3m on 85mm · detail 0.6-0.9m on 85mm\nOff-centre it slightly rather than dead-on - a symmetrical frame reads as a diagram. Target a standing figure's chest (~1.2m), not its head. Never put the camera below the floor. Leave the lens a way in: two figures shoulder to shoulder between the camera and the subject will hide it.\n\nARRANGING PEOPLE\nFigures talking stand 1.2-1.6m apart, each turned 30-40° toward the other - dead-on is a police line-up. Three or more go on an arc around the subject, never in a row.\n\nFRAMING AND LIGHT\nAspect first, because it changes the blocking: ${SET_ASPECTS.join(', ')} - a 9:16 set stacks in depth, a 21:9 set spreads across. The key light is one direction and one intensity (set_light): 135°/42° is a pleasant three-quarter key; bring the elevation down to 15-25° for long dramatic shadows, raise ambient for a soft overcast look. The light-stand piece is set dressing - it does not light anything.\n\nGENERATING\n- generate_object turns a prompt or a still into a real GLB and drops it into the set. It takes 45-120 seconds, so use it for the one or two pieces the shot is ABOUT, and block everything else. Say what you are generating before you call it.\n- generate_world paints the 360 panorama behind the set in a few seconds. Worth it whenever the brief names a place or a time of day. The camera turns inside a world; it cannot travel into it, so the set still needs its own floor and walls.\n- A generated mesh arrives at an unpredictable size. Give generate_object the height_m it should end up at; if it still looks wrong in the snapshot that comes back, update_objects it - a hero prop is usually 0.1-0.5m, a chair 0.9m, a car 4m.\n- When a generator fails, say so in one clause and leave the stand-in block in its place rather than retrying it twice.\n\nMETHOD\n- A first request for a scene is one block_set with mode \"replace\": the whole shot at once, floor to camera, rather than a dozen calls.\n- A later request is an edit: update_objects, remove_objects, set_camera, set_light. Do not rebuild a set the user has been moving by hand.\n- Respect what the user locked or hid; if a locked object is genuinely in the way, say so instead of unlocking it silently.\n- If a tool reports something dropped or missing, fix it in the next call rather than repeating the same one.\n\nREPLY STYLE\nPlain sentences, no headings, no bullet lists, no JSON, no ids. Say what you built, the lens and the distance you chose, and any judgment call worth one sentence.\n\nDescribe only what your tool calls actually DID. If you did not call set_camera, do not say where the camera is - go and call it instead. A reply that describes a camera, a light or a prop that no call placed is worse than a short one, because the user reads it as done and it is not.\n\nCURRENT SET\n${describeSet(project, stills)}`;\n}\n"],"names":[],"mappings":"oJAiBO,SAAS,cAAyB,GAAA;AACvC,EAAA,MAAM,UAA4B,GAAA,CAAC,QAAU,EAAA,KAAA,EAAO,UAAU,CAAA;AAC9D,EAAO,OAAA,UAAA,CAAW,IAAI,CAAY,QAAA,KAAA;AAChC,IAAM,MAAA,IAAA,GAAO,WAAY,CAAA,MAAA,CAAO,CAAS,KAAA,KAAA,KAAA,CAAM,aAAa,QAAQ,CAAA,CAAE,GAAI,CAAA,CAAA,KAAA,KAAS,CAAK,EAAA,EAAA,KAAA,CAAM,EAAE,CAAK,EAAA,EAAA,KAAA,CAAM,OAAU,GAAA,CAAA,GAAI,CAAG,EAAA,KAAA,CAAM,OAAO,CAAA,CAAA,CAAA,GAAM,MAAM,CAAA,IAAA,EAAO,KAAM,CAAA,IAAI,CAAE,CAAA,CAAA;AACxK,IAAO,OAAA,CAAC,CAAG,EAAA,kBAAA,CAAmB,QAAQ,CAAC,KAAK,GAAG,IAAI,CAAE,CAAA,IAAA,CAAK,IAAI,CAAA;AAAA,GAC/D,CAAE,CAAA,IAAA,CAAK,IAAI,CAAA;AACd;AAKO,SAAS,oBAAqB,CAAA,OAAA,EAAqB,IAAyB,GAAA,EAAY,EAAA;AAC7F,EAAM,MAAA;AAAA,IACJ,SAAS;AAAC,GACR,GAAA,IAAA;AACJ,EAAO,OAAA,CAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA,EAYP,gBAAgB;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA,+CAqB+B,EAAA,WAAA,CAAY,IAAK,CAAA,IAAI,CAAC,CAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;;AAAA;AAAA,EAoBrE,WAAA,CAAY,OAAS,EAAA,MAAM,CAAC,CAAA,CAAA;AAC9B"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import {findKitEntry}from'../supplyLibrary.js';import {vec3,newObjectId}from'../setProject.js';var __defProp = Object.defineProperty;
|
|
2
|
+
var __defProps = Object.defineProperties;
|
|
3
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __spreadValues = (a, b) => {
|
|
9
|
+
for (var prop in b || (b = {}))
|
|
10
|
+
if (__hasOwnProp.call(b, prop))
|
|
11
|
+
__defNormalProp(a, prop, b[prop]);
|
|
12
|
+
if (__getOwnPropSymbols)
|
|
13
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
+
if (__propIsEnum.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
}
|
|
17
|
+
return a;
|
|
18
|
+
};
|
|
19
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
+
function uniqueName(taken, base) {
|
|
21
|
+
const used = new Set(taken);
|
|
22
|
+
if (!used.has(base)) return base;
|
|
23
|
+
for (let n = 2; ; n += 1) {
|
|
24
|
+
const candidate = `${base} ${n}`;
|
|
25
|
+
if (!used.has(candidate)) return candidate;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const clampScale = (n) => Math.min(200, Math.max(1e-3, n));
|
|
29
|
+
const isNum = (v) => typeof v === "number" && Number.isFinite(v);
|
|
30
|
+
const point = (partial, fallback) => ({
|
|
31
|
+
x: isNum(partial == null ? void 0 : partial.x) ? partial.x : fallback.x,
|
|
32
|
+
y: isNum(partial == null ? void 0 : partial.y) ? partial.y : fallback.y,
|
|
33
|
+
z: isNum(partial == null ? void 0 : partial.z) ? partial.z : fallback.z
|
|
34
|
+
});
|
|
35
|
+
function scaleOf(spec, kitHeightM) {
|
|
36
|
+
if (spec.scaleXyz) {
|
|
37
|
+
const s = point(spec.scaleXyz, vec3(1, 1, 1));
|
|
38
|
+
return {
|
|
39
|
+
x: clampScale(s.x),
|
|
40
|
+
y: clampScale(s.y),
|
|
41
|
+
z: clampScale(s.z)
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (isNum(spec.heightM) && spec.heightM > 0 && kitHeightM > 0) {
|
|
45
|
+
const factor = clampScale(spec.heightM / kitHeightM);
|
|
46
|
+
return vec3(factor, factor, factor);
|
|
47
|
+
}
|
|
48
|
+
if (isNum(spec.scale) && spec.scale > 0) {
|
|
49
|
+
const factor = clampScale(spec.scale);
|
|
50
|
+
return vec3(factor, factor, factor);
|
|
51
|
+
}
|
|
52
|
+
return vec3(1, 1, 1);
|
|
53
|
+
}
|
|
54
|
+
function placeObjects(project, specs, mode = "add") {
|
|
55
|
+
var _a, _b;
|
|
56
|
+
const base = mode === "replace" ? [] : project.objects;
|
|
57
|
+
const names = new Set(base.map((o) => o.name));
|
|
58
|
+
const placed = [];
|
|
59
|
+
const unknownKits = [];
|
|
60
|
+
for (const spec of specs) {
|
|
61
|
+
const entry = findKitEntry(typeof spec.kit === "string" ? spec.kit.trim() : void 0);
|
|
62
|
+
if (!entry) {
|
|
63
|
+
if (typeof spec.kit === "string" && spec.kit.trim()) unknownKits.push(spec.kit.trim());
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const name = uniqueName(names, ((_a = spec.name) != null ? _a : "").trim() || entry.label);
|
|
67
|
+
names.add(name);
|
|
68
|
+
const rotation = spec.rotation ? point(spec.rotation, vec3()) : vec3(0, isNum(spec.rotationY) ? spec.rotationY : 0, 0);
|
|
69
|
+
placed.push(__spreadValues({
|
|
70
|
+
id: newObjectId(),
|
|
71
|
+
name,
|
|
72
|
+
source: "kit",
|
|
73
|
+
kitId: entry.id,
|
|
74
|
+
position: point(spec.position, vec3()),
|
|
75
|
+
rotation,
|
|
76
|
+
scale: scaleOf(spec, entry.heightM),
|
|
77
|
+
color: /^#[0-9a-f]{3,8}$/i.test(((_b = spec.color) != null ? _b : "").trim()) ? spec.color.trim() : entry.color
|
|
78
|
+
}, spec.locked === true ? {
|
|
79
|
+
locked: true
|
|
80
|
+
} : {}));
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
project: __spreadProps(__spreadValues({}, project), {
|
|
84
|
+
objects: [...base, ...placed]
|
|
85
|
+
}),
|
|
86
|
+
placed,
|
|
87
|
+
unknownKits
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function updateObjects(project, specs) {
|
|
91
|
+
var _a, _b, _c;
|
|
92
|
+
const byId = new Map(project.objects.map((o) => [o.id, o]));
|
|
93
|
+
const patches = /* @__PURE__ */ new Map();
|
|
94
|
+
const missing = [];
|
|
95
|
+
const names = new Set(project.objects.map((o) => o.name));
|
|
96
|
+
for (const spec of specs) {
|
|
97
|
+
const current = (_a = patches.get(spec.id)) != null ? _a : byId.get(spec.id);
|
|
98
|
+
if (!current) {
|
|
99
|
+
if (typeof spec.id === "string" && spec.id.trim()) missing.push(spec.id.trim());
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const entry = findKitEntry(current.kitId);
|
|
103
|
+
const next = __spreadValues({}, current);
|
|
104
|
+
if (typeof spec.name === "string" && spec.name.trim() && spec.name.trim() !== current.name) {
|
|
105
|
+
names.delete(current.name);
|
|
106
|
+
next.name = uniqueName(names, spec.name.trim());
|
|
107
|
+
names.add(next.name);
|
|
108
|
+
}
|
|
109
|
+
if (spec.position) next.position = point(spec.position, current.position);
|
|
110
|
+
if (spec.rotation) next.rotation = point(spec.rotation, current.rotation);
|
|
111
|
+
else if (isNum(spec.rotationY)) next.rotation = __spreadProps(__spreadValues({}, current.rotation), {
|
|
112
|
+
y: spec.rotationY
|
|
113
|
+
});
|
|
114
|
+
if (spec.scaleXyz || isNum(spec.scale) || isNum(spec.heightM)) {
|
|
115
|
+
next.scale = scaleOf(__spreadValues(__spreadValues(__spreadValues({
|
|
116
|
+
kit: (_b = current.kitId) != null ? _b : ""
|
|
117
|
+
}, spec.scaleXyz ? {
|
|
118
|
+
scaleXyz: __spreadValues(__spreadValues({}, current.scale), spec.scaleXyz)
|
|
119
|
+
} : {}), isNum(spec.scale) ? {
|
|
120
|
+
scale: spec.scale
|
|
121
|
+
} : {}), isNum(spec.heightM) ? {
|
|
122
|
+
heightM: spec.heightM
|
|
123
|
+
} : {}), (_c = entry == null ? void 0 : entry.heightM) != null ? _c : 0);
|
|
124
|
+
}
|
|
125
|
+
if (typeof spec.color === "string" && /^#[0-9a-f]{3,8}$/i.test(spec.color.trim())) next.color = spec.color.trim();
|
|
126
|
+
if (typeof spec.hidden === "boolean") next.hidden = spec.hidden;
|
|
127
|
+
if (typeof spec.locked === "boolean") next.locked = spec.locked;
|
|
128
|
+
patches.set(spec.id, next);
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
project: __spreadProps(__spreadValues({}, project), {
|
|
132
|
+
objects: project.objects.map((o) => {
|
|
133
|
+
var _a2;
|
|
134
|
+
return (_a2 = patches.get(o.id)) != null ? _a2 : o;
|
|
135
|
+
})
|
|
136
|
+
}),
|
|
137
|
+
updated: [...patches.values()],
|
|
138
|
+
missing
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function removeObjects(project, ids) {
|
|
142
|
+
const wanted = new Set(ids.filter((id) => typeof id === "string" && id.trim()).map((id) => id.trim()));
|
|
143
|
+
const removed = project.objects.filter((o) => wanted.has(o.id));
|
|
144
|
+
const found = new Set(removed.map((o) => o.id));
|
|
145
|
+
return {
|
|
146
|
+
project: __spreadProps(__spreadValues({}, project), {
|
|
147
|
+
objects: project.objects.filter((o) => !found.has(o.id))
|
|
148
|
+
}),
|
|
149
|
+
removed,
|
|
150
|
+
missing: [...wanted].filter((id) => !found.has(id))
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const FULL_FRAME_HEIGHT_MM = 24;
|
|
154
|
+
const fovFromMm = (mm) => 2 * Math.atan(FULL_FRAME_HEIGHT_MM / 2 / mm) * 180 / Math.PI;
|
|
155
|
+
function cameraFromShot(current, spec) {
|
|
156
|
+
const target = point(spec.target, current.target);
|
|
157
|
+
const fov = isNum(spec.fov) ? spec.fov : isNum(spec.lensMm) && spec.lensMm > 0 ? fovFromMm(spec.lensMm) : current.fov;
|
|
158
|
+
let position;
|
|
159
|
+
if (spec.position && (isNum(spec.position.x) || isNum(spec.position.y) || isNum(spec.position.z))) {
|
|
160
|
+
position = point(spec.position, current.position);
|
|
161
|
+
} else if (isNum(spec.distance) || isNum(spec.height) || isNum(spec.azimuthDeg)) {
|
|
162
|
+
const dx = current.position.x - target.x;
|
|
163
|
+
const dz = current.position.z - target.z;
|
|
164
|
+
const currentDistance = Math.hypot(dx, dz) || 4;
|
|
165
|
+
const distance = isNum(spec.distance) && spec.distance > 0 ? spec.distance : currentDistance;
|
|
166
|
+
const azimuth = isNum(spec.azimuthDeg) ? spec.azimuthDeg : Math.atan2(dx, dz) * 180 / Math.PI;
|
|
167
|
+
const radians = azimuth * Math.PI / 180;
|
|
168
|
+
position = {
|
|
169
|
+
x: target.x + Math.sin(radians) * distance,
|
|
170
|
+
y: isNum(spec.height) ? spec.height : current.position.y,
|
|
171
|
+
z: target.z + Math.cos(radians) * distance
|
|
172
|
+
};
|
|
173
|
+
} else {
|
|
174
|
+
position = __spreadValues({}, current.position);
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
position: __spreadProps(__spreadValues({}, position), {
|
|
178
|
+
y: Math.max(0.05, position.y)
|
|
179
|
+
}),
|
|
180
|
+
target,
|
|
181
|
+
fov: Math.min(110, Math.max(5, fov))
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function tidySet(project) {
|
|
185
|
+
const cm = (n) => Math.round(n * 1e3) / 1e3;
|
|
186
|
+
const round = (v) => ({
|
|
187
|
+
x: cm(v.x),
|
|
188
|
+
y: cm(v.y),
|
|
189
|
+
z: cm(v.z)
|
|
190
|
+
});
|
|
191
|
+
return __spreadProps(__spreadValues({}, project), {
|
|
192
|
+
objects: project.objects.map((o) => __spreadProps(__spreadValues({}, o), {
|
|
193
|
+
position: round(o.position),
|
|
194
|
+
rotation: round(o.rotation),
|
|
195
|
+
scale: round(o.scale)
|
|
196
|
+
})),
|
|
197
|
+
camera: __spreadProps(__spreadValues({}, project.camera), {
|
|
198
|
+
position: round(project.camera.position),
|
|
199
|
+
target: round(project.camera.target),
|
|
200
|
+
fov: Math.round(project.camera.fov * 10) / 10
|
|
201
|
+
})
|
|
202
|
+
});
|
|
203
|
+
}export{cameraFromShot,placeObjects,removeObjects,tidySet,uniqueName,updateObjects};//# sourceMappingURL=setOps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setOps.js","sources":["../../../../../src/features/canvas/set/agent/setOps.ts"],"sourcesContent":["/**\n * Twin of web `editors/set-editor/agent/setOps.ts`.\n *\n * What the set agent's tools do to a set, as pure functions.\n *\n * Every one takes a `SetProject` and returns a new one, so a tool call is a\n * value the reducer commits as a single undo step - the same code path a drag\n * or a delete takes. Nothing here touches React, three.js or the network, and\n * that is what makes the blocking rules testable: \"a mug on a table sits at\n * y 0.77\" is an assertion about `placeObjects`, not about a screenshot.\n *\n * The coercion is deliberately forgiving in the same way the importer is: a\n * model writing a twelve-object set will get a field wrong, and the right\n * answer is to place the other eleven and say what was dropped.\n */\nimport { findKitEntry } from '../supplyLibrary';\nimport { newObjectId, vec3, type SetCamera, type SetObject, type SetProject, type Vec3 } from '../setProject';\n\n/** A name that does not collide: \"Cube\", then \"Cube 2\". */\nexport function uniqueName(taken: Iterable<string>, base: string): string {\n const used = new Set(taken);\n if (!used.has(base)) return base;\n for (let n = 2; ; n += 1) {\n const candidate = `${base} ${n}`;\n if (!used.has(candidate)) return candidate;\n }\n}\n\n/** One placement as the model writes it. Everything but `kit` is optional. */\nexport interface PlacementSpec {\n kit: string;\n name?: string;\n position?: Partial<Vec3>;\n /** Turn around the vertical axis, in degrees - the only rotation a blocked set usually needs. */\n rotationY?: number;\n rotation?: Partial<Vec3>;\n /** Uniform multiplier. */\n scale?: number;\n scaleXyz?: Partial<Vec3>;\n /**\n * The height the piece should END UP, in metres. Scaled from the kit's own\n * height, so \"a 0.1m mug\" is one field rather than a division the model has\n * to get right. Ignored for the flat pieces (a floor has no height).\n */\n heightM?: number;\n color?: string;\n locked?: boolean;\n}\n\nexport interface PlacementResult {\n project: SetProject;\n placed: SetObject[];\n /** `kit` values that are not in the supply library, in the order they were asked for. */\n unknownKits: string[];\n}\n\nconst clampScale = (n: number): number => Math.min(200, Math.max(0.001, n));\n\nconst isNum = (v: unknown): v is number => typeof v === 'number' && Number.isFinite(v);\n\nconst point = (partial: Partial<Vec3> | undefined, fallback: Vec3): Vec3 => ({\n x: isNum(partial?.x) ? partial.x : fallback.x,\n y: isNum(partial?.y) ? partial.y : fallback.y,\n z: isNum(partial?.z) ? partial.z : fallback.z,\n});\n\n/** The scale a placement asks for, however it asked. */\nfunction scaleOf(spec: PlacementSpec, kitHeightM: number): Vec3 {\n if (spec.scaleXyz) {\n const s = point(spec.scaleXyz, vec3(1, 1, 1));\n return { x: clampScale(s.x), y: clampScale(s.y), z: clampScale(s.z) };\n }\n if (isNum(spec.heightM) && spec.heightM > 0 && kitHeightM > 0) {\n const factor = clampScale(spec.heightM / kitHeightM);\n return vec3(factor, factor, factor);\n }\n if (isNum(spec.scale) && spec.scale > 0) {\n const factor = clampScale(spec.scale);\n return vec3(factor, factor, factor);\n }\n return vec3(1, 1, 1);\n}\n\n/**\n * Add stock geometry to a set, or replace what is there.\n *\n * `replace` is the first prompt - a whole set blocked at once. `add` is every\n * prompt after it, and keeps what the user has already moved by hand.\n */\nexport function placeObjects(\n project: SetProject,\n specs: readonly PlacementSpec[],\n mode: 'replace' | 'add' = 'add',\n): PlacementResult {\n const base = mode === 'replace' ? [] : project.objects;\n const names = new Set(base.map((o) => o.name));\n const placed: SetObject[] = [];\n const unknownKits: string[] = [];\n\n for (const spec of specs) {\n const entry = findKitEntry(typeof spec.kit === 'string' ? spec.kit.trim() : undefined);\n if (!entry) {\n if (typeof spec.kit === 'string' && spec.kit.trim()) unknownKits.push(spec.kit.trim());\n continue;\n }\n const name = uniqueName(names, (spec.name ?? '').trim() || entry.label);\n names.add(name);\n const rotation = spec.rotation\n ? point(spec.rotation, vec3())\n : vec3(0, isNum(spec.rotationY) ? spec.rotationY : 0, 0);\n placed.push({\n id: newObjectId(),\n name,\n source: 'kit',\n kitId: entry.id,\n position: point(spec.position, vec3()),\n rotation,\n scale: scaleOf(spec, entry.heightM),\n color: /^#[0-9a-f]{3,8}$/i.test((spec.color ?? '').trim()) ? spec.color!.trim() : entry.color,\n ...(spec.locked === true ? { locked: true } : {}),\n });\n }\n\n return {\n project: { ...project, objects: [...base, ...placed] },\n placed,\n unknownKits,\n };\n}\n\n/** One object's patch as the model writes it. */\nexport interface UpdateSpec {\n id: string;\n name?: string;\n position?: Partial<Vec3>;\n rotationY?: number;\n rotation?: Partial<Vec3>;\n scale?: number;\n scaleXyz?: Partial<Vec3>;\n heightM?: number;\n color?: string;\n hidden?: boolean;\n locked?: boolean;\n}\n\nexport interface UpdateResult {\n project: SetProject;\n updated: SetObject[];\n /** Ids that are not in the set - a model naming an object it invented. */\n missing: string[];\n}\n\n/**\n * Move, turn, resize, recolour or hide objects that are already placed.\n *\n * Partial by design: `{id, position: {y: 0.77}}` lifts something onto a table\n * without the model having to restate x and z it never knew.\n */\nexport function updateObjects(project: SetProject, specs: readonly UpdateSpec[]): UpdateResult {\n const byId = new Map(project.objects.map((o) => [o.id, o]));\n const patches = new Map<string, SetObject>();\n const missing: string[] = [];\n const names = new Set(project.objects.map((o) => o.name));\n\n for (const spec of specs) {\n const current = patches.get(spec.id) ?? byId.get(spec.id);\n if (!current) {\n if (typeof spec.id === 'string' && spec.id.trim()) missing.push(spec.id.trim());\n continue;\n }\n const entry = findKitEntry(current.kitId);\n const next: SetObject = { ...current };\n\n if (typeof spec.name === 'string' && spec.name.trim() && spec.name.trim() !== current.name) {\n names.delete(current.name);\n next.name = uniqueName(names, spec.name.trim());\n names.add(next.name);\n }\n if (spec.position) next.position = point(spec.position, current.position);\n if (spec.rotation) next.rotation = point(spec.rotation, current.rotation);\n else if (isNum(spec.rotationY)) next.rotation = { ...current.rotation, y: spec.rotationY };\n if (spec.scaleXyz || isNum(spec.scale) || isNum(spec.heightM)) {\n // Height is measured against the kit's own size, so it has to scale\n // from 1 rather than compound with whatever scale is already on.\n next.scale = scaleOf(\n {\n kit: current.kitId ?? '',\n ...(spec.scaleXyz ? { scaleXyz: { ...current.scale, ...spec.scaleXyz } } : {}),\n ...(isNum(spec.scale) ? { scale: spec.scale } : {}),\n ...(isNum(spec.heightM) ? { heightM: spec.heightM } : {}),\n },\n entry?.heightM ?? 0,\n );\n }\n if (typeof spec.color === 'string' && /^#[0-9a-f]{3,8}$/i.test(spec.color.trim()))\n next.color = spec.color.trim();\n if (typeof spec.hidden === 'boolean') next.hidden = spec.hidden;\n if (typeof spec.locked === 'boolean') next.locked = spec.locked;\n\n patches.set(spec.id, next);\n }\n\n return {\n project: { ...project, objects: project.objects.map((o) => patches.get(o.id) ?? o) },\n updated: [...patches.values()],\n missing,\n };\n}\n\nexport interface RemoveResult {\n project: SetProject;\n removed: SetObject[];\n missing: string[];\n}\n\nexport function removeObjects(project: SetProject, ids: readonly string[]): RemoveResult {\n const wanted = new Set(ids.filter((id) => typeof id === 'string' && id.trim()).map((id) => id.trim()));\n const removed = project.objects.filter((o) => wanted.has(o.id));\n const found = new Set(removed.map((o) => o.id));\n return {\n project: { ...project, objects: project.objects.filter((o) => !found.has(o.id)) },\n removed,\n missing: [...wanted].filter((id) => !found.has(id)),\n };\n}\n\n/** A camera as the model describes a shot, rather than as three coordinates. */\nexport interface ShotSpec {\n target?: Partial<Vec3>;\n /** Metres from the target, on the ground plane. */\n distance?: number;\n /** Lens height in metres. 1.6 is standing, 1.2 seated. */\n height?: number;\n /** Where the camera stands around the target: 0 is +Z (front), 90 is +X. */\n azimuthDeg?: number;\n lensMm?: number;\n fov?: number;\n /** An explicit position wins over distance/height/azimuth. */\n position?: Partial<Vec3>;\n}\n\nconst FULL_FRAME_HEIGHT_MM = 24;\nconst fovFromMm = (mm: number): number => (2 * Math.atan(FULL_FRAME_HEIGHT_MM / 2 / mm) * 180) / Math.PI;\n\n/**\n * Where the camera ends up.\n *\n * Never below the floor: under the ground plane the frame fills with grey and\n * the set reads as broken rather than as a low angle.\n */\nexport function cameraFromShot(current: SetCamera, spec: ShotSpec): SetCamera {\n const target = point(spec.target, current.target);\n const fov = isNum(spec.fov)\n ? spec.fov\n : isNum(spec.lensMm) && spec.lensMm > 0\n ? fovFromMm(spec.lensMm)\n : current.fov;\n\n let position: Vec3;\n if (spec.position && (isNum(spec.position.x) || isNum(spec.position.y) || isNum(spec.position.z))) {\n position = point(spec.position, current.position);\n } else if (isNum(spec.distance) || isNum(spec.height) || isNum(spec.azimuthDeg)) {\n const dx = current.position.x - target.x;\n const dz = current.position.z - target.z;\n const currentDistance = Math.hypot(dx, dz) || 4;\n const distance = isNum(spec.distance) && spec.distance > 0 ? spec.distance : currentDistance;\n const azimuth = isNum(spec.azimuthDeg) ? spec.azimuthDeg : (Math.atan2(dx, dz) * 180) / Math.PI;\n const radians = (azimuth * Math.PI) / 180;\n position = {\n x: target.x + Math.sin(radians) * distance,\n y: isNum(spec.height) ? spec.height : current.position.y,\n z: target.z + Math.cos(radians) * distance,\n };\n } else {\n position = { ...current.position };\n }\n\n return {\n position: { ...position, y: Math.max(0.05, position.y) },\n target,\n fov: Math.min(110, Math.max(5, fov)),\n };\n}\n\n/** Round a set's numbers to centimetres and whole degrees, so the inspector reads cleanly. */\nexport function tidySet(project: SetProject): SetProject {\n const cm = (n: number): number => Math.round(n * 1000) / 1000;\n const round = (v: Vec3): Vec3 => ({ x: cm(v.x), y: cm(v.y), z: cm(v.z) });\n return {\n ...project,\n objects: project.objects.map((o) => ({\n ...o,\n position: round(o.position),\n rotation: round(o.rotation),\n scale: round(o.scale),\n })),\n camera: {\n ...project.camera,\n position: round(project.camera.position),\n target: round(project.camera.target),\n fov: Math.round(project.camera.fov * 10) / 10,\n },\n };\n}\n"],"names":["_a"],"mappings":"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"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import {BRAIN_SMART_MODEL,brainChat}from'../../videoEditor/agent/brainClient.js';import {buildSetSystemPrompt}from'./prompt.js';import {SET_AGENT_TOOLS,executeSetTool}from'./tools.js';var __defProp = Object.defineProperty;
|
|
2
|
+
var __defProps = Object.defineProperties;
|
|
3
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __spreadValues = (a, b) => {
|
|
9
|
+
for (var prop in b || (b = {}))
|
|
10
|
+
if (__hasOwnProp.call(b, prop))
|
|
11
|
+
__defNormalProp(a, prop, b[prop]);
|
|
12
|
+
if (__getOwnPropSymbols)
|
|
13
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
+
if (__propIsEnum.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
}
|
|
17
|
+
return a;
|
|
18
|
+
};
|
|
19
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
+
const DEFAULT_HISTORY = 12;
|
|
21
|
+
const DEFAULT_ROUNDS = 12;
|
|
22
|
+
const WRAP_UP = "Stop calling tools now. In two plain sentences, tell the user what the set holds and what is still missing.";
|
|
23
|
+
const EXHAUSTED = "I ran out of steps before finishing; the stage holds what was blocked so far.";
|
|
24
|
+
function historyToMessages(history, limit = DEFAULT_HISTORY) {
|
|
25
|
+
const out = [];
|
|
26
|
+
for (const m of history.slice(-limit)) {
|
|
27
|
+
const content = m.content.trim();
|
|
28
|
+
if (content) out.push({
|
|
29
|
+
role: m.role,
|
|
30
|
+
content
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
function parseArgs(raw) {
|
|
36
|
+
try {
|
|
37
|
+
return JSON.parse(raw || "{}");
|
|
38
|
+
} catch (e) {
|
|
39
|
+
return {};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async function runSetTurn(args) {
|
|
43
|
+
var _a, _b, _c;
|
|
44
|
+
const {
|
|
45
|
+
history,
|
|
46
|
+
userText,
|
|
47
|
+
ctx,
|
|
48
|
+
model = BRAIN_SMART_MODEL,
|
|
49
|
+
tools = SET_AGENT_TOOLS,
|
|
50
|
+
execute = executeSetTool,
|
|
51
|
+
maxRounds = DEFAULT_ROUNDS,
|
|
52
|
+
historyLimit = DEFAULT_HISTORY,
|
|
53
|
+
onStatus,
|
|
54
|
+
onStep
|
|
55
|
+
} = args;
|
|
56
|
+
const chat = (_a = args.chat) != null ? _a : ((req) => {
|
|
57
|
+
if (!args.token || !args.agentEndpoint) throw new Error("The set agent needs a signed-in session.");
|
|
58
|
+
return brainChat(__spreadProps(__spreadValues({}, req), {
|
|
59
|
+
token: args.token,
|
|
60
|
+
agentEndpoint: args.agentEndpoint
|
|
61
|
+
}));
|
|
62
|
+
});
|
|
63
|
+
const steps = [];
|
|
64
|
+
const messages = [{
|
|
65
|
+
role: "system",
|
|
66
|
+
content: buildSetSystemPrompt(ctx.project(), {
|
|
67
|
+
stills: ctx.stills
|
|
68
|
+
})
|
|
69
|
+
}, ...historyToMessages(history, historyLimit), {
|
|
70
|
+
role: "user",
|
|
71
|
+
content: userText
|
|
72
|
+
}];
|
|
73
|
+
for (let round = 0; round < maxRounds; round += 1) {
|
|
74
|
+
if ((_b = ctx.signal) == null ? void 0 : _b.aborted) throw Object.assign(new Error("Stopped"), {
|
|
75
|
+
name: "AbortError"
|
|
76
|
+
});
|
|
77
|
+
onStatus == null ? void 0 : onStatus(round === 0 ? "Reading the brief\u2026" : "Blocking the set\u2026");
|
|
78
|
+
const reply = await chat({
|
|
79
|
+
messages,
|
|
80
|
+
tools,
|
|
81
|
+
model
|
|
82
|
+
});
|
|
83
|
+
if (!((_c = reply.tool_calls) == null ? void 0 : _c.length)) return {
|
|
84
|
+
text: reply.content.trim() || "Done.",
|
|
85
|
+
steps
|
|
86
|
+
};
|
|
87
|
+
messages.push({
|
|
88
|
+
role: "assistant",
|
|
89
|
+
content: reply.content || "",
|
|
90
|
+
tool_calls: reply.tool_calls
|
|
91
|
+
});
|
|
92
|
+
for (const call of reply.tool_calls) {
|
|
93
|
+
onStatus == null ? void 0 : onStatus(call.function.name.replace(/_/g, " "));
|
|
94
|
+
const outcome = await execute(call.function.name, parseArgs(call.function.arguments), ctx);
|
|
95
|
+
const step = __spreadValues({
|
|
96
|
+
label: outcome.label
|
|
97
|
+
}, outcome.detail ? {
|
|
98
|
+
detail: outcome.detail
|
|
99
|
+
} : {});
|
|
100
|
+
steps.push(step);
|
|
101
|
+
onStep == null ? void 0 : onStep(step);
|
|
102
|
+
messages.push({
|
|
103
|
+
role: "tool",
|
|
104
|
+
tool_call_id: call.id,
|
|
105
|
+
name: call.function.name,
|
|
106
|
+
content: outcome.result
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
const reply = await chat({
|
|
112
|
+
messages: [...messages, {
|
|
113
|
+
role: "user",
|
|
114
|
+
content: WRAP_UP
|
|
115
|
+
}],
|
|
116
|
+
model
|
|
117
|
+
});
|
|
118
|
+
return {
|
|
119
|
+
text: reply.content.trim() || EXHAUSTED,
|
|
120
|
+
steps
|
|
121
|
+
};
|
|
122
|
+
} catch (e) {
|
|
123
|
+
return {
|
|
124
|
+
text: EXHAUSTED,
|
|
125
|
+
steps
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}export{historyToMessages,runSetTurn};//# sourceMappingURL=setTurn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setTurn.js","sources":["../../../../../src/features/canvas/set/agent/setTurn.ts"],"sourcesContent":["/**\n * One turn of the set agent - twin of web `editors/set-editor/agent/setTurn.ts`\n * on the native brain lane (`videoEditor/agent/brainClient`, the same gateway\n * the cut agent uses): the model reads the set and the conversation, calls\n * tools until the shot is blocked, then says what it built.\n *\n * Each tool call commits through `ctx.commit`, so the stage changes as the\n * agent works and the person can take over by hand the moment it is done.\n */\nimport {\n BRAIN_SMART_MODEL,\n brainChat,\n type BrainMessage,\n type BrainReply,\n type BrainTool,\n type BrainToolCall,\n} from '../../videoEditor/agent/brainClient';\nimport { buildSetSystemPrompt } from './prompt';\nimport { SET_AGENT_TOOLS, executeSetTool, type SetAgentContext, type ToolOutcome } from './tools';\n\n/** A line of the set prompt's transcript. Session state - not part of the set. */\nexport interface SetChatMessage {\n role: 'user' | 'assistant';\n content: string;\n}\n\n/** A brain message that can carry the assistant's tool calls (OpenAI wire shape). */\nexport type SetTurnMessage = BrainMessage & { tool_calls?: BrainToolCall[] };\n\n/** The transport, minus auth - the gateway by default, a script in tests. */\nexport type SetTurnChat = (req: {\n messages: SetTurnMessage[];\n tools?: BrainTool[];\n model: string;\n}) => Promise<BrainReply>;\n\nexport interface SetTurnStep {\n label: string;\n detail?: string;\n}\n\nexport interface SetTurnResult {\n text: string;\n steps: SetTurnStep[];\n}\n\nexport interface SetTurnArgs {\n history: readonly SetChatMessage[];\n userText: string;\n ctx: SetAgentContext;\n /** Required unless `chat` is given. */\n token?: string;\n agentEndpoint?: string;\n chat?: SetTurnChat;\n model?: string;\n tools?: BrainTool[];\n execute?: (name: string, args: unknown, ctx: SetAgentContext) => Promise<ToolOutcome>;\n maxRounds?: number;\n historyLimit?: number;\n onStatus?: (text: string) => void;\n onStep?: (step: SetTurnStep) => void;\n}\n\nconst DEFAULT_HISTORY = 12;\nconst DEFAULT_ROUNDS = 12;\nconst WRAP_UP =\n 'Stop calling tools now. In two plain sentences, tell the user what the set holds and what is still missing.';\nconst EXHAUSTED = 'I ran out of steps before finishing; the stage holds what was blocked so far.';\n\n/** The last turns of the transcript as chat messages, oldest first. */\nexport function historyToMessages(history: readonly SetChatMessage[], limit = DEFAULT_HISTORY): BrainMessage[] {\n const out: BrainMessage[] = [];\n for (const m of history.slice(-limit)) {\n const content = m.content.trim();\n if (content) out.push({ role: m.role, content });\n }\n return out;\n}\n\nfunction parseArgs(raw: string | undefined): unknown {\n try {\n return JSON.parse(raw || '{}');\n } catch {\n return {};\n }\n}\n\nexport async function runSetTurn(args: SetTurnArgs): Promise<SetTurnResult> {\n const {\n history,\n userText,\n ctx,\n model = BRAIN_SMART_MODEL,\n tools = SET_AGENT_TOOLS,\n execute = executeSetTool,\n maxRounds = DEFAULT_ROUNDS,\n historyLimit = DEFAULT_HISTORY,\n onStatus,\n onStep,\n } = args;\n const chat: SetTurnChat =\n args.chat ??\n ((req) => {\n if (!args.token || !args.agentEndpoint) throw new Error('The set agent needs a signed-in session.');\n return brainChat({ ...req, token: args.token, agentEndpoint: args.agentEndpoint });\n });\n\n const steps: SetTurnStep[] = [];\n const messages: SetTurnMessage[] = [\n { role: 'system', content: buildSetSystemPrompt(ctx.project(), { stills: ctx.stills }) },\n ...historyToMessages(history, historyLimit),\n { role: 'user', content: userText },\n ];\n\n for (let round = 0; round < maxRounds; round += 1) {\n if (ctx.signal?.aborted) throw Object.assign(new Error('Stopped'), { name: 'AbortError' });\n onStatus?.(round === 0 ? 'Reading the brief…' : 'Blocking the set…');\n const reply = await chat({ messages, tools, model });\n if (!reply.tool_calls?.length) return { text: reply.content.trim() || 'Done.', steps };\n\n messages.push({ role: 'assistant', content: reply.content || '', tool_calls: reply.tool_calls });\n for (const call of reply.tool_calls) {\n onStatus?.(call.function.name.replace(/_/g, ' '));\n const outcome = await execute(call.function.name, parseArgs(call.function.arguments), ctx);\n const step = { label: outcome.label, ...(outcome.detail ? { detail: outcome.detail } : {}) };\n steps.push(step);\n onStep?.(step);\n messages.push({ role: 'tool', tool_call_id: call.id, name: call.function.name, content: outcome.result });\n }\n }\n\n // Out of rounds: one last call without tools so the reply says what is there.\n try {\n const reply = await chat({ messages: [...messages, { role: 'user', content: WRAP_UP }], model });\n return { text: reply.content.trim() || EXHAUSTED, steps };\n } catch {\n return { text: EXHAUSTED, steps };\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {findKitEntry}from'../supplyLibrary.js';import {lightingOf,focalLengthFromFov}from'../setProject.js';const n = (value) => String(Math.round(value * 100) / 100);
|
|
2
|
+
const xyz = (v) => `${n(v.x)},${n(v.y)},${n(v.z)}`;
|
|
3
|
+
const uniform = (v) => v.x === v.y && v.y === v.z;
|
|
4
|
+
function topHeight(object) {
|
|
5
|
+
const entry = findKitEntry(object.kitId);
|
|
6
|
+
if (!entry || entry.heightM <= 0) return null;
|
|
7
|
+
return Math.round((object.position.y + entry.heightM * object.scale.y) * 100) / 100;
|
|
8
|
+
}
|
|
9
|
+
function describeObject(object) {
|
|
10
|
+
var _a;
|
|
11
|
+
const parts = [`- id=${object.id} "${object.name}"`];
|
|
12
|
+
if (object.source === "kit") parts.push(`kit=${object.kitId}`);
|
|
13
|
+
else if (object.source === "mesh") parts.push("generated mesh");
|
|
14
|
+
else parts.push(`image plane ${n((_a = object.imageWidthM) != null ? _a : 1)}m wide${object.billboard ? ", faces camera" : ""}`);
|
|
15
|
+
parts.push(`at ${xyz(object.position)}`);
|
|
16
|
+
if (object.rotation.x || object.rotation.y || object.rotation.z) {
|
|
17
|
+
parts.push(object.rotation.x || object.rotation.z ? `rot ${xyz(object.rotation)}\xB0` : `turned ${n(object.rotation.y)}\xB0`);
|
|
18
|
+
}
|
|
19
|
+
if (!uniform(object.scale) || object.scale.x !== 1) {
|
|
20
|
+
parts.push(uniform(object.scale) ? `scale ${n(object.scale.x)}` : `scale ${xyz(object.scale)}`);
|
|
21
|
+
}
|
|
22
|
+
const top = topHeight(object);
|
|
23
|
+
if (top !== null) parts.push(`top ${n(top)}m`);
|
|
24
|
+
if (object.hidden) parts.push("hidden");
|
|
25
|
+
if (object.locked) parts.push("locked");
|
|
26
|
+
return parts.join(" \xB7 ");
|
|
27
|
+
}
|
|
28
|
+
function describeObjects(project) {
|
|
29
|
+
if (project.objects.length === 0) return "SET: empty.";
|
|
30
|
+
return [`SET (${project.objects.length} objects):`, ...project.objects.map(describeObject)].join("\n");
|
|
31
|
+
}
|
|
32
|
+
function describeSet(project, stills = []) {
|
|
33
|
+
const {
|
|
34
|
+
camera,
|
|
35
|
+
world
|
|
36
|
+
} = project;
|
|
37
|
+
const lighting = lightingOf(project);
|
|
38
|
+
const lines = [`TITLE: "${project.title}" \xB7 framed ${project.aspect}`, `CAMERA: at ${xyz(camera.position)} looking at ${xyz(camera.target)} \xB7 ${n(camera.fov)}\xB0 fov (${focalLengthFromFov(camera.fov)}mm)`, `LIGHT: key from ${n(lighting.keyAzimuthDeg)}\xB0 azimuth, ${n(lighting.keyElevationDeg)}\xB0 above the horizon, intensity ${n(lighting.keyIntensity)} \xB7 ambient ${n(lighting.ambientIntensity)}`, world.imageUrl ? `WORLD: a generated 360 panorama${world.prompt ? ` ("${world.prompt}")` : ""}, turned ${n(world.rotationDeg)}\xB0 \xB7 ground ${world.showGround ? "on" : "off"}` : `WORLD: plain backdrop ${world.background} \xB7 ground ${world.showGround ? "on" : "off"} \xB7 no panorama yet`, describeObjects(project)];
|
|
39
|
+
if (stills.length > 0) {
|
|
40
|
+
lines.push(`STILLS ON THE BOARD (usable as generate_object sources):`, ...stills.map((s) => `- still_id=${s.id}${s.label ? ` "${s.label}"` : ""}`));
|
|
41
|
+
}
|
|
42
|
+
return lines.join("\n");
|
|
43
|
+
}export{describeObject,describeObjects,describeSet,topHeight};//# sourceMappingURL=snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.js","sources":["../../../../../src/features/canvas/set/agent/snapshot.ts"],"sourcesContent":["/**\n * Twin of web `editors/set-editor/agent/snapshot.ts`.\n *\n * The set as the agent reads it: one line per object, with the real ids, so a\n * follow-up (\"lower the camera\", \"move the mug left\") acts on what is actually\n * there rather than on what the model remembers placing.\n *\n * Heights are spelled out because that is the thing a model gets wrong: an\n * object's own top (`top 0.77m`) is what the next thing stands on, and a\n * snapshot that only listed positions would make every \"put the cup on the\n * table\" a guess.\n */\nimport { findKitEntry } from '../supplyLibrary';\nimport { focalLengthFromFov, lightingOf, type SetObject, type SetProject, type Vec3 } from '../setProject';\n\nconst n = (value: number): string => String(Math.round(value * 100) / 100);\nconst xyz = (v: Vec3): string => `${n(v.x)},${n(v.y)},${n(v.z)}`;\nconst uniform = (v: Vec3): boolean => v.x === v.y && v.y === v.z;\n\n/** A stock piece's top, in metres, after its scale - what you stand the next thing on. */\nexport function topHeight(object: SetObject): number | null {\n const entry = findKitEntry(object.kitId);\n if (!entry || entry.heightM <= 0) return null;\n return Math.round((object.position.y + entry.heightM * object.scale.y) * 100) / 100;\n}\n\nexport function describeObject(object: SetObject): string {\n const parts = [`- id=${object.id} \"${object.name}\"`];\n if (object.source === 'kit') parts.push(`kit=${object.kitId}`);\n else if (object.source === 'mesh') parts.push('generated mesh');\n else parts.push(`image plane ${n(object.imageWidthM ?? 1)}m wide${object.billboard ? ', faces camera' : ''}`);\n\n parts.push(`at ${xyz(object.position)}`);\n if (object.rotation.x || object.rotation.y || object.rotation.z) {\n parts.push(\n object.rotation.x || object.rotation.z ? `rot ${xyz(object.rotation)}°` : `turned ${n(object.rotation.y)}°`,\n );\n }\n if (!uniform(object.scale) || object.scale.x !== 1) {\n parts.push(uniform(object.scale) ? `scale ${n(object.scale.x)}` : `scale ${xyz(object.scale)}`);\n }\n const top = topHeight(object);\n if (top !== null) parts.push(`top ${n(top)}m`);\n if (object.hidden) parts.push('hidden');\n if (object.locked) parts.push('locked');\n return parts.join(' · ');\n}\n\n/** The objects alone - what a tool result hands back after it changed them. */\nexport function describeObjects(project: SetProject): string {\n if (project.objects.length === 0) return 'SET: empty.';\n return [`SET (${project.objects.length} objects):`, ...project.objects.map(describeObject)].join('\\n');\n}\n\n/** Stills the board already holds, offered to `generate_object` as a source. */\nexport interface StillRef {\n id: string;\n label?: string;\n}\n\nexport function describeSet(project: SetProject, stills: readonly StillRef[] = []): string {\n const { camera, world } = project;\n const lighting = lightingOf(project);\n const lines = [\n `TITLE: \"${project.title}\" · framed ${project.aspect}`,\n `CAMERA: at ${xyz(camera.position)} looking at ${xyz(camera.target)} · ${n(camera.fov)}° fov (${focalLengthFromFov(camera.fov)}mm)`,\n `LIGHT: key from ${n(lighting.keyAzimuthDeg)}° azimuth, ${n(lighting.keyElevationDeg)}° above the horizon, intensity ${n(lighting.keyIntensity)} · ambient ${n(lighting.ambientIntensity)}`,\n world.imageUrl\n ? `WORLD: a generated 360 panorama${world.prompt ? ` (\"${world.prompt}\")` : ''}, turned ${n(world.rotationDeg)}° · ground ${world.showGround ? 'on' : 'off'}`\n : `WORLD: plain backdrop ${world.background} · ground ${world.showGround ? 'on' : 'off'} · no panorama yet`,\n describeObjects(project),\n ];\n if (stills.length > 0) {\n lines.push(\n `STILLS ON THE BOARD (usable as generate_object sources):`,\n ...stills.map((s) => `- still_id=${s.id}${s.label ? ` \"${s.label}\"` : ''}`),\n );\n }\n return lines.join('\\n');\n}\n"],"names":[],"mappings":"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"}
|