@dailies-studio/shot-editor 0.1.0 → 0.2.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/dist/.vite/manifest.json +382 -0
- package/dist/assets/AgentPanel-CQejLbiX.js +2 -0
- package/dist/assets/AgentPanel-DJEphah0.css +1 -0
- package/dist/assets/AgentQuestionDialog-BqkD0kXF.js +1 -0
- package/dist/assets/CodeStudio-Dv4ihHpz.js +23 -0
- package/dist/assets/CommentsPanel-B4yhhT1T.js +1 -0
- package/dist/assets/ComponentLibraryRail-Ch_dsAru.js +1 -0
- package/dist/assets/ComponentStage-BRhRotyH.js +1 -0
- package/dist/assets/CurvePane-BjFVabKX.css +1 -0
- package/dist/assets/CurvePane-R22WzllJ.js +1 -0
- package/dist/assets/ExportDialog-CWLQWTmG.js +2 -0
- package/dist/assets/FontPicker-DWKaYL7F.js +1 -0
- package/dist/assets/FontPicker-v0IOIArM.css +1 -0
- package/dist/assets/GenerateDrawer-Bh3SqDKt.js +2 -0
- package/dist/assets/InsertDrawer-Ce1fod1L.js +1 -0
- package/dist/assets/LibraryBrowser-D9sEGz5w.js +2 -0
- package/dist/assets/MakeItYoursSection-CMO4319_.js +2 -0
- package/dist/assets/MaterialGraphCanvas-BFzl4wWR.js +23 -0
- package/dist/assets/MaterialGraphCanvas-BIUD59ik.css +1 -0
- package/dist/assets/MaterialWorkspace-CqTWqaRA.js +14 -0
- package/dist/assets/PreferencesDialog-CBf9_OoD.js +1 -0
- package/dist/assets/PresetsSection-CUEaoCVU.js +1 -0
- package/dist/assets/SoundHistory-BeS8EW1h.js +1 -0
- package/dist/assets/SoundMode-BmcZru3i.js +3 -0
- package/dist/assets/StockSounds-DdAUGRYK.js +1 -0
- package/dist/assets/TemplateFieldSection-CaeXpLFo.js +1 -0
- package/dist/assets/VersionHistoryPanel-CxG6XhNP.js +1 -0
- package/dist/assets/board-B88EPuLO.css +1 -0
- package/dist/assets/board-CL-he7SL.js +3 -0
- package/dist/assets/components-0oOSPN0l.js +1 -0
- package/dist/assets/googleFontRanges-gp32wJMo.js +1 -0
- package/dist/assets/googleFonts-BNon6M7X.js +1 -0
- package/dist/assets/iconsAndAliases-DL7gXP5j.js +1863 -0
- package/dist/assets/index-4F5NQmI8.css +1 -0
- package/dist/assets/index-BGHQGV5z.js +117 -0
- package/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/dist/assets/libraryModel-CpIwfFha.js +1 -0
- package/dist/assets/lucide-6iK64tTB.js +9319 -0
- package/dist/assets/motionStamp-CH-Hew4E.js +1 -0
- package/dist/assets/takeMeasure-Tdg0TxEi.js +1 -0
- package/dist/assets/webmcp-BsB9gxWC.js +2 -0
- package/dist/index.html +34 -3
- package/package.json +12 -9
- package/src/addFamilies.ts +66 -0
- package/src/api.ts +1631 -55
- package/src/app/App.tsx +977 -198
- package/src/app/lazyPanel.tsx +137 -0
- package/src/app/lazySurfaces.tsx +203 -0
- package/src/audio/index.tsx +41 -10
- package/src/audio/meter.ts +485 -0
- package/src/audio/meterMath.ts +273 -0
- package/src/audio/meterStore.ts +155 -0
- package/src/audio/offlineMix.ts +289 -0
- package/src/audio/preview.ts +928 -118
- package/src/bridge.ts +15 -5
- package/src/bridgeTypes.ts +1611 -36
- package/src/channels.ts +17 -32
- package/src/fonts/googleFonts.ts +4 -0
- package/src/hostApi.ts +174 -0
- package/src/icons.tsx +770 -0
- package/src/layerWords.ts +61 -0
- package/src/main.tsx +56 -4
- package/src/panels/ResizeHandle.tsx +33 -43
- package/src/panels/agent/AgentDots.tsx +36 -0
- package/src/panels/agent/AgentPanel.tsx +119 -43
- package/src/panels/agent/AgentQuestionDialog.tsx +202 -0
- package/src/panels/agent/agent.css +113 -97
- package/src/panels/agent/agentGate.ts +35 -0
- package/src/panels/code/CodeDefaultsPanel.tsx +345 -0
- package/src/panels/code/CodeStudio.tsx +373 -0
- package/src/panels/code/FileTree.tsx +265 -0
- package/src/panels/code/codeBufferGuard.ts +52 -0
- package/src/panels/code/codeParts.tsx +321 -0
- package/src/panels/comments/CommentComposer.tsx +239 -0
- package/src/panels/comments/CommentThread.tsx +346 -0
- package/src/panels/comments/CommentsPanel.tsx +336 -0
- package/src/panels/comments/InboxButton.tsx +131 -0
- package/src/panels/comments/MentionMenu.tsx +98 -0
- package/src/panels/comments/parts.tsx +171 -0
- package/src/panels/export/ExportDialog.tsx +919 -96
- package/src/panels/export/ExportScrim.tsx +57 -0
- package/src/panels/files/AssetsTab.tsx +656 -0
- package/src/panels/files/FilePanel.tsx +1466 -216
- package/src/panels/files/LayerMarks.tsx +40 -0
- package/src/panels/files/LeftColumn.tsx +37 -0
- package/src/panels/files/McpSection.tsx +690 -47
- package/src/panels/files/MissingAssetsNotice.tsx +60 -0
- package/src/panels/files/assetDrag.ts +79 -0
- package/src/panels/files/resourceParts.tsx +255 -0
- package/src/panels/fonts/FontPicker.tsx +762 -0
- package/src/panels/fonts/faceCache.ts +207 -0
- package/src/panels/fonts/fontPicker.css +271 -0
- package/src/panels/fonts/pickerModel.ts +265 -0
- package/src/panels/fonts/pickerRequest.ts +46 -0
- package/src/panels/header/AccountAvatar.tsx +76 -0
- package/src/panels/header/AgentChip.tsx +77 -0
- package/src/panels/header/AppMenu.tsx +213 -0
- package/src/panels/header/Header.tsx +564 -84
- package/src/panels/header/HistoryControls.tsx +31 -5
- package/src/panels/header/PresenceStack.tsx +329 -0
- package/src/panels/header/ProjectTitle.tsx +232 -0
- package/src/panels/header/ShareButton.tsx +59 -0
- package/src/panels/header/StatusBadges.tsx +358 -27
- package/src/panels/header/SyncDeletedDialog.tsx +88 -0
- package/src/panels/header/ZoomControls.tsx +46 -15
- package/src/panels/header/agentHandoff.ts +52 -0
- package/src/panels/header/leave.ts +26 -0
- package/src/panels/help/HelpSection.tsx +189 -163
- package/src/panels/insert/InsertDrawer.tsx +723 -0
- package/src/panels/insert/iconSet.ts +109 -0
- package/src/panels/inspector/ExposeDot.tsx +117 -0
- package/src/panels/inspector/FramingRows.tsx +314 -44
- package/src/panels/inspector/InspectorFooter.tsx +91 -0
- package/src/panels/inspector/InspectorHead.tsx +425 -0
- package/src/panels/inspector/InspectorPanel.tsx +408 -124
- package/src/panels/inspector/KeyframeDot.tsx +373 -0
- package/src/panels/inspector/KeyframeSection.tsx +257 -0
- package/src/panels/inspector/MarkerSection.tsx +55 -11
- package/src/panels/inspector/ParamRow.tsx +84 -53
- package/src/panels/inspector/RightColumn.tsx +152 -0
- package/src/panels/inspector/TimeSection.tsx +866 -0
- package/src/panels/inspector/animate/EaseControl.tsx +244 -0
- package/src/panels/inspector/animate/PresetsSection.tsx +135 -0
- package/src/panels/inspector/animate/ShotSection.tsx +562 -48
- package/src/panels/inspector/design/AlignRow.tsx +95 -31
- package/src/panels/inspector/design/AppearanceSection.tsx +118 -9
- package/src/panels/inspector/design/ClonerSection.tsx +323 -62
- package/src/panels/inspector/design/ComponentSection.tsx +50 -43
- package/src/panels/inspector/design/CornerSection.tsx +45 -14
- package/src/panels/inspector/design/DefinitionProps.tsx +315 -0
- package/src/panels/inspector/design/DefinitionSection.tsx +136 -0
- package/src/panels/inspector/design/DeviceSection.tsx +210 -0
- package/src/panels/inspector/design/FillSection.tsx +83 -51
- package/src/panels/inspector/design/FontField.tsx +196 -0
- package/src/panels/inspector/design/HtmlSection.tsx +23 -12
- package/src/panels/inspector/design/InstanceSection.tsx +373 -154
- package/src/panels/inspector/design/MaskSection.tsx +84 -0
- package/src/panels/inspector/design/MaterialSection.tsx +177 -0
- package/src/panels/inspector/design/MediaSection.tsx +273 -120
- package/src/panels/inspector/design/MeshSection.tsx +910 -82
- package/src/panels/inspector/design/PaintRows.tsx +550 -0
- package/src/panels/inspector/design/PlateStyleSection.tsx +177 -0
- package/src/panels/inspector/design/ShadowSection.tsx +221 -48
- package/src/panels/inspector/design/SplineFollowSection.tsx +235 -0
- package/src/panels/inspector/design/StrokeSection.tsx +228 -43
- package/src/panels/inspector/design/TemplateFieldSection.tsx +57 -0
- package/src/panels/inspector/design/TransformSection.tsx +244 -50
- package/src/panels/inspector/design/TypographySection.tsx +679 -110
- package/src/panels/inspector/layout.ts +295 -0
- package/src/panels/inspector/propValue.ts +199 -0
- package/src/panels/inspector/scene/CameraSection.tsx +232 -22
- package/src/panels/inspector/scene/EffectsSection.tsx +722 -71
- package/src/panels/inspector/scene/MakeItYoursSection.tsx +180 -0
- package/src/panels/inspector/scene/SceneDocSection.tsx +191 -0
- package/src/panels/inspector/scene/SceneSection.tsx +219 -44
- package/src/panels/inspector/scene/TransitionSection.tsx +523 -72
- package/src/panels/inspector/storyboard/StoryboardReferenceSection.tsx +593 -0
- package/src/panels/inspector/storyboard/VideoStill.tsx +120 -0
- package/src/panels/inspector/timeModel.ts +190 -0
- package/src/panels/inspector/useDocWrite.ts +438 -146
- package/src/panels/library/LibraryBrowser.tsx +435 -0
- package/src/panels/library/LibraryGrid.tsx +190 -0
- package/src/panels/library/LibraryPreviewPanel.tsx +349 -0
- package/src/panels/library/LibraryRail.tsx +84 -0
- package/src/panels/library/LibrarySoundPanel.tsx +293 -0
- package/src/panels/library/SoundHistory.tsx +245 -0
- package/src/panels/library/StockSounds.tsx +390 -0
- package/src/panels/library/libraryModel.ts +147 -0
- package/src/panels/library/stockSearch.ts +77 -0
- package/src/panels/maskMarks.tsx +117 -0
- package/src/panels/material/MaterialCommentNode.tsx +54 -0
- package/src/panels/material/MaterialGraphCanvas.tsx +861 -0
- package/src/panels/material/MaterialGraphMenu.tsx +45 -0
- package/src/panels/material/MaterialGroupNode.tsx +56 -0
- package/src/panels/material/MaterialLibrary.tsx +253 -0
- package/src/panels/material/MaterialNode.tsx +453 -0
- package/src/panels/material/MaterialNodePalette.tsx +349 -0
- package/src/panels/material/MaterialParameterControls.tsx +525 -0
- package/src/panels/material/MaterialRerouteNode.tsx +42 -0
- package/src/panels/material/MaterialWorkspace.tsx +501 -0
- package/src/panels/material/NodePreview.tsx +70 -0
- package/src/panels/material/flowModel.ts +441 -0
- package/src/panels/material/parameterValues.ts +231 -0
- package/src/panels/material/previewScheduler.ts +235 -0
- package/src/panels/material/reactFlow.css +6 -0
- package/src/panels/material/socketGlyph.ts +85 -0
- package/src/panels/material/viewportMemory.ts +58 -0
- package/src/panels/preferences/PreferencesDialog.tsx +215 -0
- package/src/panels/share/ShareComponentDialog.tsx +247 -0
- package/src/panels/stage/StageMenu.tsx +35 -5
- package/src/panels/stage/StagePanel.tsx +338 -23
- package/src/panels/stage/cellSlots.tsx +70 -0
- package/src/panels/storyboard/BoardCanvas.tsx +739 -0
- package/src/panels/storyboard/BoardCursors.tsx +158 -0
- package/src/panels/storyboard/BoardDropDialog.tsx +161 -0
- package/src/panels/storyboard/BoardPlan.tsx +42 -0
- package/src/panels/storyboard/ComponentSheet.tsx +187 -0
- package/src/panels/storyboard/ComponentStage.tsx +235 -0
- package/src/panels/storyboard/MiniSketch.tsx +173 -0
- package/src/panels/storyboard/PartPicker.tsx +64 -0
- package/src/panels/storyboard/PlanPanel.tsx +617 -0
- package/src/panels/storyboard/ReferencePreview.tsx +217 -0
- package/src/panels/storyboard/ShotBrief.tsx +360 -0
- package/src/panels/storyboard/ShotCard.tsx +251 -0
- package/src/panels/storyboard/StagePreview.tsx +348 -0
- package/src/panels/storyboard/StillThumbs.tsx +162 -0
- package/src/panels/storyboard/StoryboardView.tsx +79 -0
- package/src/panels/storyboard/board.css +1065 -0
- package/src/panels/storyboard/board.ts +12 -0
- package/src/panels/storyboard/boardClipboard.ts +111 -0
- package/src/panels/storyboard/boardOrder.ts +68 -0
- package/src/panels/storyboard/boardRows.tsx +425 -0
- package/src/panels/storyboard/liveCap.ts +90 -0
- package/src/panels/storyboard/plan.css +295 -0
- package/src/panels/storyboard/posterCache.ts +133 -0
- package/src/panels/storyboard/shotboard/ComponentPicker.tsx +373 -0
- package/src/panels/storyboard/shotboard/ImagePicker.tsx +145 -0
- package/src/panels/storyboard/shotboard/ShotBoard.tsx +292 -0
- package/src/panels/storyboard/shotboard/SketchCanvas.tsx +443 -0
- package/src/panels/storyboard/shotboard/SketchInspector.tsx +471 -0
- package/src/panels/storyboard/shotboard/SketchItem.tsx +240 -0
- package/src/panels/storyboard/shotboard/SketchLayers.tsx +130 -0
- package/src/panels/storyboard/shotboard/SketchPlay.tsx +108 -0
- package/src/panels/storyboard/shotboard/SketchStrip.tsx +178 -0
- package/src/panels/storyboard/shotboard/SketchToolFooter.tsx +99 -0
- package/src/panels/storyboard/shotboard/sketch.css +1392 -0
- package/src/panels/storyboard/shotboard/sketchModel.ts +219 -0
- package/src/panels/storyboard/storyboardDrop.ts +164 -0
- package/src/panels/studio/ComponentLibraryRail.tsx +346 -0
- package/src/panels/studio/ComponentSidePanel.tsx +355 -0
- package/src/panels/studio/ComponentStudio.tsx +243 -0
- package/src/panels/studio/ComponentsHome.tsx +318 -0
- package/src/panels/studio/ComponentsPreview.tsx +179 -0
- package/src/panels/studio/EveryUse.tsx +244 -0
- package/src/panels/studio/ThisUsePanel.tsx +679 -0
- package/src/panels/studio/componentParts.tsx +341 -0
- package/src/panels/studio/components.ts +6 -0
- package/src/panels/studio/newCodeComponent.ts +66 -0
- package/src/panels/timeline/AudioFxButton.tsx +290 -0
- package/src/panels/timeline/AudioLane.tsx +674 -85
- package/src/panels/timeline/BlockRow.tsx +154 -85
- package/src/panels/timeline/ChannelRow.tsx +378 -129
- package/src/panels/timeline/CurvePane.tsx +508 -0
- package/src/panels/timeline/Filmstrip.tsx +361 -0
- package/src/panels/timeline/GenerateDrawer.tsx +407 -0
- package/src/panels/timeline/KeyStretch.tsx +115 -0
- package/src/panels/timeline/LifespanRow.tsx +730 -47
- package/src/panels/timeline/MarkerLane.tsx +46 -8
- package/src/panels/timeline/PlayButton.tsx +91 -0
- package/src/panels/timeline/PropertyPicker.tsx +254 -0
- package/src/panels/timeline/ShotRow.tsx +400 -0
- package/src/panels/timeline/SoundMode.tsx +1406 -0
- package/src/panels/timeline/TimelineDock.tsx +2338 -272
- package/src/panels/timeline/TransportVolume.tsx +163 -0
- package/src/panels/timeline/beatRuler.ts +74 -0
- package/src/panels/timeline/curveModel.ts +500 -0
- package/src/panels/timeline/duckUnderVoice.ts +68 -0
- package/src/panels/timeline/keyframeMenu.ts +138 -0
- package/src/panels/timeline/labelWidth.ts +49 -0
- package/src/panels/timeline/marquee.ts +155 -0
- package/src/panels/timeline/model.ts +1860 -199
- package/src/panels/timeline/snap.ts +125 -43
- package/src/panels/timeline/staggerPreview.tsx +92 -0
- package/src/panels/timeline/takeMeasure.ts +72 -0
- package/src/panels/timeline/useLive.ts +48 -0
- package/src/panels/timeline/viewport.ts +234 -0
- package/src/panels/toolbar/BackChip.tsx +116 -0
- package/src/panels/toolbar/CameraViewBar.tsx +118 -49
- package/src/panels/toolbar/CanvasBar.tsx +117 -0
- package/src/panels/toolbar/InsertButton.tsx +38 -0
- package/src/panels/toolbar/IsolationTrail.tsx +156 -0
- package/src/panels/toolbar/PanelToggles.tsx +86 -0
- package/src/panels/toolbar/ShadingBar.tsx +119 -26
- package/src/panels/toolbar/ToolDockBar.tsx +329 -80
- package/src/panels/toolbar/ToolFooter.tsx +102 -0
- package/src/panels/toolbar/ViewModeBar.tsx +247 -38
- package/src/panels/toolbar/WorkPlaneBar.tsx +94 -0
- package/src/panels/toolbar/useAddRows.tsx +326 -0
- package/src/panels/toolbar/useDismiss.ts +122 -0
- package/src/panels/versions/VersionHistoryPanel.tsx +274 -0
- package/src/panels/versions/versionModel.ts +103 -0
- package/src/panels/virtualWindow.ts +271 -0
- package/src/shotTokens.css +151 -60
- package/src/stage/FrustumGizmo.tsx +504 -84
- package/src/stage/GridPlane.tsx +225 -16
- package/src/stage/NodePreviews.tsx +628 -0
- package/src/stage/PerfHud.tsx +437 -47
- package/src/stage/SplitRenderer.tsx +816 -169
- package/src/stage/StageApp.tsx +4274 -486
- package/src/stage/StageRootBoundary.tsx +114 -0
- package/src/stage/StageTools.tsx +651 -151
- package/src/stage/TransformGizmo.tsx +696 -0
- package/src/stage/adaptivePresentation.ts +145 -0
- package/src/stage/bridgeClient.ts +2522 -41
- package/src/stage/exportWorker.ts +9 -0
- package/src/stage/feed.ts +305 -14
- package/src/stage/fontPreview.ts +176 -0
- package/src/stage/frustumGeometry.ts +319 -1
- package/src/stage/furnitureTokens.ts +33 -0
- package/src/stage/gizmoMath.ts +508 -16
- package/src/stage/gpuTimer.ts +276 -0
- package/src/stage/instanceModules.ts +194 -33
- package/src/stage/interactions/move.ts +24 -5
- package/src/stage/isolation.ts +291 -33
- package/src/stage/jsSelfProfiler.ts +325 -0
- package/src/stage/main.tsx +116 -16
- package/src/stage/modalState.ts +222 -23
- package/src/stage/moduleEpoch.ts +60 -0
- package/src/stage/motionPathStage.ts +279 -0
- package/src/stage/motionPreview.ts +74 -0
- package/src/stage/overlays/Overlays.tsx +1112 -159
- package/src/stage/overlays/Rulers.tsx +495 -0
- package/src/stage/panes.ts +159 -10
- package/src/stage/project.ts +68 -3
- package/src/stage/renderScaleController.ts +730 -0
- package/src/stage/rulers.ts +129 -0
- package/src/stage/sceneExtent.ts +75 -0
- package/src/stage/shading.ts +20 -0
- package/src/stage/splineStage.ts +632 -0
- package/src/stage/stagePerformanceTelemetry.ts +717 -0
- package/src/stage/stageTheme.ts +36 -0
- package/src/stage/stageWarmup.ts +11 -0
- package/src/stage/textAutoSize.ts +124 -0
- package/src/stage/tier1Export.ts +641 -0
- package/src/stage/tokenColor.ts +14 -9
- package/src/stage/view.ts +56 -9
- package/src/stage/workingView.ts +277 -33
- package/src/state/accountPrefs.ts +261 -0
- package/src/state/agentActions.ts +173 -15
- package/src/state/agentActivity.ts +198 -0
- package/src/state/agentQuestion.ts +239 -0
- package/src/state/bootOpen.ts +42 -0
- package/src/state/cameraVerbs.ts +139 -0
- package/src/state/clientId.ts +6 -1
- package/src/state/comments.ts +1099 -0
- package/src/state/context.tsx +19 -1
- package/src/state/editorPrefs.ts +220 -0
- package/src/state/errorReports.ts +121 -0
- package/src/state/fieldWrite.ts +161 -0
- package/src/state/fontRecents.ts +70 -0
- package/src/state/fonts.ts +656 -0
- package/src/state/graphClipboard.ts +414 -0
- package/src/state/imageThumbs.ts +216 -0
- package/src/state/insert.ts +444 -67
- package/src/state/isolationTrail.ts +92 -0
- package/src/state/keyMoves.ts +134 -0
- package/src/state/keyframeClipboard.ts +177 -0
- package/src/state/keymap.ts +1153 -117
- package/src/state/layoutPrefs.ts +272 -0
- package/src/state/layouts.ts +205 -0
- package/src/state/measureMedia.ts +167 -0
- package/src/state/menu.ts +2341 -28
- package/src/state/motionStamp.ts +7 -0
- package/src/state/panelSize.ts +375 -5
- package/src/state/pendingChanges.ts +119 -0
- package/src/state/playHold.ts +78 -0
- package/src/state/playbackPreparation.ts +562 -0
- package/src/state/posterCapture.ts +242 -0
- package/src/state/presence.ts +254 -0
- package/src/state/projectComponents.ts +160 -0
- package/src/state/renderEvents.ts +298 -18
- package/src/state/runtimeErrors.ts +46 -0
- package/src/state/selection.ts +12 -0
- package/src/state/shortcuts.ts +1606 -0
- package/src/state/sketchGuide.ts +51 -0
- package/src/state/splineRefs.ts +87 -0
- package/src/state/stageHealth.ts +259 -0
- package/src/state/stageWatchdog.ts +47 -0
- package/src/state/stillStage.ts +330 -0
- package/src/state/stillThumbs.ts +474 -0
- package/src/state/store.ts +23591 -2982
- package/src/state/storyboardModel.ts +528 -0
- package/src/state/sync.ts +1257 -0
- package/src/state/syncCache.ts +159 -0
- package/src/state/triangles.ts +13 -0
- package/src/state/types.ts +1698 -55
- package/src/state/unsavedGuard.ts +49 -0
- package/src/state/viewMode.ts +200 -0
- package/src/state/webmcp.ts +1128 -0
- package/src/state/workspace.ts +1147 -0
- package/src/styles/assets-tab.css +304 -0
- package/src/styles/canvas.css +545 -0
- package/src/styles/comments.css +1435 -0
- package/src/styles/controls.css +162 -0
- package/src/styles/dialogs.css +390 -0
- package/src/styles/insert-drawer.css +586 -0
- package/src/styles/inspector-board.css +207 -0
- package/src/styles/inspector-object.css +486 -0
- package/src/styles/inspector-shot.css +252 -0
- package/src/styles/inspector.css +360 -0
- package/src/styles/left-column.css +1628 -0
- package/src/styles/library-preview.css +575 -0
- package/src/styles/library.css +512 -0
- package/src/styles/modes/code.css +569 -0
- package/src/styles/modes/component.css +1561 -0
- package/src/styles/modes/material.css +1201 -0
- package/src/styles/modes/storyboard.css +518 -0
- package/src/styles/share-component.css +49 -0
- package/src/styles/shell.css +653 -0
- package/src/styles/shot-brief.css +360 -0
- package/src/styles/states.css +76 -0
- package/src/styles/sync.css +380 -0
- package/src/styles/timeline-graph.css +384 -0
- package/src/styles/timeline-meters.css +89 -0
- package/src/styles/timeline-rows.css +1959 -0
- package/src/styles/timeline-sound.css +1003 -0
- package/src/styles/timeline.css +754 -0
- package/src/styles/tool-footer.css +525 -0
- package/src/styles/topbar.css +886 -0
- package/src/styles/versions.css +262 -0
- package/src/styles.css +72 -2921
- package/src/tools.ts +108 -23
- package/dist/assets/index-B7notge7.css +0 -1
- package/dist/assets/index-BBE6Ta9L.js +0 -52
- package/src/panels/files/icons.tsx +0 -118
- package/src/panels/header/DocSwitcher.tsx +0 -85
- package/src/panels/header/TransportBar.tsx +0 -86
- package/src/panels/inspector/animate/BlockTiming.tsx +0 -341
- package/src/panels/inspector/animate/DuringBlocks.tsx +0 -85
- package/src/panels/inspector/animate/InOutPresets.tsx +0 -57
- package/src/panels/inspector/animate/ResolvedChannels.tsx +0 -150
- package/src/panels/inspector/animate/useBlocks.ts +0 -43
- package/src/panels/material/MaterialPicker.tsx +0 -172
- package/src/panels/timeline/SceneStrip.tsx +0 -145
- package/src/panels/toolbar/AddMenu.tsx +0 -230
- package/src/stage/MoveGizmo.tsx +0 -411
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// WHAT AN MCP AGENT IS DOING, for the "agent working" indicator.
|
|
2
|
+
//
|
|
3
|
+
// A store of its own, like presence: it never enters undo, the save or the
|
|
4
|
+
// document. It is fed by the feed's `agent-activity` signals (renderEvents.ts),
|
|
5
|
+
// which both servers send around every MCP tool call (store mcpActivity.ts).
|
|
6
|
+
//
|
|
7
|
+
// ── WORKING BETWEEN CALLS ───────────────────────────────────────────────────
|
|
8
|
+
//
|
|
9
|
+
// An agent thinks between calls, so an agent whose last call ended is still
|
|
10
|
+
// "working" for `AGENT_GRACE_MS`. A signal is never replayed, so a missed
|
|
11
|
+
// `end` cannot leave a face up for good: a call in flight expires after
|
|
12
|
+
// `AGENT_CALL_MAX_MS`. Wall time is fine here: this is editor chrome, never
|
|
13
|
+
// the stage.
|
|
14
|
+
import { findLayer } from "@dailies-studio/shot/doc";
|
|
15
|
+
import type { Doc3D } from "@dailies-studio/shot/doc";
|
|
16
|
+
|
|
17
|
+
/** The `agent-activity` payload (store docEvents.ts `AgentActivityPayload`),
|
|
18
|
+
* restated: the chrome does not depend on the store package. */
|
|
19
|
+
export interface AgentActivityPayload {
|
|
20
|
+
v: 1;
|
|
21
|
+
sessionId: string;
|
|
22
|
+
client?: string;
|
|
23
|
+
phase: "start" | "end";
|
|
24
|
+
tool: string;
|
|
25
|
+
step: string;
|
|
26
|
+
project?: string;
|
|
27
|
+
layers?: string[];
|
|
28
|
+
file?: string;
|
|
29
|
+
component?: string;
|
|
30
|
+
props?: Record<string, unknown>;
|
|
31
|
+
frame?: number;
|
|
32
|
+
ok?: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** A frame's data as an `AgentActivityPayload`, or null for anything else. */
|
|
36
|
+
export function parseAgentActivity(data: string): AgentActivityPayload | null {
|
|
37
|
+
let raw: unknown;
|
|
38
|
+
try {
|
|
39
|
+
raw = JSON.parse(data);
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
if (raw === null || typeof raw !== "object") return null;
|
|
44
|
+
const o = raw as Record<string, unknown>;
|
|
45
|
+
if (o["v"] !== 1 || typeof o["sessionId"] !== "string" || typeof o["tool"] !== "string" || typeof o["step"] !== "string") return null;
|
|
46
|
+
if (o["phase"] !== "start" && o["phase"] !== "end") return null;
|
|
47
|
+
const text = (k: string): string | undefined => (typeof o[k] === "string" ? (o[k] as string) : undefined);
|
|
48
|
+
const out: AgentActivityPayload = { v: 1, sessionId: o["sessionId"], phase: o["phase"], tool: o["tool"], step: o["step"] };
|
|
49
|
+
for (const k of ["client", "project", "file", "component"] as const) {
|
|
50
|
+
const v = text(k);
|
|
51
|
+
if (v !== undefined) out[k] = v;
|
|
52
|
+
}
|
|
53
|
+
if (Array.isArray(o["layers"])) out.layers = o["layers"].filter((x): x is string => typeof x === "string");
|
|
54
|
+
if (o["props"] !== null && typeof o["props"] === "object" && !Array.isArray(o["props"])) out.props = o["props"] as Record<string, unknown>;
|
|
55
|
+
if (typeof o["frame"] === "number" && Number.isFinite(o["frame"])) out.frame = o["frame"];
|
|
56
|
+
if (typeof o["ok"] === "boolean") out.ok = o["ok"];
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** An agent stays "working" this long after its last call ended. */
|
|
61
|
+
export const AGENT_GRACE_MS = 20_000;
|
|
62
|
+
/** A call with no `end` is dropped after this long. */
|
|
63
|
+
export const AGENT_CALL_MAX_MS = 120_000;
|
|
64
|
+
|
|
65
|
+
/** One agent, by MCP session: what it is doing now and what it last touched. */
|
|
66
|
+
export interface AgentWork {
|
|
67
|
+
sessionId: string;
|
|
68
|
+
/** Its client's name ("Claude"), else "Agent". */
|
|
69
|
+
name: string;
|
|
70
|
+
step: string;
|
|
71
|
+
tool: string;
|
|
72
|
+
/** A call is in flight. */
|
|
73
|
+
busy: boolean;
|
|
74
|
+
project: string | null;
|
|
75
|
+
layers: readonly string[];
|
|
76
|
+
file: string | null;
|
|
77
|
+
component: string | null;
|
|
78
|
+
/** The source file and component it last wrote, kept while it works: it
|
|
79
|
+
* compiles and previews between writes, and is still the file's writer. */
|
|
80
|
+
wroteFile: string | null;
|
|
81
|
+
wroteComponent: string | null;
|
|
82
|
+
/** The last component preview it asked for, until another replaces it. */
|
|
83
|
+
preview: { component: string; props: Record<string, unknown>; frame: number } | null;
|
|
84
|
+
/** When it was last heard from (`Date.now()`). */
|
|
85
|
+
at: number;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface AgentActivityState {
|
|
89
|
+
/** In arrival order. */
|
|
90
|
+
agents: readonly AgentWork[];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface AgentActivityStore {
|
|
94
|
+
getState(): AgentActivityState;
|
|
95
|
+
subscribe(listener: () => void): () => void;
|
|
96
|
+
receive(event: AgentActivityPayload): void;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const EMPTY: AgentActivityState = { agents: [] };
|
|
100
|
+
|
|
101
|
+
/** Is `work` still working at `now`. */
|
|
102
|
+
export function isWorking(work: AgentWork, now: number): boolean {
|
|
103
|
+
return now - work.at < (work.busy ? AGENT_CALL_MAX_MS : AGENT_GRACE_MS);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Fold one signal into the agents. Pure, for the tests and the store. */
|
|
107
|
+
export function foldActivity(agents: readonly AgentWork[], event: AgentActivityPayload, now: number): AgentWork[] {
|
|
108
|
+
const seen = agents.find((a) => a.sessionId === event.sessionId);
|
|
109
|
+
const preview =
|
|
110
|
+
event.tool === "preview_component" && event.component !== undefined
|
|
111
|
+
? { component: event.component, props: event.props ?? {}, frame: event.frame ?? 0 }
|
|
112
|
+
: (seen?.preview ?? null);
|
|
113
|
+
const writes = event.tool === "write_source_file" || event.tool === "edit_component_source";
|
|
114
|
+
const next: AgentWork = {
|
|
115
|
+
sessionId: event.sessionId,
|
|
116
|
+
name: event.client ?? seen?.name ?? "Agent",
|
|
117
|
+
step: event.step,
|
|
118
|
+
tool: event.tool,
|
|
119
|
+
busy: event.phase === "start",
|
|
120
|
+
// A read of the guide names no Project: keep the one it was working in.
|
|
121
|
+
project: event.project ?? seen?.project ?? null,
|
|
122
|
+
layers: event.layers ?? (event.phase === "end" ? (seen?.layers ?? []) : []),
|
|
123
|
+
file: event.file ?? (event.phase === "end" ? (seen?.file ?? null) : null),
|
|
124
|
+
component: event.component ?? (event.phase === "end" ? (seen?.component ?? null) : null),
|
|
125
|
+
wroteFile: writes && event.file !== undefined ? event.file : (seen?.wroteFile ?? null),
|
|
126
|
+
wroteComponent: writes && event.component !== undefined ? event.component : (seen?.wroteComponent ?? null),
|
|
127
|
+
preview,
|
|
128
|
+
at: now,
|
|
129
|
+
};
|
|
130
|
+
return seen === undefined ? [...agents, next] : agents.map((a) => (a === seen ? next : a));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function createAgentActivityStore(clock: () => number = () => Date.now()): AgentActivityStore {
|
|
134
|
+
let state = EMPTY;
|
|
135
|
+
const listeners = new Set<() => void>();
|
|
136
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
137
|
+
const emit = (): void => {
|
|
138
|
+
for (const l of listeners) l();
|
|
139
|
+
};
|
|
140
|
+
/** Drop the agents that stopped working, and wake for the next one. */
|
|
141
|
+
const sweep = (): void => {
|
|
142
|
+
timer = null;
|
|
143
|
+
const now = clock();
|
|
144
|
+
const kept = state.agents.filter((a) => isWorking(a, now));
|
|
145
|
+
if (kept.length !== state.agents.length) {
|
|
146
|
+
state = { agents: kept };
|
|
147
|
+
emit();
|
|
148
|
+
}
|
|
149
|
+
if (kept.length === 0) return;
|
|
150
|
+
const due = Math.min(...kept.map((a) => a.at + (a.busy ? AGENT_CALL_MAX_MS : AGENT_GRACE_MS) - now));
|
|
151
|
+
timer = setTimeout(sweep, Math.max(250, due));
|
|
152
|
+
};
|
|
153
|
+
return {
|
|
154
|
+
getState: () => state,
|
|
155
|
+
subscribe(listener) {
|
|
156
|
+
listeners.add(listener);
|
|
157
|
+
return () => listeners.delete(listener);
|
|
158
|
+
},
|
|
159
|
+
receive(event) {
|
|
160
|
+
state = { agents: foldActivity(state.agents, event, clock()) };
|
|
161
|
+
emit();
|
|
162
|
+
if (timer !== null) clearTimeout(timer);
|
|
163
|
+
sweep();
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** The agents working in Project `name`, newest last. */
|
|
169
|
+
export function agentsIn(state: AgentActivityState, name: string | null): AgentWork[] {
|
|
170
|
+
return name === null ? [] : state.agents.filter((a) => a.project === name);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** The agent writing `file` (project-relative) or component `id`, if any. */
|
|
174
|
+
export function agentWriting(state: AgentActivityState, file: string | null, componentId: string | null): AgentWork | null {
|
|
175
|
+
return (
|
|
176
|
+
state.agents.find(
|
|
177
|
+
(a) => (file !== null && a.wroteFile === file) || (componentId !== null && a.wroteComponent === componentId),
|
|
178
|
+
) ?? null
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* The planned shots an MCP agent is building now, in `boardBuilding`'s shape:
|
|
184
|
+
* each part whose Shot holds a layer an agent working in this Project named.
|
|
185
|
+
* A card reads Building for these as it does for this tab's own agent.
|
|
186
|
+
*/
|
|
187
|
+
export function agentBuildingParts(doc: Doc3D | null, state: AgentActivityState, name: string | null): { part: string }[] {
|
|
188
|
+
if (doc === null) return [];
|
|
189
|
+
const scenes = new Set<string>();
|
|
190
|
+
for (const agent of agentsIn(state, name)) {
|
|
191
|
+
for (const ref of agent.layers) {
|
|
192
|
+
const found = findLayer(doc, ref);
|
|
193
|
+
if (found !== null) scenes.add(found.sceneId);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (scenes.size === 0) return [];
|
|
197
|
+
return (doc.storyboard?.parts ?? []).filter((p) => p.scene !== undefined && scenes.has(p.scene)).map((p) => ({ part: p.id }));
|
|
198
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
// THE AGENT'S QUESTION to the person, for the open Project (store
|
|
2
|
+
// agentQuestions.ts; plans/AGENT-QUESTION-DIALOG-PLAN.md): "The storyboard
|
|
3
|
+
// is ready. Make the video?", asked in the editor by an MCP agent between
|
|
4
|
+
// planning and making.
|
|
5
|
+
//
|
|
6
|
+
// A store of its own, like the comments': never undo, never the save. It reads
|
|
7
|
+
// `GET /api/shots/:name/question` when a Project opens and again on each
|
|
8
|
+
// `agent-question` signal, so a tab opened after the ask still shows it. An
|
|
9
|
+
// open question opens the dialog, whatever workspace is on screen; Escape or
|
|
10
|
+
// "Open storyboard" closes it unanswered, and the status chip opens it again.
|
|
11
|
+
// The dialog shows a question only over its own Project (`projectId`), so a
|
|
12
|
+
// slow or failed read never puts one Project's question over another.
|
|
13
|
+
//
|
|
14
|
+
// An EXPORT QUESTION ("The video is built. Export it?") carries the Export
|
|
15
|
+
// dialog's choices. Its yes keeps them here as `exportAsk` until the export
|
|
16
|
+
// is saved: the Export dialog opens with them, and whatever it saves (to
|
|
17
|
+
// Videos, through the studio server's render, or only to the downloads) is
|
|
18
|
+
// reported back to the question, which wakes the agent's get_answer. Each
|
|
19
|
+
// read keeps it too while the yes has no video, so a reload, another tab or
|
|
20
|
+
// another editor can still export it.
|
|
21
|
+
import * as api from "../api.js";
|
|
22
|
+
import type { AgentQuestion, AgentQuestionExport, AgentQuestionVideo } from "../api.js";
|
|
23
|
+
|
|
24
|
+
/** An export the person said yes to, until its video is reported. */
|
|
25
|
+
export interface AgentExportAsk {
|
|
26
|
+
/** The Project's id: only its own Export dialog takes the ask. */
|
|
27
|
+
projectId: string;
|
|
28
|
+
questionId: string;
|
|
29
|
+
export: AgentQuestionExport;
|
|
30
|
+
/** The studio server's render started for it, reported once it is done. */
|
|
31
|
+
serverJob?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface AgentQuestionState {
|
|
35
|
+
/** The Project store name the question was read for. */
|
|
36
|
+
project: string | null;
|
|
37
|
+
question: AgentQuestion | null;
|
|
38
|
+
/** The dialog is on screen. */
|
|
39
|
+
open: boolean;
|
|
40
|
+
/** An answer is on its way. */
|
|
41
|
+
sending: boolean;
|
|
42
|
+
/** Why the last answer was refused, in the server's words. */
|
|
43
|
+
error: string | null;
|
|
44
|
+
/** An export question's yes, until its video is reported: this tab's, or
|
|
45
|
+
* one a read found. */
|
|
46
|
+
exportAsk: AgentExportAsk | null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface AgentQuestionStore {
|
|
50
|
+
getState(): AgentQuestionState;
|
|
51
|
+
subscribe(listener: () => void): () => void;
|
|
52
|
+
/** Read the Project's question (on open, and on each signal for it). */
|
|
53
|
+
load(project: string | null): Promise<void>;
|
|
54
|
+
/** Show or hide the dialog. Hiding never answers. */
|
|
55
|
+
show(): void;
|
|
56
|
+
hide(): void;
|
|
57
|
+
/** Answer the question on screen, posted to the Project's name now. */
|
|
58
|
+
answer(project: string, choice: "yes" | "changes", note?: string): Promise<boolean>;
|
|
59
|
+
/** The studio server's render started for the export ask, or null when
|
|
60
|
+
* that render ended without a file (the next export reports instead). */
|
|
61
|
+
exportOnServer(jobId: string | null): void;
|
|
62
|
+
/** Report the saved video to `ask`'s question, posted to the Project's
|
|
63
|
+
* name now. The ask is spent at once, and kept again (without its server
|
|
64
|
+
* render) when the report is refused. Throws the server's refusal. */
|
|
65
|
+
reportVideo(ask: AgentExportAsk, project: string, video: AgentQuestionVideo): Promise<void>;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const EMPTY: AgentQuestionState = { project: null, question: null, open: false, sending: false, error: null, exportAsk: null };
|
|
69
|
+
|
|
70
|
+
const isOpen = (q: AgentQuestion | null): q is AgentQuestion => q !== null && q.status === "open";
|
|
71
|
+
|
|
72
|
+
export function createAgentQuestionStore(): AgentQuestionStore {
|
|
73
|
+
let state = EMPTY;
|
|
74
|
+
const listeners = new Set<() => void>();
|
|
75
|
+
/** The questions the person closed unanswered: a re-read never re-opens
|
|
76
|
+
* them. Any other open question opens, a Project reopened later included. */
|
|
77
|
+
const dismissed = new Set<string>();
|
|
78
|
+
let generation = 0;
|
|
79
|
+
/** The answer in flight: only the latest one clears `sending`. */
|
|
80
|
+
let answering = 0;
|
|
81
|
+
/** The Project name that answer was posted to, and a re-read of it skipped
|
|
82
|
+
* meanwhile, read again once it settles. */
|
|
83
|
+
let sendingFor: string | null = null;
|
|
84
|
+
let missed = false;
|
|
85
|
+
const set = (patch: Partial<AgentQuestionState>): void => {
|
|
86
|
+
state = { ...state, ...patch };
|
|
87
|
+
for (const l of listeners) l();
|
|
88
|
+
};
|
|
89
|
+
/** A refused answer on screen: the question closed before it landed. It
|
|
90
|
+
* stays, saying why, until the person closes it. */
|
|
91
|
+
const refusalShown = (): boolean => state.open && state.error !== null && state.question !== null && !isOpen(state.question);
|
|
92
|
+
/** The export ask a read question makes: a yes to an export whose video
|
|
93
|
+
* nobody reported, whichever tab or editor said it, so the next export in
|
|
94
|
+
* this tab reports it (after a reload too). The ask held for it is kept,
|
|
95
|
+
* with its server render. Anything else holds none. */
|
|
96
|
+
const askOf = (question: AgentQuestion | null): AgentExportAsk | null => {
|
|
97
|
+
if (question?.export === undefined || question.status !== "answered" || question.answer?.choice !== "yes" || question.answer.video !== undefined) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
if (state.exportAsk?.questionId === question.id) return state.exportAsk;
|
|
101
|
+
return { projectId: question.projectId, questionId: question.id, export: question.export };
|
|
102
|
+
};
|
|
103
|
+
const store: AgentQuestionStore = {
|
|
104
|
+
getState: () => state,
|
|
105
|
+
subscribe(listener) {
|
|
106
|
+
listeners.add(listener);
|
|
107
|
+
return () => listeners.delete(listener);
|
|
108
|
+
},
|
|
109
|
+
async load(project) {
|
|
110
|
+
const gen = ++generation;
|
|
111
|
+
if (project === null) {
|
|
112
|
+
answering++;
|
|
113
|
+
sendingFor = null;
|
|
114
|
+
set({ ...EMPTY });
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
let question: AgentQuestion | null;
|
|
118
|
+
try {
|
|
119
|
+
question = await api.getAgentQuestion(project);
|
|
120
|
+
} catch {
|
|
121
|
+
// A server without the route, or one mid-restart: the next signal or
|
|
122
|
+
// Project open asks again.
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (gen !== generation) return;
|
|
126
|
+
// An answer in flight settles the question itself (taken, or refused
|
|
127
|
+
// with why): a signal racing it must not close the dialog first. It is
|
|
128
|
+
// read again once the answer lands, so a newer ask still opens.
|
|
129
|
+
if (sendingFor === project) {
|
|
130
|
+
missed = true;
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const wanted = isOpen(question) && !dismissed.has(question.id);
|
|
134
|
+
const exportAsk = askOf(question);
|
|
135
|
+
if (refusalShown() && !wanted) {
|
|
136
|
+
set({ project, exportAsk, ...(question !== null && question.id === state.question?.id ? { question } : {}) });
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
set({
|
|
140
|
+
project,
|
|
141
|
+
question,
|
|
142
|
+
exportAsk,
|
|
143
|
+
// An open question the person has not closed opens the dialog; a
|
|
144
|
+
// closed one closes it.
|
|
145
|
+
open: wanted,
|
|
146
|
+
error: question !== null && question.id === state.question?.id ? state.error : null,
|
|
147
|
+
});
|
|
148
|
+
},
|
|
149
|
+
show() {
|
|
150
|
+
if (state.question === null) return;
|
|
151
|
+
dismissed.delete(state.question.id);
|
|
152
|
+
set({ open: true });
|
|
153
|
+
},
|
|
154
|
+
hide() {
|
|
155
|
+
const refused = refusalShown();
|
|
156
|
+
if (state.question !== null) dismissed.add(state.question.id);
|
|
157
|
+
set({ open: false, error: null });
|
|
158
|
+
// A refusal kept the screen while signals came in: read what is open now
|
|
159
|
+
// (a newer ask opens).
|
|
160
|
+
if (refused && state.project !== null) void store.load(state.project);
|
|
161
|
+
},
|
|
162
|
+
async answer(project, choice, note) {
|
|
163
|
+
const { question } = state;
|
|
164
|
+
if (!isOpen(question) || state.sending) return false;
|
|
165
|
+
const mine = ++answering;
|
|
166
|
+
sendingFor = project;
|
|
167
|
+
missed = false;
|
|
168
|
+
set({ sending: true, error: null });
|
|
169
|
+
// The store may hold another question by the time the answer comes
|
|
170
|
+
// back: then the result is that one's business, and touches nothing.
|
|
171
|
+
const current = (): boolean => mine === answering && state.question?.id === question.id;
|
|
172
|
+
const done = (patch: Partial<AgentQuestionState>): void => {
|
|
173
|
+
if (mine !== answering) return;
|
|
174
|
+
sendingFor = null;
|
|
175
|
+
set({ sending: false, ...(current() ? patch : {}) });
|
|
176
|
+
if (missed) {
|
|
177
|
+
missed = false;
|
|
178
|
+
void store.load(project);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
try {
|
|
182
|
+
const answered = await api.answerAgentQuestion(project, question.id, note === undefined ? { choice } : { choice, note });
|
|
183
|
+
done({
|
|
184
|
+
question: answered,
|
|
185
|
+
open: false,
|
|
186
|
+
// An export's yes: the Export dialog opens with its choices, and
|
|
187
|
+
// reports what it saves.
|
|
188
|
+
...(choice === "yes" && answered.export !== undefined
|
|
189
|
+
? { exportAsk: { projectId: answered.projectId, questionId: answered.id, export: answered.export } }
|
|
190
|
+
: {}),
|
|
191
|
+
});
|
|
192
|
+
return true;
|
|
193
|
+
} catch (e) {
|
|
194
|
+
const error = e instanceof Error ? e.message : String(e);
|
|
195
|
+
if (!(e instanceof api.ApiError && e.code === "question_closed")) {
|
|
196
|
+
// Not taken, or not known: show why, even to one who closed the
|
|
197
|
+
// dialog while it was sending, and let a re-read say what the
|
|
198
|
+
// question is now (one recorded after all closes the dialog).
|
|
199
|
+
if (current()) dismissed.delete(question.id);
|
|
200
|
+
const reread = current();
|
|
201
|
+
done({ open: true, error });
|
|
202
|
+
if (reread) void store.load(project);
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
// Closed meanwhile: answered in another tab, withdrawn, or replaced.
|
|
206
|
+
// Kept on screen with who answered, never as sent. The route shows no
|
|
207
|
+
// withdrawn question, so one it does not show reads as withdrawn here.
|
|
208
|
+
let now: AgentQuestion | null = null;
|
|
209
|
+
try {
|
|
210
|
+
now = await api.getAgentQuestion(project);
|
|
211
|
+
} catch {
|
|
212
|
+
// Unknown: withdrawn is the safe reading, it claims no answer.
|
|
213
|
+
}
|
|
214
|
+
done({ open: true, error, question: now !== null && now.id === question.id ? now : { ...question, status: "withdrawn" } });
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
exportOnServer(jobId) {
|
|
219
|
+
const ask = state.exportAsk;
|
|
220
|
+
if (ask === null) return;
|
|
221
|
+
const { serverJob: _was, ...rest } = ask;
|
|
222
|
+
set({ exportAsk: jobId === null ? rest : { ...rest, serverJob: jobId } });
|
|
223
|
+
},
|
|
224
|
+
async reportVideo(ask, project, video) {
|
|
225
|
+
if (state.exportAsk === ask) set({ exportAsk: null });
|
|
226
|
+
try {
|
|
227
|
+
await api.reportAgentQuestionVideo(project, ask.questionId, video);
|
|
228
|
+
} catch (e) {
|
|
229
|
+
// Kept for the next export, unless another ask came meanwhile or the
|
|
230
|
+
// Project closed. Without its server render: that one was told once,
|
|
231
|
+
// and a report that failed is never posted again on its own.
|
|
232
|
+
const { serverJob: _told, ...kept } = ask;
|
|
233
|
+
if (state.exportAsk === null && state.project !== null) set({ exportAsk: kept });
|
|
234
|
+
throw e;
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
return store;
|
|
239
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// The document fetch, started at MODULE EVALUATION instead of after the first
|
|
2
|
+
// commit.
|
|
3
|
+
//
|
|
4
|
+
// `useOpenFromUrl` used to initiate the open inside a post-mount useEffect, so
|
|
5
|
+
// the HTTP GET waterfalled behind module evaluation, createRoot and the first
|
|
6
|
+
// commit — the exact span the `open→frame` markPerf measures. The store exists
|
|
7
|
+
// at module scope in main.tsx, so nothing forces the wait: main.tsx calls
|
|
8
|
+
// `startBootOpen` right after creating the store, and the effect ADOPTS the
|
|
9
|
+
// in-flight promise for its `?def=` / `?shot=` / `?material=` continuations
|
|
10
|
+
// instead of initiating a second fetch.
|
|
11
|
+
//
|
|
12
|
+
// The adoption is guarded on the STORE IDENTITY and the document name, because
|
|
13
|
+
// tests (and any future second mount) create their own stores: a promise
|
|
14
|
+
// started against one store must never satisfy another's open. A miss simply
|
|
15
|
+
// falls back to initiating, which is the old behavior exactly.
|
|
16
|
+
import type { ShotEditorStore } from "./store.js";
|
|
17
|
+
|
|
18
|
+
let started: { store: ShotEditorStore; wanted: string; promise: Promise<void> } | null = null;
|
|
19
|
+
|
|
20
|
+
/** The `?scene=` / `?shot=` precedence rule, in one place: `scene=` names the
|
|
21
|
+
* document and wins; `shot=` is the ORIGINAL spelling of the document name
|
|
22
|
+
* and is read as one only when `scene=` is absent. Non-empty, because
|
|
23
|
+
* `?scene=` alone is a URL a truncating share sheet produces. */
|
|
24
|
+
export function wantedDocOf(params: URLSearchParams): string | null {
|
|
25
|
+
const wanted = params.get("scene") ?? params.get("shot");
|
|
26
|
+
return wanted !== null && wanted.length > 0 ? wanted : null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Kick the open for the URL's document, if it names one. Called from main.tsx
|
|
30
|
+
* at module scope — before createRoot, so the round trip overlaps the mount.
|
|
31
|
+
* `openShot` handles its own errors (they land in state.error). */
|
|
32
|
+
export function startBootOpen(store: ShotEditorStore): void {
|
|
33
|
+
const wanted = wantedDocOf(new URLSearchParams(window.location.search));
|
|
34
|
+
if (wanted === null) return;
|
|
35
|
+
started = { store, wanted, promise: store.openShot(wanted) };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** The in-flight boot open for exactly this store and document, or null. */
|
|
39
|
+
export function bootOpenFor(store: ShotEditorStore, wanted: string): Promise<void> | null {
|
|
40
|
+
if (started === null || started.store !== store || started.wanted !== wanted) return null;
|
|
41
|
+
return started.promise;
|
|
42
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// THE CAMERA VERBS: every word the chrome uses for moving the shot camera,
|
|
2
|
+
// and the sentence that explains each, in one table.
|
|
3
|
+
//
|
|
4
|
+
// ── WHY THIS FILE EXISTS ────────────────────────────────────────────────
|
|
5
|
+
//
|
|
6
|
+
// The same verb was spelled four ways: "Align camera" on the bar, "Point the
|
|
7
|
+
// shot camera where this view is looking" in its tooltip, "Point the camera at
|
|
8
|
+
// this view" in the View menu and "Point the camera at the working view" in
|
|
9
|
+
// the manual. Follow had four spellings of its own. An author meeting the
|
|
10
|
+
// third spelling had no way to know it was the first verb again, and the
|
|
11
|
+
// founder's verdict on the result was "I don't know what these options are".
|
|
12
|
+
//
|
|
13
|
+
// So the rows live here once, the way `layouts.ts` holds the layout rows: one
|
|
14
|
+
// literal, `as const satisfies Record<...>`, and the bar, the View menu, the
|
|
15
|
+
// shortcut manual and the in-pane badge all DERIVE from it. A verb cannot be
|
|
16
|
+
// renamed on one surface and not another, because no surface owns a word.
|
|
17
|
+
//
|
|
18
|
+
// ── TWO CAMERAS, SAID ONCE ──────────────────────────────────────────────
|
|
19
|
+
//
|
|
20
|
+
// The 3D view is the author's own eye: editor state, never exported. The shot
|
|
21
|
+
// camera is the picture. Every verb in this table moves the SHOT camera, and
|
|
22
|
+
// the caption says so above them, because nothing on screen ever named the
|
|
23
|
+
// pair before and every confusion about these verbs came from that.
|
|
24
|
+
|
|
25
|
+
import type { CameraGate } from "@dailies-studio/shot/doc";
|
|
26
|
+
|
|
27
|
+
export type CameraVerb = "pilot" | "toView" | "frameSelection" | "addMove";
|
|
28
|
+
|
|
29
|
+
export interface CameraVerbSpec {
|
|
30
|
+
/** The bar's row, the menu row and the manual's name. Short, a verb phrase. */
|
|
31
|
+
label: string;
|
|
32
|
+
/** What it does, in one sentence: the tooltip, the manual's line, the docs. */
|
|
33
|
+
sentence: string;
|
|
34
|
+
/** The App menu row's id, which is also how `shortcuts.ts` points a row at a
|
|
35
|
+
* menu entry and how the menu asks for its accelerator. */
|
|
36
|
+
menuId: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const CAMERA_VERBS = {
|
|
40
|
+
pilot: {
|
|
41
|
+
label: "Pilot camera",
|
|
42
|
+
sentence:
|
|
43
|
+
"Steer the shot camera from the Camera pane: middle-drag orbits, shift-drag pans, " +
|
|
44
|
+
"wheel zooms, R rolls. Esc stops.",
|
|
45
|
+
menuId: "view.camera.pilot",
|
|
46
|
+
},
|
|
47
|
+
toView: {
|
|
48
|
+
label: "Move camera to view",
|
|
49
|
+
sentence: "Put the shot camera where the 3D view is looking.",
|
|
50
|
+
menuId: "view.camera.align",
|
|
51
|
+
},
|
|
52
|
+
frameSelection: {
|
|
53
|
+
label: "Frame selection with camera",
|
|
54
|
+
sentence: "Move the shot camera so the selected layers fill the frame.",
|
|
55
|
+
menuId: "view.camera.fitSelection",
|
|
56
|
+
},
|
|
57
|
+
addMove: {
|
|
58
|
+
label: "Add camera move",
|
|
59
|
+
sentence: "Start a camera move that ends at the playhead.",
|
|
60
|
+
menuId: "view.camera.addMove",
|
|
61
|
+
},
|
|
62
|
+
} as const satisfies Record<CameraVerb, CameraVerbSpec>;
|
|
63
|
+
|
|
64
|
+
/** Every verb, in the order the bar and the menu list them. */
|
|
65
|
+
export const CAMERA_VERB_KEYS = Object.keys(CAMERA_VERBS) as readonly CameraVerb[];
|
|
66
|
+
|
|
67
|
+
/** One row, seen as a `CameraVerbSpec` rather than as its own literal type —
|
|
68
|
+
* the same widening `layoutSpec` makes, in one place. */
|
|
69
|
+
export function cameraVerb(key: CameraVerb): CameraVerbSpec {
|
|
70
|
+
return CAMERA_VERBS[key];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** The caption above the verbs: the one sentence that teaches the model. */
|
|
74
|
+
export const TWO_CAMERAS =
|
|
75
|
+
"Two cameras: the 3D view is yours and is never exported. The shot camera is the " +
|
|
76
|
+
"picture. Everything below moves the shot camera.";
|
|
77
|
+
|
|
78
|
+
/** The in-pane badge while piloting, when nothing holds the camera. */
|
|
79
|
+
export const PILOT_BADGE = "PILOTING CAMERA · ESC TO EJECT";
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* What hovering the camera gizmo says about the drag it offers, or null when a
|
|
83
|
+
* drag simply moves the scene's own camera.
|
|
84
|
+
*
|
|
85
|
+
* The gate's own answer in the drag pill's voice: short, upper case, and said
|
|
86
|
+
* BEFORE the press. The full sentence with the two ways out stays the error
|
|
87
|
+
* strip's and the inspector's.
|
|
88
|
+
*/
|
|
89
|
+
export function gateBadge(gate: CameraGate | null): string | null {
|
|
90
|
+
if (gate === null) return null;
|
|
91
|
+
const label = gate.shot === undefined ? "" : ` "${gate.shot.label.toUpperCase()}"`;
|
|
92
|
+
switch (gate.kind) {
|
|
93
|
+
case "free":
|
|
94
|
+
if (gate.writes === "editsMove") return `DRAG EDITS THE MOVE${label}`;
|
|
95
|
+
if (gate.writes === "newMove") return "DRAG ADDS A CAMERA MOVE ENDING HERE";
|
|
96
|
+
return null;
|
|
97
|
+
case "held":
|
|
98
|
+
return `HELD BY THE MOVE${label} · ⇧A TO ANIMATE`;
|
|
99
|
+
case "derived":
|
|
100
|
+
return `${label.trim()} FOLLOWS ITS LAYERS · NOTHING TO DRAG`;
|
|
101
|
+
case "shaped":
|
|
102
|
+
return `${label.trim()} IS A PATH · EDIT ITS POSES IN THE INSPECTOR`;
|
|
103
|
+
case "tooEarly":
|
|
104
|
+
return "MOVE THE PLAYHEAD LATER TO ANIMATE";
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** The pilot badge's text for a gate (and a driver), composed by the chrome. */
|
|
109
|
+
export function pilotText(gate: CameraGate | null, driver: string | null): string {
|
|
110
|
+
if (driver !== null) return pilotHoldBadge("driven", driver);
|
|
111
|
+
if (gate === null || gate.kind === "free") return PILOT_BADGE;
|
|
112
|
+
return pilotHoldBadge(gate.kind, gate.shot?.label ?? null);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The badge while piloting a camera that a gesture cannot write.
|
|
117
|
+
*
|
|
118
|
+
* SHORT, and in the badge's own voice: the gate's full sentence (with the two
|
|
119
|
+
* ways out) is the inspector's and the error strip's. `label` is the move's
|
|
120
|
+
* name, or the component's when a component drives the camera.
|
|
121
|
+
*/
|
|
122
|
+
export function pilotHoldBadge(
|
|
123
|
+
hold: "held" | "derived" | "shaped" | "tooEarly" | "driven",
|
|
124
|
+
label: string | null,
|
|
125
|
+
): string {
|
|
126
|
+
const name = label === null ? "" : ` "${label.toUpperCase()}"`;
|
|
127
|
+
switch (hold) {
|
|
128
|
+
case "held":
|
|
129
|
+
return `PILOTING CAMERA · HELD BY${name} · ⇧A TO ANIMATE · ESC TO EJECT`;
|
|
130
|
+
case "derived":
|
|
131
|
+
return `PILOTING CAMERA ·${name} FOLLOWS ITS LAYERS · LOOK ONLY · ESC TO EJECT`;
|
|
132
|
+
case "shaped":
|
|
133
|
+
return `PILOTING CAMERA ·${name} IS A PATH · EDIT ITS POSES IN THE INSPECTOR · ESC TO EJECT`;
|
|
134
|
+
case "tooEarly":
|
|
135
|
+
return "PILOTING CAMERA · MOVE THE PLAYHEAD LATER TO ANIMATE · ESC TO EJECT";
|
|
136
|
+
case "driven":
|
|
137
|
+
return `PILOTING CAMERA · DRIVEN BY${name === "" ? " A COMPONENT" : name} · LOOK ONLY · ESC TO EJECT`;
|
|
138
|
+
}
|
|
139
|
+
}
|
package/src/state/clientId.ts
CHANGED
|
@@ -9,8 +9,13 @@
|
|
|
9
9
|
// In memory rather than sessionStorage on purpose: two tabs of the same
|
|
10
10
|
// document must be two clients, and sessionStorage is per-tab but survives a
|
|
11
11
|
// reload — which would make a reloaded tab claim the pre-reload tab's writes.
|
|
12
|
+
//
|
|
13
|
+
// Hosted, it is the code host's (hostApi.ts `clientId`): the tab's one id, so
|
|
14
|
+
// an agent session asked from this chrome relays its browser tools to this
|
|
15
|
+
// tab's code host and no other.
|
|
16
|
+
import { dailiesHost } from "../hostApi.js";
|
|
12
17
|
|
|
13
|
-
export const CLIENT_ID: string = crypto.randomUUID();
|
|
18
|
+
export const CLIENT_ID: string = dailiesHost()?.clientId ?? crypto.randomUUID();
|
|
14
19
|
|
|
15
20
|
export const CLIENT_ID_HEADER = "x-studio-client-id";
|
|
16
21
|
|