@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,508 @@
|
|
|
1
|
+
// GRAPH MODE: the dock's track area, showing HOW something moves.
|
|
2
|
+
//
|
|
3
|
+
// The rows answer "when does this happen" and this answers "how does it move",
|
|
4
|
+
// and they are two resolutions of one subject — which is why this is a MODE of
|
|
5
|
+
// the dock rather than a second panel beside it. A graph stacked under forty
|
|
6
|
+
// rows gets the height neither of them can spare.
|
|
7
|
+
//
|
|
8
|
+
// ── WHAT IS HERE AND WHAT IS SHARED ─────────────────────────────────────────
|
|
9
|
+
//
|
|
10
|
+
// The curve itself is `@dailies-studio/timeline`'s `CurveGraph` over the
|
|
11
|
+
// adapter in curveModel.ts. What this file owns is the TOOLBAR (scope, ease
|
|
12
|
+
// presets, the one key's Frame and Value, Normalize, Frame all), the LEFT
|
|
13
|
+
// COLUMN (which curve is focused, which others are shown beside it, each in
|
|
14
|
+
// its colour with its value at the playhead), the empty states in the plot,
|
|
15
|
+
// and the rule for what the pane is looking at when the author has not said.
|
|
16
|
+
//
|
|
17
|
+
// The column is the same width as the rows' label column (`useLabelWidth`, the
|
|
18
|
+
// rail track's shown width), so switching modes does not move the vertical
|
|
19
|
+
// rule the whole panel is read against.
|
|
20
|
+
import {
|
|
21
|
+
CurveGraph,
|
|
22
|
+
EASE_PRESETS,
|
|
23
|
+
KeyframeFields,
|
|
24
|
+
PlayheadChip,
|
|
25
|
+
PlayheadLine,
|
|
26
|
+
matchPreset,
|
|
27
|
+
} from "@dailies-studio/timeline";
|
|
28
|
+
import type { KeyframeAddr, KeyframeFieldsView } from "@dailies-studio/timeline";
|
|
29
|
+
import { IconChevronDown, IconDiamond, formatValue, useContextMenu } from "@dailies-studio/ui";
|
|
30
|
+
import { useEffect, useMemo, useState } from "react";
|
|
31
|
+
import type { CSSProperties, MouseEvent as ReactMouseEvent, ReactElement } from "react";
|
|
32
|
+
import type { Ease } from "@dailies-studio/engine";
|
|
33
|
+
import { containerFrame, defDurationFrames, homeOf, isDefId } from "@dailies-studio/shot/doc";
|
|
34
|
+
import type { Doc3D } from "@dailies-studio/shot/doc";
|
|
35
|
+
import { useShotEditor, useShotEditorHot, useStore } from "../../state/context.js";
|
|
36
|
+
import { primaryKeyframe } from "../../state/types.js";
|
|
37
|
+
import type { ShotEditorState } from "../../state/types.js";
|
|
38
|
+
import { useLabelWidth } from "./labelWidth.js";
|
|
39
|
+
import { keyframeMenuEntries, keyframeMenuTargets } from "./keyframeMenu.js";
|
|
40
|
+
import {
|
|
41
|
+
allAnimatedRows,
|
|
42
|
+
curveRowId,
|
|
43
|
+
curveRowsOf,
|
|
44
|
+
curveSubjectLayer,
|
|
45
|
+
curveTracksOf,
|
|
46
|
+
curveValueAt,
|
|
47
|
+
parseCurveRowId,
|
|
48
|
+
sameSubject,
|
|
49
|
+
shotCurveModel,
|
|
50
|
+
unitOf,
|
|
51
|
+
} from "./curveModel.js";
|
|
52
|
+
import type { CurveSubject } from "./curveModel.js";
|
|
53
|
+
|
|
54
|
+
/** The list's curve colours, in order. Tokens (shotTokens.css), so each theme
|
|
55
|
+
* sets its own readable values. A fifth curve takes the first colour again. */
|
|
56
|
+
const CURVE_COLORS = ["var(--curve-1)", "var(--curve-2)", "var(--curve-3)", "var(--curve-4)"] as const;
|
|
57
|
+
|
|
58
|
+
/** The toolbar's ease presets: the five shapes the graph board names, each one
|
|
59
|
+
* of `EASE_PRESETS` (so the ease written is the palette's own), with the
|
|
60
|
+
* board's glyph for the shape. */
|
|
61
|
+
const TOOLBAR_PRESETS: ReadonlyArray<{ id: string; label: string; glyph: string }> = [
|
|
62
|
+
{ id: "linear", label: "Linear", glyph: "M2 12L12 2" },
|
|
63
|
+
{ id: "ease-in", label: "Ease in", glyph: "M2 12C8 12 10 8 12 2" },
|
|
64
|
+
{ id: "ease-out", label: "Ease out", glyph: "M2 12C4 6 6 2 12 2" },
|
|
65
|
+
{ id: "ease-in-out", label: "Ease in-out", glyph: "M2 12C7 12 7 2 12 2" },
|
|
66
|
+
{ id: "hold", label: "Hold", glyph: "M2 12H8V2H12" },
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
const presetEase = (id: string): Ease | undefined => EASE_PRESETS.find((p) => p.id === id)?.ease;
|
|
70
|
+
|
|
71
|
+
/** Which curves the list shows: the selection's owner, or every animated
|
|
72
|
+
* element in the container. */
|
|
73
|
+
type CurveScope = "selected" | "all";
|
|
74
|
+
|
|
75
|
+
export function CurvePane() {
|
|
76
|
+
const store = useStore();
|
|
77
|
+
const doc = useShotEditor((s) => s.doc);
|
|
78
|
+
const editingDef = useShotEditor((s) => s.editingDef);
|
|
79
|
+
const sceneId = useShotEditor((s) => s.sceneId);
|
|
80
|
+
const selection = useShotEditor((s) => s.selection);
|
|
81
|
+
const selectedKeyframes = useShotEditor((s) => s.selectedKeyframes);
|
|
82
|
+
const selectedKeyframe = primaryKeyframe({ selectedKeyframes });
|
|
83
|
+
const ppf = useShotEditor((s) => s.dockPpf);
|
|
84
|
+
const labelWidth = useLabelWidth();
|
|
85
|
+
// ── THE FRAME STAYS OUT OF THIS PANE ─────────────────────────────────────
|
|
86
|
+
//
|
|
87
|
+
// The dock's rule is that the frame stays out of the dock and lives in
|
|
88
|
+
// leaves, and this pane keeps it: the graph's playhead (`CurvePlayhead`,
|
|
89
|
+
// drawn inside `CurveGraph`) and each curve's value at the playhead
|
|
90
|
+
// (`CurveRowValue`) subscribe to it, and nothing else here does. The model
|
|
91
|
+
// is built without it: a frame laid over the model per tick re-rendered
|
|
92
|
+
// the whole pane and the graph sixty times a second for a playhead line.
|
|
93
|
+
|
|
94
|
+
const [scope, setScope] = useState<CurveScope>("selected");
|
|
95
|
+
const [normalize, setNormalize] = useState(false);
|
|
96
|
+
const [frameAll, setFrameAll] = useState(0);
|
|
97
|
+
/** The curves shown BESIDE the focused one, by row id. Local, like the dock's
|
|
98
|
+
* own disclosure sets: it is about this panel's shape and nothing else. */
|
|
99
|
+
const [shownIds, setShownIds] = useState<ReadonlySet<string>>(() => new Set());
|
|
100
|
+
|
|
101
|
+
const subject = curveSubjectLayer(
|
|
102
|
+
{ selectedKeyframe, selection, containerId: editingDef ?? sceneId },
|
|
103
|
+
doc ?? undefined,
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const rows = useMemo(() => {
|
|
107
|
+
if (doc === null || subject === null) return [];
|
|
108
|
+
return scope === "all"
|
|
109
|
+
? allAnimatedRows(doc, subject.containerId)
|
|
110
|
+
: curveRowsOf(doc, subject.containerId, subject.layerId);
|
|
111
|
+
}, [doc, scope, subject?.containerId, subject?.layerId]);
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Which curve is drawn on top and edited.
|
|
115
|
+
*
|
|
116
|
+
* Seeded from the selected keyframe, and re-seeded whenever that moves to a
|
|
117
|
+
* different property — clicking a diamond in the rows and then switching to
|
|
118
|
+
* the graph should land on the thing that was clicked.
|
|
119
|
+
*/
|
|
120
|
+
const [focus, setFocus] = useState<CurveSubject | null>(null);
|
|
121
|
+
const wanted = useMemo(() => {
|
|
122
|
+
if (selectedKeyframe === null) return null;
|
|
123
|
+
const match = rows.find(
|
|
124
|
+
(r) =>
|
|
125
|
+
r.subject.path === selectedKeyframe.path &&
|
|
126
|
+
r.subject.layerId === selectedKeyframe.layerId &&
|
|
127
|
+
r.numberTrack,
|
|
128
|
+
);
|
|
129
|
+
return match?.subject ?? null;
|
|
130
|
+
}, [rows, selectedKeyframe?.path, selectedKeyframe?.layerId]);
|
|
131
|
+
useEffect(() => {
|
|
132
|
+
if (wanted !== null && !sameSubject(wanted, focus)) setFocus(wanted);
|
|
133
|
+
}, [wanted]);
|
|
134
|
+
// A pane that opens on nothing is a panel that reads as broken, so it falls
|
|
135
|
+
// back to the first curve there is. Not stored: a fallback that persisted
|
|
136
|
+
// would fight the seeding above on the next selection.
|
|
137
|
+
const focused =
|
|
138
|
+
focus !== null && rows.some((r) => r.id === curveIdOf(focus) && r.numberTrack)
|
|
139
|
+
? focus
|
|
140
|
+
: (rows.find((r) => r.numberTrack)?.subject ?? null);
|
|
141
|
+
|
|
142
|
+
// EACH CURVE'S COLOUR, by its place among the curves in the list, so a dot
|
|
143
|
+
// and its line always agree and a curve keeps its colour as others are shown.
|
|
144
|
+
const colors = useMemo(() => {
|
|
145
|
+
const out = new Map<string, string>();
|
|
146
|
+
rows.filter((r) => r.numberTrack).forEach((r, i) => out.set(r.id, CURVE_COLORS[i % CURVE_COLORS.length]!));
|
|
147
|
+
return out;
|
|
148
|
+
}, [rows]);
|
|
149
|
+
const shown = useMemo(
|
|
150
|
+
() => rows.filter((r) => r.numberTrack && shownIds.has(r.id)).map((r) => r.subject),
|
|
151
|
+
[rows, shownIds],
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
const defOpen = editingDef !== null && subject !== null && isDefId(subject.containerId);
|
|
155
|
+
/** The playhead in the subject's own frames, read by the leaves. Held to
|
|
156
|
+
* the Shot's own frames: a cut the transport plays leaves the cold
|
|
157
|
+
* `sceneId` on the Shot it left until Pause, and the frames past it are
|
|
158
|
+
* another Shot's, which this graph does not draw. */
|
|
159
|
+
const containerId = subject?.containerId ?? null;
|
|
160
|
+
const containerSpan = defOpen || doc === null || containerId === null ? 0 : (doc.scenesById[containerId]?.durationFrames ?? 0);
|
|
161
|
+
const localFrameOf = (s: ShotEditorState): number =>
|
|
162
|
+
doc === null || containerId === null
|
|
163
|
+
? 0
|
|
164
|
+
: defOpen
|
|
165
|
+
? s.defFrame
|
|
166
|
+
: Math.min(containerFrame(doc, containerId, s.frame), Math.max(0, containerSpan - 1));
|
|
167
|
+
|
|
168
|
+
const model = useMemo(() => {
|
|
169
|
+
if (doc === null || subject === null) return null;
|
|
170
|
+
const def = defOpen ? doc.components?.[subject.containerId] : undefined;
|
|
171
|
+
const containerFrames =
|
|
172
|
+
def !== undefined
|
|
173
|
+
? defDurationFrames(def)
|
|
174
|
+
: (doc.scenesById[subject.containerId]?.durationFrames ?? 0);
|
|
175
|
+
return shotCurveModel({
|
|
176
|
+
store,
|
|
177
|
+
doc,
|
|
178
|
+
containerId: subject.containerId,
|
|
179
|
+
containerFrames,
|
|
180
|
+
frame: 0,
|
|
181
|
+
fps: doc.fps,
|
|
182
|
+
pxPerFrame: ppf,
|
|
183
|
+
focus: focused,
|
|
184
|
+
rows,
|
|
185
|
+
selected: selectedKeyframes,
|
|
186
|
+
defOpen,
|
|
187
|
+
visible: shown,
|
|
188
|
+
colors,
|
|
189
|
+
});
|
|
190
|
+
}, [store, doc, subject?.containerId, subject?.layerId, defOpen, ppf, focused, rows, selectedKeyframes, shown, colors]);
|
|
191
|
+
|
|
192
|
+
// THE SAME KEYFRAME MENU THE ROWS OPEN. The graph knows a circle was
|
|
193
|
+
// right-clicked and nothing else — ease presets are this lane's vocabulary, so
|
|
194
|
+
// the entries are built here, from the shared builder, against the real track.
|
|
195
|
+
const menu = useContextMenu();
|
|
196
|
+
const openKeyMenu = (ref: KeyframeAddr, at: { x: number; y: number }): void => {
|
|
197
|
+
const subject = parseCurveRowId(ref.rowId);
|
|
198
|
+
if (doc === null || subject === null) return;
|
|
199
|
+
const track = curveTracksOf(doc, subject.containerId, subject.layerId)?.[subject.path];
|
|
200
|
+
const index = track?.keyframes.findIndex((k) => k.id === ref.keyframeId) ?? -1;
|
|
201
|
+
const key = index < 0 ? undefined : track!.keyframes[index]!;
|
|
202
|
+
if (track === undefined || key === undefined) return;
|
|
203
|
+
const sel = {
|
|
204
|
+
containerId: subject.containerId,
|
|
205
|
+
layerId: subject.layerId,
|
|
206
|
+
path: subject.path,
|
|
207
|
+
frame: key.frame,
|
|
208
|
+
};
|
|
209
|
+
const targets = keyframeMenuTargets(store.getState().selectedKeyframes, sel);
|
|
210
|
+
if (targets.length === 1) store.selectKeyframe(sel);
|
|
211
|
+
menu.open(
|
|
212
|
+
at,
|
|
213
|
+
keyframeMenuEntries({
|
|
214
|
+
store,
|
|
215
|
+
key: {
|
|
216
|
+
kind: track.kind,
|
|
217
|
+
ease: key.easeOut,
|
|
218
|
+
last: index === track.keyframes.length - 1,
|
|
219
|
+
curved: track.spatialInterp !== undefined,
|
|
220
|
+
},
|
|
221
|
+
targets,
|
|
222
|
+
at: sel,
|
|
223
|
+
}),
|
|
224
|
+
);
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
if (doc === null) return null;
|
|
228
|
+
|
|
229
|
+
const openScopeMenu = (e: ReactMouseEvent<HTMLButtonElement>): void => {
|
|
230
|
+
const box = e.currentTarget.getBoundingClientRect();
|
|
231
|
+
menu.open({ x: box.left, y: box.bottom + 4 }, [
|
|
232
|
+
{ kind: "action", id: "selected", label: "Selected", checked: scope === "selected", onSelect: () => setScope("selected") },
|
|
233
|
+
{ kind: "action", id: "all", label: "All animated", checked: scope === "all", onSelect: () => setScope("all") },
|
|
234
|
+
]);
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
// ── THE EASE PRESETS ACT ON THE SELECTED KEYS ───────────────────────────
|
|
238
|
+
//
|
|
239
|
+
// The whole selection, on every track, through the store's one batched
|
|
240
|
+
// write: one undo step for N keys, the same write the inspector's preset
|
|
241
|
+
// select makes. The active chip is the preset every selected key already
|
|
242
|
+
// has; a mixed or custom selection lights none.
|
|
243
|
+
const selectedEases = selectedKeyframes.flatMap((at) => {
|
|
244
|
+
const key = curveTracksOf(doc, at.containerId, at.layerId)?.[at.path]?.keyframes.find((k) => k.frame === at.frame);
|
|
245
|
+
return key === undefined ? [] : [key.easeOut];
|
|
246
|
+
});
|
|
247
|
+
const presetIds = new Set(selectedEases.map(matchPreset));
|
|
248
|
+
const activePreset = presetIds.size === 1 ? [...presetIds][0]! : null;
|
|
249
|
+
const noKeys = selectedKeyframes.length === 0;
|
|
250
|
+
const applyPreset = (id: string): void => {
|
|
251
|
+
const ease = presetEase(id);
|
|
252
|
+
if (noKeys || ease === undefined) return;
|
|
253
|
+
store.setKeyframesEase(
|
|
254
|
+
selectedKeyframes.map((k) => ({ containerId: k.containerId, layerId: k.layerId, path: k.path, frame: k.frame })),
|
|
255
|
+
ease,
|
|
256
|
+
);
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// THE ONE KEY'S FRAME AND VALUE, editable in the toolbar: the inspector's own
|
|
260
|
+
// store-free fields, through the graph's gesture, so a typed frame is one
|
|
261
|
+
// undo entry exactly as it is there. Only for ONE key on the focused curve:
|
|
262
|
+
// a frame typed into a set has no one meaning.
|
|
263
|
+
const only = selectedKeyframes.length === 1 ? selectedKeyframes[0]! : null;
|
|
264
|
+
const onlyKey =
|
|
265
|
+
only === null || focused === null || model?.focusedCurve == null
|
|
266
|
+
? undefined
|
|
267
|
+
: only.containerId === focused.containerId && only.layerId === focused.layerId && only.path === focused.path
|
|
268
|
+
? model.focusedCurve.keyframes.find((k) => k.frame === only.frame)
|
|
269
|
+
: undefined;
|
|
270
|
+
const fields: KeyframeFieldsView | null =
|
|
271
|
+
onlyKey === undefined || focused === null
|
|
272
|
+
? null
|
|
273
|
+
: {
|
|
274
|
+
ref: { rowId: curveRowId(focused), keyframeId: onlyKey.id },
|
|
275
|
+
frame: onlyKey.frame,
|
|
276
|
+
value: onlyKey.value,
|
|
277
|
+
...unitOf(focused.path),
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
// WHOSE CURVES THESE ARE, named over each owner's rows: the rows are
|
|
281
|
+
// property names ("Opacity", "X"), and without the owner a column of them
|
|
282
|
+
// does not say which element the graph is drawing. The layer's own label, or
|
|
283
|
+
// the shot's name for a shot-level track.
|
|
284
|
+
const ownerName = (containerId: string, layerId: string | null): string | null =>
|
|
285
|
+
layerId === null
|
|
286
|
+
? (doc.scenesById[containerId]?.name ?? null)
|
|
287
|
+
: (homeOf(doc, containerId)?.layersById[layerId]?.label ?? null);
|
|
288
|
+
|
|
289
|
+
const curveCount = rows.filter((r) => r.numberTrack).length;
|
|
290
|
+
const toggleShown = (id: string): void =>
|
|
291
|
+
setShownIds((prev) => {
|
|
292
|
+
const next = new Set(prev);
|
|
293
|
+
if (next.has(id)) next.delete(id);
|
|
294
|
+
else next.add(id);
|
|
295
|
+
return next;
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
const list: ReactElement[] = [];
|
|
299
|
+
let owner: string | undefined;
|
|
300
|
+
for (const row of rows) {
|
|
301
|
+
const rowOwner = row.subject.layerId ?? "";
|
|
302
|
+
if (rowOwner !== owner) {
|
|
303
|
+
owner = rowOwner;
|
|
304
|
+
const name = ownerName(row.subject.containerId, row.subject.layerId);
|
|
305
|
+
// Not a `.curve-row`: a header, never a focus target. The first one sits
|
|
306
|
+
// beside the ruler, the same 28px tall.
|
|
307
|
+
if (name !== null) {
|
|
308
|
+
list.push(
|
|
309
|
+
<div key={`owner-${rowOwner}`} className="curve-scope">
|
|
310
|
+
<IconDiamond className="curve-scope-icon" aria-hidden="true" />
|
|
311
|
+
<span className="curve-scope-name">{name}</span>
|
|
312
|
+
</div>,
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
const isFocused = row.id === curveIdOf(focused);
|
|
317
|
+
const isShown = row.numberTrack && (isFocused || shownIds.has(row.id));
|
|
318
|
+
const color = colors.get(row.id);
|
|
319
|
+
list.push(
|
|
320
|
+
<button
|
|
321
|
+
key={row.id}
|
|
322
|
+
type="button"
|
|
323
|
+
className={`curve-row${isFocused ? " is-focused" : ""}${isShown ? " is-shown" : ""}${row.numberTrack ? "" : " is-flat"}`}
|
|
324
|
+
style={{ "--depth": row.depth, ...(color === undefined ? {} : { "--curve-color": color }) } as CSSProperties}
|
|
325
|
+
data-testid={`curve-row-${row.subject.path}${row.subject.component === undefined ? "" : `-${row.subject.component}`}`}
|
|
326
|
+
// A NON-NUMERIC ROW IS NOT A CURVE. A colour has no value axis, so
|
|
327
|
+
// its row is a label and the graph keeps whatever it was drawing —
|
|
328
|
+
// rather than blanking, which reads as breakage.
|
|
329
|
+
disabled={!row.numberTrack}
|
|
330
|
+
title={
|
|
331
|
+
!row.numberTrack
|
|
332
|
+
? `${row.label} has no value axis to draw`
|
|
333
|
+
: isFocused
|
|
334
|
+
? `Editing the ${row.label} curve`
|
|
335
|
+
: `Edit the ${row.label} curve. Shift-click to ${shownIds.has(row.id) ? "hide it" : "show it beside the focused one"}`
|
|
336
|
+
}
|
|
337
|
+
onClick={(e) => {
|
|
338
|
+
if (e.shiftKey) {
|
|
339
|
+
if (!isFocused) toggleShown(row.id);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
setFocus(row.subject);
|
|
343
|
+
}}
|
|
344
|
+
>
|
|
345
|
+
<span className={`curve-row-dot${row.numberTrack ? "" : " is-none"}`} aria-hidden="true" />
|
|
346
|
+
<span className="curve-row-name">{row.label}</span>
|
|
347
|
+
{row.numberTrack && <CurveRowValue doc={doc} subject={row.subject} frameOf={localFrameOf} />}
|
|
348
|
+
</button>,
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const graphReady = model !== null && model.focusedCurve !== null;
|
|
353
|
+
|
|
354
|
+
return (
|
|
355
|
+
<div className="curve-pane" data-testid="curve-pane">
|
|
356
|
+
{menu.menu}
|
|
357
|
+
<div className="curve-toolbar">
|
|
358
|
+
<div className="curve-toolbar-scope" style={{ width: labelWidth }}>
|
|
359
|
+
<span className="curve-count">Curves · {curveCount}</span>
|
|
360
|
+
<button
|
|
361
|
+
type="button"
|
|
362
|
+
className="curve-scope-btn"
|
|
363
|
+
aria-haspopup="menu"
|
|
364
|
+
aria-label={`Show curves: ${scope === "all" ? "All animated" : "Selected"}`}
|
|
365
|
+
onClick={openScopeMenu}
|
|
366
|
+
>
|
|
367
|
+
{scope === "all" ? "All animated" : "Selected"}
|
|
368
|
+
<IconChevronDown aria-hidden="true" />
|
|
369
|
+
</button>
|
|
370
|
+
</div>
|
|
371
|
+
<div className="curve-toolbar-main">
|
|
372
|
+
<div className="curve-presets" role="group" aria-label="Ease presets">
|
|
373
|
+
{TOOLBAR_PRESETS.map((p) => {
|
|
374
|
+
const active = !noKeys && activePreset === p.id;
|
|
375
|
+
return (
|
|
376
|
+
<button
|
|
377
|
+
key={p.id}
|
|
378
|
+
type="button"
|
|
379
|
+
className={`curve-preset${active ? " is-active" : ""}`}
|
|
380
|
+
aria-pressed={active}
|
|
381
|
+
aria-disabled={noKeys}
|
|
382
|
+
title={noKeys ? "Select keyframes to set their ease" : `Set the selected keyframes to ${p.label.toLowerCase()}`}
|
|
383
|
+
onClick={() => applyPreset(p.id)}
|
|
384
|
+
>
|
|
385
|
+
<svg className="curve-preset-glyph" viewBox="0 0 14 14" aria-hidden="true">
|
|
386
|
+
<path d={p.glyph} />
|
|
387
|
+
</svg>
|
|
388
|
+
{p.label}
|
|
389
|
+
</button>
|
|
390
|
+
);
|
|
391
|
+
})}
|
|
392
|
+
</div>
|
|
393
|
+
<span className="curve-divider" aria-hidden="true" />
|
|
394
|
+
<div className="curve-readouts">
|
|
395
|
+
{fields !== null && model !== null ? (
|
|
396
|
+
<KeyframeFields key={`${fields.ref.rowId}:${fields.ref.keyframeId}`} fields={fields} gesture={model.gesture} bare />
|
|
397
|
+
) : (
|
|
398
|
+
<>
|
|
399
|
+
<span className="curve-readout" title="Select one keyframe on this curve to edit its frame">
|
|
400
|
+
<span className="curve-readout-label">Frame</span>
|
|
401
|
+
<span className="curve-readout-value">–</span>
|
|
402
|
+
</span>
|
|
403
|
+
<span className="curve-readout" title="Select one keyframe on this curve to edit its value">
|
|
404
|
+
<span className="curve-readout-label">Value</span>
|
|
405
|
+
<span className="curve-readout-value">–</span>
|
|
406
|
+
</span>
|
|
407
|
+
</>
|
|
408
|
+
)}
|
|
409
|
+
</div>
|
|
410
|
+
</div>
|
|
411
|
+
<div className="curve-toolbar-view">
|
|
412
|
+
<button
|
|
413
|
+
type="button"
|
|
414
|
+
className={`curve-tool${normalize ? " is-on" : ""}`}
|
|
415
|
+
aria-pressed={normalize}
|
|
416
|
+
title="Scale every curve to its own range, so their timing reads on one axis"
|
|
417
|
+
onClick={() => setNormalize((n) => !n)}
|
|
418
|
+
>
|
|
419
|
+
Normalize
|
|
420
|
+
</button>
|
|
421
|
+
<button
|
|
422
|
+
type="button"
|
|
423
|
+
className="curve-tool is-filled"
|
|
424
|
+
disabled={!graphReady}
|
|
425
|
+
title="Zoom to fit every key on show"
|
|
426
|
+
onClick={() => setFrameAll((n) => n + 1)}
|
|
427
|
+
>
|
|
428
|
+
Frame all
|
|
429
|
+
</button>
|
|
430
|
+
</div>
|
|
431
|
+
</div>
|
|
432
|
+
<div className="curve-body">
|
|
433
|
+
<div className="curve-rows" style={{ width: labelWidth }}>
|
|
434
|
+
<div className="curve-list">{list}</div>
|
|
435
|
+
{curveCount > 0 && <div className="curve-hint">Click a curve to edit. Shift-click to show more.</div>}
|
|
436
|
+
</div>
|
|
437
|
+
<div className="curve-graph">
|
|
438
|
+
{/* THE EMPTY STATES SIT IN THE PLOT, where the curve would be: the
|
|
439
|
+
column beside it is too narrow to hold a sentence, and the plot
|
|
440
|
+
is the place an author looks when nothing is drawn. */}
|
|
441
|
+
{rows.length === 0 ? (
|
|
442
|
+
<div className="curve-empty">
|
|
443
|
+
{subject === null || (subject.layerId === null && scope === "selected")
|
|
444
|
+
? "Keyframe a property to see its curve."
|
|
445
|
+
: scope === "all"
|
|
446
|
+
? "Nothing in this shot is animated yet. Add a keyframe from the rows or the inspector."
|
|
447
|
+
: "This element has no keyframes yet. Add one from the rows or the inspector."}
|
|
448
|
+
</div>
|
|
449
|
+
) : !graphReady ? (
|
|
450
|
+
<div className="curve-empty">
|
|
451
|
+
None of these properties has a value axis to draw. Their keys stay in the rows.
|
|
452
|
+
</div>
|
|
453
|
+
) : (
|
|
454
|
+
<CurveGraph
|
|
455
|
+
model={model}
|
|
456
|
+
onKeyContextMenu={openKeyMenu}
|
|
457
|
+
normalize={normalize}
|
|
458
|
+
frameAll={frameAll}
|
|
459
|
+
playhead={({ ppf: scale, offset }) => <CurvePlayhead ppf={scale} offset={offset} frameOf={localFrameOf} />}
|
|
460
|
+
/>
|
|
461
|
+
)}
|
|
462
|
+
</div>
|
|
463
|
+
</div>
|
|
464
|
+
</div>
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/** The graph's playhead, drawn inside `CurveGraph` at its scale and inset, and
|
|
469
|
+
* the pane's one leaf that follows the frame with `CurveRowValue`: a played
|
|
470
|
+
* frame repaints a line, never the pane or the graph. */
|
|
471
|
+
function CurvePlayhead({
|
|
472
|
+
ppf,
|
|
473
|
+
offset,
|
|
474
|
+
frameOf,
|
|
475
|
+
}: {
|
|
476
|
+
ppf: number;
|
|
477
|
+
offset: number;
|
|
478
|
+
frameOf: (s: ShotEditorState) => number;
|
|
479
|
+
}) {
|
|
480
|
+
const frame = useShotEditorHot(frameOf);
|
|
481
|
+
return (
|
|
482
|
+
<PlayheadLine frame={frame} ppf={ppf} offset={offset} className="curves-playhead">
|
|
483
|
+
<PlayheadChip frame={frame} />
|
|
484
|
+
</PlayheadLine>
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/** One curve's value at the playhead, in the list. A leaf on the frame that
|
|
489
|
+
* repaints only when its number moves. */
|
|
490
|
+
function CurveRowValue({
|
|
491
|
+
doc,
|
|
492
|
+
subject,
|
|
493
|
+
frameOf,
|
|
494
|
+
}: {
|
|
495
|
+
doc: Doc3D;
|
|
496
|
+
subject: CurveSubject;
|
|
497
|
+
frameOf: (s: ShotEditorState) => number;
|
|
498
|
+
}) {
|
|
499
|
+
const value = useShotEditorHot((s) => curveValueAt(doc, subject, frameOf(s), doc.fps));
|
|
500
|
+
return value === null ? null : <span className="curve-row-value">{formatValue(value, 0.01)}</span>;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/** The row id of a subject, or "" for none. THROUGH `curveRowId`, never a
|
|
504
|
+
* second spelling of the encoding: the focused comparison and the row's own
|
|
505
|
+
* key have to be the same string by construction. */
|
|
506
|
+
function curveIdOf(subject: CurveSubject | null): string {
|
|
507
|
+
return subject === null ? "" : curveRowId(subject);
|
|
508
|
+
}
|