@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,92 @@
|
|
|
1
|
+
// The isolation trail: which layers sit above the isolated one, whether
|
|
2
|
+
// anything inside it is drawing at the playhead, and whether it can take an
|
|
3
|
+
// insert. The stage header's breadcrumb (IsolationTrail.tsx) reads the first
|
|
4
|
+
// two. One walk, so the header and anything else that names the path cannot
|
|
5
|
+
// disagree about it.
|
|
6
|
+
import {
|
|
7
|
+
canHoldChildren,
|
|
8
|
+
layerAliveAt,
|
|
9
|
+
layerChildren,
|
|
10
|
+
parentOf,
|
|
11
|
+
sceneStart,
|
|
12
|
+
type Layer3D,
|
|
13
|
+
} from "@dailies-studio/shot/doc";
|
|
14
|
+
import { wordFor } from "../layerWords.js";
|
|
15
|
+
import type { ShotEditorState } from "./types.js";
|
|
16
|
+
|
|
17
|
+
type Lookup = { layersById: Record<string, Layer3D> };
|
|
18
|
+
|
|
19
|
+
/** The isolated layer and every ancestor above it, outermost first. The path
|
|
20
|
+
* the breadcrumb walks, and the reason entering a nested subtree is reversible
|
|
21
|
+
* one level at a time. Bounded by the layer count, so a corrupt parent cycle
|
|
22
|
+
* ends instead of hanging the chrome. */
|
|
23
|
+
export function crumbsFor(scene: Lookup, id: string): string[] {
|
|
24
|
+
const out: string[] = [];
|
|
25
|
+
const limit = Object.keys(scene.layersById).length;
|
|
26
|
+
let at: string | null = id;
|
|
27
|
+
for (let i = 0; at !== null && i <= limit; i += 1) {
|
|
28
|
+
out.push(at);
|
|
29
|
+
at = parentOf(scene as never, at);
|
|
30
|
+
}
|
|
31
|
+
return out.reverse();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* True when the isolated subtree draws nothing at the playhead: the layer or
|
|
36
|
+
* an ancestor is outside its lifespan (or hidden), or every leaf under it is.
|
|
37
|
+
* The pane is then uniformly dimmed with nothing to click, and the breadcrumb
|
|
38
|
+
* says why instead of letting the mode read as broken.
|
|
39
|
+
*
|
|
40
|
+
* A selector returning a boolean, so a subscriber re-renders only when the
|
|
41
|
+
* answer flips, not on every playback frame. An empty container counts as on
|
|
42
|
+
* screen: there is nothing to be missing.
|
|
43
|
+
*/
|
|
44
|
+
export function isolationOffscreen(s: ShotEditorState): boolean {
|
|
45
|
+
if (s.doc === null || s.isolation === null || s.editingDef !== null || s.sceneId === null) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const scene = s.doc.scenesById[s.sceneId];
|
|
49
|
+
if (scene === undefined || scene.layersById[s.isolation] === undefined) return false;
|
|
50
|
+
const start = sceneStart(s.doc, s.sceneId);
|
|
51
|
+
if (start === null) return false;
|
|
52
|
+
const frame = s.frame - start;
|
|
53
|
+
if (frame < 0 || frame >= scene.durationFrames) return true;
|
|
54
|
+
const chain = crumbsFor(scene, s.isolation);
|
|
55
|
+
for (const id of chain.slice(0, -1)) {
|
|
56
|
+
const layer = scene.layersById[id];
|
|
57
|
+
if (layer !== undefined && !layerAliveAt(layer, frame)) return true;
|
|
58
|
+
}
|
|
59
|
+
const draws = (id: string): boolean => {
|
|
60
|
+
const layer = scene.layersById[id];
|
|
61
|
+
if (layer === undefined || !layerAliveAt(layer, frame)) return false;
|
|
62
|
+
const kids = layerChildren(layer);
|
|
63
|
+
return kids.length === 0 || kids.some(draws);
|
|
64
|
+
};
|
|
65
|
+
return !draws(s.isolation);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Why nothing can be added while `layer` is the isolation subject, or null
|
|
70
|
+
* when it can take children (or there is no subject).
|
|
71
|
+
*
|
|
72
|
+
* Everything outside the isolated subtree is dimmed and unpickable, so an
|
|
73
|
+
* insert that fell back to the scene root used to land where the author could
|
|
74
|
+
* neither see nor click it. A leaf subject refuses instead, with the reason.
|
|
75
|
+
* `insertContext` reports it, and the tool dock and the Use buttons render it
|
|
76
|
+
* as their disabled reason, so a refused click is never a surprise.
|
|
77
|
+
*/
|
|
78
|
+
export function leafInsertRefusal(layer: Layer3D | undefined): string | null {
|
|
79
|
+
if (layer === undefined || canHoldChildren(layer)) return null;
|
|
80
|
+
const word = wordFor(layer);
|
|
81
|
+
const noun = word === word.toUpperCase() ? word : word.toLowerCase();
|
|
82
|
+
return `Can't add inside this ${noun}. Only groups, devices and cloners hold layers.`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** `leafInsertRefusal` for the isolation the stage is showing. A string or
|
|
86
|
+
* null, so a selector over it allocates nothing. */
|
|
87
|
+
export function isolationInsertRefusal(
|
|
88
|
+
s: Pick<ShotEditorState, "doc" | "sceneId" | "isolation" | "editingDef">,
|
|
89
|
+
): string | null {
|
|
90
|
+
if (s.doc === null || s.isolation === null || s.editingDef !== null || s.sceneId === null) return null;
|
|
91
|
+
return leafInsertRefusal(s.doc.scenesById[s.sceneId]?.layersById[s.isolation]);
|
|
92
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// MOVING SEVERAL KEYFRAMES AT ONCE, each by its own distance. The keyboard
|
|
2
|
+
// nudge moves a selection rigidly; the ⌥-drag stagger fans it out; the stretch
|
|
3
|
+
// bracket scales it about its far end. Each is one
|
|
4
|
+
// pure write over the document, so the store verb around each is only the
|
|
5
|
+
// bracket (an `edit` for the nudge, a scrub capture for the drag).
|
|
6
|
+
import { homeOf, moveLayerKeyframe, moveSceneKeyframe } from "@dailies-studio/shot/doc";
|
|
7
|
+
import { retimeLandings, retimeMinFactor } from "@dailies-studio/timeline";
|
|
8
|
+
import type { Doc3D } from "@dailies-studio/shot/doc";
|
|
9
|
+
import type { KeyframeSel } from "./types.js";
|
|
10
|
+
|
|
11
|
+
export interface KeyMove {
|
|
12
|
+
key: KeyframeSel;
|
|
13
|
+
to: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const trackOf = (k: KeyframeSel): string => `${k.containerId}|${k.layerId ?? ""}|${k.path}`;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Move every key in `moves` to its `to`, or refuse the whole set.
|
|
20
|
+
*
|
|
21
|
+
* REFUSED WHOLE ON ANY COLLISION: a key landing on a key that is not moving
|
|
22
|
+
* away, or two keys landing on one frame of one track, would replace a key with
|
|
23
|
+
* nothing on screen having said so. A frame below 0 refuses too.
|
|
24
|
+
*
|
|
25
|
+
* ORDER MATTERS INSIDE THE WRITE: a key may only land once the key that sat on
|
|
26
|
+
* its target has left, or the earlier one replaces the later. Each pass moves
|
|
27
|
+
* every key whose target is free, so a rigid nudge goes front to back in its own
|
|
28
|
+
* direction and a fan goes outward.
|
|
29
|
+
*/
|
|
30
|
+
export function moveKeyRefs(doc: Doc3D, moves: readonly KeyMove[]): { doc: Doc3D; refusal: string | null } {
|
|
31
|
+
const live = moves.filter((m) => m.to !== m.key.frame);
|
|
32
|
+
if (live.length === 0) return { doc, refusal: null };
|
|
33
|
+
const leaving = new Set(live.map((m) => `${trackOf(m.key)}|${m.key.frame}`));
|
|
34
|
+
const landing = new Set<string>();
|
|
35
|
+
for (const { key, to } of live) {
|
|
36
|
+
if (to < 0) return { doc, refusal: "That would put a keyframe before frame 0." };
|
|
37
|
+
const at = `${trackOf(key)}|${to}`;
|
|
38
|
+
const tracks =
|
|
39
|
+
key.layerId === null
|
|
40
|
+
? doc.scenesById[key.containerId]?.tracks
|
|
41
|
+
: homeOf(doc, key.containerId)?.layersById[key.layerId]?.tracks;
|
|
42
|
+
const occupied = tracks?.[key.path]?.keyframes.some((other) => other.frame === to) === true;
|
|
43
|
+
if ((occupied && !leaving.has(at)) || landing.has(at))
|
|
44
|
+
return { doc, refusal: "That would drop a keyframe onto another one. Nothing was moved." };
|
|
45
|
+
landing.add(at);
|
|
46
|
+
}
|
|
47
|
+
let next = doc;
|
|
48
|
+
let pending = live;
|
|
49
|
+
while (pending.length > 0) {
|
|
50
|
+
const sitting = new Set(pending.map((m) => `${trackOf(m.key)}|${m.key.frame}`));
|
|
51
|
+
const ready = pending.filter((m) => !sitting.has(`${trackOf(m.key)}|${m.to}`));
|
|
52
|
+
// A cycle (two keys swapping places) cannot be written one move at a time.
|
|
53
|
+
if (ready.length === 0) return { doc, refusal: "Those keyframes would swap places. Nothing was moved." };
|
|
54
|
+
for (const { key, to } of ready)
|
|
55
|
+
next =
|
|
56
|
+
key.layerId === null
|
|
57
|
+
? moveSceneKeyframe(next, key.containerId, key.path, key.frame, to)
|
|
58
|
+
: moveLayerKeyframe(next, key.containerId, key.layerId, key.path, key.frame, to);
|
|
59
|
+
pending = pending.filter((m) => !ready.includes(m));
|
|
60
|
+
}
|
|
61
|
+
return { doc: next, refusal: null };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Where a stagger of `keys` by `step` sends each one.
|
|
66
|
+
*
|
|
67
|
+
* SEVERAL LAYERS: each layer's keys move together, so their spacing stays, and
|
|
68
|
+
* the layers are ranked by their earliest selected key. ONE LAYER: the keys
|
|
69
|
+
* keys are ranked by distinct frame, so keys sharing a frame move together.
|
|
70
|
+
* Rank i moves `i * step`.
|
|
71
|
+
*/
|
|
72
|
+
export function keyStaggerMoves(keys: readonly KeyframeSel[], step: number): KeyMove[] {
|
|
73
|
+
const groups = new Map<string, KeyframeSel[]>();
|
|
74
|
+
for (const k of keys) {
|
|
75
|
+
const id = `${k.containerId}|${k.layerId ?? ""}`;
|
|
76
|
+
groups.set(id, [...(groups.get(id) ?? []), k]);
|
|
77
|
+
}
|
|
78
|
+
if (groups.size > 1) {
|
|
79
|
+
const earliest = (g: readonly KeyframeSel[]): number => Math.min(...g.map((k) => k.frame));
|
|
80
|
+
const ranked = [...groups.values()].sort((a, b) => earliest(a) - earliest(b));
|
|
81
|
+
return ranked.flatMap((g, i) => g.map((key) => ({ key, to: key.frame + i * step })));
|
|
82
|
+
}
|
|
83
|
+
// Rank distinct frames, not keys: keys on one frame (a pose across tracks)
|
|
84
|
+
// stay together.
|
|
85
|
+
const frames = [...new Set(keys.map((k) => k.frame))].sort((a, b) => a - b);
|
|
86
|
+
return [...keys]
|
|
87
|
+
.sort((a, b) => a.frame - b.frame)
|
|
88
|
+
.map((key) => ({ key, to: key.frame + frames.indexOf(key.frame) * step }));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** What a stretch of the selection does: every key's move, and the factor it
|
|
92
|
+
* landed on (the chip's readout). */
|
|
93
|
+
export interface KeyStretch {
|
|
94
|
+
moves: KeyMove[];
|
|
95
|
+
factor: number;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Where a STRETCH of `keys` sends each one: the selection's frames scaled
|
|
100
|
+
* about its far end, so the edge grabbed moves by `deltaFrames` and the other
|
|
101
|
+
* stays put. Graph mode's retime bracket, over keys on any tracks and layers.
|
|
102
|
+
*
|
|
103
|
+
* In the dock's frames (a Shot's keys plus its start, `offsetOf`), so keys in
|
|
104
|
+
* two Shots stretch about one anchor. The factor stops where two keys of one
|
|
105
|
+
* track would round onto one frame (`retimeMinFactor`) and where a key would
|
|
106
|
+
* cross its container's frame 0; null when even that factor collides, or the
|
|
107
|
+
* selection spans one frame.
|
|
108
|
+
*/
|
|
109
|
+
export function keyStretchMoves(
|
|
110
|
+
keys: readonly KeyframeSel[],
|
|
111
|
+
edge: "left" | "right",
|
|
112
|
+
deltaFrames: number,
|
|
113
|
+
offsetOf: (containerId: string) => number,
|
|
114
|
+
): KeyStretch | null {
|
|
115
|
+
const placed = keys.map((key) => ({ key, at: key.frame + offsetOf(key.containerId) }));
|
|
116
|
+
if (placed.length < 2) return null;
|
|
117
|
+
const min = Math.min(...placed.map((p) => p.at));
|
|
118
|
+
const max = Math.max(...placed.map((p) => p.at));
|
|
119
|
+
if (min === max) return null;
|
|
120
|
+
const anchor = edge === "left" ? max : min;
|
|
121
|
+
const grab = edge === "left" ? min : max;
|
|
122
|
+
const byTrack = new Map<string, typeof placed>();
|
|
123
|
+
for (const p of placed) byTrack.set(trackOf(p.key), [...(byTrack.get(trackOf(p.key)) ?? []), p]);
|
|
124
|
+
const tracks = [...byTrack.values()];
|
|
125
|
+
let factor = Math.max(retimeMinFactor(tracks.map((t) => t.map((p) => p.at))), (grab + deltaFrames - anchor) / (grab - anchor));
|
|
126
|
+
// No key before its own container's frame 0: only keys below the anchor can.
|
|
127
|
+
for (const p of placed) {
|
|
128
|
+
if (p.at < anchor) factor = Math.min(factor, (anchor - offsetOf(p.key.containerId)) / (anchor - p.at));
|
|
129
|
+
}
|
|
130
|
+
const landed = retimeLandings(tracks.map((t) => t.map((p) => p.at)), anchor, factor);
|
|
131
|
+
if (landed === null) return null;
|
|
132
|
+
const moves = tracks.flatMap((t, i) => t.map((p, j) => ({ key: p.key, to: landed[i]![j]! - offsetOf(p.key.containerId) })));
|
|
133
|
+
return { moves, factor };
|
|
134
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// The keyframe clipboard: a selection out, the same motion back in somewhere
|
|
2
|
+
// else.
|
|
3
|
+
//
|
|
4
|
+
// ── WHAT A CLIP IS, AND WHY IT IS RELATIVE ────────────────────────────────
|
|
5
|
+
//
|
|
6
|
+
// Copying keys and pasting them at the playhead is the single most common way
|
|
7
|
+
// an author reuses motion — the same punch on a second card, the same settle on
|
|
8
|
+
// a whole row of them. So a clip stores frames RELATIVE to the earliest key in
|
|
9
|
+
// the whole clip, never absolute ones: paste puts that earliest key on the
|
|
10
|
+
// playhead and everything else keeps its spacing, which is what makes a
|
|
11
|
+
// two-track clip (a position and an opacity that were meant to land together)
|
|
12
|
+
// arrive still in sync.
|
|
13
|
+
//
|
|
14
|
+
// The anchor is clip-wide rather than per track for exactly that reason. Per
|
|
15
|
+
// track, every track's first key would land on the playhead and a deliberate
|
|
16
|
+
// offset between two of them would be flattened out.
|
|
17
|
+
//
|
|
18
|
+
// ── IN MEMORY, NOT ON THE SYSTEM CLIPBOARD ────────────────────────────────
|
|
19
|
+
//
|
|
20
|
+
// `graphClipboard` encodes to text because a material graph is worth carrying
|
|
21
|
+
// between browser tabs. Keyframes are not: they are meaningless without the
|
|
22
|
+
// track kinds and paths they came from, and the realistic gesture is "copy this
|
|
23
|
+
// bounce onto that layer" inside one session. A store field is the whole
|
|
24
|
+
// transport. The codec is still split out here so the upgrade path is a
|
|
25
|
+
// `JSON.stringify` and a format tag, exactly as `MATERIAL_CLIP_FORMAT` does.
|
|
26
|
+
import type { Ease, TrackKind, Value, Vec2, Vec3 } from "@dailies-studio/engine";
|
|
27
|
+
import type { Doc3D } from "@dailies-studio/shot/doc";
|
|
28
|
+
import { homeOf } from "@dailies-studio/shot/doc";
|
|
29
|
+
import type { KeyframeSel } from "./types.js";
|
|
30
|
+
|
|
31
|
+
/** One track's worth of copied keys, with the path and kind they came from. */
|
|
32
|
+
export interface KeyframeClipTrack {
|
|
33
|
+
path: string;
|
|
34
|
+
/** The SOURCE track's kind. Paste refuses a target whose kind differs — see
|
|
35
|
+
* `pasteTargets`. */
|
|
36
|
+
kind: TrackKind;
|
|
37
|
+
/** The source track was a bezier path, so its keys' handles (offsets from
|
|
38
|
+
* the key's value, no rebasing needed) say exactly what the source said:
|
|
39
|
+
* an absent side is a cleared side. */
|
|
40
|
+
bezier?: true;
|
|
41
|
+
keys: readonly ClipKey[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** One copied key. The handles are present only when the source track is a
|
|
45
|
+
* bezier path and the key has them. */
|
|
46
|
+
export interface ClipKey {
|
|
47
|
+
relFrame: number;
|
|
48
|
+
value: Value;
|
|
49
|
+
easeOut: Ease;
|
|
50
|
+
spatialIn?: Vec2 | Vec3;
|
|
51
|
+
spatialOut?: Vec2 | Vec3;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** A clip key without its frame: what is held per (track, frame). */
|
|
55
|
+
type HeldKey = Omit<ClipKey, "relFrame">;
|
|
56
|
+
|
|
57
|
+
export interface KeyframeClip {
|
|
58
|
+
version: 1;
|
|
59
|
+
tracks: readonly KeyframeClipTrack[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Read a selection out of the document into a clip, or null when it holds
|
|
64
|
+
* nothing copyable.
|
|
65
|
+
*
|
|
66
|
+
* Grouped by (container, layer, path) because that is a TRACK, and a clip's
|
|
67
|
+
* unit is a track: two keys on `transform.position` are one row of the clip,
|
|
68
|
+
* and the same frame on `transform.opacity` is another.
|
|
69
|
+
*/
|
|
70
|
+
export function copyKeyframes(doc: Doc3D, selection: readonly KeyframeSel[]): KeyframeClip | null {
|
|
71
|
+
const byTrack = new Map<string, { path: string; kind: TrackKind; bezier: boolean; frames: number[] }>();
|
|
72
|
+
const valueAt = new Map<string, HeldKey>();
|
|
73
|
+
|
|
74
|
+
for (const at of selection) {
|
|
75
|
+
const tracks =
|
|
76
|
+
at.layerId === null
|
|
77
|
+
? doc.scenesById[at.containerId]?.tracks
|
|
78
|
+
: homeOf(doc, at.containerId)?.layersById[at.layerId]?.tracks;
|
|
79
|
+
const track = tracks?.[at.path];
|
|
80
|
+
const key = track?.keyframes.find((k) => k.frame === at.frame);
|
|
81
|
+
if (track === undefined || key === undefined) continue;
|
|
82
|
+
const id = `${at.containerId}|${at.layerId ?? ""}|${at.path}`;
|
|
83
|
+
const row = byTrack.get(id) ?? {
|
|
84
|
+
path: at.path,
|
|
85
|
+
kind: track.kind,
|
|
86
|
+
bezier: track.spatialInterp?.type === "bezier",
|
|
87
|
+
frames: [],
|
|
88
|
+
};
|
|
89
|
+
row.frames.push(at.frame);
|
|
90
|
+
byTrack.set(id, row);
|
|
91
|
+
valueAt.set(`${id}|${at.frame}`, {
|
|
92
|
+
value: key.value,
|
|
93
|
+
easeOut: key.easeOut,
|
|
94
|
+
...(row.bezier && key.spatialIn !== undefined ? { spatialIn: [...key.spatialIn] as Vec2 | Vec3 } : {}),
|
|
95
|
+
...(row.bezier && key.spatialOut !== undefined ? { spatialOut: [...key.spatialOut] as Vec2 | Vec3 } : {}),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
if (byTrack.size === 0) return null;
|
|
99
|
+
|
|
100
|
+
// THE ANCHOR IS THE WHOLE CLIP'S EARLIEST FRAME — see the header.
|
|
101
|
+
let anchor = Number.POSITIVE_INFINITY;
|
|
102
|
+
for (const row of byTrack.values()) for (const f of row.frames) anchor = Math.min(anchor, f);
|
|
103
|
+
|
|
104
|
+
const tracks: KeyframeClipTrack[] = [];
|
|
105
|
+
for (const [id, row] of byTrack) {
|
|
106
|
+
const keys = row.frames
|
|
107
|
+
.slice()
|
|
108
|
+
.sort((a, b) => a - b)
|
|
109
|
+
.flatMap((frame) => {
|
|
110
|
+
const held = valueAt.get(`${id}|${frame}`);
|
|
111
|
+
return held === undefined ? [] : [{ relFrame: frame - anchor, ...held }];
|
|
112
|
+
});
|
|
113
|
+
if (keys.length > 0) tracks.push({ path: row.path, kind: row.kind, ...(row.bezier ? { bezier: true as const } : {}), keys });
|
|
114
|
+
}
|
|
115
|
+
return tracks.length === 0 ? null : { version: 1, tracks };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** One track of a clip, aimed at a subject, with the frames it will write. */
|
|
119
|
+
export interface PasteTarget {
|
|
120
|
+
path: string;
|
|
121
|
+
/** Copied off a bezier path: see `KeyframeClipTrack.bezier`. */
|
|
122
|
+
bezier?: true;
|
|
123
|
+
keys: readonly ({ frame: number } & HeldKey)[];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Where a clip lands on `subject`, and what it cannot.
|
|
128
|
+
*
|
|
129
|
+
* ── KIND EQUALITY, THEN THE EXISTING GATES ───────────────────────────────
|
|
130
|
+
*
|
|
131
|
+
* A track's kind must match EXACTLY. Anything looser is a guess about what the
|
|
132
|
+
* author meant — a `vec3` position pasted onto a `vec2` size is arithmetic
|
|
133
|
+
* nobody asked for — and the value rules that police the rest (`colour must be
|
|
134
|
+
* #hex`, an enum's options, a number's range) already live in
|
|
135
|
+
* `trackValueRefusal`, which every write goes through. So this checks the one
|
|
136
|
+
* thing those cannot, and hands the rest over.
|
|
137
|
+
*
|
|
138
|
+
* A path the subject does not track yet is a target too, so a bounce copied
|
|
139
|
+
* from one card pastes onto a card that has never been animated. Whether the
|
|
140
|
+
* subject can hold that path is the trackable-path gate's question
|
|
141
|
+
* (`keyframeRefusal`, `sceneTrackRefusal`), which the write asks and the paste
|
|
142
|
+
* reports.
|
|
143
|
+
*/
|
|
144
|
+
export function pasteTargets(
|
|
145
|
+
doc: Doc3D,
|
|
146
|
+
clip: KeyframeClip,
|
|
147
|
+
subject: { containerId: string; layerId: string | null },
|
|
148
|
+
atFrame: number,
|
|
149
|
+
): { targets: PasteTarget[]; refusals: string[] } {
|
|
150
|
+
const tracks =
|
|
151
|
+
subject.layerId === null
|
|
152
|
+
? doc.scenesById[subject.containerId]?.tracks
|
|
153
|
+
: homeOf(doc, subject.containerId)?.layersById[subject.layerId]?.tracks;
|
|
154
|
+
const targets: PasteTarget[] = [];
|
|
155
|
+
const refusals: string[] = [];
|
|
156
|
+
|
|
157
|
+
for (const row of clip.tracks) {
|
|
158
|
+
const existing = tracks?.[row.path];
|
|
159
|
+
if (existing !== undefined && existing.kind !== row.kind) {
|
|
160
|
+
refusals.push(
|
|
161
|
+
`"${row.path}" is a ${existing.kind} here and the copied keys are ${row.kind}, so they cannot land on it.`,
|
|
162
|
+
);
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
targets.push({
|
|
166
|
+
path: row.path,
|
|
167
|
+
...(row.bezier === true ? { bezier: true as const } : {}),
|
|
168
|
+
// Integer frames, and never below zero: a clip anchored later than the
|
|
169
|
+
// playhead would otherwise ask for a negative frame the validator refuses.
|
|
170
|
+
keys: row.keys.map(({ relFrame, ...held }) => ({
|
|
171
|
+
frame: Math.max(0, Math.trunc(atFrame + relFrame)),
|
|
172
|
+
...held,
|
|
173
|
+
})),
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
return { targets, refusals };
|
|
177
|
+
}
|