@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,500 @@
|
|
|
1
|
+
// THE GRAPH EDITOR'S ADAPTER: shot tracks, seen through `TimelineModel`.
|
|
2
|
+
//
|
|
3
|
+
// ── WHY THERE IS NO SVG IN THIS LANE ────────────────────────────────────────
|
|
4
|
+
//
|
|
5
|
+
// `@dailies-studio/timeline`'s `CurveGraph` is a finished value-space curve
|
|
6
|
+
// editor — dense engine sampling so a spring's overshoot is real, keyframe
|
|
7
|
+
// drags in both axes, bezier handles in value space, ctrl+wheel zoom, one
|
|
8
|
+
// history scrub per drag — and it renders and gestures ENTIRELY through a
|
|
9
|
+
// `TimelineModel`. It was built for the motion editor and this lane simply
|
|
10
|
+
// never mounted it. So the work here is a MAPPING, and a second curve editor is
|
|
11
|
+
// not written.
|
|
12
|
+
//
|
|
13
|
+
// model.ts's header explains why the DOCK does not use `TimelineModel`: a bar
|
|
14
|
+
// lane is intervals and that contract is instants. A curve is instants, so the
|
|
15
|
+
// same argument points the other way here.
|
|
16
|
+
//
|
|
17
|
+
// ── THE THREE THINGS THE MAPPING HAS TO GET RIGHT ───────────────────────────
|
|
18
|
+
//
|
|
19
|
+
// FRAMES ARE CONTAINER-LOCAL. Keyframe frames are scene-local in the document
|
|
20
|
+
// and `CurveGraph` draws its own ruler from 0, so the curve lives in the
|
|
21
|
+
// container's own clock end to end. Only `setFrame` crosses back into
|
|
22
|
+
// master time, through `sceneStart` — THE prefix sum, never a second one.
|
|
23
|
+
// A VECTOR TRACK IS ONE TRACK. `transform.position` is a single vec3 track,
|
|
24
|
+
// so its components share keyframe FRAMES. They are drawn as sub-rows and
|
|
25
|
+
// each one writes the whole vector back with its own slot replaced; the
|
|
26
|
+
// shared frames are the honest drawing of what the document holds, and
|
|
27
|
+
// splitting them would be a schema change.
|
|
28
|
+
// A DRAG IS ONE SCRUB. `gesture` is the dock's own bracket
|
|
29
|
+
// (`beginBlockDrag` / `captureCurveOps` / `endBlockDrag`), so a curve drag
|
|
30
|
+
// is one undo entry and Escape restores the base — the same promise every
|
|
31
|
+
// other gesture in this dock makes.
|
|
32
|
+
import { keyframeAddrKey } from "@dailies-studio/timeline";
|
|
33
|
+
import type { FocusedCurve, KeyframeAddr, TimelineModel, TimelineRow } from "@dailies-studio/timeline";
|
|
34
|
+
import {
|
|
35
|
+
containerFrame,
|
|
36
|
+
homeOf,
|
|
37
|
+
layerChildren,
|
|
38
|
+
layerTrackablePath,
|
|
39
|
+
sceneStart,
|
|
40
|
+
sceneTrackablePath,
|
|
41
|
+
trackablePath,
|
|
42
|
+
} from "@dailies-studio/shot/doc";
|
|
43
|
+
import type { Doc3D, ShotTrack } from "@dailies-studio/shot/doc";
|
|
44
|
+
import type { Rational, TrackKind, Value } from "@dailies-studio/engine";
|
|
45
|
+
import { evaluateTrack } from "@dailies-studio/engine";
|
|
46
|
+
import type { CurveKeyRef, CurveOp, KeyframeSel } from "../../state/types.js";
|
|
47
|
+
import { frontFirst } from "./model.js";
|
|
48
|
+
import type { ShotEditorStore } from "../../state/store.js";
|
|
49
|
+
|
|
50
|
+
/** One curve's address: a track, and which component of it when it is a
|
|
51
|
+
* vector. The pane's focus and every row id are this. */
|
|
52
|
+
export interface CurveSubject {
|
|
53
|
+
containerId: string;
|
|
54
|
+
/** Null = the SHOT itself (its background and post stack). */
|
|
55
|
+
layerId: string | null;
|
|
56
|
+
path: string;
|
|
57
|
+
/** Which component of a vector track, or absent for a scalar. */
|
|
58
|
+
component?: number;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const SEP = "|";
|
|
62
|
+
|
|
63
|
+
/** `TimelineModel` row ids are OPAQUE to the shared components, so this lane
|
|
64
|
+
* gets to choose the encoding. A PIPE, because every part that goes into one
|
|
65
|
+
* is an id slug (sc_…, l3_…, cmp_…) or a dotted path, and none can contain it. */
|
|
66
|
+
export function curveRowId(s: CurveSubject): string {
|
|
67
|
+
return [s.containerId, s.layerId ?? "", s.path, s.component ?? ""].join(SEP);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function parseCurveRowId(id: string): CurveSubject | null {
|
|
71
|
+
const [containerId, layerId, path, component] = id.split(SEP);
|
|
72
|
+
if (containerId === undefined || layerId === undefined || path === undefined) return null;
|
|
73
|
+
return {
|
|
74
|
+
containerId,
|
|
75
|
+
layerId: layerId === "" ? null : layerId,
|
|
76
|
+
path,
|
|
77
|
+
...(component === undefined || component === "" ? {} : { component: Number(component) }),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function sameSubject(a: CurveSubject | null, b: CurveSubject | null): boolean {
|
|
82
|
+
return a !== null && b !== null && curveRowId(a) === curveRowId(b);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** The component labels a vector track's sub-rows carry. `transform.size` is
|
|
86
|
+
* width and height rather than x and y, which is what the inspector calls it. */
|
|
87
|
+
function componentNames(path: string, kind: TrackKind): readonly string[] {
|
|
88
|
+
if (kind === "vec2") return path === "transform.size" ? ["W", "H"] : ["X", "Y"];
|
|
89
|
+
if (kind === "vec3") return ["X", "Y", "Z"];
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Is this kind drawable in VALUE SPACE? A colour and a boolean have no axis, so
|
|
94
|
+
* their rows stay dope rows: diamonds, no curve. */
|
|
95
|
+
function numericKind(kind: TrackKind): boolean {
|
|
96
|
+
return kind === "number" || kind === "vec2" || kind === "vec3";
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** The tracks of one subject, as rows — a vector expanded into its components,
|
|
100
|
+
* which is the only way a vec3 reaches a value-space curve at all. */
|
|
101
|
+
export function curveRowsOf(
|
|
102
|
+
doc: Doc3D,
|
|
103
|
+
containerId: string,
|
|
104
|
+
layerId: string | null,
|
|
105
|
+
): Array<TimelineRow & { subject: CurveSubject }> {
|
|
106
|
+
const tracks = curveTracksOf(doc, containerId, layerId);
|
|
107
|
+
if (tracks === undefined) return [];
|
|
108
|
+
// The layer itself, for the LAYER-scoped lookup below: `instance.props.<key>`
|
|
109
|
+
// is typed by the component's contract, so a global lookup cannot label it or
|
|
110
|
+
// report its kind. Same `homeOf` route `tracksOf` takes.
|
|
111
|
+
const layer = layerId === null ? undefined : homeOf(doc, containerId)?.layersById[layerId];
|
|
112
|
+
const out: Array<TimelineRow & { subject: CurveSubject }> = [];
|
|
113
|
+
// Sorted by path so two layers with the same tracks list them the same way —
|
|
114
|
+
// `channelRows`' rule, for its reason: a `Record`'s own order is not one.
|
|
115
|
+
for (const path of Object.keys(tracks).sort()) {
|
|
116
|
+
const track = tracks[path]!;
|
|
117
|
+
const spec =
|
|
118
|
+
layerId === null
|
|
119
|
+
? sceneTrackablePath(path)
|
|
120
|
+
: layer === undefined
|
|
121
|
+
? trackablePath(path)
|
|
122
|
+
: layerTrackablePath(layer, path);
|
|
123
|
+
const label = spec?.label ?? path;
|
|
124
|
+
const kind = spec?.kind ?? track.kind;
|
|
125
|
+
const keyframes = track.keyframes.map((k) => ({
|
|
126
|
+
id: k.id,
|
|
127
|
+
frame: k.frame,
|
|
128
|
+
easeType: k.easeOut.type,
|
|
129
|
+
}));
|
|
130
|
+
const names = componentNames(path, kind);
|
|
131
|
+
if (names.length === 0) {
|
|
132
|
+
const subject: CurveSubject = { containerId, layerId, path };
|
|
133
|
+
out.push({ id: curveRowId(subject), label, depth: 0, numberTrack: numericKind(kind), keyframes, subject });
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
// The parent row is the TRACK — one row per component under it, because
|
|
137
|
+
// that is where the keyframes really live and a reader has to see that the
|
|
138
|
+
// frames are shared.
|
|
139
|
+
const parent: CurveSubject = { containerId, layerId, path };
|
|
140
|
+
out.push({ id: curveRowId(parent), label, depth: 0, numberTrack: false, keyframes, subject: parent });
|
|
141
|
+
names.forEach((name, component) => {
|
|
142
|
+
const subject: CurveSubject = { containerId, layerId, path, component };
|
|
143
|
+
out.push({
|
|
144
|
+
id: curveRowId(subject),
|
|
145
|
+
label: name,
|
|
146
|
+
depth: 1,
|
|
147
|
+
numberTrack: true,
|
|
148
|
+
keyframes,
|
|
149
|
+
subject,
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
return out;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** The tracks of a curve subject. EXPORTED because the pane needs the real
|
|
157
|
+
* track — its kind, and which key is last — to build a keyframe menu, and a
|
|
158
|
+
* second lookup there would be a second spelling of the `homeOf` rule. */
|
|
159
|
+
export function curveTracksOf(
|
|
160
|
+
doc: Doc3D,
|
|
161
|
+
containerId: string,
|
|
162
|
+
layerId: string | null,
|
|
163
|
+
): Record<string, ShotTrack> | undefined {
|
|
164
|
+
if (layerId === null) return doc.scenesById[containerId]?.tracks;
|
|
165
|
+
// THROUGH `homeOf`, which is the one function that reads a scene and a
|
|
166
|
+
// definition through the same shape — a local branch here would be a second
|
|
167
|
+
// copy of that rule, and the def arm spells its roots differently.
|
|
168
|
+
return homeOf(doc, containerId)?.layersById[layerId]?.tracks;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** The number a curve draws for a value: the value itself, or one component of
|
|
172
|
+
* it. Non-numeric collapses to 0 — a row that reaches here is one the pane's
|
|
173
|
+
* `numberTrack` gate should not have focused. */
|
|
174
|
+
function numberOf(value: Value | undefined, component: number | undefined): number {
|
|
175
|
+
if (component !== undefined) return Array.isArray(value) ? Number(value[component] ?? 0) : 0;
|
|
176
|
+
return typeof value === "number" ? value : 0;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* The value axis's unit, as a spread so an unknown unit leaves the field
|
|
181
|
+
* ABSENT (`exactOptionalPropertyTypes`).
|
|
182
|
+
*
|
|
183
|
+
* The doc's trackable paths carry no unit metadata, only a naming rule: every
|
|
184
|
+
* angle in degrees ends in `Deg` (`transform.rollDeg`, `mesh.primitive.arcDeg`,
|
|
185
|
+
* `splineFollow.bankDeg`, …). That suffix is the one unit this can say
|
|
186
|
+
* without guessing, and the inspector's own rows already label those "°".
|
|
187
|
+
* The keyframe inspector's Value field reads this same function.
|
|
188
|
+
*/
|
|
189
|
+
export function unitOf(path: string): { unit: string } | Record<never, never> {
|
|
190
|
+
return path.endsWith("Deg") ? { unit: "°" } : {};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* One subject's curve: its keys as numbers and THE ENGINE'S OWN SAMPLER, so the
|
|
195
|
+
* drawn curve and the rendered frame are one function. A local re-implementation
|
|
196
|
+
* would lose the spring's inherited boundary velocity, which is exactly the
|
|
197
|
+
* shape an author opens this panel to look at. Null when the track is gone or
|
|
198
|
+
* has no keys.
|
|
199
|
+
*/
|
|
200
|
+
function curveOf(doc: Doc3D, subject: CurveSubject, fps: Rational): FocusedCurve | null {
|
|
201
|
+
const track = curveTracksOf(doc, subject.containerId, subject.layerId)?.[subject.path];
|
|
202
|
+
if (track === undefined || track.keyframes.length === 0) return null;
|
|
203
|
+
return {
|
|
204
|
+
rowId: curveRowId(subject),
|
|
205
|
+
keyframes: track.keyframes.map((k) => ({
|
|
206
|
+
id: k.id,
|
|
207
|
+
frame: k.frame,
|
|
208
|
+
value: numberOf(k.value, subject.component),
|
|
209
|
+
easeOut: k.easeOut,
|
|
210
|
+
})),
|
|
211
|
+
sample: (from, to, n) => {
|
|
212
|
+
const out: Array<{ frame: number; value: number }> = [];
|
|
213
|
+
const step = n <= 1 ? 0 : (to - from) / (n - 1);
|
|
214
|
+
for (let i = 0; i < n; i += 1) {
|
|
215
|
+
const f = from + step * i;
|
|
216
|
+
out.push({ frame: f, value: numberOf(evaluateTrack(track, f, fps), subject.component) });
|
|
217
|
+
}
|
|
218
|
+
return out;
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** The number a curve row reads at a CONTAINER-LOCAL frame, for the list's
|
|
224
|
+
* value column. Null when the track is gone or has no keys. */
|
|
225
|
+
export function curveValueAt(doc: Doc3D, subject: CurveSubject, localFrame: number, fps: Rational): number | null {
|
|
226
|
+
const track = curveTracksOf(doc, subject.containerId, subject.layerId)?.[subject.path];
|
|
227
|
+
if (track === undefined || track.keyframes.length === 0) return null;
|
|
228
|
+
return numberOf(evaluateTrack(track, localFrame, fps), subject.component);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Everything `CurveGraph` reads and writes, over one focused track.
|
|
233
|
+
*
|
|
234
|
+
* A PLAIN FUNCTION, not a hook: the pane holds the memo (it knows which of its
|
|
235
|
+
* inputs are identity-stable) and this stays testable without a renderer.
|
|
236
|
+
*/
|
|
237
|
+
export function shotCurveModel(args: {
|
|
238
|
+
store: ShotEditorStore;
|
|
239
|
+
doc: Doc3D;
|
|
240
|
+
/** The container the rows belong to, and its own duration. */
|
|
241
|
+
containerId: string;
|
|
242
|
+
containerFrames: number;
|
|
243
|
+
/** The MASTER playhead. Converted to container-local here and nowhere else. */
|
|
244
|
+
frame: number;
|
|
245
|
+
fps: Rational;
|
|
246
|
+
pxPerFrame: number;
|
|
247
|
+
focus: CurveSubject | null;
|
|
248
|
+
rows: Array<TimelineRow & { subject: CurveSubject }>;
|
|
249
|
+
/** The WHOLE selection, not just its primary: the graph lights every key it
|
|
250
|
+
* holds, which is what a marquee or a shift-click has to produce. */
|
|
251
|
+
selected: readonly { path: string; frame: number; layerId: string | null; containerId: string }[];
|
|
252
|
+
/** Def-local clocks do not convert: a definition has no master position. */
|
|
253
|
+
defOpen: boolean;
|
|
254
|
+
/** The OTHER curves the author has asked to see beside the focused one
|
|
255
|
+
* (shift-click in the list). The focused subject is skipped if listed. */
|
|
256
|
+
visible?: readonly CurveSubject[];
|
|
257
|
+
/** Each row's list colour, by row id, so the graph draws a curve in the
|
|
258
|
+
* colour of its dot. */
|
|
259
|
+
colors?: ReadonlyMap<string, string>;
|
|
260
|
+
}): TimelineModel {
|
|
261
|
+
const { store, doc, containerId, fps, focus, rows } = args;
|
|
262
|
+
const track = focus === null ? undefined : curveTracksOf(doc, focus.containerId, focus.layerId)?.[focus.path];
|
|
263
|
+
const colorOf = (s: CurveSubject): { color: string } | Record<never, never> => {
|
|
264
|
+
const c = args.colors?.get(curveRowId(s));
|
|
265
|
+
return c === undefined ? {} : { color: c };
|
|
266
|
+
};
|
|
267
|
+
const curves = (args.visible ?? [])
|
|
268
|
+
.filter((s) => !sameSubject(s, focus))
|
|
269
|
+
.flatMap((s) => {
|
|
270
|
+
const c = curveOf(doc, s, fps);
|
|
271
|
+
return c === null ? [] : [{ ...c, ...colorOf(s) }];
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
const keyOf = (subject: CurveSubject, keyframeId: string): CurveKeyRef => ({
|
|
275
|
+
containerId: subject.containerId,
|
|
276
|
+
layerId: subject.layerId,
|
|
277
|
+
path: subject.path,
|
|
278
|
+
keyframeId,
|
|
279
|
+
...(subject.component === undefined ? {} : { component: subject.component }),
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
/** A shared `KeyframeAddr` back into this lane's own address. Null when the
|
|
283
|
+
* row is not one of ours or the keyframe has gone. */
|
|
284
|
+
const refOf = (addr: KeyframeAddr): CurveKeyRef | null => {
|
|
285
|
+
const subject = parseCurveRowId(addr.rowId);
|
|
286
|
+
if (subject === null) return null;
|
|
287
|
+
return keyOf(subject, addr.keyframeId);
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const focusedCurve = focus === null || track === undefined ? null : curveOf(doc, focus, fps);
|
|
291
|
+
|
|
292
|
+
// The SELECTION, mapped from the store's position address onto the id one the
|
|
293
|
+
// shared components use. The store keeps positions on purpose (a key can be
|
|
294
|
+
// replaced by a drag landing on its frame); this direction is a lookup, and
|
|
295
|
+
// the reverse — id to position — happens inside the capture against the base.
|
|
296
|
+
const selectedAddrs: KeyframeAddr[] = [];
|
|
297
|
+
if (focus !== null) {
|
|
298
|
+
for (const at of args.selected) {
|
|
299
|
+
const owner = curveTracksOf(doc, at.containerId, at.layerId)?.[at.path];
|
|
300
|
+
const key = owner?.keyframes.find((k) => k.frame === at.frame);
|
|
301
|
+
if (key === undefined) continue;
|
|
302
|
+
// Every row that draws this track — the parent and each component — shows
|
|
303
|
+
// the same keyframe, so the selection lights up on whichever is focused.
|
|
304
|
+
const subject: CurveSubject = {
|
|
305
|
+
containerId: at.containerId,
|
|
306
|
+
layerId: at.layerId,
|
|
307
|
+
path: at.path,
|
|
308
|
+
...(focus.path === at.path && focus.component !== undefined ? { component: focus.component } : {}),
|
|
309
|
+
};
|
|
310
|
+
selectedAddrs.push({ rowId: curveRowId(subject), keyframeId: key.id });
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return {
|
|
315
|
+
rows,
|
|
316
|
+
durationFrames: args.containerFrames,
|
|
317
|
+
fps,
|
|
318
|
+
frame: args.defOpen
|
|
319
|
+
? args.frame
|
|
320
|
+
: containerFrame(doc, containerId, args.frame),
|
|
321
|
+
pxPerFrame: args.pxPerFrame,
|
|
322
|
+
// The dock's own zoom bounds, shared deliberately: the graph and the rows
|
|
323
|
+
// are two faces of one timeline and a second zoom range would make ⌘-wheel
|
|
324
|
+
// mean two different things in one panel.
|
|
325
|
+
pxPerFrameBounds: { min: 0.2, max: 60 },
|
|
326
|
+
selectedKeyframes: new Set(selectedAddrs.map(keyframeAddrKey)),
|
|
327
|
+
selectedAddrs,
|
|
328
|
+
curves,
|
|
329
|
+
focusedCurve:
|
|
330
|
+
focus === null || focusedCurve === null
|
|
331
|
+
? null
|
|
332
|
+
: {
|
|
333
|
+
// Keys and the engine sampler (`curveOf`).
|
|
334
|
+
...focusedCurve,
|
|
335
|
+
...colorOf(focus),
|
|
336
|
+
...unitOf(focus.path),
|
|
337
|
+
},
|
|
338
|
+
|
|
339
|
+
setFrame(next) {
|
|
340
|
+
// The graph's only caller is CurveGraph's `scrubTo`, a pointer drag on
|
|
341
|
+
// the ruler and the graph background, so it takes the clock exactly like
|
|
342
|
+
// the dock's ruler does.
|
|
343
|
+
store.pauseForScrub();
|
|
344
|
+
// BACK INTO MASTER TIME, through the one prefix sum. A definition has no
|
|
345
|
+
// master position at all, so it seeks its own clock.
|
|
346
|
+
if (args.defOpen) {
|
|
347
|
+
store.seekDef(next);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
store.seek(next + (sceneStart(doc, containerId) ?? 0));
|
|
351
|
+
},
|
|
352
|
+
setPxPerFrame(px) {
|
|
353
|
+
store.setDockPpf(px);
|
|
354
|
+
},
|
|
355
|
+
selectKeyframes(refs, mode = "set") {
|
|
356
|
+
// ID TO POSITION, here and only here. The shared components address a key
|
|
357
|
+
// by its stable id (a React key that moved mid-drag would remount the
|
|
358
|
+
// circle holding pointer capture); the store keeps positions, because a
|
|
359
|
+
// drag landing on an occupied frame REPLACES the key an id would still
|
|
360
|
+
// point at. This is the one seam between the two.
|
|
361
|
+
const at = refs.flatMap((addr) => {
|
|
362
|
+
const ref = refOf(addr);
|
|
363
|
+
if (ref === null) return [];
|
|
364
|
+
const owner = curveTracksOf(doc, ref.containerId, ref.layerId)?.[ref.path];
|
|
365
|
+
const key = owner?.keyframes.find((k) => k.id === ref.keyframeId);
|
|
366
|
+
return key === undefined
|
|
367
|
+
? []
|
|
368
|
+
: [{ containerId: ref.containerId, layerId: ref.layerId, path: ref.path, frame: key.frame }];
|
|
369
|
+
});
|
|
370
|
+
if (at.length === 0) {
|
|
371
|
+
// An empty "set" is a deselect — a marquee that caught nothing. An
|
|
372
|
+
// empty add/toggle is not: it would clear a selection the author was
|
|
373
|
+
// extending.
|
|
374
|
+
if (mode === "set") store.selectKeyframe(null);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
store.focusScene(at[0]!.containerId);
|
|
378
|
+
// FOLDED IN ORDER, so the last one the gesture named ends up primary —
|
|
379
|
+
// which is what makes a shift-click read as "extend to here".
|
|
380
|
+
at.forEach((k, i) => store.selectKeyframes(k, i === 0 ? mode : "add"));
|
|
381
|
+
},
|
|
382
|
+
toggleKeyframe(rowId, frame) {
|
|
383
|
+
const subject = parseCurveRowId(rowId);
|
|
384
|
+
if (subject === null) return;
|
|
385
|
+
store.focusScene(subject.containerId);
|
|
386
|
+
// AT THE CLICKED FRAME, not the playhead's. The diamond verbs key where
|
|
387
|
+
// the playhead is because that is what a diamond means; a double-click in
|
|
388
|
+
// a curve names a point ON the curve, and moving the playhead to reach it
|
|
389
|
+
// would be the panel answering a different question.
|
|
390
|
+
store.toggleKeyframeAt(subject.containerId, subject.layerId, subject.path, frame);
|
|
391
|
+
},
|
|
392
|
+
setEase(refs, ease) {
|
|
393
|
+
// One batched write, so an ease on N keys is ONE undo step.
|
|
394
|
+
const keys: KeyframeSel[] = [];
|
|
395
|
+
for (const addr of refs) {
|
|
396
|
+
const ref = refOf(addr);
|
|
397
|
+
if (ref === null) continue;
|
|
398
|
+
const owner = curveTracksOf(doc, ref.containerId, ref.layerId)?.[ref.path];
|
|
399
|
+
const key = owner?.keyframes.find((k) => k.id === ref.keyframeId);
|
|
400
|
+
if (key === undefined) continue;
|
|
401
|
+
keys.push({ containerId: ref.containerId, layerId: ref.layerId, path: ref.path, frame: key.frame });
|
|
402
|
+
}
|
|
403
|
+
store.setKeyframesEase(keys, ease);
|
|
404
|
+
},
|
|
405
|
+
gesture: {
|
|
406
|
+
begin: () => store.beginBlockDrag(),
|
|
407
|
+
capture: (ops) => {
|
|
408
|
+
const mapped: CurveOp[] = [];
|
|
409
|
+
for (const op of ops) {
|
|
410
|
+
if (op.kind === "setValue") {
|
|
411
|
+
const key = refOf(op.ref);
|
|
412
|
+
if (key !== null) mapped.push({ kind: "setValue", key, value: op.value });
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
const keys = op.refs.map(refOf).filter((k): k is CurveKeyRef => k !== null);
|
|
416
|
+
if (keys.length === 0) continue;
|
|
417
|
+
if (op.kind === "move") {
|
|
418
|
+
// `snap` rides through verbatim — the ANCHOR (`keys[0]`) survives
|
|
419
|
+
// the map because `refs.map` preserves order, which is the whole
|
|
420
|
+
// contract the op's own comment states.
|
|
421
|
+
mapped.push({
|
|
422
|
+
kind: "move",
|
|
423
|
+
keys,
|
|
424
|
+
deltaFrames: op.deltaFrames,
|
|
425
|
+
...(op.snap === undefined ? {} : { snap: op.snap }),
|
|
426
|
+
});
|
|
427
|
+
} else mapped.push({ kind: "setEase", keys, ease: op.ease });
|
|
428
|
+
}
|
|
429
|
+
store.captureCurveOps(mapped);
|
|
430
|
+
},
|
|
431
|
+
end: (commit) => store.endBlockDrag(commit),
|
|
432
|
+
},
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* The layer a curve pane should be showing, given what is selected.
|
|
438
|
+
*
|
|
439
|
+
* A LADDER, and the last rung is the one that matters: a pane that opened empty
|
|
440
|
+
* whenever nothing happened to be selected would read as broken in the most
|
|
441
|
+
* common case there is — an author switches to the graph to look at the
|
|
442
|
+
* animation they can already see in the rows.
|
|
443
|
+
*
|
|
444
|
+
* a focused KEYFRAME the most specific thing the author has pointed at,
|
|
445
|
+
* and it carries its own container (in the scene
|
|
446
|
+
* context the playhead's shot and the clicked
|
|
447
|
+
* diamond's shot can differ).
|
|
448
|
+
* the SELECTED layer what every other panel is showing.
|
|
449
|
+
* the shot's own tracks when it has some: a post-stack animation belongs
|
|
450
|
+
* to the shot and to no layer.
|
|
451
|
+
* the first layer that so the pane opens on something rather than on a
|
|
452
|
+
* carries a track sentence.
|
|
453
|
+
*/
|
|
454
|
+
export function curveSubjectLayer(
|
|
455
|
+
state: {
|
|
456
|
+
selectedKeyframe: { containerId: string; layerId: string | null } | null;
|
|
457
|
+
selection: readonly string[];
|
|
458
|
+
containerId: string | null;
|
|
459
|
+
},
|
|
460
|
+
doc?: Doc3D,
|
|
461
|
+
): { containerId: string; layerId: string | null } | null {
|
|
462
|
+
const key = state.selectedKeyframe;
|
|
463
|
+
if (key !== null) return { containerId: key.containerId, layerId: key.layerId };
|
|
464
|
+
const containerId = state.containerId;
|
|
465
|
+
if (containerId === null) return null;
|
|
466
|
+
const first = state.selection[0];
|
|
467
|
+
if (first !== undefined) return { containerId, layerId: first };
|
|
468
|
+
if (doc === undefined) return { containerId, layerId: null };
|
|
469
|
+
if (Object.keys(doc.scenesById[containerId]?.tracks ?? {}).length > 0) return { containerId, layerId: null };
|
|
470
|
+
return { containerId, layerId: animatedLayers(doc, containerId)[0] ?? null };
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Every layer in a container that carries a track, IN OUTLINE ORDER, walking
|
|
475
|
+
* the forest: the order every other list in this chrome uses (front first,
|
|
476
|
+
* `frontFirst`), and the only one where "the first" means what a reader would
|
|
477
|
+
* point at, the highest row. A layer inside a group is reachable, which a flat
|
|
478
|
+
* scan of the roots would have missed. A group with tracks of its own is listed
|
|
479
|
+
* before its children.
|
|
480
|
+
*/
|
|
481
|
+
export function animatedLayers(doc: Doc3D, containerId: string): string[] {
|
|
482
|
+
const home = homeOf(doc, containerId);
|
|
483
|
+
const out: string[] = [];
|
|
484
|
+
const walk = (ids: readonly string[]): void => {
|
|
485
|
+
for (const { id } of frontFirst(ids)) {
|
|
486
|
+
const layer = home?.layersById[id];
|
|
487
|
+
if (layer === undefined) continue;
|
|
488
|
+
if (Object.keys(layer.tracks ?? {}).length > 0) out.push(id);
|
|
489
|
+
walk(layerChildren(layer));
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
walk(home?.layers ?? []);
|
|
493
|
+
return out;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/** The ALL ANIMATED scope's rows: the shot's own tracks, then every animated
|
|
497
|
+
* layer's, each owner's rows in `curveRowsOf` order. */
|
|
498
|
+
export function allAnimatedRows(doc: Doc3D, containerId: string): Array<TimelineRow & { subject: CurveSubject }> {
|
|
499
|
+
return [null, ...animatedLayers(doc, containerId)].flatMap((layerId) => curveRowsOf(doc, containerId, layerId));
|
|
500
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// "Duck under voice": the mixer's one-press duck, written as the per-clip
|
|
2
|
+
// `duck` the schema already has (audioDuck.ts derives the dip from it in all
|
|
3
|
+
// three mixers). No new field: the button is a shorthand for setting every
|
|
4
|
+
// bed's `duck.sources` to the voice clips, and its pressed state is read back
|
|
5
|
+
// from those same sources, so an author who edits one duck by hand sees the
|
|
6
|
+
// button let go.
|
|
7
|
+
//
|
|
8
|
+
// A VOICE TRACK IS FOUND BY ITS NAME, because the document has no track role.
|
|
9
|
+
// Sound effects are never ducked: a hit under a line is meant to be heard.
|
|
10
|
+
import { authoredAudioClips, audioTracksOf, removeAudioDuck, updateAudioClip } from "@dailies-studio/shot/doc";
|
|
11
|
+
import type { Doc3D } from "@dailies-studio/shot/doc";
|
|
12
|
+
|
|
13
|
+
const VOICE_RE = /\b(voice|voices|vo|voice-?over|narration|narrator|dialog|dialogue|speech)\b/i;
|
|
14
|
+
const SFX_RE = /\b(sfx|fx|effects?|foley)\b/i;
|
|
15
|
+
|
|
16
|
+
export interface VoiceDuck {
|
|
17
|
+
/** Clip ids on voice tracks (a recording's sound counts). */
|
|
18
|
+
sources: string[];
|
|
19
|
+
/** Real audio clips on the other, non-effects tracks. */
|
|
20
|
+
targets: string[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function voiceDuckPlan(doc: Doc3D): VoiceDuck {
|
|
24
|
+
const voice = new Set(audioTracksOf(doc).filter((t) => VOICE_RE.test(t.name)).map((t) => t.id));
|
|
25
|
+
const sfx = new Set(audioTracksOf(doc).filter((t) => SFX_RE.test(t.name)).map((t) => t.id));
|
|
26
|
+
const sources: string[] = [];
|
|
27
|
+
const targets: string[] = [];
|
|
28
|
+
for (const m of authoredAudioClips(doc)) {
|
|
29
|
+
if (voice.has(m.trackId)) sources.push(m.clip.id);
|
|
30
|
+
else if (!sfx.has(m.trackId) && m.layerId === undefined) targets.push(m.clip.id);
|
|
31
|
+
}
|
|
32
|
+
return { sources, targets };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** `available`: there is a voice to duck under and a bed to duck. `active`:
|
|
36
|
+
* every bed already ducks under every voice clip. */
|
|
37
|
+
export function voiceDuckState(doc: Doc3D): { available: boolean; active: boolean } {
|
|
38
|
+
const { sources, targets } = voiceDuckPlan(doc);
|
|
39
|
+
if (sources.length === 0 || targets.length === 0) return { available: false, active: false };
|
|
40
|
+
const byId = new Map(authoredAudioClips(doc).map((m) => [m.clip.id, m.clip]));
|
|
41
|
+
const active = targets.every((id) => {
|
|
42
|
+
const have = byId.get(id)?.duck?.sources ?? [];
|
|
43
|
+
return sources.every((s) => have.includes(s));
|
|
44
|
+
});
|
|
45
|
+
return { available: true, active };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** On: add the voice clips to every bed's duck (keeping any other sources and
|
|
49
|
+
* the strength). Off: take them out again, and drop a duck left empty. */
|
|
50
|
+
export function applyVoiceDuck(doc: Doc3D, on: boolean): Doc3D {
|
|
51
|
+
const { sources, targets } = voiceDuckPlan(doc);
|
|
52
|
+
if (sources.length === 0) return doc;
|
|
53
|
+
const byId = new Map(authoredAudioClips(doc).map((m) => [m.clip.id, m.clip]));
|
|
54
|
+
let next = doc;
|
|
55
|
+
for (const id of targets) {
|
|
56
|
+
const duck = byId.get(id)?.duck;
|
|
57
|
+
const have = duck?.sources ?? [];
|
|
58
|
+
const merged = on ? [...have, ...sources.filter((s) => !have.includes(s))] : have.filter((s) => !sources.includes(s));
|
|
59
|
+
if (merged.length === have.length && merged.every((s, i) => s === have[i])) continue;
|
|
60
|
+
next =
|
|
61
|
+
merged.length === 0
|
|
62
|
+
? removeAudioDuck(next, id)
|
|
63
|
+
: updateAudioClip(next, id, {
|
|
64
|
+
duck: { sources: merged, ...(duck?.strength === undefined ? {} : { strength: duck.strength }) },
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return next;
|
|
68
|
+
}
|