@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,373 @@
|
|
|
1
|
+
// THE KEYFRAME DIAMOND: the inspector's one way to start animating a property,
|
|
2
|
+
// and the only place a track is created.
|
|
3
|
+
//
|
|
4
|
+
// ── ONE WIDGET, THIRTY PATHS ────────────────────────────────────────────
|
|
5
|
+
//
|
|
6
|
+
// It takes a dotted PATH and nothing else, because the path table
|
|
7
|
+
// (`TRACKABLE_PATHS`) already knows the kind, the label, the layer kinds it
|
|
8
|
+
// exists on and the container it needs — so a section adds animation to a row
|
|
9
|
+
// by naming the path it edits, and cannot get the rules for that path wrong in
|
|
10
|
+
// a way the gate would then reject.
|
|
11
|
+
//
|
|
12
|
+
// ── THREE STATES, AND THEY ARE NOT DECORATION ───────────────────────────
|
|
13
|
+
//
|
|
14
|
+
// All three are one 16px button (`.insp-key`, ui/widgets.css) INSIDE the box
|
|
15
|
+
// that shows the value it keys: render it as that field's `trailing`, its
|
|
16
|
+
// accessory lane (a NumberField's, a ColorField's, a labelled InspectorRow's
|
|
17
|
+
// pill), never in a column after the box, so a field that cannot be keyed is
|
|
18
|
+
// exactly as wide as one that can.
|
|
19
|
+
//
|
|
20
|
+
// keyed a key sits AT the playhead: a filled `--kf` diamond. Pressing
|
|
21
|
+
// removes it.
|
|
22
|
+
// tracked a track exists, and this frame is between keys: a hollow `--kf`
|
|
23
|
+
// diamond. Pressing keys the value the layer is holding right now.
|
|
24
|
+
// rest no track: a hollow diamond in the third ink. Pressing mints one
|
|
25
|
+
// from the authored value, which does not move the picture: a
|
|
26
|
+
// one-key track holds that value at every frame, exactly as the
|
|
27
|
+
// constant did.
|
|
28
|
+
//
|
|
29
|
+
// The state is `aria-pressed` (keyed is pressed) under ONE constant name,
|
|
30
|
+
// "Keyframe <property>", the way a toggle button speaks. A name that flipped
|
|
31
|
+
// between "Add" and "Remove" said the state twice ("Remove Position keyframe,
|
|
32
|
+
// pressed"); the verb lives in the tooltip.
|
|
33
|
+
//
|
|
34
|
+
// ── A VECTOR'S BOXES EACH CARRY ITS DIAMOND ─────────────────────────────
|
|
35
|
+
//
|
|
36
|
+
// A track is per VECTOR: `transform.position` carries x, y and z in one vec3,
|
|
37
|
+
// so keying any of them keys all three (validate refuses `transform.position`
|
|
38
|
+
// beside `transform.position.z`). So every box that shows a part of one
|
|
39
|
+
// vector carries that vector's diamond, and they light together: Position X,
|
|
40
|
+
// Y and Z each show the position track's state, and a press on any keys the
|
|
41
|
+
// vector. `parts` names the parts in the tooltip ("X, Y and Z key together"),
|
|
42
|
+
// so a diamond in the Y box never reads as Y's own.
|
|
43
|
+
//
|
|
44
|
+
// ── DISABLED WHILE IT CANNOT TELL THE TRUTH ─────────────────────────────
|
|
45
|
+
//
|
|
46
|
+
// Under `LockedControls` (a viewer's panel) it writes nothing. While the
|
|
47
|
+
// transport PLAYS, its state is read off the HELD inspector frame
|
|
48
|
+
// (`useInspectorFrame` holds the readouts during playback) while a press keys
|
|
49
|
+
// the LIVE one, so it is disabled with a tooltip that says why, rather than a
|
|
50
|
+
// button that keys a frame it is not showing. Both keep full strength
|
|
51
|
+
// (widgets.css): a disabled diamond still says whether the property moves.
|
|
52
|
+
//
|
|
53
|
+
// ── AND THE FOURTH: ABSENT ──────────────────────────────────────────────
|
|
54
|
+
//
|
|
55
|
+
// A path this layer cannot carry gets NO button, rather than a dead one. The
|
|
56
|
+
// refusal is the gate's own predicate (`layerTrackRefusal`) plus the device
|
|
57
|
+
// screen rule, so what the chrome offers and what the document accepts are the
|
|
58
|
+
// same set by construction — this lane's autosave-400 rule.
|
|
59
|
+
//
|
|
60
|
+
// ── AND ITS TWIN RIDES ALONG ────────────────────────────────────────────
|
|
61
|
+
//
|
|
62
|
+
// `ExposeDot` sits in the same lane, from the same `path`, so every box that
|
|
63
|
+
// carries a diamond can also publish that property while a definition is open
|
|
64
|
+
// — two edits rather than twenty-six call sites. It asks its OWN refusals (see
|
|
65
|
+
// its header): the two sets overlap and neither contains the other, which is
|
|
66
|
+
// why it is rendered beside the diamond rather than under its eligibility.
|
|
67
|
+
// NOT for a diamond over SEVERAL paths: a pin publishes one target, and the
|
|
68
|
+
// Corner row published only its top-left corner under the name "Radius", so
|
|
69
|
+
// an instance's override moved one corner while the row writes four.
|
|
70
|
+
//
|
|
71
|
+
// ONE PIN PER TRACK, where the diamond is one per box. A pin publishes the
|
|
72
|
+
// whole track, so Position's three boxes each carrying one drew three
|
|
73
|
+
// identical "Publish Position" buttons, and the second accessory squeezed the
|
|
74
|
+
// units out of the Tilt pair at the panel's floor. The vector's FIRST box keeps
|
|
75
|
+
// the pin, and every other box that shows a part of the same track (Y, Z, H,
|
|
76
|
+
// Tilt Y, a fill's Opacity, a stroke's Alpha) passes `pin={false}`. A
|
|
77
|
+
// ControlSpec row's vector (an instance prop, a material parameter) keeps its
|
|
78
|
+
// one pin out of the boxes altogether, in the row's label column (PropRow's
|
|
79
|
+
// `actions`, drawn with `diamond={false}`): its boxes are thirds of the row,
|
|
80
|
+
// and the first one's lane could not hold a diamond and a pin, so the pin was
|
|
81
|
+
// half clipped at the panel's floor. Each box passes `pin={false}`, and
|
|
82
|
+
// `parts` from the axis words its boxes show.
|
|
83
|
+
import { IconDiamond, useLocked } from "@dailies-studio/ui";
|
|
84
|
+
import {
|
|
85
|
+
deviceScreenDerivesPath,
|
|
86
|
+
deviceScreenOwnerOf,
|
|
87
|
+
exposedPropWriting,
|
|
88
|
+
homeOf,
|
|
89
|
+
instancePropKeyOf,
|
|
90
|
+
layerSnapshotContract,
|
|
91
|
+
layerTrackRefusal,
|
|
92
|
+
layerTrackablePath,
|
|
93
|
+
sceneTrackRefusal,
|
|
94
|
+
sceneTrackablePath,
|
|
95
|
+
trackablePath,
|
|
96
|
+
} from "@dailies-studio/shot/doc";
|
|
97
|
+
import type { Doc3D, Layer3D } from "@dailies-studio/shot/doc";
|
|
98
|
+
import type { Contract, ControlSpec } from "@dailies-studio/contract";
|
|
99
|
+
import { useShotEditor, useStore } from "../../state/context.js";
|
|
100
|
+
import { ExposeDot } from "./ExposeDot.js";
|
|
101
|
+
import { useInspectorFrame, useSelectedLayers } from "./useDocWrite.js";
|
|
102
|
+
|
|
103
|
+
/** Why a diamond is disabled while the transport runs, as its tooltip. */
|
|
104
|
+
const PLAYING_TITLE = "Pause playback to add or remove a keyframe";
|
|
105
|
+
|
|
106
|
+
/** The registry's contract for an instance layer, or undefined. Read from the
|
|
107
|
+
* store so every `KeyframeDot` call site stays "give me a path" — the
|
|
108
|
+
* `instance.props.<key>` family needs a contract to be typed at all, and a
|
|
109
|
+
* prop-name argument at forty call sites is how one of them ends up passing the
|
|
110
|
+
* wrong one. `InstanceSection` is what puts it in the store (`loadContract`);
|
|
111
|
+
* absent, `layerTrackRefusal` falls back to the layer's own snapshot. */
|
|
112
|
+
function contractOf(contracts: Record<string, Contract>, layer: Layer3D): Contract | undefined {
|
|
113
|
+
return layer.kind === "instance" ? contracts[layer.instance.component] : undefined;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** The contract's own spec for an `instance.props.<key>` path on this layer,
|
|
117
|
+
* or undefined: the live registry's, else the layer's snapshot, the order
|
|
118
|
+
* `layerTrackablePath` reads them in. */
|
|
119
|
+
function instanceSpecOf(layer: Layer3D, path: string, live: Contract | undefined): ControlSpec | undefined {
|
|
120
|
+
const key = instancePropKeyOf(path);
|
|
121
|
+
if (key === null || layer.kind !== "instance") return undefined;
|
|
122
|
+
return (live ?? layerSnapshotContract(layer))?.keys[key];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** "X and Y", "X, Y and Z": a vector's parts as one phrase. */
|
|
126
|
+
function listWords(words: readonly string[]): string {
|
|
127
|
+
return words.length <= 1 ? (words[0] ?? "") : `${words.slice(0, -1).join(", ")} and ${words[words.length - 1]}`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** The tooltip's "X, Y and Z key together.", for a diamond over two or more
|
|
131
|
+
* parts; undefined for one, which keys only itself. */
|
|
132
|
+
function togetherOf(parts: readonly string[] | undefined): string | undefined {
|
|
133
|
+
return parts === undefined || parts.length < 2 ? undefined : `${listWords(parts)} key together.`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* The diamond for one property: render it as the `trailing` of the box that
|
|
138
|
+
* shows the value (a NumberField's, a ColorField's, a labelled row's).
|
|
139
|
+
*
|
|
140
|
+
* `label` overrides what the tooltip calls the property, and defaults to the
|
|
141
|
+
* table's own. The override exists because a row's wording is sometimes the
|
|
142
|
+
* SECTION's — the table says "Corner TL" and the row says "TL" — and a tooltip
|
|
143
|
+
* that disagreed with the control beside it would be worse than a generic one.
|
|
144
|
+
*
|
|
145
|
+
* `parts` names what keys together when the diamond sits in a box that shows
|
|
146
|
+
* one part of a vector (Position's "X", "Y" and "Z"), or over a control that
|
|
147
|
+
* writes several paths at once (the four corners). The tooltip says so.
|
|
148
|
+
*
|
|
149
|
+
* `pin={false}` leaves the expose pin out of this box, for every box of a
|
|
150
|
+
* vector after the one that carries it (see the header: one pin per track).
|
|
151
|
+
* `diamond={false}` draws the pin alone, named and typed as it would be beside
|
|
152
|
+
* the diamond, for a row that carries its track's pin outside the boxes.
|
|
153
|
+
*/
|
|
154
|
+
export function KeyframeDot({
|
|
155
|
+
path,
|
|
156
|
+
label,
|
|
157
|
+
parts,
|
|
158
|
+
pin = true,
|
|
159
|
+
diamond = true,
|
|
160
|
+
}: {
|
|
161
|
+
path: string | readonly string[];
|
|
162
|
+
label?: string;
|
|
163
|
+
parts?: readonly string[];
|
|
164
|
+
pin?: boolean;
|
|
165
|
+
diamond?: boolean;
|
|
166
|
+
}) {
|
|
167
|
+
const store = useStore();
|
|
168
|
+
const doc = useShotEditor((s) => s.doc);
|
|
169
|
+
const contracts = useShotEditor((s) => s.contracts);
|
|
170
|
+
const layers = useSelectedLayers();
|
|
171
|
+
const { sceneId, sceneFrame } = useInspectorFrame();
|
|
172
|
+
// ── ONE CONTROL MAY OWN SEVERAL PATHS ─────────────────────────────────────
|
|
173
|
+
//
|
|
174
|
+
// Not a convenience: a control that edits four fields at once (the uniform
|
|
175
|
+
// corner radius writes `tl`/`tr`/`br`/`bl`) has one diamond because it has
|
|
176
|
+
// one value, and four diamonds would offer an independence the control does
|
|
177
|
+
// not have. The four are keyed together, in one transaction.
|
|
178
|
+
const paths = typeof path === "string" ? [path] : path;
|
|
179
|
+
const first = paths[0];
|
|
180
|
+
// The table's ENTRY, or — for a contract-driven path, which no table knows —
|
|
181
|
+
// the FIRST selected layer's spec. Callers that pass `label` skip both for the
|
|
182
|
+
// wording.
|
|
183
|
+
const tabled = first === undefined ? undefined : (trackablePath(first) ?? undefined);
|
|
184
|
+
const live = layers[0] === undefined ? undefined : contractOf(contracts, layers[0]);
|
|
185
|
+
const fromContract =
|
|
186
|
+
first === undefined || layers[0] === undefined
|
|
187
|
+
? undefined
|
|
188
|
+
: (layerTrackablePath(layers[0], first, live, doc?.materials) ?? undefined);
|
|
189
|
+
const name = label ?? tabled?.label ?? fromContract?.label ?? first ?? "";
|
|
190
|
+
// THE KIND THE PIN STORES is the contract vocabulary, so a published prop is
|
|
191
|
+
// drawn by the control it was published from. For `instance.props.<key>`
|
|
192
|
+
// that is the CONTRACT's kind (an enum, an asset, a curve), never the kind
|
|
193
|
+
// its track keys as: `contractTrackablePath` types an asset or a curve as a
|
|
194
|
+
// "string" track, and a prop minted with it was a text box over an asset id
|
|
195
|
+
// or a curve's JSON. A material parameter's entry already speaks it.
|
|
196
|
+
const exposedKind =
|
|
197
|
+
(first === undefined || layers[0] === undefined ? undefined : instanceSpecOf(layers[0], first, live)?.kind) ??
|
|
198
|
+
tabled?.kind ??
|
|
199
|
+
fromContract?.kind;
|
|
200
|
+
const together = togetherOf(parts);
|
|
201
|
+
|
|
202
|
+
if (doc === null || sceneId === null || layers.length === 0 || first === undefined) return null;
|
|
203
|
+
// EVERY selected layer has to be able to carry it. A diamond that keyed three
|
|
204
|
+
// of four would be a control whose effect depends on a selection the author
|
|
205
|
+
// is not looking at.
|
|
206
|
+
const eligible = layers.every((layer) =>
|
|
207
|
+
paths.every((p) => keyframeRefusal(doc, sceneId, layer, p, contractOf(contracts, layer)) === null),
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
const tracked = layers.some((layer) => paths.some((p) => layer.tracks?.[p] !== undefined));
|
|
211
|
+
const keyed =
|
|
212
|
+
tracked &&
|
|
213
|
+
layers.every((layer) =>
|
|
214
|
+
paths.every((p) => layer.tracks?.[p]?.keyframes.some((k) => k.frame === sceneFrame) === true),
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
// ── THE TWO VERBS SHARE THE LANE, AND NOT THE ELIGIBILITY ─────────────────
|
|
218
|
+
//
|
|
219
|
+
// The pin is rendered BESIDE the diamond rather than after an early return,
|
|
220
|
+
// and that is not tidiness: the two refusals are genuinely different sets.
|
|
221
|
+
// `keyframeRefusal` rejects any path the trackable table does not hold and
|
|
222
|
+
// any layer kind it does not list, while `exposeProp` asks only for a
|
|
223
|
+
// resolvable default and no colliding track — so a path with no table row
|
|
224
|
+
// (a section may name a plain authored field) is publishable while being
|
|
225
|
+
// unkeyable. Gating the pin behind the diamond would have hidden exactly
|
|
226
|
+
// those.
|
|
227
|
+
//
|
|
228
|
+
// `ExposeDot` returns null on its own outside a definition, so in the shot
|
|
229
|
+
// context this renders nothing, and the box's lane hides itself empty.
|
|
230
|
+
//
|
|
231
|
+
// A diamond over several paths is named by ALL of them: under the first
|
|
232
|
+
// alone, Media's Zoom (the four crop edges) took Crop X's test id, so a
|
|
233
|
+
// lookup by that id found the button that keys all four.
|
|
234
|
+
return (
|
|
235
|
+
<>
|
|
236
|
+
{eligible && diamond ? (
|
|
237
|
+
<Diamond
|
|
238
|
+
id={paths.join("+")}
|
|
239
|
+
name={name}
|
|
240
|
+
tracked={tracked}
|
|
241
|
+
keyed={keyed}
|
|
242
|
+
{...(together === undefined ? {} : { together })}
|
|
243
|
+
onToggle={() => store.toggleKeyframe(paths)}
|
|
244
|
+
/>
|
|
245
|
+
) : null}
|
|
246
|
+
{pin && paths.length === 1 ? <ExposeDot path={first} label={name} kind={exposedKind} /> : null}
|
|
247
|
+
</>
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* The same diamond with the SHOT as its subject: its background colour and its
|
|
253
|
+
* post stack (P5).
|
|
254
|
+
*
|
|
255
|
+
* A second component rather than a flag, because nothing about the subject is
|
|
256
|
+
* shared — a different path table, a different refusal, a different store verb,
|
|
257
|
+
* and no fan-out at all (a shot is one subject however many layers are
|
|
258
|
+
* selected). What IS shared is every pixel of the control, which is `Diamond`.
|
|
259
|
+
*
|
|
260
|
+
* ── ABSENT, NOT DEAD, ON AN EFFECT THAT IS OFF ──────────────────────────
|
|
261
|
+
*
|
|
262
|
+
* `sceneTrackRefusal` refuses `effects.bloom.strength` while `bloom` is
|
|
263
|
+
* undefined, and the row it would sit in is not rendered then either (the
|
|
264
|
+
* toggle hides its params). Asked anyway, because the toggle and the table are
|
|
265
|
+
* two different lists and this is the one the save gate reads.
|
|
266
|
+
*
|
|
267
|
+
* `parts` is `KeyframeDot`'s: the parts of a vector its one track keys (a
|
|
268
|
+
* grade's R, G and B, a ray centre's X and Y), which the tooltip says key
|
|
269
|
+
* together.
|
|
270
|
+
*/
|
|
271
|
+
export function SceneKeyframeDot({ path, label, parts }: { path: string; label?: string; parts?: readonly string[] }) {
|
|
272
|
+
const store = useStore();
|
|
273
|
+
const doc = useShotEditor((s) => s.doc);
|
|
274
|
+
const editingDef = useShotEditor((s) => s.editingDef);
|
|
275
|
+
const sceneId = useShotEditor((s) => s.sceneId);
|
|
276
|
+
const { sceneFrame } = useInspectorFrame();
|
|
277
|
+
const name = label ?? (sceneTrackablePath(path)?.label ?? path);
|
|
278
|
+
const scene = doc === null || sceneId === null ? undefined : doc.scenesById[sceneId];
|
|
279
|
+
// A DEFINITION HAS NEITHER. `useInspectorFrame` reports the def-local clock
|
|
280
|
+
// while one is open, so a diamond drawn here would key the shot under the
|
|
281
|
+
// playhead at a frame from a different timeline.
|
|
282
|
+
if (scene === undefined || editingDef !== null) return null;
|
|
283
|
+
if (sceneTrackRefusal(scene, path) !== null) return null;
|
|
284
|
+
|
|
285
|
+
const track = scene.tracks?.[path];
|
|
286
|
+
const keyed = track?.keyframes.some((k) => k.frame === sceneFrame) === true;
|
|
287
|
+
const together = togetherOf(parts);
|
|
288
|
+
return (
|
|
289
|
+
<Diamond
|
|
290
|
+
id={path}
|
|
291
|
+
name={name}
|
|
292
|
+
tracked={track !== undefined}
|
|
293
|
+
keyed={keyed}
|
|
294
|
+
{...(together === undefined ? {} : { together })}
|
|
295
|
+
onToggle={() => store.toggleSceneKeyframe([path])}
|
|
296
|
+
/>
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/** The control itself. One look, one wording, one set of states — so a shot
|
|
301
|
+
* property and a layer property cannot end up reading differently. */
|
|
302
|
+
function Diamond({
|
|
303
|
+
id,
|
|
304
|
+
name,
|
|
305
|
+
tracked,
|
|
306
|
+
keyed,
|
|
307
|
+
together,
|
|
308
|
+
onToggle,
|
|
309
|
+
}: {
|
|
310
|
+
/** What its test id names: the path it keys, or every path, `+`-joined. */
|
|
311
|
+
id: string;
|
|
312
|
+
name: string;
|
|
313
|
+
tracked: boolean;
|
|
314
|
+
keyed: boolean;
|
|
315
|
+
/** "X, Y and Z key together.", for a diamond over the parts of one vector:
|
|
316
|
+
* a box that shows one of them, or one box that shows them all. */
|
|
317
|
+
together?: string;
|
|
318
|
+
onToggle: () => void;
|
|
319
|
+
}) {
|
|
320
|
+
const locked = useLocked();
|
|
321
|
+
const playing = useShotEditor((s) => s.playing);
|
|
322
|
+
const verb = keyed
|
|
323
|
+
? `Remove the ${name} keyframe at this frame`
|
|
324
|
+
: tracked
|
|
325
|
+
? `Keyframe ${name} here: it is animated, and this frame is between keys`
|
|
326
|
+
: `Animate ${name}: the first keyframe takes the value it already has, so nothing moves`;
|
|
327
|
+
return (
|
|
328
|
+
<button
|
|
329
|
+
type="button"
|
|
330
|
+
className={`insp-key${keyed ? " is-keyed" : tracked ? " is-tracked" : ""}`}
|
|
331
|
+
data-testid={`keyframe-dot-${id}`}
|
|
332
|
+
aria-label={`Keyframe ${name}`}
|
|
333
|
+
aria-pressed={keyed}
|
|
334
|
+
title={playing ? PLAYING_TITLE : together === undefined ? verb : `${verb}. ${together}`}
|
|
335
|
+
disabled={locked || playing}
|
|
336
|
+
onClick={onToggle}
|
|
337
|
+
>
|
|
338
|
+
<IconDiamond filled={keyed} />
|
|
339
|
+
</button>
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Why this layer cannot carry a track on this path, or null.
|
|
345
|
+
*
|
|
346
|
+
* The GATE'S OWN predicate, plus the one rule that is not a property of the
|
|
347
|
+
* document: `applyDeviceScreen` derives a screen child's position, size and
|
|
348
|
+
* corners at render time and strips their tracks, so a keyframe there would
|
|
349
|
+
* save, validate, sample, and move nothing.
|
|
350
|
+
*
|
|
351
|
+
* Kept in step with `trackOps`' copy by both reading `layerTrackRefusal` and
|
|
352
|
+
* `deviceScreenDerivesPath` — the affordance and the op ask the same two
|
|
353
|
+
* questions, in the same order, of the same functions.
|
|
354
|
+
*/
|
|
355
|
+
function keyframeRefusal(
|
|
356
|
+
doc: Doc3D,
|
|
357
|
+
containerId: string,
|
|
358
|
+
layer: Layer3D,
|
|
359
|
+
path: string,
|
|
360
|
+
live?: Contract | undefined,
|
|
361
|
+
): string | null {
|
|
362
|
+
const own = layerTrackRefusal(layer, path, live, doc.materials);
|
|
363
|
+
if (own !== null) return own;
|
|
364
|
+
// An exposed prop drives its target through the same `writeAtPath` a track
|
|
365
|
+
// substitutes with, so validate refuses both owning one path. Asked here too,
|
|
366
|
+
// or the diamond offers a keyframe the save rejects.
|
|
367
|
+
const exposed = exposedPropWriting(doc, containerId, layer.id, path);
|
|
368
|
+
if (exposed !== null) return `driven by the "${exposed.label}" property`;
|
|
369
|
+
if (!deviceScreenDerivesPath(path)) return null;
|
|
370
|
+
const home = homeOf(doc, containerId);
|
|
371
|
+
const owner = home === null ? null : deviceScreenOwnerOf(home, layer.id);
|
|
372
|
+
return owner === null ? null : `derived from "${owner.label}"`;
|
|
373
|
+
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
// THE FOCUSED KEYFRAME'S EASING — this lane's adapter over the shared
|
|
2
|
+
// `@dailies-studio/timeline` `KeyframeEditor` (a single key's Frame and Value
|
|
3
|
+
// fields, preset palette, bezier pad, spring stiffness/damping/mass, live
|
|
4
|
+
// engine-sampled preview).
|
|
5
|
+
//
|
|
6
|
+
// ── WHY THIS FILE EXISTS AT ALL ────────────────────────────────────────────
|
|
7
|
+
//
|
|
8
|
+
// The component was finished, tested and mounted in the legacy editor, and this
|
|
9
|
+
// lane never mounted it. The cost was not cosmetic: the dock's diamond menu can
|
|
10
|
+
// pick the SPRING preset but nothing anywhere could edit a spring's parameters,
|
|
11
|
+
// so every keyframe spring in every shot document was `SPRING_DEFAULT`
|
|
12
|
+
// (170/26/1) forever — while a BLOCK, one panel away, got the full editor. Two
|
|
13
|
+
// ease models with two levels of access is exactly the drift `EASE_PRESETS`
|
|
14
|
+
// being one shared table exists to prevent.
|
|
15
|
+
//
|
|
16
|
+
// ── WRITTEN FOR A SET, AND NOW HOLDING ONE ─────────────────────────────────
|
|
17
|
+
//
|
|
18
|
+
// `addresses` is the store's whole selection and every derivation below folds
|
|
19
|
+
// over it (`every` for the two kind facts, `map` for the eases and refs), which
|
|
20
|
+
// is what the shared component's contract has always been (`refs:
|
|
21
|
+
// KeyframeAddr[]`, `setEase(refs, …)`). It was written this way while the store
|
|
22
|
+
// still held one, so widening that field cost this file a single line.
|
|
23
|
+
//
|
|
24
|
+
// The one thing that stays single-subject is the PREVIEW: a settle curve is a
|
|
25
|
+
// picture of one segment, and drawing four over each other says nothing.
|
|
26
|
+
//
|
|
27
|
+
// ── THE KIND RULES ARE THE ENGINE'S, NEVER A HAND-COPY ─────────────────────
|
|
28
|
+
//
|
|
29
|
+
// `springable` and `allDiscrete` come from `SPRINGABLE_KINDS` / `DISCRETE_KINDS`
|
|
30
|
+
// so the palette cannot offer what `evaluateTrack` refuses — a spring on a
|
|
31
|
+
// `quaternion` track (reachable through `instance.props.<key>`) THROWS in the
|
|
32
|
+
// sampler, and the ops layer refuses that write. An affordance that disagrees
|
|
33
|
+
// with the gate is the defect this rule removes.
|
|
34
|
+
import { useRef } from "react";
|
|
35
|
+
import { DEFAULT_SHOT_FPS, homeOf, sampleTrackAt, trackablePath } from "@dailies-studio/shot/doc";
|
|
36
|
+
import type { ShotTrack } from "@dailies-studio/shot/doc";
|
|
37
|
+
import { DISCRETE_KINDS, SPRINGABLE_KINDS } from "@dailies-studio/engine";
|
|
38
|
+
import type { Ease, Keyframe, Rational, Value } from "@dailies-studio/engine";
|
|
39
|
+
import { KeyframeEditor, keyframeAddrKey } from "@dailies-studio/timeline";
|
|
40
|
+
import type { KeyframeAddr, KeyframeFieldsView, KeyframeSelectionView } from "@dailies-studio/timeline";
|
|
41
|
+
import { InspectorSection } from "@dailies-studio/ui";
|
|
42
|
+
import { useShotEditor, useStore } from "../../state/context.js";
|
|
43
|
+
import type { CurveKeyRef, CurveOp } from "../../state/types.js";
|
|
44
|
+
import { unitOf } from "../timeline/curveModel.js";
|
|
45
|
+
import { timecode } from "../timeline/model.js";
|
|
46
|
+
|
|
47
|
+
/** How many points the preview polyline samples over one segment. The shared
|
|
48
|
+
* component draws whatever it is handed. */
|
|
49
|
+
const PREVIEW_SAMPLES = 96;
|
|
50
|
+
|
|
51
|
+
/** One resolved address: where it points, and what it points AT. */
|
|
52
|
+
interface Entry {
|
|
53
|
+
/** The address as the store holds it — position-keyed, which is what every
|
|
54
|
+
* keyframe verb in this lane already takes. */
|
|
55
|
+
at: { containerId: string; layerId: string | null; path: string; frame: number };
|
|
56
|
+
track: ShotTrack;
|
|
57
|
+
kf: Keyframe;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The row id the shared component addresses a key by. It never leaves this
|
|
62
|
+
* file — `mapAt` turns it straight back into a store address — so it only has
|
|
63
|
+
* to be unique within the selection, and (container, layer, path) is exactly
|
|
64
|
+
* the track's identity.
|
|
65
|
+
*/
|
|
66
|
+
function rowIdOf(at: Entry["at"]): string {
|
|
67
|
+
return `${at.containerId}|${at.layerId ?? ""}|${at.path}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function KeyframeSection() {
|
|
71
|
+
const store = useStore();
|
|
72
|
+
const doc = useShotEditor((s) => s.doc);
|
|
73
|
+
const addresses = useShotEditor((s) => s.selectedKeyframes);
|
|
74
|
+
// A STATE SLICE OR NULL, never a fresh fallback object:
|
|
75
|
+
// a selector minting `{num,den}` per call is never Object.is-stable and spins
|
|
76
|
+
// useSyncExternalStore. The fallback is `DEFAULT_SHOT_FPS`, the timebase a
|
|
77
|
+
// fresh document is created at, rather than a third copy of those two numbers.
|
|
78
|
+
const fps = useShotEditor((s) => s.doc?.fps ?? null) ?? DEFAULT_SHOT_FPS;
|
|
79
|
+
// Whether the open block-drag scrub is OURS. A ref, not a local: the adapter
|
|
80
|
+
// below is rebuilt every render and a Frame scrub re-renders on each capture.
|
|
81
|
+
const ownsDrag = useRef(false);
|
|
82
|
+
|
|
83
|
+
if (doc === null) return null;
|
|
84
|
+
|
|
85
|
+
const entries: Entry[] = [];
|
|
86
|
+
for (const at of addresses) {
|
|
87
|
+
// TWO SUBJECTS, TWO HOMES. A layer's tracks come through `homeOf`, so a
|
|
88
|
+
// keyframe inside an open component DEFINITION resolves exactly as one in
|
|
89
|
+
// a shot does. A `layerId: null` address is the SHOT ITSELF, whose tracks
|
|
90
|
+
// hang off the scene — a definition has no scene-level tracks at all,
|
|
91
|
+
// which is why this arm reads `scenesById` and not the home.
|
|
92
|
+
const track: ShotTrack | undefined =
|
|
93
|
+
at.layerId === null
|
|
94
|
+
? doc.scenesById[at.containerId]?.tracks?.[at.path]
|
|
95
|
+
: homeOf(doc, at.containerId)?.layersById[at.layerId]?.tracks?.[at.path];
|
|
96
|
+
const kf = track?.keyframes.find((k) => k.frame === at.frame);
|
|
97
|
+
if (track !== undefined && kf !== undefined) entries.push({ at, track, kf });
|
|
98
|
+
}
|
|
99
|
+
// AN UNRESOLVABLE ADDRESS RENDERS NOTHING rather than an empty frame: the key
|
|
100
|
+
// can be deleted, undone away or have its layer deselected while this is
|
|
101
|
+
// mounted, and a heading over no controls would outlive its subject.
|
|
102
|
+
if (entries.length === 0) return null;
|
|
103
|
+
|
|
104
|
+
const refs: KeyframeAddr[] = entries.map((e) => ({ rowId: rowIdOf(e.at), keyframeId: e.kf.id }));
|
|
105
|
+
const byKey = new Map<string, Entry>(entries.map((e) => [keyframeAddrKey({ rowId: rowIdOf(e.at), keyframeId: e.kf.id }), e]));
|
|
106
|
+
const mapAt = (addrs: readonly KeyframeAddr[]): Entry["at"][] =>
|
|
107
|
+
addrs.flatMap((a) => {
|
|
108
|
+
const e = byKey.get(keyframeAddrKey(a));
|
|
109
|
+
return e === undefined ? [] : [e.at];
|
|
110
|
+
});
|
|
111
|
+
/** The same address as the store's curve ops take it: by keyframe ID, which
|
|
112
|
+
* is what lets a frame edit move the key without losing track of it. */
|
|
113
|
+
const keyOf = (a: KeyframeAddr): CurveKeyRef | null => {
|
|
114
|
+
const e = byKey.get(keyframeAddrKey(a));
|
|
115
|
+
return e === undefined
|
|
116
|
+
? null
|
|
117
|
+
: { containerId: e.at.containerId, layerId: e.at.layerId, path: e.at.path, keyframeId: e.kf.id };
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const selection: KeyframeSelectionView = {
|
|
121
|
+
eases: entries.map((e) => e.kf.easeOut),
|
|
122
|
+
allDiscrete: entries.every((e) => DISCRETE_KINDS.has(e.track.kind)),
|
|
123
|
+
springable: entries.every((e) => SPRINGABLE_KINDS.has(e.track.kind)),
|
|
124
|
+
refs,
|
|
125
|
+
preview: previewFor(entries.length === 1 ? entries[0]! : null, fps),
|
|
126
|
+
};
|
|
127
|
+
const fields = fieldsFor(entries.length === 1 ? entries[0]! : null);
|
|
128
|
+
|
|
129
|
+
/** One ease write on every target, as ONE undo step. The store verb is the
|
|
130
|
+
* same one the dock's diamond menu calls, so the panel and the menu cannot
|
|
131
|
+
* disagree about what setting an ease does. */
|
|
132
|
+
const writeEase = (targets: readonly Entry["at"][], ease: Ease) => store.setKeyframesEase(targets, ease);
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<InspectorSection className="insp-object" title={headingFor(entries, fps)} persistKey="shot:keyframe">
|
|
136
|
+
<KeyframeEditor
|
|
137
|
+
selection={selection}
|
|
138
|
+
fields={fields}
|
|
139
|
+
setEase={(r, ease) => writeEase(mapAt(r), ease)}
|
|
140
|
+
gesture={{
|
|
141
|
+
// ── THE GRAPH'S GESTURE, NOT THE INSPECTOR SCRUB ──────────────────
|
|
142
|
+
//
|
|
143
|
+
// The pads emit `setEase`, and the Frame and Value fields emit `move`
|
|
144
|
+
// and `setValue`: the same three ops a graph drag does, so they take
|
|
145
|
+
// the same store lane (`captureCurveOps`). That lane matters for the
|
|
146
|
+
// Frame field. The selection is position-keyed, and the inspector
|
|
147
|
+
// scrub does not carry it along a move, so the first step of a Frame
|
|
148
|
+
// drag left the selection on an empty frame and unmounted this
|
|
149
|
+
// section under the pointer. `captureCurveOps` relocates the
|
|
150
|
+
// selection with the key, and its cancel restores it.
|
|
151
|
+
//
|
|
152
|
+
// Every capture still re-derives from the scrub's BASE, which is what
|
|
153
|
+
// makes the whole drag one undo entry and Escape exact.
|
|
154
|
+
//
|
|
155
|
+
// OWNERSHIP IS CHECKED, because this lane has no inspector guard. A
|
|
156
|
+
// typed commit lands on blur, and a press on a dock bar opens that
|
|
157
|
+
// bar's drag BEFORE the field blurs. Without the check the typed op
|
|
158
|
+
// was captured into the bar's scrub and `end(true)` committed and
|
|
159
|
+
// closed it under the bar's pointer. A refused begin now makes the
|
|
160
|
+
// capture and the end no-ops, so the typed value is dropped instead.
|
|
161
|
+
begin: () => {
|
|
162
|
+
ownsDrag.current = store.beginBlockDrag();
|
|
163
|
+
},
|
|
164
|
+
capture: (ops) => {
|
|
165
|
+
if (!ownsDrag.current) return;
|
|
166
|
+
const mapped: CurveOp[] = [];
|
|
167
|
+
for (const op of ops) {
|
|
168
|
+
if (op.kind === "setValue") {
|
|
169
|
+
const key = keyOf(op.ref);
|
|
170
|
+
if (key !== null) mapped.push({ kind: "setValue", key, value: op.value });
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
const keys = op.refs.map(keyOf).filter((k): k is CurveKeyRef => k !== null);
|
|
174
|
+
if (keys.length === 0) continue;
|
|
175
|
+
mapped.push(
|
|
176
|
+
op.kind === "move"
|
|
177
|
+
? { kind: "move", keys, deltaFrames: op.deltaFrames, ...(op.snap === undefined ? {} : { snap: op.snap }) }
|
|
178
|
+
: { kind: "setEase", keys, ease: op.ease },
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
store.captureCurveOps(mapped);
|
|
182
|
+
},
|
|
183
|
+
end: (commit) => {
|
|
184
|
+
if (!ownsDrag.current) return;
|
|
185
|
+
ownsDrag.current = false;
|
|
186
|
+
store.endBlockDrag(commit);
|
|
187
|
+
},
|
|
188
|
+
}}
|
|
189
|
+
/>
|
|
190
|
+
</InspectorSection>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* The single-selection Frame and Value fields, or null for a set.
|
|
196
|
+
*
|
|
197
|
+
* Value is for a plain `number` track only. A vector's components and a
|
|
198
|
+
* colour's channels have no one number to show, and the ops layer's
|
|
199
|
+
* `setValue` writes a vector only through a named component, which this
|
|
200
|
+
* address does not carry. The Frame field still applies to every kind.
|
|
201
|
+
*/
|
|
202
|
+
function fieldsFor(single: Entry | null): KeyframeFieldsView | null {
|
|
203
|
+
if (single === null) return null;
|
|
204
|
+
const { at, track, kf } = single;
|
|
205
|
+
const v = kf.value;
|
|
206
|
+
return {
|
|
207
|
+
ref: { rowId: rowIdOf(at), keyframeId: kf.id },
|
|
208
|
+
frame: kf.frame,
|
|
209
|
+
value: track.kind === "number" && typeof v === "number" ? v : null,
|
|
210
|
+
// THE CURVE PANE'S OWN RULE, so the field and the graph's axis cannot
|
|
211
|
+
// name two different units for one property.
|
|
212
|
+
...unitOf(at.path),
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** "Opacity at 0:00.12" for one key, "3 keyframes" for a set. The path's own
|
|
217
|
+
* label through `trackablePath`, so the heading names the property the way the
|
|
218
|
+
* inspector row and the dock row both already name it; the position is
|
|
219
|
+
* timecode, like every other position. */
|
|
220
|
+
function headingFor(entries: readonly Entry[], fps: Rational): string {
|
|
221
|
+
if (entries.length !== 1) return `${entries.length} keyframes`;
|
|
222
|
+
const e = entries[0]!;
|
|
223
|
+
const label = trackablePath(e.at.path)?.label ?? e.at.path;
|
|
224
|
+
return `${label} at ${timecode(e.kf.frame, fps)}`;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* The single-selection segment preview, or null.
|
|
229
|
+
*
|
|
230
|
+
* Number tracks only, and only with a NEXT key — an ease governs the segment
|
|
231
|
+
* LEAVING its keyframe, so the last key has no curve to draw (and writing its
|
|
232
|
+
* ease changes nothing, which the dock's menu says out loud). Sampled through
|
|
233
|
+
* `sampleTrackAt`, the engine's own sampler, so a spring's overshoot in this
|
|
234
|
+
* preview is the overshoot the export renders.
|
|
235
|
+
*/
|
|
236
|
+
function previewFor(single: Entry | null, fps: Rational): KeyframeSelectionView["preview"] {
|
|
237
|
+
if (single === null || single.track.kind !== "number") return null;
|
|
238
|
+
const { track, kf } = single;
|
|
239
|
+
const i = track.keyframes.findIndex((k) => k.id === kf.id);
|
|
240
|
+
const next = i < 0 ? undefined : track.keyframes[i + 1];
|
|
241
|
+
if (next === undefined) return null;
|
|
242
|
+
return {
|
|
243
|
+
fromFrame: kf.frame,
|
|
244
|
+
toFrame: next.frame,
|
|
245
|
+
isSpring: kf.easeOut.type === "spring",
|
|
246
|
+
sample: (from, to, n) => {
|
|
247
|
+
const out: { frame: number; value: number }[] = [];
|
|
248
|
+
const steps = Math.max(1, Math.min(n, PREVIEW_SAMPLES));
|
|
249
|
+
for (let s = 0; s <= steps; s += 1) {
|
|
250
|
+
const frame = from + ((to - from) * s) / steps;
|
|
251
|
+
const v: Value | undefined = sampleTrackAt(track, frame, fps);
|
|
252
|
+
out.push({ frame, value: typeof v === "number" ? v : 0 });
|
|
253
|
+
}
|
|
254
|
+
return out;
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
}
|