@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,261 @@
|
|
|
1
|
+
// The editor preferences on the ACCOUNT (gap 5), hosted only.
|
|
2
|
+
//
|
|
3
|
+
// The hosted editor runs on a per-workspace origin, so its localStorage is per
|
|
4
|
+
// workspace and never shared with the dashboard. The account keeps one copy
|
|
5
|
+
// (`users.editor_prefs`, answered on `/auth/me` and written by
|
|
6
|
+
// `PUT /auth/prefs`), so a choice follows the person across workspaces and
|
|
7
|
+
// devices, and a theme picked in dashboard Settings reaches the editor.
|
|
8
|
+
//
|
|
9
|
+
// localStorage stays the boot cache: the store is born from it
|
|
10
|
+
// (`readEditorPrefs`), so the first paint never waits on the network, and a
|
|
11
|
+
// local install or an offline start behaves exactly as before.
|
|
12
|
+
//
|
|
13
|
+
// ONE CLOCK. The only stamp compared is the server's `updatedAt`, against the
|
|
14
|
+
// server stamp this browser last adopted or got back from a save
|
|
15
|
+
// (`SyncState.syncedAt`). A client clock running ahead or behind changes
|
|
16
|
+
// nothing.
|
|
17
|
+
//
|
|
18
|
+
// FIELD BY FIELD. A local change marks its field dirty (kept in localStorage,
|
|
19
|
+
// so a save that never landed is retried at the next boot), and a save sends
|
|
20
|
+
// ONLY the dirty fields. The server's `json_patch` merge then keeps whatever
|
|
21
|
+
// another surface wrote to the rest (the dashboard writes only `theme`).
|
|
22
|
+
//
|
|
23
|
+
// THE MERGE, once per page load:
|
|
24
|
+
// - the account's stamp differs from `syncedAt`: something else wrote since,
|
|
25
|
+
// so adopt every field it holds that is not dirty here;
|
|
26
|
+
// - dirty fields are then sent; an account that has never saved gets this
|
|
27
|
+
// browser's whole copy, if this browser ever saved one.
|
|
28
|
+
// After that, every change is one debounced PUT of the dirty fields, after
|
|
29
|
+
// `SAVE_DEBOUNCE_MS` of quiet: the Preferences dialog is a row of switches,
|
|
30
|
+
// and a burst of clicks is one write.
|
|
31
|
+
import { useEffect, useRef } from "react";
|
|
32
|
+
import { useStudioTheme } from "@dailies-studio/ui/theme";
|
|
33
|
+
import type { StudioThemePreference } from "@dailies-studio/ui/theme";
|
|
34
|
+
import { useStore } from "./context.js";
|
|
35
|
+
import { EDITOR_PREF_VALID, hasStoredEditorPrefs } from "./editorPrefs.js";
|
|
36
|
+
import { isCloudWorkspace } from "./posterCapture.js";
|
|
37
|
+
import type { ShotEditorStore } from "./store.js";
|
|
38
|
+
|
|
39
|
+
/** Quiet time before a change is saved to the account. */
|
|
40
|
+
export const SAVE_DEBOUNCE_MS = 800;
|
|
41
|
+
|
|
42
|
+
/** The sync bookkeeping, beside `EDITOR_PREFS_KEY`. */
|
|
43
|
+
export const ACCOUNT_SYNC_KEY = "dailies.editor.sync";
|
|
44
|
+
|
|
45
|
+
/** The fields the account keeps, as the server's closed schema names them
|
|
46
|
+
* (@dailies-studio/db editorPrefs.ts `EDITOR_PREF_FIELDS`). */
|
|
47
|
+
export interface AccountPrefs {
|
|
48
|
+
theme: StudioThemePreference;
|
|
49
|
+
showGrid: boolean;
|
|
50
|
+
rulers: boolean;
|
|
51
|
+
smartGuides: boolean;
|
|
52
|
+
motionPaths: boolean;
|
|
53
|
+
toolbarPosition: "bottom" | "top";
|
|
54
|
+
blockLabels: "fit" | "off";
|
|
55
|
+
selectMovesPlayhead: boolean;
|
|
56
|
+
fieldLabels: "compact" | "named";
|
|
57
|
+
showFpsBadge: boolean;
|
|
58
|
+
showBackendBadge: boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
type Field = keyof AccountPrefs;
|
|
62
|
+
type StoreField = Exclude<Field, "theme">;
|
|
63
|
+
const STORE_FIELDS: readonly StoreField[] = [
|
|
64
|
+
"showGrid",
|
|
65
|
+
"rulers",
|
|
66
|
+
"smartGuides",
|
|
67
|
+
"motionPaths",
|
|
68
|
+
"toolbarPosition",
|
|
69
|
+
"blockLabels",
|
|
70
|
+
"selectMovesPlayhead",
|
|
71
|
+
"fieldLabels",
|
|
72
|
+
"showFpsBadge",
|
|
73
|
+
"showBackendBadge",
|
|
74
|
+
];
|
|
75
|
+
const FIELDS: readonly Field[] = ["theme", ...STORE_FIELDS];
|
|
76
|
+
|
|
77
|
+
const isTheme = (v: unknown): v is StudioThemePreference => v === "system" || v === "light" || v === "dark";
|
|
78
|
+
const isValid = (field: Field, v: unknown): boolean =>
|
|
79
|
+
field === "theme" ? isTheme(v) : EDITOR_PREF_VALID[field](v);
|
|
80
|
+
|
|
81
|
+
/** The account copy's fields, as the store and the theme hold them now. */
|
|
82
|
+
export function accountPrefsOf(store: ShotEditorStore, theme: StudioThemePreference): AccountPrefs {
|
|
83
|
+
const s = store.getState();
|
|
84
|
+
const out = { theme } as AccountPrefs;
|
|
85
|
+
for (const field of STORE_FIELDS) (out as unknown as Record<string, unknown>)[field] = s[field];
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** One store field through the setter the chrome already uses. */
|
|
90
|
+
function setStoreField(store: ShotEditorStore, field: StoreField, value: unknown): void {
|
|
91
|
+
switch (field) {
|
|
92
|
+
case "showGrid":
|
|
93
|
+
if (value !== store.getState().showGrid) store.toggleGrid();
|
|
94
|
+
return;
|
|
95
|
+
case "showFpsBadge":
|
|
96
|
+
return store.setFpsBadge(value as boolean);
|
|
97
|
+
case "showBackendBadge":
|
|
98
|
+
return store.setBackendBadge(value as boolean);
|
|
99
|
+
case "selectMovesPlayhead":
|
|
100
|
+
return store.setSelectMovesPlayhead(value as boolean);
|
|
101
|
+
default:
|
|
102
|
+
store.setEditorPref(field, value as never);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** What this browser knows about the account copy. */
|
|
107
|
+
interface SyncState {
|
|
108
|
+
/** The server's `updatedAt` last adopted or answered to a save, 0 for never. */
|
|
109
|
+
syncedAt: number;
|
|
110
|
+
/** Fields changed here and not yet saved. */
|
|
111
|
+
dirty: Field[];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function readSync(): SyncState {
|
|
115
|
+
try {
|
|
116
|
+
const parsed: unknown = JSON.parse(localStorage.getItem(ACCOUNT_SYNC_KEY) ?? "null");
|
|
117
|
+
const o = (typeof parsed === "object" && parsed !== null ? parsed : {}) as Record<string, unknown>;
|
|
118
|
+
const syncedAt = typeof o["syncedAt"] === "number" && Number.isFinite(o["syncedAt"]) ? o["syncedAt"] : 0;
|
|
119
|
+
const dirty = Array.isArray(o["dirty"]) ? FIELDS.filter((f) => (o["dirty"] as unknown[]).includes(f)) : [];
|
|
120
|
+
return { syncedAt, dirty };
|
|
121
|
+
} catch {
|
|
122
|
+
return { syncedAt: 0, dirty: [] };
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function writeSync(sync: SyncState): void {
|
|
127
|
+
try {
|
|
128
|
+
localStorage.setItem(ACCOUNT_SYNC_KEY, JSON.stringify(sync));
|
|
129
|
+
} catch {
|
|
130
|
+
/* storage disabled: the session still syncs, it just forgets between loads */
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
type ServerPrefs = Partial<Record<Field, unknown>> & { updatedAt?: unknown };
|
|
135
|
+
|
|
136
|
+
/** Mounted once, in App: the boot merge, then a debounced save per change. */
|
|
137
|
+
export function useAccountPrefs(): void {
|
|
138
|
+
const store = useStore();
|
|
139
|
+
const { preference, setPreference } = useStudioTheme();
|
|
140
|
+
// The theme as the sync knows it. The store subscription runs outside
|
|
141
|
+
// React, so it reads the theme here, not from a render.
|
|
142
|
+
const theme = useRef(preference);
|
|
143
|
+
// The change hook, set while hosted.
|
|
144
|
+
const changed = useRef<(() => void) | null>(null);
|
|
145
|
+
|
|
146
|
+
useEffect(() => {
|
|
147
|
+
if (!isCloudWorkspace()) return;
|
|
148
|
+
let live = true;
|
|
149
|
+
let ready = false;
|
|
150
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
151
|
+
const sync = readSync();
|
|
152
|
+
const dirty = new Set<Field>(sync.dirty);
|
|
153
|
+
const persist = () => writeSync({ syncedAt: sync.syncedAt, dirty: [...dirty] });
|
|
154
|
+
// The value each field had when last sent, adopted or loaded. A field
|
|
155
|
+
// that moves off it is a local change.
|
|
156
|
+
const baseline = accountPrefsOf(store, theme.current);
|
|
157
|
+
|
|
158
|
+
/** Adopt the account's fields, except those `skip` names (changed here
|
|
159
|
+
* since). The baseline moves FIRST, so the adoption is not a change. */
|
|
160
|
+
const adopt = (saved: ServerPrefs, skip: ReadonlySet<Field>) => {
|
|
161
|
+
for (const field of FIELDS) {
|
|
162
|
+
const value = saved[field];
|
|
163
|
+
if (skip.has(field) || !isValid(field, value)) continue;
|
|
164
|
+
(baseline as unknown as Record<string, unknown>)[field] = value;
|
|
165
|
+
if (field === "theme") {
|
|
166
|
+
theme.current = value as StudioThemePreference;
|
|
167
|
+
setPreference(value as StudioThemePreference);
|
|
168
|
+
} else setStoreField(store, field, value);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const save = () => {
|
|
173
|
+
if (!live || dirty.size === 0) return;
|
|
174
|
+
const sent = accountPrefsOf(store, theme.current);
|
|
175
|
+
const body: Partial<AccountPrefs> = {};
|
|
176
|
+
for (const field of dirty) (body as Record<string, unknown>)[field] = sent[field];
|
|
177
|
+
// A failed save costs only the sync: the dirty marks stay in
|
|
178
|
+
// localStorage and the next change or boot sends them again.
|
|
179
|
+
void fetch("/auth/prefs", {
|
|
180
|
+
method: "PUT",
|
|
181
|
+
credentials: "same-origin",
|
|
182
|
+
headers: { "content-type": "application/json" },
|
|
183
|
+
body: JSON.stringify(body),
|
|
184
|
+
})
|
|
185
|
+
.then((res) => (res.ok ? res.json() : null))
|
|
186
|
+
.then((answer: { editorPrefs?: ServerPrefs | null } | null) => {
|
|
187
|
+
const saved = answer?.editorPrefs;
|
|
188
|
+
if (!live || saved == null || typeof saved.updatedAt !== "number") return;
|
|
189
|
+
const now = accountPrefsOf(store, theme.current);
|
|
190
|
+
// Clean only what is still what was sent: a change made while the
|
|
191
|
+
// request flew stays dirty for the next save.
|
|
192
|
+
for (const field of Object.keys(body) as Field[]) if (now[field] === sent[field]) dirty.delete(field);
|
|
193
|
+
sync.syncedAt = saved.updatedAt;
|
|
194
|
+
persist();
|
|
195
|
+
// The answer is the whole merged copy: take what another surface
|
|
196
|
+
// wrote meanwhile, but never over a field just sent or still dirty.
|
|
197
|
+
adopt(saved, new Set<Field>([...dirty, ...(Object.keys(body) as Field[])]));
|
|
198
|
+
})
|
|
199
|
+
.catch(() => {});
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const schedule = () => {
|
|
203
|
+
if (timer !== null) clearTimeout(timer);
|
|
204
|
+
timer = setTimeout(() => {
|
|
205
|
+
timer = null;
|
|
206
|
+
save();
|
|
207
|
+
}, SAVE_DEBOUNCE_MS);
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
changed.current = () => {
|
|
211
|
+
const now = accountPrefsOf(store, theme.current);
|
|
212
|
+
let moved = false;
|
|
213
|
+
for (const field of FIELDS) {
|
|
214
|
+
if (now[field] === baseline[field]) continue;
|
|
215
|
+
(baseline as unknown as Record<string, unknown>)[field] = now[field];
|
|
216
|
+
dirty.add(field);
|
|
217
|
+
moved = true;
|
|
218
|
+
}
|
|
219
|
+
if (!moved) return;
|
|
220
|
+
persist();
|
|
221
|
+
if (ready) schedule();
|
|
222
|
+
};
|
|
223
|
+
const unsubscribe = store.subscribeCold(() => changed.current?.());
|
|
224
|
+
|
|
225
|
+
void fetch("/auth/me", { credentials: "same-origin" })
|
|
226
|
+
.then((res) => (res.ok ? res.json() : null))
|
|
227
|
+
.then((me: { signedIn?: boolean; editorPrefs?: ServerPrefs | null } | null) => {
|
|
228
|
+
if (!live || me?.signedIn !== true) return;
|
|
229
|
+
const saved = me.editorPrefs ?? null;
|
|
230
|
+
if (saved !== null && typeof saved.updatedAt === "number") {
|
|
231
|
+
if (saved.updatedAt !== sync.syncedAt) {
|
|
232
|
+
adopt(saved, dirty);
|
|
233
|
+
sync.syncedAt = saved.updatedAt;
|
|
234
|
+
persist();
|
|
235
|
+
}
|
|
236
|
+
} else if (hasStoredEditorPrefs()) {
|
|
237
|
+
// An account that never saved: seed it with this browser's copy.
|
|
238
|
+
for (const field of FIELDS) dirty.add(field);
|
|
239
|
+
persist();
|
|
240
|
+
}
|
|
241
|
+
ready = true;
|
|
242
|
+
save();
|
|
243
|
+
})
|
|
244
|
+
.catch(() => {});
|
|
245
|
+
|
|
246
|
+
return () => {
|
|
247
|
+
live = false;
|
|
248
|
+
unsubscribe();
|
|
249
|
+
changed.current = null;
|
|
250
|
+
if (timer !== null) clearTimeout(timer);
|
|
251
|
+
};
|
|
252
|
+
}, [store, setPreference]);
|
|
253
|
+
|
|
254
|
+
// A theme change: the same path as a store field. Before the hosted effect
|
|
255
|
+
// runs (or on a local install), only the ref moves.
|
|
256
|
+
useEffect(() => {
|
|
257
|
+
if (theme.current === preference) return;
|
|
258
|
+
theme.current = preference;
|
|
259
|
+
changed.current?.();
|
|
260
|
+
}, [preference]);
|
|
261
|
+
}
|
|
@@ -14,8 +14,11 @@
|
|
|
14
14
|
import { AgentApiError, applyAgentEvent, createItemIds } from "@dailies-studio/agent-client";
|
|
15
15
|
import type { AgentEvent, AgentProposalItem, TranscriptSink } from "@dailies-studio/agent-client";
|
|
16
16
|
import { ApiError, agent } from "../api.js";
|
|
17
|
+
import { boardSubjectOf, isPartId } from "./storyboardModel.js";
|
|
17
18
|
import type { ShotEditorStore } from "./store.js";
|
|
18
19
|
import type { ShotEditorState } from "./types.js";
|
|
20
|
+
import { authoringFrame } from "./viewMode.js";
|
|
21
|
+
import { readOnlyReason } from "./workspace.js";
|
|
19
22
|
|
|
20
23
|
function message(e: unknown): string {
|
|
21
24
|
return e instanceof Error ? e.message : String(e);
|
|
@@ -29,6 +32,18 @@ function isConflict(e: unknown): boolean {
|
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
const itemId = createItemIds("sh");
|
|
35
|
+
const activeTurns = new WeakMap<ShotEditorStore, object>();
|
|
36
|
+
|
|
37
|
+
/** The Project each agent session was asked from (its doc id and display
|
|
38
|
+
* title), by session id, per store. A card stays pending across a Project
|
|
39
|
+
* switch, so a component file the agent made is made for the Project that
|
|
40
|
+
* asked, never for whichever one is open when the card is applied. */
|
|
41
|
+
const askedFrom = new WeakMap<ShotEditorStore, Map<string, { docId: string; title: string }>>();
|
|
42
|
+
/** How many asks `askedFrom` remembers, newest kept.
|
|
43
|
+
* ponytail: a create card from an older ask links nothing and says where it
|
|
44
|
+
* lists, as a card no ask of this page made does. Drop an entry once its
|
|
45
|
+
* session's cards are all applied if that ever bites. */
|
|
46
|
+
export const ASKED_FROM_MAX = 32;
|
|
32
47
|
|
|
33
48
|
export async function refreshAgentCredentials(store: ShotEditorStore): Promise<void> {
|
|
34
49
|
try {
|
|
@@ -57,7 +72,8 @@ export async function clearAgentKey(store: ShotEditorStore): Promise<void> {
|
|
|
57
72
|
|
|
58
73
|
/**
|
|
59
74
|
* What "this" means in the shot chrome: which surface is asking, which shot is
|
|
60
|
-
* open, which
|
|
75
|
+
* open, which definition the author has entered, and which scene and frame they
|
|
76
|
+
* are looking at.
|
|
61
77
|
*
|
|
62
78
|
* `surface: "shot"` is load-bearing, not decoration — it is what makes the
|
|
63
79
|
* server append the shot addendum to the system prompt AND compose the shot
|
|
@@ -72,16 +88,64 @@ export function buildShotAgentContext(s: ShotEditorState): unknown {
|
|
|
72
88
|
// The playhead and the open scene, unconditionally. Every layer in this lane
|
|
73
89
|
// renders AT a frame, so "which moment am I looking at" is always a real
|
|
74
90
|
// statement about what the author means by "this".
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
91
|
+
//
|
|
92
|
+
// AND THE FRAME FOLLOWS THE CONTEXT, because the context decides what a frame
|
|
93
|
+
// MEANS. Inside a definition the chrome's clock is `defFrame` — def-local,
|
|
94
|
+
// starting at 0 — and `frame` is master time, which nothing on screen is
|
|
95
|
+
// showing. Sending the master frame there would have the agent read one moment
|
|
96
|
+
// while the author is looking at another, with no way for either to notice.
|
|
97
|
+
if (s.editingDef !== null) shot.editingDef = s.editingDef;
|
|
98
|
+
shot.frame = authoringFrame(s);
|
|
99
|
+
// The board, when it is what the author is looking at: the flag tells the
|
|
100
|
+
// model to read get_storyboard before touching scenes, and the selected
|
|
101
|
+
// card or board subject (a still, a gap or a cut) is "this" in the same
|
|
102
|
+
// sense the selected layer is. The board shows no layers and enters no
|
|
103
|
+
// Shot, so it sends no layer; the part is the open shot board's plan, else
|
|
104
|
+
// the one shot card selected, and the scene is that plan's Shot.
|
|
105
|
+
if (s.storyboardOpen) {
|
|
106
|
+
shot.storyboardOpen = true;
|
|
107
|
+
const reference = s.storyboardSelection.find((id) => id.startsWith("ref_"));
|
|
108
|
+
if (reference !== undefined) shot.selectedReferenceId = reference;
|
|
109
|
+
const subject = boardSubjectOf(s.doc, s.storyboardSelection);
|
|
110
|
+
if (subject !== null) shot.boardSubject = subject.id;
|
|
111
|
+
const only = s.storyboardSelection.length === 1 ? s.storyboardSelection[0]! : null;
|
|
112
|
+
const partId = s.storyboardPlan ?? (only !== null && isPartId(only) ? only : null);
|
|
113
|
+
const part = partId === null ? undefined : s.doc?.storyboard?.parts.find((p) => p.id === partId);
|
|
114
|
+
if (part !== undefined) {
|
|
115
|
+
shot.selectedPartId = part.id;
|
|
116
|
+
if (part.scene !== undefined && s.doc?.scenes.includes(part.scene) === true) shot.sceneId = part.scene;
|
|
117
|
+
// On the shot board, the sketch on screen and its first selected item.
|
|
118
|
+
const sketch = s.storyboardPlan === part.id ? part.sketches?.find((k) => k.id === s.storyboardSketch) : undefined;
|
|
119
|
+
if (sketch !== undefined) {
|
|
120
|
+
shot.sketchId = sketch.id;
|
|
121
|
+
const item = s.sketchSelection.find((id) => sketch.items.some((i) => i.id === id));
|
|
122
|
+
if (item !== undefined) shot.selectedSketchItemId = item;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
if (s.sceneId !== null) shot.sceneId = s.sceneId;
|
|
127
|
+
const primary = s.selection[0];
|
|
128
|
+
if (primary !== undefined) shot.selectedLayerId = primary;
|
|
129
|
+
}
|
|
79
130
|
return { surface: "shot", ...(Object.keys(shot).length > 0 ? { shot } : {}) };
|
|
80
131
|
}
|
|
81
132
|
|
|
82
133
|
/** Every key `buildShotAgentContext` may put under `context.shot`. The server
|
|
83
134
|
* rejects anything else, so this list and the server's are one contract. */
|
|
84
|
-
export const SHOT_CONTEXT_KEYS = [
|
|
135
|
+
export const SHOT_CONTEXT_KEYS = [
|
|
136
|
+
"docName",
|
|
137
|
+
"componentName",
|
|
138
|
+
"frame",
|
|
139
|
+
"sceneId",
|
|
140
|
+
"editingDef",
|
|
141
|
+
"selectedLayerId",
|
|
142
|
+
"storyboardOpen",
|
|
143
|
+
"selectedReferenceId",
|
|
144
|
+
"selectedPartId",
|
|
145
|
+
"boardSubject",
|
|
146
|
+
"sketchId",
|
|
147
|
+
"selectedSketchItemId",
|
|
148
|
+
];
|
|
85
149
|
|
|
86
150
|
/** The store, seen as the shared reducer's three transcript verbs. */
|
|
87
151
|
function sinkFor(store: ShotEditorStore): TranscriptSink {
|
|
@@ -97,23 +161,77 @@ function sinkFor(store: ShotEditorStore): TranscriptSink {
|
|
|
97
161
|
export async function askAgent(store: ShotEditorStore, prompt: string): Promise<void> {
|
|
98
162
|
const s = store.getState();
|
|
99
163
|
if (s.agentRunning || prompt.trim() === "") return;
|
|
164
|
+
const turn = {};
|
|
165
|
+
const userId = itemId();
|
|
166
|
+
activeTurns.set(store, turn);
|
|
100
167
|
store.setAgentError(null);
|
|
101
|
-
store.appendAgentItem({ id:
|
|
168
|
+
store.appendAgentItem({ id: userId, kind: "user", text: prompt });
|
|
102
169
|
store.setAgentRunning(true);
|
|
170
|
+
let valid = true;
|
|
171
|
+
let sessionId: string | null = null;
|
|
172
|
+
let cancelling: Promise<void> | null = null;
|
|
173
|
+
const ownsTurn = (): boolean => activeTurns.get(store) === turn;
|
|
174
|
+
const cancelObsolete = (): Promise<void> => {
|
|
175
|
+
if (sessionId === null) return Promise.resolve();
|
|
176
|
+
cancelling ??= agent.cancel(sessionId).catch(() => {});
|
|
177
|
+
return cancelling;
|
|
178
|
+
};
|
|
179
|
+
const stillCurrent = (): boolean => {
|
|
180
|
+
const current = store.getState();
|
|
181
|
+
return valid && ownsTurn() && current.name === s.name && current.doc?.id === s.doc?.id &&
|
|
182
|
+
current.agentItems.some((item) => item.id === userId);
|
|
183
|
+
};
|
|
184
|
+
// Latch any Project departure, even A -> B -> A between two awaited
|
|
185
|
+
// responses. Clearing the originating transcript also ends its turn.
|
|
186
|
+
// Release this turn's running flag immediately so the new Project can ask;
|
|
187
|
+
// its eventual finally must not stop a newer request. Cold: a departure
|
|
188
|
+
// and the transcript are never a transport tick.
|
|
189
|
+
const unsubscribe = store.subscribeCold(() => {
|
|
190
|
+
if (!valid || stillCurrent()) return;
|
|
191
|
+
valid = false;
|
|
192
|
+
if (ownsTurn()) {
|
|
193
|
+
activeTurns.delete(store);
|
|
194
|
+
store.setAgentRunning(false);
|
|
195
|
+
}
|
|
196
|
+
void cancelObsolete();
|
|
197
|
+
});
|
|
103
198
|
try {
|
|
104
|
-
|
|
199
|
+
await store.flush();
|
|
200
|
+
if (!stillCurrent()) return;
|
|
201
|
+
if (store.getState().dirty)
|
|
202
|
+
throw new Error("The Project changed while saving. Let it finish saving, then ask the agent again.");
|
|
203
|
+
const created = await agent.createSession({
|
|
105
204
|
prompt,
|
|
106
205
|
...(s.agentModel !== null ? { model: s.agentModel } : {}),
|
|
107
206
|
context: buildShotAgentContext(s),
|
|
108
207
|
});
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
208
|
+
sessionId = created.sessionId;
|
|
209
|
+
if (s.doc !== null) {
|
|
210
|
+
const asked = askedFrom.get(store) ?? new Map<string, { docId: string; title: string }>();
|
|
211
|
+
asked.set(sessionId, { docId: s.doc.id, title: s.doc.title ?? s.name ?? s.doc.name });
|
|
212
|
+
// A Map keeps insertion order, so its first key is the oldest ask.
|
|
213
|
+
const oldest = asked.keys().next();
|
|
214
|
+
if (asked.size > ASKED_FROM_MAX && oldest.done !== true) asked.delete(oldest.value);
|
|
215
|
+
askedFrom.set(store, asked);
|
|
216
|
+
}
|
|
217
|
+
if (!stillCurrent()) {
|
|
218
|
+
await cancelObsolete();
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const currentSession = sessionId;
|
|
222
|
+
store.setAgentSession(currentSession);
|
|
223
|
+
await agent.events(currentSession, (event: AgentEvent) => {
|
|
224
|
+
if (!stillCurrent()) return;
|
|
225
|
+
applyAgentEvent(sinkFor(store), currentSession, event, itemId);
|
|
112
226
|
});
|
|
113
227
|
} catch (e) {
|
|
114
|
-
store.setAgentError(message(e));
|
|
228
|
+
if (stillCurrent()) store.setAgentError(message(e));
|
|
115
229
|
} finally {
|
|
116
|
-
|
|
230
|
+
unsubscribe();
|
|
231
|
+
if (ownsTurn()) {
|
|
232
|
+
activeTurns.delete(store);
|
|
233
|
+
store.setAgentRunning(false);
|
|
234
|
+
}
|
|
117
235
|
}
|
|
118
236
|
}
|
|
119
237
|
|
|
@@ -127,6 +245,29 @@ export async function cancelAgent(store: ShotEditorStore): Promise<void> {
|
|
|
127
245
|
}
|
|
128
246
|
}
|
|
129
247
|
|
|
248
|
+
/**
|
|
249
|
+
* A component file the agent created is MADE FOR THE PROJECT THAT ASKED, as
|
|
250
|
+
* New › Code makes one for the Project it was pressed in: listed under This
|
|
251
|
+
* project even before a shot uses it. Only while that Project is the one
|
|
252
|
+
* open, read after every await of the apply: otherwise nothing is linked and
|
|
253
|
+
* the author is told where to tick it. A `.ts` helper is not a component
|
|
254
|
+
* file, so it is never linked. Nothing here throws (`setComponentFile`
|
|
255
|
+
* answers its refusals).
|
|
256
|
+
*/
|
|
257
|
+
function linkMadeFile(store: ShotEditorStore, sessionId: string, rel: string): void {
|
|
258
|
+
const asked = askedFrom.get(store)?.get(sessionId);
|
|
259
|
+
if (asked === undefined) {
|
|
260
|
+
store.reportError(`${rel} lists under Your library until someone ticks Made for this project in the Project it is for.`);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
if (store.getState().doc?.id !== asked.docId) {
|
|
264
|
+
store.reportError(`${rel} was made for ${asked.title}: open it there and tick Made for this project.`);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const out = store.setComponentFile(rel, true);
|
|
268
|
+
if (!out.ok) store.reportError(`${rel} shows under Your library. ${out.reason}`);
|
|
269
|
+
}
|
|
270
|
+
|
|
130
271
|
function proposalOf(store: ShotEditorStore, id: string): AgentProposalItem | undefined {
|
|
131
272
|
return store
|
|
132
273
|
.getState()
|
|
@@ -154,6 +295,12 @@ function proposalOf(store: ShotEditorStore, id: string): AgentProposalItem | und
|
|
|
154
295
|
export async function applyAgentProposal(store: ShotEditorStore, proposalId: string): Promise<void> {
|
|
155
296
|
const item = proposalOf(store, proposalId);
|
|
156
297
|
if (item === undefined || item.status !== "pending") return;
|
|
298
|
+
// An apply writes the document on the server. The card stays pending, so an
|
|
299
|
+
// editor who opens the Project later can still apply it.
|
|
300
|
+
if (store.getState().readOnly) {
|
|
301
|
+
store.reportError(readOnlyReason(store.getState()));
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
157
304
|
store.patchAgentProposal(proposalId, { status: "applying", error: null });
|
|
158
305
|
// THE AUTHOR'S OWN EDITS GO FIRST. Apply is a button in the same window as
|
|
159
306
|
// the canvas, so "drag a layer, then click Apply" is an ordinary gesture —
|
|
@@ -182,6 +329,18 @@ export async function applyAgentProposal(store: ShotEditorStore, proposalId: str
|
|
|
182
329
|
return;
|
|
183
330
|
}
|
|
184
331
|
|
|
332
|
+
// A FILE APPLY UNDER AN OPEN BUFFER: the bus says "files changed", which
|
|
333
|
+
// marks a clean buffer for nothing, so the editor would go on showing the
|
|
334
|
+
// text from before the apply and a later save would write it back. A clean
|
|
335
|
+
// buffer takes the disk; a dirty one cannot exist here (`askAgent` refuses
|
|
336
|
+
// while the file is dirty) and, if typed into since, keeps its 409 path.
|
|
337
|
+
if (item.proposalKind === "edit" || item.proposalKind === "create") {
|
|
338
|
+
const rel = item.path.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
339
|
+
const buffer = store.getState().codeFiles[rel];
|
|
340
|
+
if (buffer !== undefined && buffer.content === buffer.savedContent) await store.reloadCodeFile(rel);
|
|
341
|
+
if (item.proposalKind === "create" && /\.[jt]sx$/.test(rel)) linkMadeFile(store, item.sessionId, rel);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
185
344
|
if (item.proposalKind !== "shot") return;
|
|
186
345
|
if (store.getState().name !== item.docName && item.baseRev === null) {
|
|
187
346
|
// A CREATE names a shot this chrome does not have open — by definition, it
|
|
@@ -198,6 +357,5 @@ export async function applyAgentProposal(store: ShotEditorStore, proposalId: str
|
|
|
198
357
|
}
|
|
199
358
|
}
|
|
200
359
|
// …but an EDIT of some other shot never yanks the author out of the document
|
|
201
|
-
// they are working in. It landed on disk
|
|
202
|
-
void store.refreshShots();
|
|
360
|
+
// they are working in. It landed on disk, and Project Home lists it.
|
|
203
361
|
}
|