@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
|
@@ -30,13 +30,43 @@ import type { PointerEvent as ReactPointerEvent } from "react";
|
|
|
30
30
|
import { frameToPx, startDrag } from "@dailies-studio/timeline";
|
|
31
31
|
import type { Rational } from "@dailies-studio/engine";
|
|
32
32
|
import type { MarkerRef } from "@dailies-studio/shot/doc";
|
|
33
|
-
import { useShotEditor, useStore } from "../../state/context.js";
|
|
33
|
+
import { useShotEditor, useShotEditorHot, useStore } from "../../state/context.js";
|
|
34
34
|
import { frameDelta, timecode } from "./model.js";
|
|
35
35
|
import { isSnapSuppressed } from "./snap.js";
|
|
36
36
|
import type { SnapPress } from "./snap.js";
|
|
37
|
+
import { formatCombo } from "../../state/shortcuts.js";
|
|
37
38
|
|
|
38
|
-
export function MarkerLane(props: {
|
|
39
|
-
|
|
39
|
+
export function MarkerLane(props: {
|
|
40
|
+
ppf: number;
|
|
41
|
+
labelWidth: number;
|
|
42
|
+
beginSnap?: SnapPress;
|
|
43
|
+
/**
|
|
44
|
+
* The SHOT CONTEXT's window, or absent in the scene context.
|
|
45
|
+
*
|
|
46
|
+
* A marker's frame is a MASTER frame and stays one; this only RE-ZEROES the
|
|
47
|
+
* lane on the shot, exactly as every other row in the dock is re-zeroed.
|
|
48
|
+
*
|
|
49
|
+
* ── EVERY MARKER IS STILL RENDERED, AND CLIPPED BY THE TRACK BOX ────────
|
|
50
|
+
*
|
|
51
|
+
* Narrowing the list to the window was the obvious thing and it is wrong,
|
|
52
|
+
* because the pin is the thing being DRAGGED: `dragMarker` writes an
|
|
53
|
+
* unclamped master frame, so pulling a mark one frame past the shot's edge
|
|
54
|
+
* unmounted its own pin mid-gesture — the pointer capture died with the node,
|
|
55
|
+
* the drag was silently discarded, and the beat was left wherever the last
|
|
56
|
+
* committed move had put it. The same filter also made the snap guide point
|
|
57
|
+
* at a mark the lane refused to draw. Drawing them all and letting
|
|
58
|
+
* `.marker-track`'s clip hide what is outside costs a handful of spans and
|
|
59
|
+
* has neither failure.
|
|
60
|
+
*
|
|
61
|
+
* DRAWN AT ALL, unlike the component context which prunes both master lanes:
|
|
62
|
+
* a definition runs on a second clock with no master position, so its ruler
|
|
63
|
+
* cannot place a master frame. A shot IS a window of master time, and
|
|
64
|
+
* beat-synced authoring inside one is the whole point of the tool.
|
|
65
|
+
*/
|
|
66
|
+
shotWindow?: { origin: number; len: number } | undefined;
|
|
67
|
+
}) {
|
|
68
|
+
const { ppf, labelWidth, beginSnap, shotWindow } = props;
|
|
69
|
+
const origin = shotWindow?.origin ?? 0;
|
|
40
70
|
const store = useStore();
|
|
41
71
|
const doc = useShotEditor((s) => s.doc);
|
|
42
72
|
const selected = useShotEditor((s) => s.selectedMarker);
|
|
@@ -67,6 +97,7 @@ export function MarkerLane(props: { ppf: number; labelWidth: number; beginSnap?:
|
|
|
67
97
|
marker={marker}
|
|
68
98
|
selected={marker.id === selected}
|
|
69
99
|
ppf={ppf}
|
|
100
|
+
origin={origin}
|
|
70
101
|
fps={doc.fps}
|
|
71
102
|
beginSnap={beginSnap}
|
|
72
103
|
/>
|
|
@@ -81,16 +112,23 @@ function MarkerPin(props: {
|
|
|
81
112
|
marker: MarkerRef;
|
|
82
113
|
selected: boolean;
|
|
83
114
|
ppf: number;
|
|
115
|
+
/** The shot context's re-zero. DRAWING ONLY: the drag below is a signed
|
|
116
|
+
* delta on a master frame, which no offset touches. */
|
|
117
|
+
origin: number;
|
|
84
118
|
fps: Rational;
|
|
85
119
|
beginSnap: SnapPress | undefined;
|
|
86
120
|
}) {
|
|
87
|
-
const { marker, selected, ppf, fps, beginSnap } = props;
|
|
121
|
+
const { marker, selected, ppf, origin, fps, beginSnap } = props;
|
|
122
|
+
/** What the ruler above this lane is reading. Master time outside a shot,
|
|
123
|
+
* and the shot's own zero inside one — a tooltip that said 0:42 under a
|
|
124
|
+
* ruler reading 0:02 is two clocks in one panel. */
|
|
125
|
+
const shown = marker.frame - origin;
|
|
88
126
|
const store = useStore();
|
|
89
127
|
// Subscribed to the BOOLEAN, not the frame: useSyncExternalStore only
|
|
90
128
|
// notifies when the selected value changes, so a pin re-renders exactly
|
|
91
129
|
// twice per playhead pass-through instead of once per playback frame — and
|
|
92
130
|
// the lane above it never re-renders at all.
|
|
93
|
-
const atPlayhead =
|
|
131
|
+
const atPlayhead = useShotEditorHot((s) => s.frame === marker.frame);
|
|
94
132
|
|
|
95
133
|
const drag = (e: ReactPointerEvent<HTMLElement>): void => {
|
|
96
134
|
e.preventDefault();
|
|
@@ -120,12 +158,12 @@ function MarkerPin(props: {
|
|
|
120
158
|
<span
|
|
121
159
|
className={`marker-pin${selected ? " is-selected" : ""}${atPlayhead ? " is-at-playhead" : ""}`}
|
|
122
160
|
data-marker={marker.id}
|
|
123
|
-
style={{ left: frameToPx(
|
|
161
|
+
style={{ left: frameToPx(shown, ppf) }}
|
|
124
162
|
role="button"
|
|
125
163
|
tabIndex={-1}
|
|
126
|
-
aria-label={`${label} at ${timecode(
|
|
164
|
+
aria-label={`${label} at ${timecode(shown, fps)}`}
|
|
127
165
|
aria-pressed={selected}
|
|
128
|
-
title={`${label} · ${timecode(
|
|
166
|
+
title={formatCombo(`${label} · ${timecode(shown, fps)} · drag to move it, ⌘drag to ignore snapping`)}
|
|
129
167
|
onPointerDown={drag}
|
|
130
168
|
>
|
|
131
169
|
{/* The diamond is a rotated square rather than an SVG: it is 8px of
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// THE PLAY CONTROL, one for the dock (`dock-play`) and Code Studio's preview
|
|
2
|
+
// strip (`components-play`), so the two cannot disagree about what Play is
|
|
3
|
+
// doing. Each caller passes its own class; the sheets place the look.
|
|
4
|
+
//
|
|
5
|
+
// idle the Play glyph, "Play (Space)".
|
|
6
|
+
// pending a queued Play (`playIntent`): the cancel glyph, `aria-busy`,
|
|
7
|
+
// and after 300ms a ring around the glyph. The ring turns while
|
|
8
|
+
// the scene loads or the stage hands over, and counts the
|
|
9
|
+
// percent while a Play pass draws. The title says which.
|
|
10
|
+
// playing the Pause glyph, "Pause (Space)".
|
|
11
|
+
// refused a start `playHold` refuses (the board, an export lock): the
|
|
12
|
+
// Play glyph, disabled, with the refusal itself as its title, so
|
|
13
|
+
// it names which hold this is. `setPlaying` refuses on the same
|
|
14
|
+
// `playHold`, so the greyed control is the guard agreeing with
|
|
15
|
+
// itself, not a second rule.
|
|
16
|
+
//
|
|
17
|
+
// A press is `setPlaying(!playOn(state))`, as Space is, so a second press
|
|
18
|
+
// cancels a queued Play. Only a start is ever refused: a running transport
|
|
19
|
+
// and a queued Play can always be called off, whatever opens meanwhile.
|
|
20
|
+
//
|
|
21
|
+
// A leaf with its own subscriptions, so a play edge, and each percent of a
|
|
22
|
+
// Play pass, re-renders this button and not the dock around it.
|
|
23
|
+
import { IconClose, IconPause, IconPlay } from "@dailies-studio/ui";
|
|
24
|
+
import { useShotEditor, useStore } from "../../state/context.js";
|
|
25
|
+
import { SCENE_WAITING, playHold, playOn, preparingPercent, preparingTitle } from "../../state/playHold.js";
|
|
26
|
+
|
|
27
|
+
/** Every Play control, whoever places it: input aimed at one does not end a
|
|
28
|
+
* background preparing pass, which its click upgrades (`StagePanel`). */
|
|
29
|
+
export const PLAY_CONTROL = "[data-play-control]";
|
|
30
|
+
|
|
31
|
+
export function PlayButton(props: { className: string; testId?: string }) {
|
|
32
|
+
const store = useStore();
|
|
33
|
+
const playing = useShotEditor((s) => s.playing);
|
|
34
|
+
const intent = useShotEditor((s) => s.playIntent);
|
|
35
|
+
// The refusal and its wording. A string selector is compared by value, so
|
|
36
|
+
// this is as cold a subscription as a boolean.
|
|
37
|
+
const refusal = useShotEditor(playHold);
|
|
38
|
+
const pending = !playing && intent !== null;
|
|
39
|
+
const title = playing
|
|
40
|
+
? "Pause (Space)"
|
|
41
|
+
: intent === null
|
|
42
|
+
? (refusal ?? "Play (Space)")
|
|
43
|
+
: intent.phase === "preparing"
|
|
44
|
+
? preparingTitle(intent)
|
|
45
|
+
: SCENE_WAITING;
|
|
46
|
+
return (
|
|
47
|
+
<button
|
|
48
|
+
type="button"
|
|
49
|
+
className={props.className}
|
|
50
|
+
data-testid={props.testId}
|
|
51
|
+
data-play-control=""
|
|
52
|
+
data-state={playing ? "playing" : pending ? "pending" : "idle"}
|
|
53
|
+
aria-label={playing ? "Pause" : pending ? "Cancel play" : "Play"}
|
|
54
|
+
aria-busy={pending ? true : undefined}
|
|
55
|
+
disabled={!playing && !pending && refusal !== null}
|
|
56
|
+
title={title}
|
|
57
|
+
// Read at the press, not from this render: the store may have moved on.
|
|
58
|
+
onClick={() => store.setPlaying(!playOn(store.getState()))}
|
|
59
|
+
>
|
|
60
|
+
{playing ? <IconPause /> : pending ? <IconClose /> : <IconPlay />}
|
|
61
|
+
{pending ? <PlayRing percent={intent.phase === "preparing" ? preparingPercent(intent) : null} /> : null}
|
|
62
|
+
</button>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** The queued Play's ring: the font field's write-in-flight ring
|
|
67
|
+
* (`.font-field-ring`, inspector-object.css), so the product has one ring. A
|
|
68
|
+
* level while a Play pass counts; with nothing to count, a short arc that
|
|
69
|
+
* turns, or a still dashed ring under reduced motion. Each context's sheet
|
|
70
|
+
* lays it over the control and holds it back 300ms, so a start that is
|
|
71
|
+
* ready at once never flashes it. Drawn on a 36 grid: the dock's ring is
|
|
72
|
+
* 36px, so the ring's 1.5 stroke draws 1.5px there. */
|
|
73
|
+
function PlayRing(props: { percent: number | null }) {
|
|
74
|
+
const { percent } = props;
|
|
75
|
+
return (
|
|
76
|
+
<span className="play-ring" aria-hidden="true">
|
|
77
|
+
<svg className={`font-field-ring${percent === null ? " is-indeterminate" : ""}`} viewBox="0 0 36 36">
|
|
78
|
+
<circle className="font-field-ring-track" cx="18" cy="18" r="17" />
|
|
79
|
+
<circle
|
|
80
|
+
className="font-field-ring-level"
|
|
81
|
+
cx="18"
|
|
82
|
+
cy="18"
|
|
83
|
+
r="17"
|
|
84
|
+
pathLength={100}
|
|
85
|
+
{...(percent === null ? {} : { strokeDasharray: `${percent} 100` })}
|
|
86
|
+
transform="rotate(-90 18 18)"
|
|
87
|
+
/>
|
|
88
|
+
</svg>
|
|
89
|
+
</span>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
// "+ property": THE SECOND DOOR INTO KEYFRAMING.
|
|
2
|
+
//
|
|
3
|
+
// ── WHY IT EXISTS AT ALL ────────────────────────────────────────────────────
|
|
4
|
+
//
|
|
5
|
+
// A channel row appears once a property carries animation and not before, which
|
|
6
|
+
// is Lottie's rule (animated properties, not every property) and is right: a
|
|
7
|
+
// layer has thirty trackable paths and nobody wants thirty rows. But it left
|
|
8
|
+
// the inspector as the ONLY way to start one — an author who wanted to animate
|
|
9
|
+
// the stroke width had to know which section holds it and find the diamond in
|
|
10
|
+
// it. The timeline is where you go to animate something, so the timeline is
|
|
11
|
+
// where "animate something else" belongs too.
|
|
12
|
+
//
|
|
13
|
+
// ── THE LIST IS THE TABLE, AND SO ARE THE REFUSALS ──────────────────────────
|
|
14
|
+
//
|
|
15
|
+
// Every entry comes from `TRACKABLE_PATHS` / `SCENE_TRACKABLE_PATHS` — the same
|
|
16
|
+
// single source validate, the agent ops and the inspector diamond read — so a
|
|
17
|
+
// path this offers is a path the gate accepts, by construction rather than by
|
|
18
|
+
// two lists agreeing.
|
|
19
|
+
//
|
|
20
|
+
// A path this layer cannot carry is shown GREYED WITH ITS REASON rather than
|
|
21
|
+
// hidden. `layerTrackRefusal` already returns the sentence ("needs a stroke",
|
|
22
|
+
// "needs a solid fill"), and "why can I not animate the stroke width" is a
|
|
23
|
+
// question the author is entitled to have answered where they asked it. That is
|
|
24
|
+
// the one place this deliberately differs from `KeyframeDot`, which is ABSENT on
|
|
25
|
+
// a refused path: a diamond sits in a row the author is already looking at, and
|
|
26
|
+
// this is a list they opened to go looking.
|
|
27
|
+
import { Popover } from "@dailies-studio/ui";
|
|
28
|
+
import {
|
|
29
|
+
INSTANCE_PROP_PREFIX,
|
|
30
|
+
SCENE_TRACKABLE_PATHS,
|
|
31
|
+
TRACKABLE_PATHS,
|
|
32
|
+
contractTrackablePath,
|
|
33
|
+
layerSnapshotContract,
|
|
34
|
+
layerTrackRefusal,
|
|
35
|
+
pathAllowsKind,
|
|
36
|
+
sceneTrackRefusal,
|
|
37
|
+
} from "@dailies-studio/shot/doc";
|
|
38
|
+
import type { Layer3D, Scene3D } from "@dailies-studio/shot/doc";
|
|
39
|
+
import { controlLabel } from "@dailies-studio/contract";
|
|
40
|
+
import type { Contract } from "@dailies-studio/contract";
|
|
41
|
+
import { useMemo, useRef, useState } from "react";
|
|
42
|
+
import type { CSSProperties } from "react";
|
|
43
|
+
import { useShotEditor, useStore } from "../../state/context.js";
|
|
44
|
+
|
|
45
|
+
/** One offer. `refusal` non-null means the row is disabled and says why. */
|
|
46
|
+
interface Offer {
|
|
47
|
+
path: string;
|
|
48
|
+
label: string;
|
|
49
|
+
group: string;
|
|
50
|
+
tracked: boolean;
|
|
51
|
+
refusal: string | null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** The first path segment, title-cased, is the section an author already knows
|
|
55
|
+
* the property by: "transform.opacity" is under Transform, "paint.fill.color"
|
|
56
|
+
* under Paint. Derived rather than tabled, because a second column naming the
|
|
57
|
+
* group is a second thing to keep in step with the path. A one-segment path
|
|
58
|
+
* is a field of the layer itself (`blurPx`, schema v47), which the inspector
|
|
59
|
+
* edits in Appearance, so it groups there rather than under its own name. */
|
|
60
|
+
export function groupOf(path: string): string {
|
|
61
|
+
if (!path.includes(".")) return "Appearance";
|
|
62
|
+
const head = path.split(".")[0] ?? "";
|
|
63
|
+
return head.charAt(0).toUpperCase() + head.slice(1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The array-indexed paths (`paint.shadows.<i>.blurPx`) are offered ONCE PER
|
|
68
|
+
* SHADOW the layer actually has. The table key carries the placeholder, so a
|
|
69
|
+
* literal offer would be a path that resolves to nothing and a keyframe that
|
|
70
|
+
* writes nowhere.
|
|
71
|
+
*/
|
|
72
|
+
function expandIndexed(path: string, layer: Layer3D): string[] {
|
|
73
|
+
if (!path.includes(".<i>.")) return [path];
|
|
74
|
+
const shadows = "paint" in layer ? (layer.paint?.shadows ?? []) : [];
|
|
75
|
+
return shadows.map((_, i) => path.replace(".<i>.", `.${i}.`));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function layerOffers(layer: Layer3D, live: Contract | undefined): Offer[] {
|
|
79
|
+
const out: Offer[] = [];
|
|
80
|
+
for (const [key, spec] of Object.entries(TRACKABLE_PATHS)) {
|
|
81
|
+
// A path that is not a property of this KIND is not a refusal to explain,
|
|
82
|
+
// it is a property of something else. Those are the only ones left out.
|
|
83
|
+
if (!pathAllowsKind(spec, layer.kind)) continue;
|
|
84
|
+
for (const path of expandIndexed(key, layer)) {
|
|
85
|
+
out.push({
|
|
86
|
+
path,
|
|
87
|
+
label: spec.label,
|
|
88
|
+
group: groupOf(path),
|
|
89
|
+
tracked: layer.tracks?.[path] !== undefined,
|
|
90
|
+
refusal: layerTrackRefusal(layer, path),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
out.push(...instanceOffers(layer, live));
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* An instance's props, from the CONTRACT rather than from the table — the one
|
|
100
|
+
* offer family whose list is a property of the component and not of this lane.
|
|
101
|
+
*
|
|
102
|
+
* Grouped under "Component" and not under `groupOf`'s "Instance": the inspector
|
|
103
|
+
* section that edits these props is titled Component, and two names for one
|
|
104
|
+
* group is what `groupOf`'s own comment is guarding against.
|
|
105
|
+
*
|
|
106
|
+
* A non-animatable spec (`slot`, `event`) is LEFT OUT rather than greyed. Every
|
|
107
|
+
* other refusal in this list is something the author can fix by adding a stroke
|
|
108
|
+
* or a shadow; "this prop is a slot" is a fact about the component's source, so
|
|
109
|
+
* a row explaining it would be a row that never becomes pressable.
|
|
110
|
+
*/
|
|
111
|
+
function instanceOffers(layer: Layer3D, live: Contract | undefined): Offer[] {
|
|
112
|
+
if (layer.kind !== "instance") return [];
|
|
113
|
+
const contract = live ?? layerSnapshotContract(layer);
|
|
114
|
+
if (contract === null || contract === undefined) return [];
|
|
115
|
+
const out: Offer[] = [];
|
|
116
|
+
for (const spec of Object.values(contract.keys)) {
|
|
117
|
+
if (contractTrackablePath(spec) === null) continue;
|
|
118
|
+
const path = `${INSTANCE_PROP_PREFIX}${spec.key}`;
|
|
119
|
+
out.push({
|
|
120
|
+
path,
|
|
121
|
+
label: controlLabel(spec),
|
|
122
|
+
group: "Component",
|
|
123
|
+
tracked: layer.tracks?.[path] !== undefined,
|
|
124
|
+
refusal: layerTrackRefusal(layer, path, live),
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return out;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function sceneOffers(scene: Scene3D): Offer[] {
|
|
131
|
+
return Object.entries(SCENE_TRACKABLE_PATHS).map(([path, spec]) => ({
|
|
132
|
+
path,
|
|
133
|
+
label: spec.label,
|
|
134
|
+
group: groupOf(path),
|
|
135
|
+
tracked: scene.tracks?.[path] !== undefined,
|
|
136
|
+
refusal: sceneTrackRefusal(scene, path),
|
|
137
|
+
}));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* The button and its list. `subject` decides which table is read and which verb
|
|
142
|
+
* the pick routes through; everything else is one component because every pixel
|
|
143
|
+
* of it is shared — the `KeyframeDot`/`SceneKeyframeDot` split's own reasoning.
|
|
144
|
+
*/
|
|
145
|
+
export function PropertyPicker({
|
|
146
|
+
subject,
|
|
147
|
+
containerId,
|
|
148
|
+
labelWidth,
|
|
149
|
+
depth,
|
|
150
|
+
status = null,
|
|
151
|
+
}: {
|
|
152
|
+
subject: { kind: "layer"; layer: Layer3D } | { kind: "shot"; scene: Scene3D };
|
|
153
|
+
containerId: string;
|
|
154
|
+
labelWidth: number;
|
|
155
|
+
depth: number;
|
|
156
|
+
/** The selected-key readout drawn beside the button, from the rows above
|
|
157
|
+
* (ChannelRow's `selectedKeyStatus`). Null draws nothing. */
|
|
158
|
+
status?: string | null;
|
|
159
|
+
}) {
|
|
160
|
+
const store = useStore();
|
|
161
|
+
const anchor = useRef<HTMLButtonElement>(null);
|
|
162
|
+
const [open, setOpen] = useState(false);
|
|
163
|
+
// The registry's contracts, for the instance-prop offers. `InstanceSection` is
|
|
164
|
+
// what loads one; until it has, the layer's snapshot answers.
|
|
165
|
+
const contracts = useShotEditor((s) => s.contracts);
|
|
166
|
+
const live =
|
|
167
|
+
subject.kind === "layer" && subject.layer.kind === "instance"
|
|
168
|
+
? contracts[subject.layer.instance.component]
|
|
169
|
+
: undefined;
|
|
170
|
+
|
|
171
|
+
const offers = useMemo(
|
|
172
|
+
() => (subject.kind === "layer" ? layerOffers(subject.layer, live) : sceneOffers(subject.scene)),
|
|
173
|
+
[subject.kind, subject.kind === "layer" ? subject.layer : subject.scene, live],
|
|
174
|
+
);
|
|
175
|
+
const groups = useMemo(() => {
|
|
176
|
+
const byGroup = new Map<string, Offer[]>();
|
|
177
|
+
for (const offer of offers) {
|
|
178
|
+
const at = byGroup.get(offer.group);
|
|
179
|
+
if (at === undefined) byGroup.set(offer.group, [offer]);
|
|
180
|
+
else at.push(offer);
|
|
181
|
+
}
|
|
182
|
+
return [...byGroup.entries()];
|
|
183
|
+
}, [offers]);
|
|
184
|
+
|
|
185
|
+
const testId = subject.kind === "layer" ? subject.layer.id : containerId;
|
|
186
|
+
|
|
187
|
+
const pick = (offer: Offer): void => {
|
|
188
|
+
setOpen(false);
|
|
189
|
+
if (offer.refusal !== null) return;
|
|
190
|
+
// THE ROW'S SUBJECT, NOT THE SELECTION. Both verbs fan out over what is
|
|
191
|
+
// selected (a diamond in the inspector keys every selected layer), so the
|
|
192
|
+
// row makes its own layer the selection first — which is what clicking any
|
|
193
|
+
// row in this dock does anyway.
|
|
194
|
+
store.focusScene(containerId);
|
|
195
|
+
if (subject.kind === "layer") {
|
|
196
|
+
store.select([subject.layer.id]);
|
|
197
|
+
store.toggleKeyframe([offer.path]);
|
|
198
|
+
} else {
|
|
199
|
+
store.toggleSceneKeyframe([offer.path]);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
<div className="channel-row channel-add-row">
|
|
205
|
+
<div className="channel-label-cell" style={{ width: labelWidth, "--depth": depth } as CSSProperties}>
|
|
206
|
+
<button
|
|
207
|
+
ref={anchor}
|
|
208
|
+
type="button"
|
|
209
|
+
className="channel-add"
|
|
210
|
+
data-testid={`property-add-${testId}`}
|
|
211
|
+
aria-haspopup="true"
|
|
212
|
+
aria-expanded={open}
|
|
213
|
+
title="Animate another property: the first keyframe takes the value it already has, so nothing moves"
|
|
214
|
+
onClick={() => setOpen((was) => !was)}
|
|
215
|
+
>
|
|
216
|
+
+ property
|
|
217
|
+
</button>
|
|
218
|
+
</div>
|
|
219
|
+
{/* In the LANE, not the label cell: the label column is one rail track
|
|
220
|
+
wide and the button already fills its share, while the lane has room
|
|
221
|
+
for "Opacity 0.42 · frame 24 · Ease Out". */}
|
|
222
|
+
<div className="channel-lane channel-add-status" data-testid={`property-add-status-${testId}`}>
|
|
223
|
+
{status}
|
|
224
|
+
</div>
|
|
225
|
+
<Popover open={open} anchor={anchor} onClose={() => setOpen(false)} className="property-pop">
|
|
226
|
+
{groups.map(([group, list]) => (
|
|
227
|
+
<div key={group} className="property-group">
|
|
228
|
+
<div className="property-group-label">{group}</div>
|
|
229
|
+
{list.map((offer) => (
|
|
230
|
+
<button
|
|
231
|
+
key={offer.path}
|
|
232
|
+
type="button"
|
|
233
|
+
className={`property-item${offer.tracked ? " is-tracked" : ""}`}
|
|
234
|
+
data-testid={`property-item-${offer.path}`}
|
|
235
|
+
disabled={offer.refusal !== null}
|
|
236
|
+
// The refusal IS the title on a disabled row, and the promise on
|
|
237
|
+
// an available one.
|
|
238
|
+
title={offer.refusal ?? `Keyframe ${offer.label} at the playhead`}
|
|
239
|
+
onClick={() => pick(offer)}
|
|
240
|
+
>
|
|
241
|
+
<span className="property-item-name">{offer.label}</span>
|
|
242
|
+
{offer.refusal !== null ? (
|
|
243
|
+
<span className="property-item-note">{offer.refusal.replace(/^"[^"]*"\s*/, "")}</span>
|
|
244
|
+
) : offer.tracked ? (
|
|
245
|
+
<span className="property-item-note">animated</span>
|
|
246
|
+
) : null}
|
|
247
|
+
</button>
|
|
248
|
+
))}
|
|
249
|
+
</div>
|
|
250
|
+
))}
|
|
251
|
+
</Popover>
|
|
252
|
+
</div>
|
|
253
|
+
);
|
|
254
|
+
}
|