@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
|
@@ -8,54 +8,92 @@
|
|
|
8
8
|
// Both halves are load-bearing and neither is optional:
|
|
9
9
|
//
|
|
10
10
|
// Reading BASE would tell the author 0 while the layer sat at 150, because
|
|
11
|
-
// what is on screen is
|
|
12
|
-
// 2D lane's read-effective rule was written to close, and it is
|
|
13
|
-
// because a shot's
|
|
11
|
+
// what is on screen is the tracks resolved at the frame. That is the exact
|
|
12
|
+
// defect the 2D lane's read-effective rule was written to close, and it is
|
|
13
|
+
// worse here because a shot's keyframed fields are the normal case rather
|
|
14
|
+
// than the exception.
|
|
14
15
|
//
|
|
15
16
|
// Writing the typed value into the BASE would make the field disagree with
|
|
16
17
|
// itself: type 100, watch it read back 250. So a write goes through
|
|
17
18
|
// `setEffectiveField`, which is the same arithmetic a canvas drag uses —
|
|
18
19
|
// the inspector and a drag are one operation with different input devices.
|
|
19
20
|
//
|
|
21
|
+
// ── AND THE THIRD HALF: A DRAG ─────────────────────────────────────────
|
|
22
|
+
//
|
|
23
|
+
// A widget that is DRAGGED (the colour picker's three bars, a slider) never
|
|
24
|
+
// calls `commit` while the pointer is down. It calls `scrub.move` per pointer
|
|
25
|
+
// event and lets the release commit. So a section that wires only the commit
|
|
26
|
+
// half has a control that moves its thumb and writes nothing. `useScrub` and
|
|
27
|
+
// `useSelectionScrub` below are that half, over the store's scrub trio.
|
|
28
|
+
//
|
|
20
29
|
// ── WHAT IS GONE FROM THE 2D LANE'S VERSION ────────────────────────────
|
|
21
30
|
//
|
|
22
|
-
// `useNodeTrackField` and everything around it.
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
import
|
|
31
|
+
// `useNodeTrackField` and everything around it. This lane HAS keyframe tracks
|
|
32
|
+
// (schema v12) and does have diamonds — `KeyframeDot` — but not a per-property
|
|
33
|
+
// sequenced/static fork and not a `selectionRefusal`. Both were consequences of
|
|
34
|
+
// the 2D lane's per-field write path; here every section keeps building a plain
|
|
35
|
+
// `layer -> layer` and `routeLayerEdit` at `writeSelection` below turns the diff
|
|
36
|
+
// into a keyframe on any path the layer animates. So `planSelection` collapses
|
|
37
|
+
// to dedupe plus document order, and no section knows tracks exist.
|
|
38
|
+
import { useCallback, useMemo, useRef, useSyncExternalStore } from "react";
|
|
39
|
+
import type { ShotEditorStore } from "../../state/store.js";
|
|
40
|
+
import {
|
|
41
|
+
containerFrame,
|
|
42
|
+
effectiveTransform,
|
|
43
|
+
homeOf,
|
|
44
|
+
keyframeMinter,
|
|
45
|
+
lockRefusal,
|
|
46
|
+
lockedLayerEdited,
|
|
47
|
+
resolveSceneTracksAt,
|
|
48
|
+
resolveTracksAt,
|
|
49
|
+
routeSceneEdit,
|
|
50
|
+
sceneAsShown,
|
|
51
|
+
} from "@dailies-studio/shot/doc";
|
|
52
|
+
import type { Doc3D, Layer3D, Scene3D, TransformField, Transform3D } from "@dailies-studio/shot/doc";
|
|
53
|
+
import type { ScrubHandlers } from "@dailies-studio/ui";
|
|
30
54
|
import { useShotEditor, useStore } from "../../state/context.js";
|
|
55
|
+
import { authoringFrame } from "../../state/viewMode.js";
|
|
56
|
+
import { commitTransformField, writeSelection } from "../../state/fieldWrite.js";
|
|
31
57
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
58
|
+
// A `NO_SCRUB` triple of no-ops used to live here, passed by every section that
|
|
59
|
+
// believed "a colour swatch takes no drag". The claim was false: `ColorPicker`
|
|
60
|
+
// routes its SV square, its hue bar and its alpha bar ENTIRELY through
|
|
61
|
+
// `scrub.move`, and `PropControl`'s sliders take the same path, so every
|
|
62
|
+
// section that passed it had a colour reachable only through the hex and R/G/B
|
|
63
|
+
// boxes. `useScrub` and `useSelectionScrub` below are what replaced it, and it
|
|
64
|
+
// is deleted rather than deprecated so a new section cannot reintroduce the bug
|
|
65
|
+
// by reaching for the convenient-looking constant.
|
|
36
66
|
|
|
37
67
|
/** THE one document-edit seam. Every inspector commit is a transaction, so it
|
|
38
|
-
* is one undo entry however many layers it touched.
|
|
68
|
+
* is one undo entry however many layers it touched. A write that edits a
|
|
69
|
+
* locked layer lands nothing and names that layer, the same rule
|
|
70
|
+
* `writeSelection` applies per layer. */
|
|
39
71
|
export function useDocWrite(): (write: (doc: Doc3D) => Doc3D) => void {
|
|
40
72
|
const store = useStore();
|
|
41
|
-
return useCallback(
|
|
73
|
+
return useCallback(
|
|
74
|
+
(write) => {
|
|
75
|
+
let refusal: string | null = null;
|
|
76
|
+
const ran = store.edit((base) => {
|
|
77
|
+
const next = write(base);
|
|
78
|
+
const locked = lockedLayerEdited(base, next);
|
|
79
|
+
if (locked === null) return next;
|
|
80
|
+
refusal = lockRefusal(locked);
|
|
81
|
+
return base;
|
|
82
|
+
});
|
|
83
|
+
if (ran && refusal !== null) store.reportError(refusal);
|
|
84
|
+
},
|
|
85
|
+
[store],
|
|
86
|
+
);
|
|
42
87
|
}
|
|
43
88
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
for (const id of doc.scenes) {
|
|
53
|
-
const duration = Math.max(1, Math.trunc(doc.scenesById[id]?.durationFrames ?? 0));
|
|
54
|
-
if (id === sceneId) break;
|
|
55
|
-
start += duration;
|
|
56
|
-
}
|
|
57
|
-
return Math.max(0, frame - start);
|
|
58
|
-
}
|
|
89
|
+
// `sceneLocalFrame` and a private `containerFrame` used to live here, and the
|
|
90
|
+
// store carried a third open-coding of the same arithmetic. Three prefix sums
|
|
91
|
+
// over one mapping, one of which clamped a scene duration at 1 where the other
|
|
92
|
+
// two clamped at 0 — they agreed only because `validate` refuses a duration
|
|
93
|
+
// below 1, which is a gate holding up an invariant nobody asked it to hold.
|
|
94
|
+
// There is one now: `containerFrame` in @dailies-studio/shot, imported above.
|
|
95
|
+
// Every site here calls it, including the ones that used to open-code its
|
|
96
|
+
// definition-vs-shot fork inline.
|
|
59
97
|
|
|
60
98
|
/** How stale the inspector's DISPLAYED frame may run while the playhead is
|
|
61
99
|
* moving fast. ~10Hz keeps a scrub's numbers live enough to read against the
|
|
@@ -83,69 +121,233 @@ const INSPECTOR_FRAME_MS = 100;
|
|
|
83
121
|
* that subscribed the raw frame would run its resolver fold per tick. */
|
|
84
122
|
export function useCoalescedFrame(): number {
|
|
85
123
|
const store = useStore();
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
124
|
+
const source = coalescedFrameSource(store, "frame");
|
|
125
|
+
return useSyncExternalStore(source.subscribe, source.get, source.get);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** The definition clock (`defFrame`), coalesced by the same policy. A
|
|
129
|
+
* readout inside a definition that subscribed the hot channel re-rendered
|
|
130
|
+
* per tick of the def-local clock while the transport played. */
|
|
131
|
+
export function useCoalescedDefFrame(): number {
|
|
132
|
+
const store = useStore();
|
|
133
|
+
const source = coalescedFrameSource(store, "defFrame");
|
|
134
|
+
return useSyncExternalStore(source.subscribe, source.get, source.get);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* ONE coalescer per store, shared by every `useCoalescedFrame` caller.
|
|
139
|
+
*
|
|
140
|
+
* Each caller used to own a raw store subscription, a `useState` and a
|
|
141
|
+
* trailing timer. `useSelectionValue` instantiates two per field, so the
|
|
142
|
+
* Typography section alone held eighteen subscriptions and eighteen timers,
|
|
143
|
+
* every one of which woke on every store notification (sixty a second while
|
|
144
|
+
* playing) to read the state and return. This is the same policy, held once:
|
|
145
|
+
* the frame is HELD while the transport plays (the pause itself notifies and
|
|
146
|
+
* falls through to the flush), trails at ~10 Hz during a scrub, and leads on
|
|
147
|
+
* a lone seek. Subscribers see one notification per coalesced change through
|
|
148
|
+
* `useSyncExternalStore`.
|
|
149
|
+
*
|
|
150
|
+
* The store subscription exists only while someone listens. A fresh listener
|
|
151
|
+
* syncs the frame from the store WITHOUT touching `lastPush`: mounting must
|
|
152
|
+
* not consume the leading edge, or the first seek after mount would coalesce
|
|
153
|
+
* instead of landing immediately. With nobody listening, `get` reads the
|
|
154
|
+
* store live rather than a value that went stale in between.
|
|
155
|
+
*/
|
|
156
|
+
interface CoalescedFrameSource {
|
|
157
|
+
get(): number;
|
|
158
|
+
subscribe(listener: () => void): () => void;
|
|
159
|
+
}
|
|
160
|
+
type CoalescedClock = "frame" | "defFrame";
|
|
161
|
+
const COALESCED_FRAME_SOURCES = new WeakMap<ShotEditorStore, Partial<Record<CoalescedClock, CoalescedFrameSource>>>();
|
|
162
|
+
function coalescedFrameSource(store: ShotEditorStore, key: CoalescedClock): CoalescedFrameSource {
|
|
163
|
+
let bucket = COALESCED_FRAME_SOURCES.get(store);
|
|
164
|
+
if (bucket === undefined) {
|
|
165
|
+
bucket = {};
|
|
166
|
+
COALESCED_FRAME_SOURCES.set(store, bucket);
|
|
167
|
+
}
|
|
168
|
+
const existing = bucket[key];
|
|
169
|
+
if (existing !== undefined) return existing;
|
|
170
|
+
const listeners = new Set<() => void>();
|
|
171
|
+
let frame = store.getState()[key];
|
|
172
|
+
let lastPush = 0;
|
|
173
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
174
|
+
let unsubscribe: (() => void) | null = null;
|
|
175
|
+
// Only a frame CHANGE spends the leading edge. Every store notification lands
|
|
176
|
+
// here, so a late non-frame one (an asset-usage fetch after open) would
|
|
177
|
+
// otherwise make the next lone seek trail.
|
|
178
|
+
const push = (next: number): void => {
|
|
179
|
+
if (next === frame) return;
|
|
180
|
+
lastPush = performance.now();
|
|
181
|
+
frame = next;
|
|
182
|
+
for (const listener of [...listeners]) listener();
|
|
183
|
+
};
|
|
184
|
+
const onChange = (): void => {
|
|
185
|
+
const s = store.getState();
|
|
186
|
+
if (s.playing) return;
|
|
187
|
+
if (timer !== null) {
|
|
188
|
+
clearTimeout(timer);
|
|
189
|
+
timer = null;
|
|
190
|
+
}
|
|
191
|
+
const since = performance.now() - lastPush;
|
|
192
|
+
if (since >= INSPECTOR_FRAME_MS) {
|
|
193
|
+
push(s[key]);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
// The trailing flush re-reads the store when it fires, so the value that
|
|
197
|
+
// lands is the newest one, not the one that armed the timer.
|
|
198
|
+
timer = setTimeout(() => {
|
|
199
|
+
timer = null;
|
|
200
|
+
push(store.getState()[key]);
|
|
201
|
+
}, INSPECTOR_FRAME_MS - since);
|
|
202
|
+
};
|
|
203
|
+
const source: CoalescedFrameSource = {
|
|
204
|
+
get: () => (listeners.size === 0 ? store.getState()[key] : frame),
|
|
205
|
+
subscribe(listener) {
|
|
206
|
+
if (listeners.size === 0) {
|
|
207
|
+
frame = store.getState()[key];
|
|
208
|
+
unsubscribe = store.subscribe(onChange);
|
|
107
209
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (timer !== null) clearTimeout(timer);
|
|
124
|
-
};
|
|
125
|
-
}, [store]);
|
|
126
|
-
return frame;
|
|
210
|
+
listeners.add(listener);
|
|
211
|
+
return () => {
|
|
212
|
+
listeners.delete(listener);
|
|
213
|
+
if (listeners.size !== 0) return;
|
|
214
|
+
unsubscribe?.();
|
|
215
|
+
unsubscribe = null;
|
|
216
|
+
if (timer !== null) {
|
|
217
|
+
clearTimeout(timer);
|
|
218
|
+
timer = null;
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
bucket[key] = source;
|
|
224
|
+
return source;
|
|
127
225
|
}
|
|
128
226
|
|
|
129
227
|
/** The scene the inspector is looking at, and the SCENE-LOCAL frame — the two
|
|
130
|
-
* things every effective read needs. The frame is the COALESCED one.
|
|
228
|
+
* things every effective read needs. The frame is the COALESCED one.
|
|
229
|
+
*
|
|
230
|
+
* With a DEFINITION open (unify P2) the container is the definition and the
|
|
231
|
+
* frame is the DEF-LOCAL clock: interior keyframes live on def-local frames, so
|
|
232
|
+
* an effective read at a master-derived frame would show values from a clock
|
|
233
|
+
* the stage is not rendering. */
|
|
131
234
|
export function useInspectorFrame(): { sceneId: string | null; sceneFrame: number } {
|
|
132
235
|
const sceneId = useShotEditor((s) => s.sceneId);
|
|
236
|
+
const editingDef = useShotEditor((s) => s.editingDef);
|
|
237
|
+
const defFrame = useCoalescedDefFrame();
|
|
133
238
|
const frame = useCoalescedFrame();
|
|
134
239
|
const doc = useShotEditor((s) => s.doc);
|
|
135
|
-
if (doc === null
|
|
136
|
-
return { sceneId, sceneFrame:
|
|
240
|
+
if (doc === null) return { sceneId: null, sceneFrame: 0 };
|
|
241
|
+
if (editingDef !== null) return { sceneId: editingDef, sceneFrame: defFrame };
|
|
242
|
+
if (sceneId === null) return { sceneId: null, sceneFrame: 0 };
|
|
243
|
+
return { sceneId, sceneFrame: containerFrame(doc, sceneId, frame) };
|
|
137
244
|
}
|
|
138
245
|
|
|
139
|
-
/** Every selected layer, in document order
|
|
140
|
-
* subjects, so no section re-derives the list with a
|
|
246
|
+
/** Every selected layer, in document order, AS PAINTED at the playhead. The ONE
|
|
247
|
+
* place a panel gets its subjects, so no section re-derives the list with a
|
|
248
|
+
* different answer. Resolves through the CONTAINER (`editingDef ?? sceneId`,
|
|
249
|
+
* the store's own `layerContainer` rule), so the right rail works inside a
|
|
250
|
+
* definition.
|
|
251
|
+
*
|
|
252
|
+
* ── `resolveTracksAt` IS THE READOUT HALF OF THE ASYMMETRY ──────────────
|
|
253
|
+
*
|
|
254
|
+
* The header above states that readouts come from the layer as painted, and
|
|
255
|
+
* until this call only the TRANSFORM half was implemented (`effectiveTransform`
|
|
256
|
+
* -> `trackedBaseTransform`, which substitutes five channels and nothing else).
|
|
257
|
+
* Every other tracked path was read straight off the authored constant — and
|
|
258
|
+
* that constant is DEAD once a track exists, because `routeTrackedEdit` mints
|
|
259
|
+
* the keyframe and writes the old base BACK (trackOps.ts). Base pinned plus a
|
|
260
|
+
* readout that reads the base is a control that can never move again: the
|
|
261
|
+
* slider does not follow the pointer, an arrow nudge is always base+step, and
|
|
262
|
+
* typing the number the field is showing is diffed against itself and writes
|
|
263
|
+
* nothing at all. That is one bug over ~70 trackable paths, from
|
|
264
|
+
* `instance.props.<key>` through `paint.fill.color`, `mesh.color`,
|
|
265
|
+
* `text.style.sizePx` and the crop boxes.
|
|
266
|
+
*
|
|
267
|
+
* The two halves compose rather than collide: `resolveTracksAt` SKIPS transform
|
|
268
|
+
* paths (tracks.ts), leaving them to `effectiveTransform` below, and it returns
|
|
269
|
+
* its input by reference for an untracked layer, so the common case allocates
|
|
270
|
+
* nothing. `tracks` stays on the result, which is what keeps `KeyframeDot`'s
|
|
271
|
+
* diamond states reading true off a resolved layer.
|
|
272
|
+
*
|
|
273
|
+
* DISPLAY ONLY. Writes never come through here: `writeSelection` re-reads its
|
|
274
|
+
* subjects from the document it is handed, and a scrub capture re-derives from
|
|
275
|
+
* the gesture's base, so a sampled value cannot be baked into a constant.
|
|
276
|
+
*/
|
|
141
277
|
export function useSelectedLayers(): Layer3D[] {
|
|
142
278
|
const doc = useShotEditor((s) => s.doc);
|
|
143
279
|
const sceneId = useShotEditor((s) => s.sceneId);
|
|
280
|
+
const editingDef = useShotEditor((s) => s.editingDef);
|
|
144
281
|
const selection = useShotEditor((s) => s.selection);
|
|
145
|
-
|
|
282
|
+
const { sceneFrame } = useInspectorFrame();
|
|
283
|
+
// Memoised on its five inputs: the inspector repaints whole on every
|
|
284
|
+
// document identity change, i.e. once per rAF frame of any drag anywhere,
|
|
285
|
+
// and this ran its map/filter/resolve chain once per `useSelectionValue`
|
|
286
|
+
// site in the open sections (nine in Typography alone) per repaint.
|
|
287
|
+
return useMemo(() => {
|
|
288
|
+
if (doc === null) return EMPTY_LAYERS;
|
|
289
|
+
const container = editingDef ?? sceneId;
|
|
290
|
+
if (container === null) return EMPTY_LAYERS;
|
|
291
|
+
const home = homeOf(doc, container);
|
|
292
|
+
if (home === null) return EMPTY_LAYERS;
|
|
293
|
+
return selection
|
|
294
|
+
.map((id) => home.layersById[id])
|
|
295
|
+
.filter((l): l is Layer3D => l !== undefined)
|
|
296
|
+
.map((l) => resolveTracksAt(l, sceneFrame, doc.fps));
|
|
297
|
+
}, [doc, sceneId, editingDef, selection, sceneFrame]);
|
|
298
|
+
}
|
|
299
|
+
const EMPTY_LAYERS: Layer3D[] = [];
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* `effectiveTransform`, replayed per (layer, frame, fps).
|
|
303
|
+
*
|
|
304
|
+
* Every `useSelectionValue` site folds it for every selected layer on every
|
|
305
|
+
* inspector repaint; the layer objects are immutable on edit and the frame is
|
|
306
|
+
* the shared coalesced one, so the same fold was answered up to nine times per
|
|
307
|
+
* section per repaint. One entry per layer object, replaced when the frame or
|
|
308
|
+
* fps moves.
|
|
309
|
+
*/
|
|
310
|
+
const EFFECTIVE_TRANSFORMS = new WeakMap<
|
|
311
|
+
Layer3D,
|
|
312
|
+
{ sceneFrame: number; fpsNum: number; fpsDen: number; transform: Transform3D }
|
|
313
|
+
>();
|
|
314
|
+
function effectiveTransformCached(layer: Layer3D, sceneFrame: number, fps: Doc3D["fps"]): Transform3D {
|
|
315
|
+
const cached = EFFECTIVE_TRANSFORMS.get(layer);
|
|
316
|
+
if (
|
|
317
|
+
cached !== undefined &&
|
|
318
|
+
cached.sceneFrame === sceneFrame &&
|
|
319
|
+
cached.fpsNum === fps.num &&
|
|
320
|
+
cached.fpsDen === fps.den
|
|
321
|
+
) {
|
|
322
|
+
return cached.transform;
|
|
323
|
+
}
|
|
324
|
+
const transform = effectiveTransform(layer, sceneFrame, fps);
|
|
325
|
+
EFFECTIVE_TRANSFORMS.set(layer, { sceneFrame, fpsNum: fps.num, fpsDen: fps.den, transform });
|
|
326
|
+
return transform;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* The shot the scene sections describe, AS PAINTED at the playhead.
|
|
331
|
+
*
|
|
332
|
+
* `useSelectedLayers`'s twin one level up, for the same reason and against the
|
|
333
|
+
* same failure: `routeSceneEdit` runs the SAME `routeTrackedEdit`, so a scene
|
|
334
|
+
* constant is pinned the moment a track exists on it, and the two sections that
|
|
335
|
+
* read `doc.scenesById[sceneId]` directly (the background well and the 27
|
|
336
|
+
* keyframable post-effect params) would show it forever.
|
|
337
|
+
*
|
|
338
|
+
* The frame is `containerFrame`'s and NOT `useInspectorFrame`'s: that hook
|
|
339
|
+
* substitutes the DEFINITION and its own clock while one is open, and a shot is
|
|
340
|
+
* not a definition — asking it for a scene would hand back `undefined` and blank
|
|
341
|
+
* the section. Display only, exactly as above.
|
|
342
|
+
*/
|
|
343
|
+
export function useInspectorScene(): Scene3D | null {
|
|
344
|
+
const doc = useShotEditor((s) => s.doc);
|
|
345
|
+
const sceneId = useShotEditor((s) => s.sceneId);
|
|
346
|
+
const frame = useCoalescedFrame();
|
|
347
|
+
if (doc === null || sceneId === null) return null;
|
|
146
348
|
const scene = doc.scenesById[sceneId];
|
|
147
|
-
if (scene === undefined) return
|
|
148
|
-
return
|
|
349
|
+
if (scene === undefined) return null;
|
|
350
|
+
return resolveSceneTracksAt(scene, containerFrame(doc, sceneId, frame), doc.fps);
|
|
149
351
|
}
|
|
150
352
|
|
|
151
353
|
export interface SelectionValue<T> {
|
|
@@ -160,17 +362,54 @@ export interface SelectionValue<T> {
|
|
|
160
362
|
* Read one property across the selection, from the layer AS PAINTED.
|
|
161
363
|
*
|
|
162
364
|
* `read` is handed the EFFECTIVE transform for transform properties and the
|
|
163
|
-
* layer itself for paint ones
|
|
164
|
-
*
|
|
365
|
+
* layer itself for paint ones.
|
|
366
|
+
*
|
|
367
|
+
* ── `read` MUST RETURN A SCALAR ─────────────────────────────────────────
|
|
368
|
+
*
|
|
369
|
+
* A number, a string, a boolean, null or undefined. Members are compared with
|
|
370
|
+
* `Object.is`, so an OBJECT read is mixed BY IDENTITY: `l => l.paint.shadows[0]`
|
|
371
|
+
* reports `mixed` for every multi-selection, including one where all the shadows
|
|
372
|
+
* are identical, and the row shows "Mixed" over values the author can see agree.
|
|
373
|
+
* Read the scalar the row displays — `l => l.paint.shadows?.[0]?.color`, one
|
|
374
|
+
* hook per field.
|
|
375
|
+
*
|
|
376
|
+
* A deep equal here is NOT the fix, for the reason the 2D lane's twin states:
|
|
377
|
+
* it would need a key-order-independent equality, which is a second and quieter
|
|
378
|
+
* definition of "the same value" living in a module with no business owning one.
|
|
379
|
+
*
|
|
380
|
+
* The rule cannot be stated in the type system over an arbitrary `T`, so a
|
|
381
|
+
* violation is REPORTED instead: a permanently wrong "Mixed" that says nothing
|
|
382
|
+
* is exactly the silent readout this lane keeps removing.
|
|
165
383
|
*/
|
|
166
384
|
export function useSelectionValue<T>(read: (layer: Layer3D, effective: Transform3D) => T): SelectionValue<T> {
|
|
167
385
|
const layers = useSelectedLayers();
|
|
168
386
|
const { sceneFrame } = useInspectorFrame();
|
|
169
387
|
const doc = useShotEditor((s) => s.doc);
|
|
170
388
|
if (layers.length === 0 || doc === null) return { value: undefined, mixed: false };
|
|
171
|
-
const values = layers.map((l) => read(l,
|
|
389
|
+
const values = layers.map((l) => read(l, effectiveTransformCached(l, sceneFrame, doc.fps)));
|
|
172
390
|
const first = values[0]!;
|
|
173
|
-
|
|
391
|
+
const mixed = values.some((v) => !Object.is(v, first));
|
|
392
|
+
// Only in the mixed branch: a scalar read costs one `typeof` on a value that
|
|
393
|
+
// already disagreed, and an agreeing selection costs nothing at all.
|
|
394
|
+
if (mixed) reportObjectRead(first);
|
|
395
|
+
return { value: first, mixed };
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/** Shapes already reported, so a wrong read is loud ONCE rather than sixty
|
|
399
|
+
* times a second. This runs inside render, and the inspector re-renders on
|
|
400
|
+
* every coalesced frame while the playhead moves. Keyed on the value's own
|
|
401
|
+
* keys, so two different broken reads each get their own line. */
|
|
402
|
+
const OBJECT_READS = new Set<string>();
|
|
403
|
+
|
|
404
|
+
function reportObjectRead(value: unknown): void {
|
|
405
|
+
if (value === null || typeof value !== "object") return;
|
|
406
|
+
const shape = Object.keys(value).join(",");
|
|
407
|
+
if (OBJECT_READS.has(shape)) return;
|
|
408
|
+
OBJECT_READS.add(shape);
|
|
409
|
+
console.error(
|
|
410
|
+
`[shot inspector] useSelectionValue read an OBJECT ({${shape}}), so "Mixed" is decided by ` +
|
|
411
|
+
`identity and is true for every multi-selection. Read the scalar the row displays instead.`,
|
|
412
|
+
);
|
|
174
413
|
}
|
|
175
414
|
|
|
176
415
|
/**
|
|
@@ -180,44 +419,129 @@ export function useSelectionValue<T>(read: (layer: Layer3D, effective: Transform
|
|
|
180
419
|
* that way — so a fan-out cannot depend on the order the author happened to
|
|
181
420
|
* click in, and an id cannot be visited twice.
|
|
182
421
|
*/
|
|
183
|
-
export function useSelectionWrite(): (
|
|
184
|
-
build: (layer: Layer3D) => Layer3D,
|
|
185
|
-
opts?: { includeLocked?: boolean },
|
|
186
|
-
) => void {
|
|
422
|
+
export function useSelectionWrite(): (build: (layer: Layer3D) => Layer3D) => void {
|
|
187
423
|
const store = useStore();
|
|
188
424
|
return useCallback(
|
|
189
|
-
(build
|
|
425
|
+
(build) => {
|
|
190
426
|
// LIVE at the commit, like the frame in useTransformWrite: a
|
|
191
427
|
// render-captured sceneId paired with a live frame would write into
|
|
192
|
-
// last render's scene when the commit lands right on a crossing.
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const
|
|
196
|
-
if (
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const layer = layersById[id];
|
|
204
|
-
// A locked layer is skipped — that is what locked MEANS. The one
|
|
205
|
-
// exception is the lock toggle itself, or a lock would be one-way.
|
|
206
|
-
if (layer === undefined || (layer.locked === true && opts?.includeLocked !== true)) continue;
|
|
207
|
-
const next = build(layer);
|
|
208
|
-
if (next === layer) continue;
|
|
209
|
-
layersById[id] = next;
|
|
210
|
-
changed = true;
|
|
211
|
-
}
|
|
212
|
-
return changed
|
|
213
|
-
? { ...doc, scenesById: { ...doc.scenesById, [sceneId]: { ...scene, layersById } } }
|
|
214
|
-
: doc;
|
|
428
|
+
// last render's scene when the commit lands right on a crossing. The
|
|
429
|
+
// CONTAINER rule: a definition while one is open.
|
|
430
|
+
const s = store.getState();
|
|
431
|
+
const container = s.editingDef ?? s.sceneId;
|
|
432
|
+
if (container === null || s.selection.length === 0 || s.doc === null) return;
|
|
433
|
+
const at = containerFrame(s.doc, container, authoringFrame(s));
|
|
434
|
+
let refusal: string | null = null;
|
|
435
|
+
const ran = store.edit((doc) => {
|
|
436
|
+
const out = writeSelection(doc, container, s.selection, build, at);
|
|
437
|
+
refusal = out.refusal;
|
|
438
|
+
return out.doc;
|
|
215
439
|
});
|
|
440
|
+
if (ran && refusal !== null) store.reportError(refusal);
|
|
216
441
|
},
|
|
217
442
|
[store],
|
|
218
443
|
);
|
|
219
444
|
}
|
|
220
445
|
|
|
446
|
+
/**
|
|
447
|
+
* `writeSelection`'s twin with the SHOT as the subject: build the next scene,
|
|
448
|
+
* and let `routeSceneEdit` turn the diff into a keyframe on any path the shot
|
|
449
|
+
* animates (P5).
|
|
450
|
+
*
|
|
451
|
+
* Exported because the two scene sections each own a pure patch function
|
|
452
|
+
* (`patchScene`, `patchEffect`) that both their typed and their drag path
|
|
453
|
+
* already run, and this wraps exactly those — so the background well and fifteen
|
|
454
|
+
* effect fields become keyframe-aware without either section learning what a
|
|
455
|
+
* track is. Same rule as the layer half, one level up.
|
|
456
|
+
*
|
|
457
|
+
* The FRAME is read live by the caller, never captured at render, for the reason
|
|
458
|
+
* `useSelectionScrub` reads it live: a drag held across a shot boundary would
|
|
459
|
+
* otherwise write keys at a frame the playhead has left.
|
|
460
|
+
*
|
|
461
|
+
* IDENTITY WHEN NOTHING CHANGED, so `edit` records no history entry and a drag
|
|
462
|
+
* that ends where it started commits nothing.
|
|
463
|
+
*/
|
|
464
|
+
export function writeScene(
|
|
465
|
+
doc: Doc3D,
|
|
466
|
+
sceneId: string,
|
|
467
|
+
build: (scene: Scene3D) => Scene3D,
|
|
468
|
+
sceneFrame: number,
|
|
469
|
+
): Doc3D {
|
|
470
|
+
const scene = doc.scenesById[sceneId];
|
|
471
|
+
if (scene === undefined) return doc;
|
|
472
|
+
// Built from the shot as shown, for `writeSelection`'s reason.
|
|
473
|
+
const shown = sceneAsShown(scene, sceneFrame, doc.fps);
|
|
474
|
+
const next = routeSceneEdit(scene, build(shown), sceneFrame, keyframeMinter(doc), shown);
|
|
475
|
+
if (next === scene) return doc;
|
|
476
|
+
return { ...doc, scenesById: { ...doc.scenesById, [sceneId]: next } };
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* A PANEL DRAG as ONE undo entry: the store's scrub trio wearing the shape the
|
|
481
|
+
* shared widgets ask for (`ScrubHandlers`, ui/TextField.ts).
|
|
482
|
+
*
|
|
483
|
+
* `write` is PURE, `(doc, raw) => doc`, and it MUST RE-READ its subject from
|
|
484
|
+
* the doc it is HANDED. Every capture re-derives from the gesture's BASE
|
|
485
|
+
* (store.ts's `scrubCapture`), so a move passes the ABSOLUTE value the author is
|
|
486
|
+
* pointing at and never a delta from the previous move. A builder that instead
|
|
487
|
+
* closed over a value read at RENDER time re-applies the whole drag on top of
|
|
488
|
+
* itself sixty times a second, and the only symptom is a control that runs away
|
|
489
|
+
* from the cursor.
|
|
490
|
+
*
|
|
491
|
+
* NO ERROR IS RETURNED, unlike the 2D lane's `DocWrite`. This chrome has one
|
|
492
|
+
* error strip and the store owns it: `beginScrub` puts its own refusal there
|
|
493
|
+
* when another lane holds the history (a stage drag, a wheel dolly under
|
|
494
|
+
* follow), and the two later verbs no-op on a refused scrub, so a widget may
|
|
495
|
+
* drive all three blind, which is exactly what `ColorPicker` does.
|
|
496
|
+
*/
|
|
497
|
+
export function useScrub(write: (doc: Doc3D, raw: unknown) => Doc3D): ScrubHandlers {
|
|
498
|
+
const store = useStore();
|
|
499
|
+
// Deliberately not memoized. Every call site passes an inline builder, so a
|
|
500
|
+
// dependency on it would rebuild this object every render anyway, and the
|
|
501
|
+
// widgets read these handlers off their props at POINTER time, which is what
|
|
502
|
+
// keeps the newest builder the one a move runs.
|
|
503
|
+
return {
|
|
504
|
+
start: () => store.beginScrub(),
|
|
505
|
+
move: (raw) => store.scrubCapture((doc) => write(doc, raw)),
|
|
506
|
+
end: (commit) => store.endScrub(commit),
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* The same bracket, fanned out over the selection: `useSelectionWrite`'s builder
|
|
512
|
+
* with the widget's raw value beside it, through the same `writeSelection`.
|
|
513
|
+
*
|
|
514
|
+
* The subjects are read LIVE per capture rather than at render, for the reason
|
|
515
|
+
* the commit path reads them live: a fan-out over the selection a panel last
|
|
516
|
+
* RENDERED against would write into layers the author has since left.
|
|
517
|
+
*/
|
|
518
|
+
export function useSelectionScrub(build: (layer: Layer3D, raw: unknown) => Layer3D): ScrubHandlers {
|
|
519
|
+
const store = useStore();
|
|
520
|
+
// The last capture's lock refusal, named once at the release rather than per
|
|
521
|
+
// pointer event.
|
|
522
|
+
const refusal = useRef<string | null>(null);
|
|
523
|
+
const scrub = useScrub((doc, raw) => {
|
|
524
|
+
const s = store.getState();
|
|
525
|
+
const container = s.editingDef ?? s.sceneId;
|
|
526
|
+
if (container === null) return doc;
|
|
527
|
+
// The frame LIVE at the capture, and against the document being written
|
|
528
|
+
// rather than the store's — a scrub re-derives from its base, so the shot
|
|
529
|
+
// starts here are the base's.
|
|
530
|
+
const at = containerFrame(doc, container, authoringFrame(s));
|
|
531
|
+
const out = writeSelection(doc, container, s.selection, (layer) => build(layer, raw), at);
|
|
532
|
+
refusal.current = out.refusal;
|
|
533
|
+
return out.doc;
|
|
534
|
+
});
|
|
535
|
+
return {
|
|
536
|
+
...scrub,
|
|
537
|
+
end: (commit) => {
|
|
538
|
+
scrub.end(commit);
|
|
539
|
+
if (commit && refusal.current !== null) store.reportError(refusal.current);
|
|
540
|
+
refusal.current = null;
|
|
541
|
+
},
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
|
|
221
545
|
/**
|
|
222
546
|
* Type a transform field across the selection.
|
|
223
547
|
*
|
|
@@ -227,42 +551,10 @@ export function useSelectionWrite(): (
|
|
|
227
551
|
*/
|
|
228
552
|
export function useTransformWrite(): (field: TransformField, value: number) => void {
|
|
229
553
|
const store = useStore();
|
|
230
|
-
const write = useSelectionWrite();
|
|
231
554
|
return useCallback(
|
|
232
555
|
(field, value) => {
|
|
233
|
-
|
|
234
|
-
// the compensation arithmetic must target the frame actually painted, or
|
|
235
|
-
// the typed value lands up to 100ms of playhead away from where it reads
|
|
236
|
-
// back.
|
|
237
|
-
const s = store.getState();
|
|
238
|
-
const doc = s.doc;
|
|
239
|
-
if (doc === null || s.sceneId === null) return;
|
|
240
|
-
const sceneId = s.sceneId;
|
|
241
|
-
const sceneFrame = sceneLocalFrame(doc, sceneId, s.frame);
|
|
242
|
-
let refusal: string | null = null;
|
|
243
|
-
// ANIMATE ON: the typed value becomes a During block ending at the
|
|
244
|
-
// playhead — the same fork a stage drag takes (store.ts) and the camera
|
|
245
|
-
// takes (writeFraming), because the inspector and a drag are one
|
|
246
|
-
// operation with different input devices. OFF: the base write below.
|
|
247
|
-
if (s.animateMode) {
|
|
248
|
-
store.edit((base) => {
|
|
249
|
-
const out = animateFieldEdit(base, sceneId, s.selection, field, value, sceneFrame, base.fps);
|
|
250
|
-
refusal = out.refusals[0] ?? null;
|
|
251
|
-
return out.doc;
|
|
252
|
-
});
|
|
253
|
-
store.reportError(refusal);
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
write((layer) => {
|
|
257
|
-
const out = setEffectiveField(layer, field, value, sceneFrame, doc.fps);
|
|
258
|
-
if (out.refusal !== null) {
|
|
259
|
-
refusal ??= out.refusal;
|
|
260
|
-
return layer;
|
|
261
|
-
}
|
|
262
|
-
return out.transform === layer.transform ? layer : { ...layer, transform: out.transform };
|
|
263
|
-
});
|
|
264
|
-
store.reportError(refusal);
|
|
556
|
+
store.reportError(commitTransformField(store, field, value, store.getState().selection));
|
|
265
557
|
},
|
|
266
|
-
[store
|
|
558
|
+
[store],
|
|
267
559
|
);
|
|
268
560
|
}
|