@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,234 @@
|
|
|
1
|
+
// WHICH PIXELS OF THE DOCK ARE ON SCREEN RIGHT NOW.
|
|
2
|
+
//
|
|
3
|
+
// A dock-local store, read by the filmstrip canvases and by nothing else. It
|
|
4
|
+
// exists for one reason: a video row's bar can be a hundred thousand pixels
|
|
5
|
+
// wide at a deep zoom, and a strip that drew and fetched all of it would spend
|
|
6
|
+
// a scrub's worth of work on pixels nobody is looking at, and would ask for a
|
|
7
|
+
// canvas past the width a browser will allocate.
|
|
8
|
+
//
|
|
9
|
+
// ── WHY THIS IS NOT THE SHOT STORE ────────────────────────────────────────
|
|
10
|
+
//
|
|
11
|
+
// Nothing here is document state, nothing here survives a reload, and nothing
|
|
12
|
+
// outside this lane may read it. Putting a scroll position in the shot store
|
|
13
|
+
// would put a value that changes at scroll rate on a channel every panel in the
|
|
14
|
+
// chrome subscribes to. The hot channel is worse still: `useShotEditorHot` is
|
|
15
|
+
// the FRAME, its contract is "the smallest leaf that must repaint every tick",
|
|
16
|
+
// and a scroll position is neither the frame nor on the frame's schedule.
|
|
17
|
+
// See `useLive.ts`'s header for the rule this one obeys.
|
|
18
|
+
//
|
|
19
|
+
// ── THE QUANTUM IS THE POINT ──────────────────────────────────────────────
|
|
20
|
+
//
|
|
21
|
+
// `scrollLeft` moves in single pixels, so an unquantised store would notify
|
|
22
|
+
// every subscriber on every pixel of a pan and a filmstrip would redraw sixty
|
|
23
|
+
// times a second while a hand is on the trackpad. Rounded out to a 64 px grid,
|
|
24
|
+
// a subscriber hears about a scroll once every 64 px and the strip it draws is
|
|
25
|
+
// stable in between. The range is rounded OUTWARD (floor the left, ceil the
|
|
26
|
+
// right), so the quantised range always CONTAINS the real one: a coarser grid
|
|
27
|
+
// can cost an extra column of work, never a missing one.
|
|
28
|
+
//
|
|
29
|
+
// ── AND THE MARGIN IS WHY A QUANTUM IS ENOUGH ─────────────────────────────
|
|
30
|
+
//
|
|
31
|
+
// Work is admitted for the visible range plus `DOCK_VIEWPORT_MARGIN_PX` on each
|
|
32
|
+
// side, which is eight quanta. So a bar that scrolls into view was already
|
|
33
|
+
// drawn before it arrived, and the strip is never caught blank at the leading
|
|
34
|
+
// edge of a pan.
|
|
35
|
+
|
|
36
|
+
/** The grid the visible range is rounded onto, in content pixels. */
|
|
37
|
+
export const DOCK_VIEWPORT_QUANTUM_PX = 64;
|
|
38
|
+
|
|
39
|
+
/** How far outside the visible range a bar still draws and still fetches. */
|
|
40
|
+
export const DOCK_VIEWPORT_MARGIN_PX = 512;
|
|
41
|
+
|
|
42
|
+
/** The scroller this lane measures. One selector, spelled once, because a
|
|
43
|
+
* filmstrip finds its own scroller by walking up from its canvas rather than
|
|
44
|
+
* being handed one: the dock's row tree is owned by other components and this
|
|
45
|
+
* lane must not make them thread a ref through it. */
|
|
46
|
+
export const DOCK_SCROLLER_SELECTOR = ".dock-scroll";
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The visible slice of the dock's scrolled content, in CONTENT pixels.
|
|
50
|
+
*
|
|
51
|
+
* Content pixels and not client pixels: content x 0 is the left edge of the
|
|
52
|
+
* scrolled content, which is where the sticky label column sits, so a bar's
|
|
53
|
+
* content x is `labelWidth + itsLaneLeft` and needs no layout read to know.
|
|
54
|
+
*/
|
|
55
|
+
export interface DockViewport {
|
|
56
|
+
left: number;
|
|
57
|
+
right: number;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Nothing has been measured.
|
|
62
|
+
*
|
|
63
|
+
* An empty range rather than a zero-width one at the origin, and the
|
|
64
|
+
* difference matters: `visibleSlice` reads `right <= left` as "no answer" and
|
|
65
|
+
* admits the bar, because a strip that drew nothing until a scroll event
|
|
66
|
+
* arrived would be blank on every first paint and permanently blank in any
|
|
67
|
+
* environment with no layout at all (jsdom measures every element at zero).
|
|
68
|
+
* A viewport that cannot be measured must not be able to hide a row.
|
|
69
|
+
*/
|
|
70
|
+
export const DOCK_VIEWPORT_UNMEASURED: DockViewport = Object.freeze({ left: 0, right: 0 });
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* How much of a bar an UNMEASURED viewport admits, in bar-local pixels.
|
|
74
|
+
*
|
|
75
|
+
* ponytail: a flat ceiling rather than a second measurement path. It exists so
|
|
76
|
+
* that "we do not know what is visible" cannot mean "allocate a canvas as wide
|
|
77
|
+
* as the bar", which at 60 px/frame over a fifty-second film is six figures of
|
|
78
|
+
* pixels and no browser's problem to solve. Roughly a laptop dock's width, so
|
|
79
|
+
* a real browser that has not scrolled yet still draws a full screen of strip.
|
|
80
|
+
*/
|
|
81
|
+
export const DOCK_VIEWPORT_UNMEASURED_PX = 2048;
|
|
82
|
+
|
|
83
|
+
interface Entry {
|
|
84
|
+
/** Replaced only when the QUANTISED range moves, because `useSyncExternalStore`
|
|
85
|
+
* compares snapshots by identity and a fresh object per read is an infinite
|
|
86
|
+
* render loop. */
|
|
87
|
+
view: DockViewport;
|
|
88
|
+
listeners: Set<() => void>;
|
|
89
|
+
/** The one coalescing slot. Non-null means a measure is already booked for
|
|
90
|
+
* the next frame, so a burst of scroll events costs one measurement. */
|
|
91
|
+
raf: number | null;
|
|
92
|
+
detach: () => void;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const entries = new WeakMap<HTMLElement, Entry>();
|
|
96
|
+
|
|
97
|
+
function quantise(el: HTMLElement): DockViewport {
|
|
98
|
+
const q = DOCK_VIEWPORT_QUANTUM_PX;
|
|
99
|
+
const from = el.scrollLeft;
|
|
100
|
+
const to = from + el.clientWidth;
|
|
101
|
+
// A scroller with no width has not been laid out. Reporting `{0, 0}` here is
|
|
102
|
+
// what routes it to the unmeasured branch rather than to "nothing is visible".
|
|
103
|
+
if (!(to > from) || !Number.isFinite(from) || !Number.isFinite(to)) return DOCK_VIEWPORT_UNMEASURED;
|
|
104
|
+
return { left: Math.floor(from / q) * q, right: Math.ceil(to / q) * q };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function measure(el: HTMLElement, entry: Entry): void {
|
|
108
|
+
const next = quantise(el);
|
|
109
|
+
if (next.left === entry.view.left && next.right === entry.view.right) return;
|
|
110
|
+
entry.view = next;
|
|
111
|
+
for (const fn of [...entry.listeners]) {
|
|
112
|
+
try {
|
|
113
|
+
fn();
|
|
114
|
+
} catch {
|
|
115
|
+
// One subscriber throwing must not stop the others hearing about it.
|
|
116
|
+
// Same posture as the filmstrip loader's `notify`: this lane is
|
|
117
|
+
// decoration and may not fail an edit.
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function schedule(el: HTMLElement, entry: Entry): void {
|
|
123
|
+
if (entry.raf !== null) return;
|
|
124
|
+
if (typeof requestAnimationFrame !== "function") {
|
|
125
|
+
measure(el, entry);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
entry.raf = requestAnimationFrame(() => {
|
|
129
|
+
entry.raf = null;
|
|
130
|
+
measure(el, entry);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function entryFor(el: HTMLElement): Entry {
|
|
135
|
+
const existing = entries.get(el);
|
|
136
|
+
if (existing !== undefined) return existing;
|
|
137
|
+
const entry: Entry = {
|
|
138
|
+
view: DOCK_VIEWPORT_UNMEASURED,
|
|
139
|
+
listeners: new Set(),
|
|
140
|
+
raf: null,
|
|
141
|
+
detach: () => {},
|
|
142
|
+
};
|
|
143
|
+
const onChange = (): void => schedule(el, entry);
|
|
144
|
+
// PASSIVE: this listener never calls `preventDefault`, and saying so keeps it
|
|
145
|
+
// off the critical path of the dock's own wheel handling, which is
|
|
146
|
+
// deliberately non-passive (see TimelineDock's shift-pan).
|
|
147
|
+
el.addEventListener("scroll", onChange, { passive: true });
|
|
148
|
+
// A resize changes the visible range without a scroll event: collapsing the
|
|
149
|
+
// inspector widens the dock and uncovers bars nothing scrolled to.
|
|
150
|
+
const ro = typeof ResizeObserver === "function" ? new ResizeObserver(onChange) : null;
|
|
151
|
+
ro?.observe(el);
|
|
152
|
+
entry.detach = () => {
|
|
153
|
+
el.removeEventListener("scroll", onChange);
|
|
154
|
+
ro?.disconnect();
|
|
155
|
+
if (entry.raf !== null && typeof cancelAnimationFrame === "function") cancelAnimationFrame(entry.raf);
|
|
156
|
+
entry.raf = null;
|
|
157
|
+
entries.delete(el);
|
|
158
|
+
};
|
|
159
|
+
entries.set(el, entry);
|
|
160
|
+
// Measured once up front, so the first subscriber does not have to wait for a
|
|
161
|
+
// scroll that may never come.
|
|
162
|
+
entry.view = quantise(el);
|
|
163
|
+
return entry;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** The scroller a dock element lives in, or null when it is not in one. */
|
|
167
|
+
export function dockScrollerOf(el: Element | null): HTMLElement | null {
|
|
168
|
+
if (el === null) return null;
|
|
169
|
+
try {
|
|
170
|
+
return el.closest<HTMLElement>(DOCK_SCROLLER_SELECTOR);
|
|
171
|
+
} catch {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* The current snapshot. Stable by identity between changes.
|
|
178
|
+
*
|
|
179
|
+
* A PURE READ: it never creates an entry and never attaches a listener, because
|
|
180
|
+
* `useSyncExternalStore` calls its getter during render and a getter with a
|
|
181
|
+
* side effect is a listener attached from a render React may throw away.
|
|
182
|
+
* Subscribing is what starts measuring, and React re-reads immediately after
|
|
183
|
+
* subscribing, so the first measurement is never missed.
|
|
184
|
+
*/
|
|
185
|
+
export function readDockViewport(el: HTMLElement | null): DockViewport {
|
|
186
|
+
if (el === null) return DOCK_VIEWPORT_UNMEASURED;
|
|
187
|
+
return entries.get(el)?.view ?? DOCK_VIEWPORT_UNMEASURED;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Subscribe to a scroller's quantised range. Returns the unsubscribe, which
|
|
191
|
+
* tears the listeners down with the last subscriber: this store holds a DOM
|
|
192
|
+
* node, so an entry that outlived its dock would pin a detached tree. */
|
|
193
|
+
export function subscribeDockViewport(el: HTMLElement | null, onChange: () => void): () => void {
|
|
194
|
+
if (el === null) return () => {};
|
|
195
|
+
const entry = entryFor(el);
|
|
196
|
+
entry.listeners.add(onChange);
|
|
197
|
+
return () => {
|
|
198
|
+
entry.listeners.delete(onChange);
|
|
199
|
+
if (entry.listeners.size === 0) entry.detach();
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Test hook, and the one way to drop an entry without a subscriber churn. */
|
|
204
|
+
export function resetDockViewport(el: HTMLElement | null): void {
|
|
205
|
+
if (el === null) return;
|
|
206
|
+
entries.get(el)?.detach();
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* The slice of a bar worth drawing, in BAR-LOCAL pixels, or null when the bar
|
|
211
|
+
* is far enough off screen that neither its pixels nor its tiles are worth
|
|
212
|
+
* asking for.
|
|
213
|
+
*
|
|
214
|
+
* ONE function for the whole rule, so "is this row visible" and "which part of
|
|
215
|
+
* it do I draw" cannot answer differently: the skip is exactly the case where
|
|
216
|
+
* the intersection is empty, rather than a second threshold beside it.
|
|
217
|
+
*
|
|
218
|
+
* @param barContentX The bar's left edge in CONTENT pixels (`labelWidth`
|
|
219
|
+
* plus its lane-local left), which the caller already
|
|
220
|
+
* computed to place the bar and must not recompute here.
|
|
221
|
+
*/
|
|
222
|
+
export function visibleSlice(
|
|
223
|
+
view: DockViewport,
|
|
224
|
+
barContentX: number,
|
|
225
|
+
barWidthPx: number,
|
|
226
|
+
): { fromPx: number; toPx: number } | null {
|
|
227
|
+
if (!(barWidthPx > 0) || !Number.isFinite(barContentX)) return null;
|
|
228
|
+
if (!(view.right > view.left)) {
|
|
229
|
+
return { fromPx: 0, toPx: Math.min(barWidthPx, DOCK_VIEWPORT_UNMEASURED_PX) };
|
|
230
|
+
}
|
|
231
|
+
const fromPx = Math.max(0, view.left - DOCK_VIEWPORT_MARGIN_PX - barContentX);
|
|
232
|
+
const toPx = Math.min(barWidthPx, view.right + DOCK_VIEWPORT_MARGIN_PX - barContentX);
|
|
233
|
+
return toPx > fromPx ? { fromPx, toPx } : null;
|
|
234
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// The way out of whatever the author is inside, over the canvas they are
|
|
2
|
+
// looking at.
|
|
3
|
+
//
|
|
4
|
+
// ── WHY ANOTHER ONE ─────────────────────────────────────────────────────
|
|
5
|
+
//
|
|
6
|
+
// The rail's back row and the header's shot crumb also climb a level, but the
|
|
7
|
+
// rail collapses under `[` and neither is over the picture. The author's eyes
|
|
8
|
+
// and pointer are on the canvas; the exit belongs there too. The timeline's
|
|
9
|
+
// corner names the place and is not a way out (TimelineDock.tsx).
|
|
10
|
+
//
|
|
11
|
+
// FIRST IN THE CANVAS BAR (CanvasBar.tsx), which is where a back control goes
|
|
12
|
+
// in every editor that has one, and it reads left to right with the rest of
|
|
13
|
+
// the bar: where am I, which pane, how that pane is drawn.
|
|
14
|
+
//
|
|
15
|
+
// ── ABSENT INSIDE AN ISOLATION ──────────────────────────────────────────
|
|
16
|
+
//
|
|
17
|
+
// There the canvas bar shows the isolation trail instead (IsolationTrail.tsx),
|
|
18
|
+
// which names every level and climbs to any of them. `backTarget` below still
|
|
19
|
+
// answers one level up for the rail's back row, which is the Escape ladder's
|
|
20
|
+
// rule (keymap.ts).
|
|
21
|
+
//
|
|
22
|
+
// ── AND IT NAMES NO KEY, BECAUSE ESCAPE IS NOT THIS CLICK ───────────────
|
|
23
|
+
//
|
|
24
|
+
// Never inside a component: an open definition is the Components workspace
|
|
25
|
+
// (`workspaceOf`), so the stage's bar (`StageBar` in CanvasBar.tsx) is not
|
|
26
|
+
// rendered there and Components brings its own way back.
|
|
27
|
+
//
|
|
28
|
+
// The shot title carries no hint, and that is a judgement rather than an
|
|
29
|
+
// oversight. A single Escape from an entered shot DOES run `leaveScene`
|
|
30
|
+
// — but only after the ladder's earlier rungs decline it, so with anything
|
|
31
|
+
// selected, the first press deselects and the shot stays open. This click
|
|
32
|
+
// never deselects. A hint that holds only when the canvas is empty is the same
|
|
33
|
+
// defect one sentence weaker.
|
|
34
|
+
//
|
|
35
|
+
// ABSENT IN THE PROJECT CONTEXT, not disabled. Same call the tool dock makes,
|
|
36
|
+
// for the same reason: at the top of the tree a back control points at nothing,
|
|
37
|
+
// and a permanently dead button is worse than no button.
|
|
38
|
+
import { parentOf } from "@dailies-studio/shot/doc";
|
|
39
|
+
import { CHEVRON_LEFT_ICON } from "../../icons.js";
|
|
40
|
+
import { useShotEditor, useStore } from "../../state/context.js";
|
|
41
|
+
import type { ShotEditorStore } from "../../state/store.js";
|
|
42
|
+
import type { ShotEditorState } from "../../state/types.js";
|
|
43
|
+
import { isSceneContext } from "../../state/viewMode.js";
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* ONE LEVEL UP, FROM WHEREVER THE AUTHOR IS: the destination's name and the
|
|
47
|
+
* verb that goes there. This chip and the rail's back row (FilePanel.tsx) both
|
|
48
|
+
* read it, so the two controls cannot disagree about where "back" is.
|
|
49
|
+
*
|
|
50
|
+
* `label` is null when the destination is the Project. Inside a definition
|
|
51
|
+
* (only the rail's back row reads that branch) it is the Components home.
|
|
52
|
+
* Isolation climbs with
|
|
53
|
+
* `exitIsolation`, the store's own one-level verb and the one Escape runs, so
|
|
54
|
+
* the parent is read the way that verb reads it: from `sceneId`'s scene.
|
|
55
|
+
*/
|
|
56
|
+
export function backTarget(s: ShotEditorState): {
|
|
57
|
+
label: string | null;
|
|
58
|
+
run: (store: ShotEditorStore) => void;
|
|
59
|
+
} | null {
|
|
60
|
+
if (s.doc === null) return null;
|
|
61
|
+
if (s.editingDef !== null) {
|
|
62
|
+
return { label: "All components", run: (store) => void store.openComponents() };
|
|
63
|
+
}
|
|
64
|
+
const scene = s.sceneId === null ? undefined : s.doc.scenesById[s.sceneId];
|
|
65
|
+
if (s.isolation !== null && scene?.layersById[s.isolation] !== undefined) {
|
|
66
|
+
const parent = parentOf(scene, s.isolation);
|
|
67
|
+
return {
|
|
68
|
+
label:
|
|
69
|
+
parent === null
|
|
70
|
+
? scene.name
|
|
71
|
+
: (scene.layersById[parent]?.label ?? parent),
|
|
72
|
+
run: (store) => store.exitIsolation(),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (s.enteredScene === null) return null;
|
|
76
|
+
// `leaveScene` already clears the isolation, the selection and the armed
|
|
77
|
+
// tool (see twoContexts.test.ts), so there is no exit chain to repeat here.
|
|
78
|
+
return { label: null, run: (store) => store.leaveScene() };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function BackChip() {
|
|
82
|
+
const store = useStore();
|
|
83
|
+
const open = useShotEditor((s) => s.name !== null);
|
|
84
|
+
// The project context, which is where this chip has nothing to point at.
|
|
85
|
+
// `isSceneContext` also asks `doc !== null`, which costs nothing here: the
|
|
86
|
+
// store writes `name` and `doc` together, so `open` above already said it.
|
|
87
|
+
const inScene = useShotEditor(isSceneContext);
|
|
88
|
+
const isolated = useShotEditor(
|
|
89
|
+
(s) => s.editingDef === null && s.isolation !== null,
|
|
90
|
+
);
|
|
91
|
+
// INSIDE AN ISOLATION THE TRAIL IS THE WAY OUT (IsolationTrail.tsx): it
|
|
92
|
+
// names every level and climbs to any of them, so a one-level chip beside
|
|
93
|
+
// it would be a second, shorter copy.
|
|
94
|
+
if (!open || inScene || isolated) return null;
|
|
95
|
+
|
|
96
|
+
const label = "Shots";
|
|
97
|
+
const title = "Back to all shots, to arrange the project";
|
|
98
|
+
|
|
99
|
+
// A plain 28px control in the docked canvas bar. It used to sit in an island
|
|
100
|
+
// wrapper of its own (`.back-bar`), which went with the floating panels.
|
|
101
|
+
return (
|
|
102
|
+
<button
|
|
103
|
+
type="button"
|
|
104
|
+
className="back-chip"
|
|
105
|
+
data-testid="strip-back"
|
|
106
|
+
title={title}
|
|
107
|
+
aria-label={title}
|
|
108
|
+
onClick={() => backTarget(store.getState())?.run(store)}
|
|
109
|
+
>
|
|
110
|
+
<span className="tool-icon" aria-hidden="true">
|
|
111
|
+
{CHEVRON_LEFT_ICON}
|
|
112
|
+
</span>
|
|
113
|
+
<span className="back-chip-label">{label}</span>
|
|
114
|
+
</button>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
@@ -1,70 +1,139 @@
|
|
|
1
|
-
// The camera verbs
|
|
1
|
+
// The camera verbs, as ONE menu button over the stage.
|
|
2
2
|
//
|
|
3
|
-
// ── WHY
|
|
3
|
+
// ── WHY A MENU WITH WORDS, AND NOT THREE GLYPHS ─────────────────────────
|
|
4
4
|
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
5
|
+
// This bar was three unlabelled icon buttons (a camera, a link, a fit) whose
|
|
6
|
+
// sentences lived in `title` tooltips, and the founder's verdict was "I don't
|
|
7
|
+
// know what these options are, when they apply, or whether they are good". The
|
|
8
|
+
// words had been cut for 310px of control-strip budget. A menu button costs
|
|
9
|
+
// one control's width, keeps every word on a row an author can read, and has a
|
|
10
|
+
// CAPTION: the one sentence that names the two cameras, which nothing on
|
|
11
|
+
// screen had ever said.
|
|
11
12
|
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
13
|
+
// THE WORD "Camera" IS THE GROUP'S, not the trigger's. The canvas bar labels
|
|
14
|
+
// each pane's group with the pane's own word (CanvasBar.tsx), right before
|
|
15
|
+
// this button, so the trigger is a glyph square like the shading one beside
|
|
16
|
+
// the "3D" word. A word on both would read "Camera Camera".
|
|
15
17
|
//
|
|
16
|
-
//
|
|
18
|
+
// Every word comes from `state/cameraVerbs.ts`, which the View menu and the
|
|
19
|
+
// shortcut manual read too. This file decides only what is disabled and why.
|
|
17
20
|
//
|
|
18
|
-
//
|
|
19
|
-
// says the opposite of what it does — nothing is locked, the camera is being
|
|
20
|
-
// moved by every orbit — and that it is easy to forget it is on. So it is named
|
|
21
|
-
// for what happens, it is `aria-pressed`, and the affordance that stops it
|
|
22
|
-
// being forgotten is drawn INSIDE the pane (see Overlays' FollowBadge). A
|
|
23
|
-
// checkbox in a panel nobody is looking at while they orbit is how the Blender
|
|
24
|
-
// version gets forgotten.
|
|
21
|
+
// ── SHOWN WHEREVER THE CAMERA PANE IS ───────────────────────────────────
|
|
25
22
|
//
|
|
26
|
-
//
|
|
23
|
+
// CanvasBar mounts it in the camera pane's group, so it is on screen in the
|
|
24
|
+
// Camera and Split layouts, which is where a document OPENS and where Pilot
|
|
25
|
+
// does its work. In the 3D layout the camera pane is not on screen and
|
|
26
|
+
// neither is this menu: its verbs are in the View menu, "Move camera to
|
|
27
|
+
// view" included. Hidden inside a component definition (no shot camera) and
|
|
28
|
+
// under Video (no furniture on a finished picture).
|
|
29
|
+
//
|
|
30
|
+
// The menu portals through `Popover`, exactly as `ShadingBar` does and for its
|
|
31
|
+
// reasons: it closes on an outside press and on Escape, flips when the window
|
|
32
|
+
// is short, and clamps on-screen.
|
|
33
|
+
import { useRef, useState } from "react";
|
|
34
|
+
import { Popover } from "@dailies-studio/ui";
|
|
27
35
|
import { useShotEditor, useStore } from "../../state/context.js";
|
|
36
|
+
import { inDefinition, videoView } from "../../state/viewMode.js";
|
|
37
|
+
import { shortcutHint } from "../../state/shortcuts.js";
|
|
38
|
+
import { CAMERA_VERB_KEYS, TWO_CAMERAS, cameraVerb } from "../../state/cameraVerbs.js";
|
|
39
|
+
import type { CameraVerb } from "../../state/cameraVerbs.js";
|
|
40
|
+
import { CAMERA_ICON } from "../../icons.js";
|
|
28
41
|
|
|
29
42
|
export function CameraViewBar() {
|
|
30
43
|
const store = useStore();
|
|
31
|
-
const mode = useShotEditor((s) => s.stageLayout.mode);
|
|
32
|
-
const follows = useShotEditor((s) => s.cameraFollowsView);
|
|
33
|
-
const hasSelection = useShotEditor((s) => s.selection.length > 0);
|
|
34
44
|
const open = useShotEditor((s) => s.name !== null);
|
|
35
|
-
|
|
45
|
+
const pilot = useShotEditor((s) => s.pilot);
|
|
46
|
+
const driver = useShotEditor((s) => s.cameraDriver);
|
|
47
|
+
const hasSelection = useShotEditor((s) => s.selection.length > 0);
|
|
48
|
+
const inDef = useShotEditor(inDefinition);
|
|
49
|
+
const watching = useShotEditor(videoView);
|
|
50
|
+
const inShot = useShotEditor((s) => s.enteredScene !== null);
|
|
51
|
+
const anchor = useRef<HTMLButtonElement>(null);
|
|
52
|
+
const [menuOpen, setMenuOpen] = useState(false);
|
|
53
|
+
if (!open || inDef || watching) {
|
|
54
|
+
// Returning null is not an unmount: a menu left open when the gate closed
|
|
55
|
+
// would come back open. ShadingBar's own note.
|
|
56
|
+
if (menuOpen) setMenuOpen(false);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const driven =
|
|
61
|
+
driver === null
|
|
62
|
+
? null
|
|
63
|
+
: `The camera is driven by ${driver}, so the shot camera has no effect while it is.`;
|
|
64
|
+
|
|
65
|
+
/** Why a verb cannot run right now, or null when it can. */
|
|
66
|
+
const refusal = (key: CameraVerb): string | null => {
|
|
67
|
+
if (key === "addMove") return inShot ? null : "Open a shot to add a move to it.";
|
|
68
|
+
if (driven !== null) return driven;
|
|
69
|
+
if (key === "frameSelection" && !hasSelection) return "Select a layer first.";
|
|
70
|
+
return null;
|
|
71
|
+
};
|
|
72
|
+
const run = (key: CameraVerb): void => {
|
|
73
|
+
setMenuOpen(false);
|
|
74
|
+
switch (key) {
|
|
75
|
+
case "pilot":
|
|
76
|
+
store.setPilot(!pilot);
|
|
77
|
+
return;
|
|
78
|
+
case "toView":
|
|
79
|
+
store.alignCameraToWorkingView();
|
|
80
|
+
return;
|
|
81
|
+
case "frameSelection":
|
|
82
|
+
store.fitCameraToSelection();
|
|
83
|
+
return;
|
|
84
|
+
case "addMove":
|
|
85
|
+
store.addShotAtPlayhead();
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
36
89
|
|
|
37
90
|
return (
|
|
38
91
|
<div className="camera-view-bar" role="group" aria-label="Camera" data-testid="camera-view-bar">
|
|
39
92
|
<button
|
|
93
|
+
ref={anchor}
|
|
40
94
|
type="button"
|
|
41
|
-
className="
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
<button
|
|
48
|
-
type="button"
|
|
49
|
-
className={`btn${follows ? " is-armed" : ""}`}
|
|
50
|
-
aria-pressed={follows}
|
|
51
|
-
title="Keep the shot camera pointed where this view looks, while you orbit"
|
|
52
|
-
onClick={() => store.setCameraFollowsView(!follows)}
|
|
53
|
-
>
|
|
54
|
-
Follow view
|
|
55
|
-
</button>
|
|
56
|
-
<button
|
|
57
|
-
type="button"
|
|
58
|
-
className="btn"
|
|
59
|
-
// Disabled rather than refusing, because "nothing is selected" is
|
|
60
|
-
// visible on screen — a refusal message is for the cases the author
|
|
61
|
-
// cannot see, like a shot painting over the value they just wrote.
|
|
62
|
-
disabled={!hasSelection}
|
|
63
|
-
title="Frame the selected layers"
|
|
64
|
-
onClick={() => store.fitCameraToSelection()}
|
|
95
|
+
className={`tool-button has-caret${pilot ? " is-armed" : ""}`}
|
|
96
|
+
aria-haspopup="menu"
|
|
97
|
+
aria-expanded={menuOpen}
|
|
98
|
+
aria-label="Camera"
|
|
99
|
+
title={TWO_CAMERAS}
|
|
100
|
+
onClick={() => setMenuOpen((v) => !v)}
|
|
65
101
|
>
|
|
66
|
-
|
|
102
|
+
<span className="tool-icon" aria-hidden="true">
|
|
103
|
+
{CAMERA_ICON}
|
|
104
|
+
</span>
|
|
105
|
+
<span className="tool-caret" aria-hidden="true" />
|
|
67
106
|
</button>
|
|
107
|
+
<Popover open={menuOpen} anchor={anchor} onClose={() => setMenuOpen(false)}>
|
|
108
|
+
<div className="shading-menu camera-menu" role="menu" data-testid="camera-menu">
|
|
109
|
+
<div className="camera-menu-caption" role="presentation">
|
|
110
|
+
{TWO_CAMERAS}
|
|
111
|
+
</div>
|
|
112
|
+
{CAMERA_VERB_KEYS.map((key) => {
|
|
113
|
+
const spec = cameraVerb(key);
|
|
114
|
+
const why = refusal(key);
|
|
115
|
+
const armed = key === "pilot" && pilot;
|
|
116
|
+
return (
|
|
117
|
+
// THE SENTENCE HANGS ON THE WRAPPER: a disabled button fires no
|
|
118
|
+
// pointer events, so its own `title` could never open in the one
|
|
119
|
+
// state that needs explaining.
|
|
120
|
+
<span key={key} style={{ display: "flex" }} title={why ?? spec.sentence}>
|
|
121
|
+
<button
|
|
122
|
+
type="button"
|
|
123
|
+
role={key === "pilot" ? "menuitemcheckbox" : "menuitem"}
|
|
124
|
+
{...(key === "pilot" ? { "aria-checked": pilot } : {})}
|
|
125
|
+
className={`tool-button${armed ? " is-armed" : ""}`}
|
|
126
|
+
disabled={why !== null}
|
|
127
|
+
onClick={() => run(key)}
|
|
128
|
+
>
|
|
129
|
+
{spec.label}
|
|
130
|
+
<span className="camera-menu-key">{shortcutHint(spec.menuId).trim()}</span>
|
|
131
|
+
</button>
|
|
132
|
+
</span>
|
|
133
|
+
);
|
|
134
|
+
})}
|
|
135
|
+
</div>
|
|
136
|
+
</Popover>
|
|
68
137
|
</div>
|
|
69
138
|
);
|
|
70
139
|
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// The canvas bar: the docked row over the canvas cell (`.control-strip`).
|
|
2
|
+
//
|
|
3
|
+
// Left to right: the way out (BackChip, or the IsolationTrail inside an
|
|
4
|
+
// isolation), one group per pane on screen, then
|
|
5
|
+
// the stage's badges as the spring and the zoom, pushed right, and last the
|
|
6
|
+
// panel toggles, which every workspace keeps.
|
|
7
|
+
// It names the PLACE and how each pane is drawn. The docked footer under the
|
|
8
|
+
// canvas carries the tools and the view track (ToolFooter.tsx).
|
|
9
|
+
//
|
|
10
|
+
// ── ONE GROUP PER PANE, OVER ITS PANE ───────────────────────────────────
|
|
11
|
+
//
|
|
12
|
+
// Each group starts with the pane's word from `LAYOUT_SPECS` and holds that
|
|
13
|
+
// pane's controls. In the split the camera group starts AT THE SEAM, so each
|
|
14
|
+
// group sits over the pane it acts on, unless that would clip a control. The
|
|
15
|
+
// CSS does the placing (`.canvas-bar-lead.is-split` in styles/canvas.css) from
|
|
16
|
+
// `--split-x`, which App writes beside `--rail-w`, so this renders no geometry
|
|
17
|
+
// of its own.
|
|
18
|
+
//
|
|
19
|
+
// The pane that zoom and fit address (`activePane`) has its word in accent
|
|
20
|
+
// ink, so in a split the bar says which half the zoom buttons will move.
|
|
21
|
+
//
|
|
22
|
+
// ── THE VIEW TRACK IS IN THE FOOTER ─────────────────────────────────────
|
|
23
|
+
//
|
|
24
|
+
// Clean view and 3D | Split | Camera (ViewModeBar) answer for the whole
|
|
25
|
+
// canvas, not for one pane, so they never sat inside a pane's group. They
|
|
26
|
+
// rode this bar right of the spring until the Paper redesign moved them to
|
|
27
|
+
// the right half of the tool footer, which is where Paper draws them. What
|
|
28
|
+
// that freed is why this bar's give order is short now (canvas.css).
|
|
29
|
+
//
|
|
30
|
+
// ── WHAT IS ON SCREEN, NOT WHAT IS CHOSEN ───────────────────────────────
|
|
31
|
+
//
|
|
32
|
+
// Video draws the camera pane over any layout without writing it
|
|
33
|
+
// (`shownLayoutMode`). The groups follow the picture.
|
|
34
|
+
//
|
|
35
|
+
// ── NEVER EMPTY, NEVER A NEW HEIGHT ─────────────────────────────────────
|
|
36
|
+
//
|
|
37
|
+
// Zoom and the badges are here at every level and in Video, and the bar is
|
|
38
|
+
// always one `--panel-head-h` row, so the canvas cell under it never moves.
|
|
39
|
+
import type { ReactNode } from "react";
|
|
40
|
+
import { useShotEditor } from "../../state/context.js";
|
|
41
|
+
import { layoutSpec } from "../../state/layouts.js";
|
|
42
|
+
import { shownLayoutMode } from "../../state/viewMode.js";
|
|
43
|
+
import type { StagePane } from "../../stage/panes.js";
|
|
44
|
+
import { StageBadges } from "../header/StatusBadges.js";
|
|
45
|
+
import { ZoomControls } from "../header/ZoomControls.js";
|
|
46
|
+
import { BackChip } from "./BackChip.js";
|
|
47
|
+
import { CameraViewBar } from "./CameraViewBar.js";
|
|
48
|
+
import { IsolationTrail } from "./IsolationTrail.js";
|
|
49
|
+
import { ShadingBar } from "./ShadingBar.js";
|
|
50
|
+
import { PanelToggles } from "./PanelToggles.js";
|
|
51
|
+
import { WorkPlaneBar } from "./WorkPlaneBar.js";
|
|
52
|
+
import { CellSlot } from "../stage/cellSlots.js";
|
|
53
|
+
import { workspaceOf } from "../../state/workspace.js";
|
|
54
|
+
|
|
55
|
+
export function CanvasBar() {
|
|
56
|
+
// A MODE BRINGS ITS OWN BAR: the way back and its own verbs, portalled into
|
|
57
|
+
// the slot below (cellSlots.tsx). The stage's panes and zoom mean nothing
|
|
58
|
+
// over a board, a graph or a code editor.
|
|
59
|
+
const stage = useShotEditor((s) => workspaceOf(s) === "stage");
|
|
60
|
+
return (
|
|
61
|
+
<>
|
|
62
|
+
<CellSlot name="bar" className="canvas-bar-mode" />
|
|
63
|
+
{stage ? <StageBar /> : null}
|
|
64
|
+
{/* Last in every workspace: a mode's bar is its own, the panels are the
|
|
65
|
+
window's. */}
|
|
66
|
+
<PanelToggles />
|
|
67
|
+
</>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function StageBar() {
|
|
72
|
+
const open = useShotEditor((s) => s.name !== null);
|
|
73
|
+
const layout = useShotEditor(shownLayoutMode);
|
|
74
|
+
const activePane = useShotEditor((s) => s.activePane);
|
|
75
|
+
// No document, no panes: the bar keeps its row and holds nothing to act on.
|
|
76
|
+
const working = open && layout !== "camera";
|
|
77
|
+
const camera = open && layout !== "working";
|
|
78
|
+
const split = working && camera;
|
|
79
|
+
return (
|
|
80
|
+
<>
|
|
81
|
+
<div className={`canvas-bar-lead${split ? " is-split" : ""}`}>
|
|
82
|
+
<BackChip />
|
|
83
|
+
<IsolationTrail />
|
|
84
|
+
{working ? (
|
|
85
|
+
<PaneGroup pane="working" active={activePane === "working"}>
|
|
86
|
+
<ShadingBar />
|
|
87
|
+
<WorkPlaneBar />
|
|
88
|
+
</PaneGroup>
|
|
89
|
+
) : null}
|
|
90
|
+
</div>
|
|
91
|
+
{camera ? (
|
|
92
|
+
<PaneGroup pane="camera" active={activePane === "camera"}>
|
|
93
|
+
<CameraViewBar />
|
|
94
|
+
</PaneGroup>
|
|
95
|
+
) : null}
|
|
96
|
+
{/* The badges are the bar's spring: they push the zoom to the end and
|
|
97
|
+
are the first thing to give when the bar runs short (canvas.css). */}
|
|
98
|
+
<StageBadges />
|
|
99
|
+
<ZoomControls />
|
|
100
|
+
</>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function PaneGroup(props: { pane: StagePane; active: boolean; children: ReactNode }) {
|
|
105
|
+
const word = layoutSpec(props.pane).label;
|
|
106
|
+
return (
|
|
107
|
+
<div
|
|
108
|
+
className={`canvas-pane${props.active ? " is-active" : ""}`}
|
|
109
|
+
role="group"
|
|
110
|
+
aria-label={`${word} pane`}
|
|
111
|
+
data-testid={`canvas-pane-${props.pane}`}
|
|
112
|
+
>
|
|
113
|
+
<span className="canvas-pane-word">{word}</span>
|
|
114
|
+
{props.children}
|
|
115
|
+
</div>
|
|
116
|
+
);
|
|
117
|
+
}
|