@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
|
@@ -1,36 +1,35 @@
|
|
|
1
|
-
// The channel rows under an expanded layer: one row per
|
|
2
|
-
// diamonds at the ends — the Figma/Lottie read, and since the preset-named
|
|
3
|
-
// block lane left the expansion, the ONLY expanded read.
|
|
1
|
+
// The channel rows under an expanded layer: one row per keyframed property.
|
|
4
2
|
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// block lane uses — `dragBlock`, `selectBlock`. Dragging the "opacity
|
|
10
|
-
// keyframes" therefore moves the Fade block on every channel it writes, in one
|
|
11
|
-
// undo entry. The preset's name lives in the span's tooltip and the Animate
|
|
12
|
-
// tab. There is nothing a channel row can do that a block cannot, which is
|
|
13
|
-
// what keeps the document blocks-not-tracks.
|
|
3
|
+
// Every row is one stored track (model.ts's `channelRows`), drawn as diamonds,
|
|
4
|
+
// and every diamond gesture routes through the track ops. `dragKeyframe` shares
|
|
5
|
+
// the dock's scrub bracket, so a diamond drag is one undo entry like everything
|
|
6
|
+
// else in this dock.
|
|
14
7
|
//
|
|
15
8
|
// ── THE LIVE VALUE IS A LEAF ────────────────────────────────────────────
|
|
16
9
|
//
|
|
17
|
-
//
|
|
10
|
+
// A transform row's label cell shows the resolved value AT THE PLAYHEAD. That
|
|
18
11
|
// number changes ~30 times a second during playback, so it is its own
|
|
19
12
|
// component with its own `frame` subscription — the dock's DockTime rule. The
|
|
20
13
|
// rows themselves never see the frame.
|
|
21
|
-
import { frameToPx, startDrag } from "@dailies-studio/timeline";
|
|
14
|
+
import { EASE_PRESETS, frameToPx, matchPreset, startDrag } from "@dailies-studio/timeline";
|
|
15
|
+
import { useContextMenu } from "@dailies-studio/ui";
|
|
22
16
|
import { resolveLayerAt } from "@dailies-studio/shot/doc";
|
|
23
|
-
import type { Layer3D } from "@dailies-studio/shot/doc";
|
|
24
|
-
import type { Rational } from "@dailies-studio/engine";
|
|
25
|
-
import type { PointerEvent as ReactPointerEvent } from "react";
|
|
26
|
-
import { useMemo } from "react";
|
|
27
|
-
import {
|
|
17
|
+
import type { Layer3D, Scene3D } from "@dailies-studio/shot/doc";
|
|
18
|
+
import type { Ease, Rational } from "@dailies-studio/engine";
|
|
19
|
+
import type { CSSProperties, MouseEvent as ReactMouseEvent, PointerEvent as ReactPointerEvent } from "react";
|
|
20
|
+
import { memo, useMemo } from "react";
|
|
21
|
+
import { TRANSFORM_CHANNELS } from "../../channels.js";
|
|
22
|
+
import type { ChannelSpec } from "../../channels.js";
|
|
28
23
|
import { useShotEditor, useStore } from "../../state/context.js";
|
|
29
24
|
import { useCoalescedFrame } from "../inspector/useDocWrite.js";
|
|
30
|
-
import {
|
|
31
|
-
import
|
|
25
|
+
import { PropertyPicker } from "./PropertyPicker.js";
|
|
26
|
+
import { keyframeMenuEntries, keyframeMenuTargets } from "./keyframeMenu.js";
|
|
27
|
+
import { channelRowsCached, frameDelta, sceneChannelRows } from "./model.js";
|
|
28
|
+
import type { ChannelRowModel, TrackKey } from "./model.js";
|
|
32
29
|
import type { SnapPress } from "./snap.js";
|
|
33
30
|
import { isSnapSuppressed } from "./snap.js";
|
|
31
|
+
import { StaggerChip, setStaggerPreview, useStaggerReadout } from "./staggerPreview.js";
|
|
32
|
+
import { sameKeyframeSel } from "../../state/types.js";
|
|
34
33
|
|
|
35
34
|
export function ChannelRows({
|
|
36
35
|
layer,
|
|
@@ -39,8 +38,8 @@ export function ChannelRows({
|
|
|
39
38
|
ppf,
|
|
40
39
|
labelWidth,
|
|
41
40
|
offsetFrames,
|
|
41
|
+
originFrames,
|
|
42
42
|
fps,
|
|
43
|
-
lifespan,
|
|
44
43
|
beginSnap,
|
|
45
44
|
}: {
|
|
46
45
|
layer: Layer3D;
|
|
@@ -50,94 +49,276 @@ export function ChannelRows({
|
|
|
50
49
|
labelWidth: number;
|
|
51
50
|
/** The scene's master start. Drawing only, like every bar in this lane. */
|
|
52
51
|
offsetFrames: number;
|
|
52
|
+
originFrames: number;
|
|
53
53
|
fps: Rational;
|
|
54
|
-
/** The owning layer's lifespan. A span outside it is kept and drawn dead,
|
|
55
|
-
* the block lane's own rule. */
|
|
56
|
-
lifespan?: Lifespan | undefined;
|
|
57
54
|
beginSnap?: SnapPress | undefined;
|
|
58
55
|
}) {
|
|
59
56
|
// Derived per layer identity: the rows change exactly when the layer's
|
|
60
|
-
//
|
|
61
|
-
// that should not pay a re-derive.
|
|
62
|
-
const rows =
|
|
57
|
+
// tracks do, and the dock re-renders for ppf/guide
|
|
58
|
+
// changes that should not pay a re-derive.
|
|
59
|
+
const rows = channelRowsCached(layer);
|
|
60
|
+
// ONE MENU PER LAYER, not one per diamond. The hook holds an open/closed
|
|
61
|
+
// state and a focus-restore ref, and a document with a keyframed position
|
|
62
|
+
// over sixty frames would otherwise mount sixty of them for one menu that can
|
|
63
|
+
// only ever be open once.
|
|
64
|
+
const menu = useContextMenu();
|
|
65
|
+
const selectedKeys = useSelectedKeyframeSet();
|
|
66
|
+
const status = useMemo(() => selectedKeyStatus(rows, sceneId, layer.id, selectedKeys), [rows, sceneId, layer.id, selectedKeys]);
|
|
63
67
|
// A layer with no animation still answers the disclosure: the hint is where
|
|
64
|
-
// the first
|
|
68
|
+
// the first keyframe comes from, and an empty expansion reads as broken. The
|
|
69
|
+
// picker below it is the answer to the hint, in reach of the sentence.
|
|
65
70
|
if (rows.length === 0) {
|
|
66
71
|
return (
|
|
67
|
-
|
|
68
|
-
<div className="channel-
|
|
69
|
-
<
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
<>
|
|
73
|
+
<div className="channel-row channel-empty" data-testid="channel-empty">
|
|
74
|
+
<div className="channel-label-cell" style={{ width: labelWidth, "--depth": depth } as CSSProperties}>
|
|
75
|
+
<span className="channel-name">No animation</span>
|
|
76
|
+
</div>
|
|
77
|
+
<div className="channel-lane channel-empty-hint">
|
|
78
|
+
Pick a property below, turn on Animate (⇧A) and edit, or press ◆ in the inspector.
|
|
79
|
+
</div>
|
|
73
80
|
</div>
|
|
74
|
-
|
|
81
|
+
<PropertyPicker
|
|
82
|
+
subject={{ kind: "layer", layer }}
|
|
83
|
+
containerId={sceneId}
|
|
84
|
+
labelWidth={labelWidth}
|
|
85
|
+
depth={depth}
|
|
86
|
+
/>
|
|
87
|
+
</>
|
|
75
88
|
);
|
|
76
89
|
}
|
|
77
90
|
return (
|
|
78
91
|
<>
|
|
92
|
+
{menu.menu}
|
|
79
93
|
{rows.map((row) => (
|
|
80
94
|
<ChannelRow
|
|
81
|
-
key={row.
|
|
95
|
+
key={row.path}
|
|
82
96
|
row={row}
|
|
83
97
|
layer={layer}
|
|
98
|
+
layerId={layer.id}
|
|
84
99
|
sceneId={sceneId}
|
|
85
100
|
depth={depth}
|
|
86
101
|
ppf={ppf}
|
|
87
102
|
labelWidth={labelWidth}
|
|
88
103
|
offsetFrames={offsetFrames}
|
|
104
|
+
originFrames={originFrames}
|
|
89
105
|
fps={fps}
|
|
90
|
-
lifespan={lifespan}
|
|
91
106
|
beginSnap={beginSnap}
|
|
107
|
+
openMenu={menu.open}
|
|
108
|
+
selectedKeys={selectedKeys}
|
|
92
109
|
/>
|
|
93
110
|
))}
|
|
111
|
+
<PropertyPicker
|
|
112
|
+
subject={{ kind: "layer", layer }}
|
|
113
|
+
containerId={sceneId}
|
|
114
|
+
labelWidth={labelWidth}
|
|
115
|
+
depth={depth}
|
|
116
|
+
status={status}
|
|
117
|
+
/>
|
|
94
118
|
</>
|
|
95
119
|
);
|
|
96
120
|
}
|
|
97
121
|
|
|
122
|
+
/**
|
|
123
|
+
* The SHOT's own channel rows (P5): its background colour and its post stack.
|
|
124
|
+
*
|
|
125
|
+
* The same `ChannelRow`, with no layer: no transform readout corresponds to a
|
|
126
|
+
* background colour, so the live-value cell has nothing to show. Rendering nothing at all
|
|
127
|
+
* when the shot has no tracks is the opt-in the layer rows follow: a property
|
|
128
|
+
* lane appears once it carries animation and not before.
|
|
129
|
+
*/
|
|
130
|
+
export function SceneChannelRows({
|
|
131
|
+
scene,
|
|
132
|
+
depth,
|
|
133
|
+
ppf,
|
|
134
|
+
labelWidth,
|
|
135
|
+
offsetFrames,
|
|
136
|
+
originFrames,
|
|
137
|
+
fps,
|
|
138
|
+
beginSnap,
|
|
139
|
+
}: {
|
|
140
|
+
scene: Scene3D;
|
|
141
|
+
depth: number;
|
|
142
|
+
ppf: number;
|
|
143
|
+
labelWidth: number;
|
|
144
|
+
offsetFrames: number;
|
|
145
|
+
originFrames: number;
|
|
146
|
+
fps: Rational;
|
|
147
|
+
beginSnap?: SnapPress | undefined;
|
|
148
|
+
}) {
|
|
149
|
+
const rows = useMemo(() => sceneChannelRows(scene), [scene]);
|
|
150
|
+
const menu = useContextMenu();
|
|
151
|
+
const selectedKeys = useSelectedKeyframeSet();
|
|
152
|
+
const status = useMemo(() => selectedKeyStatus(rows, scene.id, null, selectedKeys), [rows, scene.id, selectedKeys]);
|
|
153
|
+
return (
|
|
154
|
+
<>
|
|
155
|
+
{menu.menu}
|
|
156
|
+
{rows.map((row) => (
|
|
157
|
+
<ChannelRow
|
|
158
|
+
key={row.path}
|
|
159
|
+
row={row}
|
|
160
|
+
layer={null}
|
|
161
|
+
layerId={null}
|
|
162
|
+
sceneId={scene.id}
|
|
163
|
+
depth={depth}
|
|
164
|
+
ppf={ppf}
|
|
165
|
+
labelWidth={labelWidth}
|
|
166
|
+
offsetFrames={offsetFrames}
|
|
167
|
+
originFrames={originFrames}
|
|
168
|
+
fps={fps}
|
|
169
|
+
beginSnap={beginSnap}
|
|
170
|
+
openMenu={menu.open}
|
|
171
|
+
selectedKeys={selectedKeys}
|
|
172
|
+
/>
|
|
173
|
+
))}
|
|
174
|
+
{/* Drawn even with no rows above it, which is why this component no
|
|
175
|
+
longer returns null on an untracked shot: the picker is the way a
|
|
176
|
+
shot's FIRST post-stack keyframe gets made from the timeline. */}
|
|
177
|
+
<PropertyPicker
|
|
178
|
+
subject={{ kind: "shot", scene }}
|
|
179
|
+
containerId={scene.id}
|
|
180
|
+
labelWidth={labelWidth}
|
|
181
|
+
depth={depth}
|
|
182
|
+
status={status}
|
|
183
|
+
/>
|
|
184
|
+
</>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** The `useContextMenu` opener, threaded down to the diamonds. */
|
|
189
|
+
type OpenMenu = ReturnType<typeof useContextMenu>["open"];
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* The selected keyframes as one set of addresses, built ONCE per selection
|
|
193
|
+
* change by the rows' parent.
|
|
194
|
+
*
|
|
195
|
+
* Every diamond used to hold its own `useShotEditor` selector scanning
|
|
196
|
+
* `selectedKeyframes` for itself: five hundred diamonds times ten selected
|
|
197
|
+
* keyframes was five thousand comparisons per cold notification, i.e. per
|
|
198
|
+
* rAF frame of a keyframe drag and per keystroke. Membership is now one `has`
|
|
199
|
+
* per diamond, and a memoised diamond re-renders only when ITS bit flips.
|
|
200
|
+
*/
|
|
201
|
+
function keyframeAddress(containerId: string, layerId: string | null, path: string, frame: number): string {
|
|
202
|
+
return `${containerId}|${layerId ?? ""}|${path}@${frame}`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* The status line beside "+ property": what is selected on THESE rows, in the
|
|
207
|
+
* words a diamond's own title uses. Null when nothing here is selected, so the
|
|
208
|
+
* line is absent rather than saying "nothing".
|
|
209
|
+
*
|
|
210
|
+
* Read from the selection set and the rows, never from the frame: it changes
|
|
211
|
+
* when the selection or the keys do, and playback does not touch it.
|
|
212
|
+
*/
|
|
213
|
+
function selectedKeyStatus(
|
|
214
|
+
rows: readonly ChannelRowModel[],
|
|
215
|
+
containerId: string,
|
|
216
|
+
layerId: string | null,
|
|
217
|
+
selected: ReadonlySet<string>,
|
|
218
|
+
): string | null {
|
|
219
|
+
if (selected.size === 0) return null;
|
|
220
|
+
let count = 0;
|
|
221
|
+
let only: { label: string; key: TrackKey } | null = null;
|
|
222
|
+
for (const row of rows) {
|
|
223
|
+
for (const key of row.keys) {
|
|
224
|
+
if (!selected.has(keyframeAddress(containerId, layerId, row.path, key.frame))) continue;
|
|
225
|
+
count += 1;
|
|
226
|
+
only ??= { label: row.label, key };
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (only === null) return null;
|
|
230
|
+
if (count > 1) return `${count} keys selected`;
|
|
231
|
+
return `${only.label} ${only.key.value} · frame ${only.key.frame} · ${easeLabel(only.key.ease)}`;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function useSelectedKeyframeSet(): ReadonlySet<string> {
|
|
235
|
+
const selected = useShotEditor((s) => s.selectedKeyframes);
|
|
236
|
+
return useMemo(
|
|
237
|
+
() => new Set(selected.map((at) => keyframeAddress(at.containerId, at.layerId, at.path, at.frame))),
|
|
238
|
+
[selected],
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
98
242
|
function ChannelRow({
|
|
99
243
|
row,
|
|
100
244
|
layer,
|
|
245
|
+
layerId,
|
|
101
246
|
sceneId,
|
|
102
247
|
depth,
|
|
103
248
|
ppf,
|
|
104
249
|
labelWidth,
|
|
105
250
|
offsetFrames,
|
|
251
|
+
originFrames,
|
|
106
252
|
fps,
|
|
107
|
-
lifespan,
|
|
108
253
|
beginSnap,
|
|
254
|
+
openMenu,
|
|
255
|
+
selectedKeys,
|
|
109
256
|
}: {
|
|
110
257
|
row: ChannelRowModel;
|
|
111
|
-
|
|
258
|
+
/** Null when the subject is the SHOT (P5): its background and its post stack
|
|
259
|
+
* are tracked on the scene, and no transform readout corresponds to either,
|
|
260
|
+
* so the live-value readout below simply has nothing to read. */
|
|
261
|
+
layer: Layer3D | null;
|
|
262
|
+
layerId: string | null;
|
|
112
263
|
sceneId: string;
|
|
113
264
|
depth: number;
|
|
114
265
|
ppf: number;
|
|
115
266
|
labelWidth: number;
|
|
116
267
|
offsetFrames: number;
|
|
268
|
+
originFrames: number;
|
|
117
269
|
fps: Rational;
|
|
118
|
-
lifespan?: Lifespan | undefined;
|
|
119
270
|
beginSnap?: SnapPress | undefined;
|
|
271
|
+
openMenu: OpenMenu;
|
|
272
|
+
selectedKeys: ReadonlySet<string>;
|
|
120
273
|
}) {
|
|
274
|
+
const store = useStore();
|
|
275
|
+
const readout = useStaggerReadout(staggerRowId(sceneId, layerId, row.path));
|
|
276
|
+
const spec = TRANSFORM_CHANNELS.get(row.path);
|
|
121
277
|
return (
|
|
122
|
-
<div className="channel-row" data-channel-row={row.
|
|
123
|
-
<div className="channel-label-cell" style={{ width: labelWidth,
|
|
278
|
+
<div className="channel-row" data-channel-row={row.path} data-row-scene={sceneId}>
|
|
279
|
+
<div className="channel-label-cell" style={{ width: labelWidth, "--depth": depth } as CSSProperties}>
|
|
124
280
|
<span className="channel-name">{row.label}</span>
|
|
125
|
-
|
|
281
|
+
{/* NO `originFrames` HERE. This readout converts the MASTER playhead
|
|
282
|
+
into scene-local time (`frame - offsetFrames`), which is a real
|
|
283
|
+
clock conversion and not a drawing offset — the shot context's
|
|
284
|
+
re-zero must not reach it. */}
|
|
285
|
+
{spec === undefined || layer === null ? null : (
|
|
286
|
+
<ChannelValue layer={layer} path={row.path} spec={spec} offsetFrames={offsetFrames} fps={fps} />
|
|
287
|
+
)}
|
|
288
|
+
{/* REMOVE ANIMATION. The verb is on the row rather than only in the
|
|
289
|
+
inspector because this is where the author is looking at the thing
|
|
290
|
+
they want gone, and because deleting keys one at a time to clear a
|
|
291
|
+
track is a chore with a cliff at the last one (the last delete
|
|
292
|
+
removes the track anyway). */}
|
|
293
|
+
<button
|
|
294
|
+
type="button"
|
|
295
|
+
className="channel-clear"
|
|
296
|
+
data-testid={`channel-clear-${row.path}`}
|
|
297
|
+
title={`Remove the ${row.label} keyframes: the layer keeps the value it was authored with`}
|
|
298
|
+
aria-label={`Remove ${row.label} keyframes`}
|
|
299
|
+
onClick={() => store.clearTrack(sceneId, layerId, row.path)}
|
|
300
|
+
>
|
|
301
|
+
×
|
|
302
|
+
</button>
|
|
126
303
|
</div>
|
|
127
304
|
<div className="channel-lane">
|
|
128
|
-
{row.
|
|
129
|
-
<
|
|
130
|
-
key={
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
305
|
+
{row.keys.map((key) => (
|
|
306
|
+
<TrackKeyView
|
|
307
|
+
key={key.id}
|
|
308
|
+
trackKey={key}
|
|
309
|
+
rowLabel={row.label}
|
|
310
|
+
path={row.path}
|
|
311
|
+
layerId={layerId}
|
|
312
|
+
containerId={sceneId}
|
|
313
|
+
selected={selectedKeys.has(keyframeAddress(sceneId, layerId, row.path, key.frame))}
|
|
135
314
|
ppf={ppf}
|
|
136
315
|
offsetFrames={offsetFrames}
|
|
137
|
-
|
|
316
|
+
originFrames={originFrames}
|
|
138
317
|
beginSnap={beginSnap}
|
|
318
|
+
openMenu={openMenu}
|
|
139
319
|
/>
|
|
140
320
|
))}
|
|
321
|
+
{readout === null ? null : <StaggerChip step={readout.step} left={readout.anchor ?? 0} />}
|
|
141
322
|
</div>
|
|
142
323
|
</div>
|
|
143
324
|
);
|
|
@@ -148,119 +329,187 @@ function ChannelRow({
|
|
|
148
329
|
* outside this scene simply shows the layer's held value. */
|
|
149
330
|
function ChannelValue({
|
|
150
331
|
layer,
|
|
151
|
-
|
|
332
|
+
path,
|
|
333
|
+
spec,
|
|
152
334
|
offsetFrames,
|
|
153
335
|
fps,
|
|
154
336
|
}: {
|
|
155
337
|
layer: Layer3D;
|
|
156
|
-
|
|
338
|
+
path: string;
|
|
339
|
+
spec: ChannelSpec;
|
|
157
340
|
offsetFrames: number;
|
|
158
341
|
fps: Rational;
|
|
159
342
|
}) {
|
|
160
343
|
// The COALESCED frame, not the raw one — the inspector's posture, applied
|
|
161
|
-
// here for the inspector's reason: this runs a full `resolveLayerAt`
|
|
344
|
+
// here for the inspector's reason: this runs a full `resolveLayerAt` track
|
|
162
345
|
// fold in render, one instance per open channel row, and the raw frame ran
|
|
163
346
|
// every one of them per playback tick (~750 folds/s with five layers
|
|
164
347
|
// expanded). Held while playing, ~10 Hz while scrubbing, leading edge on a
|
|
165
348
|
// lone seek — a readout, not an animation.
|
|
166
349
|
const frame = useCoalescedFrame();
|
|
167
|
-
const spec = CHANNEL_BY_KEY.get(channel);
|
|
168
|
-
if (spec === undefined) return null;
|
|
169
350
|
return (
|
|
170
|
-
<span className="channel-value" data-testid={`channel-value-${
|
|
351
|
+
<span className="channel-value" data-testid={`channel-value-${path}`}>
|
|
171
352
|
{spec.read(resolveLayerAt(layer, frame - offsetFrames, fps))}
|
|
172
353
|
</span>
|
|
173
354
|
);
|
|
174
355
|
}
|
|
175
356
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
357
|
+
/**
|
|
358
|
+
* ONE KEYFRAME.
|
|
359
|
+
*
|
|
360
|
+
* Drag moves it (through the dock's scrub bracket, so it is one undo
|
|
361
|
+
* entry and Escape restores the frame it started on), a plain press selects it,
|
|
362
|
+
* and a right-click sets the ease of the segment LEAVING it — which is what
|
|
363
|
+
* `easeOut` means and why the menu is on the key rather than between two.
|
|
364
|
+
*/
|
|
365
|
+
const TrackKeyView = memo(function TrackKeyView({
|
|
366
|
+
trackKey,
|
|
367
|
+
rowLabel,
|
|
368
|
+
path,
|
|
179
369
|
layerId,
|
|
180
|
-
|
|
370
|
+
containerId,
|
|
181
371
|
ppf,
|
|
182
372
|
offsetFrames,
|
|
183
|
-
|
|
373
|
+
originFrames,
|
|
184
374
|
beginSnap,
|
|
375
|
+
openMenu,
|
|
376
|
+
selected,
|
|
185
377
|
}: {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
378
|
+
trackKey: TrackKey;
|
|
379
|
+
rowLabel: string;
|
|
380
|
+
path: string;
|
|
381
|
+
/** Membership in the selection, decided by the parent's one set. */
|
|
382
|
+
selected: boolean;
|
|
383
|
+
/** Null = the shot itself, the address `selectedKeyframe` and every keyframe
|
|
384
|
+
* store verb already take. */
|
|
385
|
+
layerId: string | null;
|
|
386
|
+
containerId: string;
|
|
190
387
|
ppf: number;
|
|
191
388
|
offsetFrames: number;
|
|
192
|
-
|
|
389
|
+
originFrames: number;
|
|
193
390
|
beginSnap?: SnapPress | undefined;
|
|
391
|
+
openMenu: OpenMenu;
|
|
194
392
|
}) {
|
|
195
393
|
const store = useStore();
|
|
196
|
-
const selected = useShotEditor((s) => s.selectedBlock?.blockId === span.blockId);
|
|
197
|
-
const left = frameToPx(span.frame + offsetFrames, ppf);
|
|
198
|
-
const width = Math.max(4, frameToPx(span.durationFrames, ppf));
|
|
199
|
-
const dead = deadSpans(span, lifespan);
|
|
200
394
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
395
|
+
const drag = (e: ReactPointerEvent<HTMLElement>): void => {
|
|
396
|
+
e.preventDefault();
|
|
397
|
+
e.stopPropagation();
|
|
398
|
+
store.focusScene(containerId);
|
|
399
|
+
const at = { containerId, layerId, path, frame: trackKey.frame };
|
|
400
|
+
// ── ⌥ ON A SELECTED KEY, WITH OTHERS SELECTED, FANS THEM OUT ─────────
|
|
401
|
+
//
|
|
402
|
+
// Paper page 23, "8 · Stagger". ⌥ is free on a key otherwise, so a lone key
|
|
403
|
+
// or an unselected one falls through to the plain drag below. No snap: each
|
|
404
|
+
// key moves a different distance, so a guide would describe none of them.
|
|
405
|
+
const held = store.getState().selectedKeyframes;
|
|
406
|
+
if (e.altKey && held.length > 1 && held.some((k) => sameKeyframeSel(k, at))) {
|
|
210
407
|
const startX = e.clientX;
|
|
211
|
-
const
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
snap === undefined ? anchor + delta : snap(anchor + delta + offsetFrames) - offsetFrames;
|
|
216
|
-
store.beginBlockDrag();
|
|
408
|
+
const grabbed = staggerRowId(containerId, layerId, path);
|
|
409
|
+
const anchor = frameToPx(trackKey.frame + offsetFrames - originFrames, ppf);
|
|
410
|
+
if (!store.beginBlockDrag()) return;
|
|
411
|
+
setStaggerPreview({ from: {}, grabbed, step: 0, anchor });
|
|
217
412
|
startDrag(e, {
|
|
218
413
|
onMove(ev) {
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
414
|
+
const step = frameDelta(ev.clientX - startX, ppf);
|
|
415
|
+
// A refused capture leaves the keys on their press frames: +0f.
|
|
416
|
+
const took = store.dragKeyStagger(step);
|
|
417
|
+
setStaggerPreview({ from: {}, grabbed, step: took ? step : 0, anchor });
|
|
418
|
+
},
|
|
419
|
+
onEnd: (commit) => {
|
|
420
|
+
setStaggerPreview(null);
|
|
421
|
+
store.endBlockDrag(commit);
|
|
222
422
|
},
|
|
223
|
-
onEnd: (commit) => store.endBlockDrag(commit),
|
|
224
423
|
});
|
|
225
|
-
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
// ── A MODIFIED PRESS EXTENDS THE SELECTION AND DOES NOT DRAG ──────────
|
|
427
|
+
//
|
|
428
|
+
// Shift adds, ctrl/meta toggles — the same two modifiers `CurveGraph`
|
|
429
|
+
// reads off a click, so building a selection means one thing on both faces
|
|
430
|
+
// of the dock. It returns BEFORE `startDrag`, deliberately: ctrl is also
|
|
431
|
+
// this row's snap suppressor (`isSnapSuppressed`), so a modified press that
|
|
432
|
+
// also began a drag would move the key the author was only trying to add.
|
|
433
|
+
if (e.shiftKey || e.ctrlKey || e.metaKey) {
|
|
434
|
+
store.selectKeyframes(at, e.shiftKey ? "add" : "toggle");
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
store.selectKeyframe(at);
|
|
438
|
+
const startX = e.clientX;
|
|
439
|
+
const from = trackKey.frame;
|
|
440
|
+
// THE SAME SNAP the bars use, excluding NOTHING — and that is correct
|
|
441
|
+
// rather than an omission. A bar has to exclude itself because its own two
|
|
442
|
+
// edges are snap targets, so it would be pinned at zero travel; a keyframe
|
|
443
|
+
// frame is not a target at all (`collectSnapTargets` collects lifespans,
|
|
444
|
+
// clips, markers, the playhead and shot boundaries), so there is
|
|
445
|
+
// nothing of its own for it to catch on.
|
|
446
|
+
const snap = beginSnap?.(undefined, isSnapSuppressed(e));
|
|
447
|
+
store.beginBlockDrag();
|
|
448
|
+
startDrag(e, {
|
|
449
|
+
onMove(ev) {
|
|
450
|
+
const delta = frameDelta(ev.clientX - startX, ppf);
|
|
451
|
+
// Through `offsetFrames`, never `originFrames`: `beginSnap` freezes
|
|
452
|
+
// MASTER targets, so the conversion is to master time and back. The
|
|
453
|
+
// re-zero is a drawing offset and must not reach an edit.
|
|
454
|
+
const raw = from + delta;
|
|
455
|
+
const at = snap === undefined ? raw : snap(raw + offsetFrames) - offsetFrames;
|
|
456
|
+
store.dragKeyframe(containerId, layerId, path, from, at);
|
|
457
|
+
},
|
|
458
|
+
onEnd: (commit) => store.endBlockDrag(commit),
|
|
459
|
+
});
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
const contextMenu = (e: ReactMouseEvent<HTMLElement>): void => {
|
|
463
|
+
e.preventDefault();
|
|
464
|
+
e.stopPropagation();
|
|
465
|
+
// Point at the key under the cursor, the way the strip's menu selects the
|
|
466
|
+
// bar it opened on. So the Delete item and a following Backspace address
|
|
467
|
+
// the same thing.
|
|
468
|
+
// POINT AT THE KEY UNDER THE CURSOR — unless it is already IN the
|
|
469
|
+
// selection, in which case the menu is about the whole set. Right-clicking
|
|
470
|
+
// one of four selected keys to ease all four is the gesture every timeline
|
|
471
|
+
// has; collapsing to one here would silently drop three.
|
|
472
|
+
const at = { containerId, layerId, path, frame: trackKey.frame };
|
|
473
|
+
const targets = keyframeMenuTargets(store.getState().selectedKeyframes, at);
|
|
474
|
+
if (targets.length === 1) store.selectKeyframe(at);
|
|
475
|
+
// THE SHARED BUILDER: the graph's circles open the same menu, and the three
|
|
476
|
+
// kind rules it enforces are not the sort that survive being written twice.
|
|
477
|
+
openMenu(
|
|
478
|
+
{ x: e.clientX, y: e.clientY },
|
|
479
|
+
keyframeMenuEntries({
|
|
480
|
+
store,
|
|
481
|
+
key: { kind: trackKey.kind, ease: trackKey.ease, last: trackKey.last, curved: trackKey.curved },
|
|
482
|
+
targets,
|
|
483
|
+
at,
|
|
484
|
+
}),
|
|
485
|
+
);
|
|
486
|
+
};
|
|
226
487
|
|
|
227
488
|
return (
|
|
228
|
-
<
|
|
229
|
-
className={`channel-
|
|
230
|
-
style={{ left,
|
|
231
|
-
data-
|
|
232
|
-
data-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
{/* KEPT, NOT DELETED — the block lane's rule, restated per channel:
|
|
241
|
-
shortening a lifespan is a trim, and the part that can never play is
|
|
242
|
-
dashed in place so the author can see how far to pull the edge back. */}
|
|
243
|
-
{dead.map((d) => (
|
|
244
|
-
<span
|
|
245
|
-
key={d.from}
|
|
246
|
-
className="channel-dead"
|
|
247
|
-
aria-hidden="true"
|
|
248
|
-
style={{ left: frameToPx(d.from - span.frame, ppf), width: frameToPx(d.to - d.from, ppf) }}
|
|
249
|
-
/>
|
|
250
|
-
))}
|
|
251
|
-
<span className="channel-key channel-key-in" data-key="in" onPointerDown={drag("in")} aria-hidden="true" />
|
|
252
|
-
{span.spring ? (
|
|
253
|
-
// The settle tail, same statement the block bar makes: a spring keeps
|
|
254
|
-
// moving past its window, and its end is physics rather than a handle.
|
|
255
|
-
<span className="channel-spring-tail" aria-hidden="true" />
|
|
256
|
-
) : (
|
|
257
|
-
<span
|
|
258
|
-
className="channel-key channel-key-out"
|
|
259
|
-
data-key="out"
|
|
260
|
-
onPointerDown={drag("out")}
|
|
261
|
-
aria-hidden="true"
|
|
262
|
-
/>
|
|
263
|
-
)}
|
|
264
|
-
</div>
|
|
489
|
+
<span
|
|
490
|
+
className={`channel-key ease-${trackKey.ease.type}${selected ? " is-selected" : ""}`}
|
|
491
|
+
style={{ left: frameToPx(trackKey.frame + offsetFrames - originFrames, ppf) }}
|
|
492
|
+
data-track-key={`${path}@${trackKey.frame}`}
|
|
493
|
+
data-testid={`track-key-${path}-${trackKey.frame}`}
|
|
494
|
+
role="button"
|
|
495
|
+
tabIndex={-1}
|
|
496
|
+
aria-label={`${rowLabel} keyframe at frame ${trackKey.frame}`}
|
|
497
|
+
title={`${rowLabel} ${trackKey.value} · frame ${trackKey.frame} · ${easeLabel(trackKey.ease)}`}
|
|
498
|
+
onPointerDown={drag}
|
|
499
|
+
onContextMenu={contextMenu}
|
|
500
|
+
/>
|
|
265
501
|
);
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
/** The readout's slot for a key row: one per track. */
|
|
505
|
+
function staggerRowId(containerId: string, layerId: string | null, path: string): string {
|
|
506
|
+
return `keys:${containerId}|${layerId ?? ""}|${path}`;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/** The ease's own name, from the shared palette — never a second spelling of
|
|
510
|
+
* "Ease In-Out" living in this file. `matchPreset` compares bezier handles
|
|
511
|
+
* exactly, so a hand-authored curve comes back null and reads as "Custom". */
|
|
512
|
+
function easeLabel(ease: Ease): string {
|
|
513
|
+
const id = matchPreset(ease);
|
|
514
|
+
return EASE_PRESETS.find((p) => p.id === id)?.label ?? "Custom";
|
|
266
515
|
}
|