@dailies-studio/shot-editor 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.vite/manifest.json +382 -0
- package/dist/assets/AgentPanel-CQejLbiX.js +2 -0
- package/dist/assets/AgentPanel-DJEphah0.css +1 -0
- package/dist/assets/AgentQuestionDialog-BqkD0kXF.js +1 -0
- package/dist/assets/CodeStudio-Dv4ihHpz.js +23 -0
- package/dist/assets/CommentsPanel-B4yhhT1T.js +1 -0
- package/dist/assets/ComponentLibraryRail-Ch_dsAru.js +1 -0
- package/dist/assets/ComponentStage-BRhRotyH.js +1 -0
- package/dist/assets/CurvePane-BjFVabKX.css +1 -0
- package/dist/assets/CurvePane-R22WzllJ.js +1 -0
- package/dist/assets/ExportDialog-CWLQWTmG.js +2 -0
- package/dist/assets/FontPicker-DWKaYL7F.js +1 -0
- package/dist/assets/FontPicker-v0IOIArM.css +1 -0
- package/dist/assets/GenerateDrawer-Bh3SqDKt.js +2 -0
- package/dist/assets/InsertDrawer-Ce1fod1L.js +1 -0
- package/dist/assets/LibraryBrowser-D9sEGz5w.js +2 -0
- package/dist/assets/MakeItYoursSection-CMO4319_.js +2 -0
- package/dist/assets/MaterialGraphCanvas-BFzl4wWR.js +23 -0
- package/dist/assets/MaterialGraphCanvas-BIUD59ik.css +1 -0
- package/dist/assets/MaterialWorkspace-CqTWqaRA.js +14 -0
- package/dist/assets/PreferencesDialog-CBf9_OoD.js +1 -0
- package/dist/assets/PresetsSection-CUEaoCVU.js +1 -0
- package/dist/assets/SoundHistory-BeS8EW1h.js +1 -0
- package/dist/assets/SoundMode-BmcZru3i.js +3 -0
- package/dist/assets/StockSounds-DdAUGRYK.js +1 -0
- package/dist/assets/TemplateFieldSection-CaeXpLFo.js +1 -0
- package/dist/assets/VersionHistoryPanel-CxG6XhNP.js +1 -0
- package/dist/assets/board-B88EPuLO.css +1 -0
- package/dist/assets/board-CL-he7SL.js +3 -0
- package/dist/assets/components-0oOSPN0l.js +1 -0
- package/dist/assets/googleFontRanges-gp32wJMo.js +1 -0
- package/dist/assets/googleFonts-BNon6M7X.js +1 -0
- package/dist/assets/iconsAndAliases-DL7gXP5j.js +1863 -0
- package/dist/assets/index-4F5NQmI8.css +1 -0
- package/dist/assets/index-BGHQGV5z.js +117 -0
- package/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/dist/assets/libraryModel-CpIwfFha.js +1 -0
- package/dist/assets/lucide-6iK64tTB.js +9319 -0
- package/dist/assets/motionStamp-CH-Hew4E.js +1 -0
- package/dist/assets/takeMeasure-Tdg0TxEi.js +1 -0
- package/dist/assets/webmcp-BsB9gxWC.js +2 -0
- package/dist/index.html +34 -3
- package/package.json +12 -9
- package/src/addFamilies.ts +66 -0
- package/src/api.ts +1631 -55
- package/src/app/App.tsx +977 -198
- package/src/app/lazyPanel.tsx +137 -0
- package/src/app/lazySurfaces.tsx +203 -0
- package/src/audio/index.tsx +41 -10
- package/src/audio/meter.ts +485 -0
- package/src/audio/meterMath.ts +273 -0
- package/src/audio/meterStore.ts +155 -0
- package/src/audio/offlineMix.ts +289 -0
- package/src/audio/preview.ts +928 -118
- package/src/bridge.ts +15 -5
- package/src/bridgeTypes.ts +1611 -36
- package/src/channels.ts +17 -32
- package/src/fonts/googleFonts.ts +4 -0
- package/src/hostApi.ts +174 -0
- package/src/icons.tsx +770 -0
- package/src/layerWords.ts +61 -0
- package/src/main.tsx +56 -4
- package/src/panels/ResizeHandle.tsx +33 -43
- package/src/panels/agent/AgentDots.tsx +36 -0
- package/src/panels/agent/AgentPanel.tsx +119 -43
- package/src/panels/agent/AgentQuestionDialog.tsx +202 -0
- package/src/panels/agent/agent.css +113 -97
- package/src/panels/agent/agentGate.ts +35 -0
- package/src/panels/code/CodeDefaultsPanel.tsx +345 -0
- package/src/panels/code/CodeStudio.tsx +373 -0
- package/src/panels/code/FileTree.tsx +265 -0
- package/src/panels/code/codeBufferGuard.ts +52 -0
- package/src/panels/code/codeParts.tsx +321 -0
- package/src/panels/comments/CommentComposer.tsx +239 -0
- package/src/panels/comments/CommentThread.tsx +346 -0
- package/src/panels/comments/CommentsPanel.tsx +336 -0
- package/src/panels/comments/InboxButton.tsx +131 -0
- package/src/panels/comments/MentionMenu.tsx +98 -0
- package/src/panels/comments/parts.tsx +171 -0
- package/src/panels/export/ExportDialog.tsx +919 -96
- package/src/panels/export/ExportScrim.tsx +57 -0
- package/src/panels/files/AssetsTab.tsx +656 -0
- package/src/panels/files/FilePanel.tsx +1466 -216
- package/src/panels/files/LayerMarks.tsx +40 -0
- package/src/panels/files/LeftColumn.tsx +37 -0
- package/src/panels/files/McpSection.tsx +690 -47
- package/src/panels/files/MissingAssetsNotice.tsx +60 -0
- package/src/panels/files/assetDrag.ts +79 -0
- package/src/panels/files/resourceParts.tsx +255 -0
- package/src/panels/fonts/FontPicker.tsx +762 -0
- package/src/panels/fonts/faceCache.ts +207 -0
- package/src/panels/fonts/fontPicker.css +271 -0
- package/src/panels/fonts/pickerModel.ts +265 -0
- package/src/panels/fonts/pickerRequest.ts +46 -0
- package/src/panels/header/AccountAvatar.tsx +76 -0
- package/src/panels/header/AgentChip.tsx +77 -0
- package/src/panels/header/AppMenu.tsx +213 -0
- package/src/panels/header/Header.tsx +564 -84
- package/src/panels/header/HistoryControls.tsx +31 -5
- package/src/panels/header/PresenceStack.tsx +329 -0
- package/src/panels/header/ProjectTitle.tsx +232 -0
- package/src/panels/header/ShareButton.tsx +59 -0
- package/src/panels/header/StatusBadges.tsx +358 -27
- package/src/panels/header/SyncDeletedDialog.tsx +88 -0
- package/src/panels/header/ZoomControls.tsx +46 -15
- package/src/panels/header/agentHandoff.ts +52 -0
- package/src/panels/header/leave.ts +26 -0
- package/src/panels/help/HelpSection.tsx +189 -163
- package/src/panels/insert/InsertDrawer.tsx +723 -0
- package/src/panels/insert/iconSet.ts +109 -0
- package/src/panels/inspector/ExposeDot.tsx +117 -0
- package/src/panels/inspector/FramingRows.tsx +314 -44
- package/src/panels/inspector/InspectorFooter.tsx +91 -0
- package/src/panels/inspector/InspectorHead.tsx +425 -0
- package/src/panels/inspector/InspectorPanel.tsx +408 -124
- package/src/panels/inspector/KeyframeDot.tsx +373 -0
- package/src/panels/inspector/KeyframeSection.tsx +257 -0
- package/src/panels/inspector/MarkerSection.tsx +55 -11
- package/src/panels/inspector/ParamRow.tsx +84 -53
- package/src/panels/inspector/RightColumn.tsx +152 -0
- package/src/panels/inspector/TimeSection.tsx +866 -0
- package/src/panels/inspector/animate/EaseControl.tsx +244 -0
- package/src/panels/inspector/animate/PresetsSection.tsx +135 -0
- package/src/panels/inspector/animate/ShotSection.tsx +562 -48
- package/src/panels/inspector/design/AlignRow.tsx +95 -31
- package/src/panels/inspector/design/AppearanceSection.tsx +118 -9
- package/src/panels/inspector/design/ClonerSection.tsx +323 -62
- package/src/panels/inspector/design/ComponentSection.tsx +50 -43
- package/src/panels/inspector/design/CornerSection.tsx +45 -14
- package/src/panels/inspector/design/DefinitionProps.tsx +315 -0
- package/src/panels/inspector/design/DefinitionSection.tsx +136 -0
- package/src/panels/inspector/design/DeviceSection.tsx +210 -0
- package/src/panels/inspector/design/FillSection.tsx +83 -51
- package/src/panels/inspector/design/FontField.tsx +196 -0
- package/src/panels/inspector/design/HtmlSection.tsx +23 -12
- package/src/panels/inspector/design/InstanceSection.tsx +373 -154
- package/src/panels/inspector/design/MaskSection.tsx +84 -0
- package/src/panels/inspector/design/MaterialSection.tsx +177 -0
- package/src/panels/inspector/design/MediaSection.tsx +273 -120
- package/src/panels/inspector/design/MeshSection.tsx +910 -82
- package/src/panels/inspector/design/PaintRows.tsx +550 -0
- package/src/panels/inspector/design/PlateStyleSection.tsx +177 -0
- package/src/panels/inspector/design/ShadowSection.tsx +221 -48
- package/src/panels/inspector/design/SplineFollowSection.tsx +235 -0
- package/src/panels/inspector/design/StrokeSection.tsx +228 -43
- package/src/panels/inspector/design/TemplateFieldSection.tsx +57 -0
- package/src/panels/inspector/design/TransformSection.tsx +244 -50
- package/src/panels/inspector/design/TypographySection.tsx +679 -110
- package/src/panels/inspector/layout.ts +295 -0
- package/src/panels/inspector/propValue.ts +199 -0
- package/src/panels/inspector/scene/CameraSection.tsx +232 -22
- package/src/panels/inspector/scene/EffectsSection.tsx +722 -71
- package/src/panels/inspector/scene/MakeItYoursSection.tsx +180 -0
- package/src/panels/inspector/scene/SceneDocSection.tsx +191 -0
- package/src/panels/inspector/scene/SceneSection.tsx +219 -44
- package/src/panels/inspector/scene/TransitionSection.tsx +523 -72
- package/src/panels/inspector/storyboard/StoryboardReferenceSection.tsx +593 -0
- package/src/panels/inspector/storyboard/VideoStill.tsx +120 -0
- package/src/panels/inspector/timeModel.ts +190 -0
- package/src/panels/inspector/useDocWrite.ts +438 -146
- package/src/panels/library/LibraryBrowser.tsx +435 -0
- package/src/panels/library/LibraryGrid.tsx +190 -0
- package/src/panels/library/LibraryPreviewPanel.tsx +349 -0
- package/src/panels/library/LibraryRail.tsx +84 -0
- package/src/panels/library/LibrarySoundPanel.tsx +293 -0
- package/src/panels/library/SoundHistory.tsx +245 -0
- package/src/panels/library/StockSounds.tsx +390 -0
- package/src/panels/library/libraryModel.ts +147 -0
- package/src/panels/library/stockSearch.ts +77 -0
- package/src/panels/maskMarks.tsx +117 -0
- package/src/panels/material/MaterialCommentNode.tsx +54 -0
- package/src/panels/material/MaterialGraphCanvas.tsx +861 -0
- package/src/panels/material/MaterialGraphMenu.tsx +45 -0
- package/src/panels/material/MaterialGroupNode.tsx +56 -0
- package/src/panels/material/MaterialLibrary.tsx +253 -0
- package/src/panels/material/MaterialNode.tsx +453 -0
- package/src/panels/material/MaterialNodePalette.tsx +349 -0
- package/src/panels/material/MaterialParameterControls.tsx +525 -0
- package/src/panels/material/MaterialRerouteNode.tsx +42 -0
- package/src/panels/material/MaterialWorkspace.tsx +501 -0
- package/src/panels/material/NodePreview.tsx +70 -0
- package/src/panels/material/flowModel.ts +441 -0
- package/src/panels/material/parameterValues.ts +231 -0
- package/src/panels/material/previewScheduler.ts +235 -0
- package/src/panels/material/reactFlow.css +6 -0
- package/src/panels/material/socketGlyph.ts +85 -0
- package/src/panels/material/viewportMemory.ts +58 -0
- package/src/panels/preferences/PreferencesDialog.tsx +215 -0
- package/src/panels/share/ShareComponentDialog.tsx +247 -0
- package/src/panels/stage/StageMenu.tsx +35 -5
- package/src/panels/stage/StagePanel.tsx +338 -23
- package/src/panels/stage/cellSlots.tsx +70 -0
- package/src/panels/storyboard/BoardCanvas.tsx +739 -0
- package/src/panels/storyboard/BoardCursors.tsx +158 -0
- package/src/panels/storyboard/BoardDropDialog.tsx +161 -0
- package/src/panels/storyboard/BoardPlan.tsx +42 -0
- package/src/panels/storyboard/ComponentSheet.tsx +187 -0
- package/src/panels/storyboard/ComponentStage.tsx +235 -0
- package/src/panels/storyboard/MiniSketch.tsx +173 -0
- package/src/panels/storyboard/PartPicker.tsx +64 -0
- package/src/panels/storyboard/PlanPanel.tsx +617 -0
- package/src/panels/storyboard/ReferencePreview.tsx +217 -0
- package/src/panels/storyboard/ShotBrief.tsx +360 -0
- package/src/panels/storyboard/ShotCard.tsx +251 -0
- package/src/panels/storyboard/StagePreview.tsx +348 -0
- package/src/panels/storyboard/StillThumbs.tsx +162 -0
- package/src/panels/storyboard/StoryboardView.tsx +79 -0
- package/src/panels/storyboard/board.css +1065 -0
- package/src/panels/storyboard/board.ts +12 -0
- package/src/panels/storyboard/boardClipboard.ts +111 -0
- package/src/panels/storyboard/boardOrder.ts +68 -0
- package/src/panels/storyboard/boardRows.tsx +425 -0
- package/src/panels/storyboard/liveCap.ts +90 -0
- package/src/panels/storyboard/plan.css +295 -0
- package/src/panels/storyboard/posterCache.ts +133 -0
- package/src/panels/storyboard/shotboard/ComponentPicker.tsx +373 -0
- package/src/panels/storyboard/shotboard/ImagePicker.tsx +145 -0
- package/src/panels/storyboard/shotboard/ShotBoard.tsx +292 -0
- package/src/panels/storyboard/shotboard/SketchCanvas.tsx +443 -0
- package/src/panels/storyboard/shotboard/SketchInspector.tsx +471 -0
- package/src/panels/storyboard/shotboard/SketchItem.tsx +240 -0
- package/src/panels/storyboard/shotboard/SketchLayers.tsx +130 -0
- package/src/panels/storyboard/shotboard/SketchPlay.tsx +108 -0
- package/src/panels/storyboard/shotboard/SketchStrip.tsx +178 -0
- package/src/panels/storyboard/shotboard/SketchToolFooter.tsx +99 -0
- package/src/panels/storyboard/shotboard/sketch.css +1392 -0
- package/src/panels/storyboard/shotboard/sketchModel.ts +219 -0
- package/src/panels/storyboard/storyboardDrop.ts +164 -0
- package/src/panels/studio/ComponentLibraryRail.tsx +346 -0
- package/src/panels/studio/ComponentSidePanel.tsx +355 -0
- package/src/panels/studio/ComponentStudio.tsx +243 -0
- package/src/panels/studio/ComponentsHome.tsx +318 -0
- package/src/panels/studio/ComponentsPreview.tsx +179 -0
- package/src/panels/studio/EveryUse.tsx +244 -0
- package/src/panels/studio/ThisUsePanel.tsx +679 -0
- package/src/panels/studio/componentParts.tsx +341 -0
- package/src/panels/studio/components.ts +6 -0
- package/src/panels/studio/newCodeComponent.ts +66 -0
- package/src/panels/timeline/AudioFxButton.tsx +290 -0
- package/src/panels/timeline/AudioLane.tsx +674 -85
- package/src/panels/timeline/BlockRow.tsx +154 -85
- package/src/panels/timeline/ChannelRow.tsx +378 -129
- package/src/panels/timeline/CurvePane.tsx +508 -0
- package/src/panels/timeline/Filmstrip.tsx +361 -0
- package/src/panels/timeline/GenerateDrawer.tsx +407 -0
- package/src/panels/timeline/KeyStretch.tsx +115 -0
- package/src/panels/timeline/LifespanRow.tsx +730 -47
- package/src/panels/timeline/MarkerLane.tsx +46 -8
- package/src/panels/timeline/PlayButton.tsx +91 -0
- package/src/panels/timeline/PropertyPicker.tsx +254 -0
- package/src/panels/timeline/ShotRow.tsx +400 -0
- package/src/panels/timeline/SoundMode.tsx +1406 -0
- package/src/panels/timeline/TimelineDock.tsx +2338 -272
- package/src/panels/timeline/TransportVolume.tsx +163 -0
- package/src/panels/timeline/beatRuler.ts +74 -0
- package/src/panels/timeline/curveModel.ts +500 -0
- package/src/panels/timeline/duckUnderVoice.ts +68 -0
- package/src/panels/timeline/keyframeMenu.ts +138 -0
- package/src/panels/timeline/labelWidth.ts +49 -0
- package/src/panels/timeline/marquee.ts +155 -0
- package/src/panels/timeline/model.ts +1860 -199
- package/src/panels/timeline/snap.ts +125 -43
- package/src/panels/timeline/staggerPreview.tsx +92 -0
- package/src/panels/timeline/takeMeasure.ts +72 -0
- package/src/panels/timeline/useLive.ts +48 -0
- package/src/panels/timeline/viewport.ts +234 -0
- package/src/panels/toolbar/BackChip.tsx +116 -0
- package/src/panels/toolbar/CameraViewBar.tsx +118 -49
- package/src/panels/toolbar/CanvasBar.tsx +117 -0
- package/src/panels/toolbar/InsertButton.tsx +38 -0
- package/src/panels/toolbar/IsolationTrail.tsx +156 -0
- package/src/panels/toolbar/PanelToggles.tsx +86 -0
- package/src/panels/toolbar/ShadingBar.tsx +119 -26
- package/src/panels/toolbar/ToolDockBar.tsx +329 -80
- package/src/panels/toolbar/ToolFooter.tsx +102 -0
- package/src/panels/toolbar/ViewModeBar.tsx +247 -38
- package/src/panels/toolbar/WorkPlaneBar.tsx +94 -0
- package/src/panels/toolbar/useAddRows.tsx +326 -0
- package/src/panels/toolbar/useDismiss.ts +122 -0
- package/src/panels/versions/VersionHistoryPanel.tsx +274 -0
- package/src/panels/versions/versionModel.ts +103 -0
- package/src/panels/virtualWindow.ts +271 -0
- package/src/shotTokens.css +151 -60
- package/src/stage/FrustumGizmo.tsx +504 -84
- package/src/stage/GridPlane.tsx +225 -16
- package/src/stage/NodePreviews.tsx +628 -0
- package/src/stage/PerfHud.tsx +437 -47
- package/src/stage/SplitRenderer.tsx +816 -169
- package/src/stage/StageApp.tsx +4274 -486
- package/src/stage/StageRootBoundary.tsx +114 -0
- package/src/stage/StageTools.tsx +651 -151
- package/src/stage/TransformGizmo.tsx +696 -0
- package/src/stage/adaptivePresentation.ts +145 -0
- package/src/stage/bridgeClient.ts +2522 -41
- package/src/stage/exportWorker.ts +9 -0
- package/src/stage/feed.ts +305 -14
- package/src/stage/fontPreview.ts +176 -0
- package/src/stage/frustumGeometry.ts +319 -1
- package/src/stage/furnitureTokens.ts +33 -0
- package/src/stage/gizmoMath.ts +508 -16
- package/src/stage/gpuTimer.ts +276 -0
- package/src/stage/instanceModules.ts +194 -33
- package/src/stage/interactions/move.ts +24 -5
- package/src/stage/isolation.ts +291 -33
- package/src/stage/jsSelfProfiler.ts +325 -0
- package/src/stage/main.tsx +116 -16
- package/src/stage/modalState.ts +222 -23
- package/src/stage/moduleEpoch.ts +60 -0
- package/src/stage/motionPathStage.ts +279 -0
- package/src/stage/motionPreview.ts +74 -0
- package/src/stage/overlays/Overlays.tsx +1112 -159
- package/src/stage/overlays/Rulers.tsx +495 -0
- package/src/stage/panes.ts +159 -10
- package/src/stage/project.ts +68 -3
- package/src/stage/renderScaleController.ts +730 -0
- package/src/stage/rulers.ts +129 -0
- package/src/stage/sceneExtent.ts +75 -0
- package/src/stage/shading.ts +20 -0
- package/src/stage/splineStage.ts +632 -0
- package/src/stage/stagePerformanceTelemetry.ts +717 -0
- package/src/stage/stageTheme.ts +36 -0
- package/src/stage/stageWarmup.ts +11 -0
- package/src/stage/textAutoSize.ts +124 -0
- package/src/stage/tier1Export.ts +641 -0
- package/src/stage/tokenColor.ts +14 -9
- package/src/stage/view.ts +56 -9
- package/src/stage/workingView.ts +277 -33
- package/src/state/accountPrefs.ts +261 -0
- package/src/state/agentActions.ts +173 -15
- package/src/state/agentActivity.ts +198 -0
- package/src/state/agentQuestion.ts +239 -0
- package/src/state/bootOpen.ts +42 -0
- package/src/state/cameraVerbs.ts +139 -0
- package/src/state/clientId.ts +6 -1
- package/src/state/comments.ts +1099 -0
- package/src/state/context.tsx +19 -1
- package/src/state/editorPrefs.ts +220 -0
- package/src/state/errorReports.ts +121 -0
- package/src/state/fieldWrite.ts +161 -0
- package/src/state/fontRecents.ts +70 -0
- package/src/state/fonts.ts +656 -0
- package/src/state/graphClipboard.ts +414 -0
- package/src/state/imageThumbs.ts +216 -0
- package/src/state/insert.ts +444 -67
- package/src/state/isolationTrail.ts +92 -0
- package/src/state/keyMoves.ts +134 -0
- package/src/state/keyframeClipboard.ts +177 -0
- package/src/state/keymap.ts +1153 -117
- package/src/state/layoutPrefs.ts +272 -0
- package/src/state/layouts.ts +205 -0
- package/src/state/measureMedia.ts +167 -0
- package/src/state/menu.ts +2341 -28
- package/src/state/motionStamp.ts +7 -0
- package/src/state/panelSize.ts +375 -5
- package/src/state/pendingChanges.ts +119 -0
- package/src/state/playHold.ts +78 -0
- package/src/state/playbackPreparation.ts +562 -0
- package/src/state/posterCapture.ts +242 -0
- package/src/state/presence.ts +254 -0
- package/src/state/projectComponents.ts +160 -0
- package/src/state/renderEvents.ts +298 -18
- package/src/state/runtimeErrors.ts +46 -0
- package/src/state/selection.ts +12 -0
- package/src/state/shortcuts.ts +1606 -0
- package/src/state/sketchGuide.ts +51 -0
- package/src/state/splineRefs.ts +87 -0
- package/src/state/stageHealth.ts +259 -0
- package/src/state/stageWatchdog.ts +47 -0
- package/src/state/stillStage.ts +330 -0
- package/src/state/stillThumbs.ts +474 -0
- package/src/state/store.ts +23591 -2982
- package/src/state/storyboardModel.ts +528 -0
- package/src/state/sync.ts +1257 -0
- package/src/state/syncCache.ts +159 -0
- package/src/state/triangles.ts +13 -0
- package/src/state/types.ts +1698 -55
- package/src/state/unsavedGuard.ts +49 -0
- package/src/state/viewMode.ts +200 -0
- package/src/state/webmcp.ts +1128 -0
- package/src/state/workspace.ts +1147 -0
- package/src/styles/assets-tab.css +304 -0
- package/src/styles/canvas.css +545 -0
- package/src/styles/comments.css +1435 -0
- package/src/styles/controls.css +162 -0
- package/src/styles/dialogs.css +390 -0
- package/src/styles/insert-drawer.css +586 -0
- package/src/styles/inspector-board.css +207 -0
- package/src/styles/inspector-object.css +486 -0
- package/src/styles/inspector-shot.css +252 -0
- package/src/styles/inspector.css +360 -0
- package/src/styles/left-column.css +1628 -0
- package/src/styles/library-preview.css +575 -0
- package/src/styles/library.css +512 -0
- package/src/styles/modes/code.css +569 -0
- package/src/styles/modes/component.css +1561 -0
- package/src/styles/modes/material.css +1201 -0
- package/src/styles/modes/storyboard.css +518 -0
- package/src/styles/share-component.css +49 -0
- package/src/styles/shell.css +653 -0
- package/src/styles/shot-brief.css +360 -0
- package/src/styles/states.css +76 -0
- package/src/styles/sync.css +380 -0
- package/src/styles/timeline-graph.css +384 -0
- package/src/styles/timeline-meters.css +89 -0
- package/src/styles/timeline-rows.css +1959 -0
- package/src/styles/timeline-sound.css +1003 -0
- package/src/styles/timeline.css +754 -0
- package/src/styles/tool-footer.css +525 -0
- package/src/styles/topbar.css +886 -0
- package/src/styles/versions.css +262 -0
- package/src/styles.css +72 -2921
- package/src/tools.ts +108 -23
- package/dist/assets/index-B7notge7.css +0 -1
- package/dist/assets/index-BBE6Ta9L.js +0 -52
- package/src/panels/files/icons.tsx +0 -118
- package/src/panels/header/DocSwitcher.tsx +0 -85
- package/src/panels/header/TransportBar.tsx +0 -86
- package/src/panels/inspector/animate/BlockTiming.tsx +0 -341
- package/src/panels/inspector/animate/DuringBlocks.tsx +0 -85
- package/src/panels/inspector/animate/InOutPresets.tsx +0 -57
- package/src/panels/inspector/animate/ResolvedChannels.tsx +0 -150
- package/src/panels/inspector/animate/useBlocks.ts +0 -43
- package/src/panels/material/MaterialPicker.tsx +0 -172
- package/src/panels/timeline/SceneStrip.tsx +0 -145
- package/src/panels/toolbar/AddMenu.tsx +0 -230
- package/src/stage/MoveGizmo.tsx +0 -411
|
@@ -1,43 +1,128 @@
|
|
|
1
1
|
// One camera move: what kind it is, how it eases, and where it starts and ends.
|
|
2
|
-
// The
|
|
2
|
+
// The inspector's focused-object slot whenever a bar on the CAMERA row is
|
|
3
|
+
// focused.
|
|
3
4
|
//
|
|
4
|
-
// ── WHY THIS PANEL
|
|
5
|
+
// ── WHY THIS PANEL WRITES STRAIGHT THROUGH AND THE SCENE'S CAMERA DOES NOT ─
|
|
5
6
|
//
|
|
6
7
|
// `scene.camera` is invisible from the first shot's start onwards, which is why
|
|
7
8
|
// CameraSection has to read effective, write base and refuse. A SHOT has no
|
|
8
9
|
// such hazard: `from` and `to` are exactly what `cameraAt` interpolates between
|
|
9
10
|
// at this shot's own frames, so writing either is visible the moment the
|
|
10
11
|
// playhead is inside the move. Two truths, one of them safe, and the difference
|
|
11
|
-
// is worth stating because the two panels
|
|
12
|
+
// is worth stating because the two panels draw the same rows (`FramingRows`)
|
|
13
|
+
// and are not the same write.
|
|
12
14
|
//
|
|
13
15
|
// The "set from current framing" buttons are the bridge back. They take what is
|
|
14
16
|
// on screen at the playhead (`cameraAt`, the same read every other surface
|
|
15
17
|
// makes) and write it into one end, so an author who framed a pose by dragging
|
|
16
18
|
// the frustum keeps it without retyping five numbers.
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
|
|
19
|
+
import { useMemo } from "react";
|
|
20
|
+
import {
|
|
21
|
+
Combobox,
|
|
22
|
+
IconMinus,
|
|
23
|
+
IconPlus,
|
|
24
|
+
InspectorRow,
|
|
25
|
+
InspectorSection,
|
|
26
|
+
NumberField,
|
|
27
|
+
SegmentedControl,
|
|
28
|
+
Toggle,
|
|
29
|
+
useLocked,
|
|
30
|
+
wordOptions,
|
|
31
|
+
} from "@dailies-studio/ui";
|
|
32
|
+
import { DEFAULT_SHOT_FPS, MAX_SHAKE_PX, MAX_SHAKE_ROLL_DEG, MAX_SPLINE_ANGLE_DEG, MIN_PATH_WAYPOINTS, MAX_TRACK_MARGIN, MAX_TRACK_SMOOTH_FRAMES, MIN_SHAKE_FREQ_HZ, SPLINE_DOMAINS, TRACK_FITS, containerFrame, isZoomRigBreak, steadyCameraAt, maxShakeFreqHz, meshPrimitiveOf } from "@dailies-studio/shot/doc";
|
|
33
|
+
import type { Framing, MoveShot, Scene3D, Shot, SplineDomain, SplineShot, TrackShot } from "@dailies-studio/shot/doc";
|
|
34
|
+
import type { ShotEditorStore } from "../../../state/store.js";
|
|
20
35
|
import { useShotEditor, useStore } from "../../../state/context.js";
|
|
21
|
-
import { CAMERA_ROW_ID } from "../../timeline/model.js";
|
|
22
36
|
import { FramingRows } from "../FramingRows.js";
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
37
|
+
import type { FramingScrub } from "../FramingRows.js";
|
|
38
|
+
import { useInspectorFrame } from "../useDocWrite.js";
|
|
39
|
+
import { EaseControl } from "./EaseControl.js";
|
|
40
|
+
import { cameraSplineSourceIsSafe } from "../../../state/splineRefs.js";
|
|
25
41
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
42
|
+
/**
|
|
43
|
+
* A DRAGGED field of this panel, as one undo entry: the panel scrub around
|
|
44
|
+
* the move verb it writes through. Every verb here (`setShotFraming`,
|
|
45
|
+
* `setShotWaypoint`, `setTrackShot`, `setShotShake`, `setSplineShot`) captures
|
|
46
|
+
* into an open panel scrub and edits once otherwise (store.ts), so a drag
|
|
47
|
+
* moves the camera on the stage under the pointer and a typed value is one
|
|
48
|
+
* plain edit, through the same write.
|
|
49
|
+
*/
|
|
50
|
+
function dragThrough<T>(store: ShotEditorStore, write: (value: T) => void) {
|
|
51
|
+
return {
|
|
52
|
+
onScrubStart: () => store.beginScrub(),
|
|
53
|
+
onScrub: write,
|
|
54
|
+
onScrubEnd: (commit: boolean) => store.endScrub(commit),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** A pose's fields as a `FramingScrub` over one move verb, `dragThrough`'s
|
|
59
|
+
* bracket in the shape `FramingRows` drives. */
|
|
60
|
+
function poseScrub(store: ShotEditorStore, write: (patch: Partial<Framing>) => void): FramingScrub {
|
|
61
|
+
return {
|
|
62
|
+
start: () => store.beginScrub(),
|
|
63
|
+
move: write,
|
|
64
|
+
end: (commit) => store.endScrub(commit),
|
|
65
|
+
commit: write,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// PATH IS IN THE CONTROL NOW. It used to be absent with the reason stated in
|
|
70
|
+
// the panel — "authored by an agent" — because converting a two-pose move into
|
|
71
|
+
// a path would have produced something this chrome had no editor for. It has
|
|
72
|
+
// one below, so the refusal was retired with the gap that justified it.
|
|
73
|
+
//
|
|
74
|
+
// SPLINE stays out: its poses are sampled off a scene layer's geometry, so
|
|
75
|
+
// there are no authored framings to carry into it. Its door is the explicit
|
|
76
|
+
// "Camera rail" combobox, which names the layer.
|
|
77
|
+
//
|
|
78
|
+
// `satisfies readonly Shot["kind"][]` is what keeps this a DELIBERATE subset
|
|
79
|
+
// rather than a stale one: it cannot list a kind the schema does not have, so
|
|
80
|
+
// renaming one in `types.ts` fails here instead of leaving a segment that
|
|
81
|
+
// writes a value nothing accepts. It deliberately does NOT check the other
|
|
82
|
+
// direction, because the two absences above are the point.
|
|
83
|
+
const KINDS = ["dolly", "zoom", "path"] as const satisfies readonly Shot["kind"][];
|
|
84
|
+
|
|
85
|
+
/** The segments, labelled by capitalising the kind: the schema's kind names are
|
|
86
|
+
* already the words the control shows, so a fourth offered kind needs no
|
|
87
|
+
* second edit to get a label. */
|
|
88
|
+
const KIND_OPTIONS = wordOptions(KINDS);
|
|
89
|
+
|
|
90
|
+
/** The track fits, capitalised the same way and off the schema's own list, so
|
|
91
|
+
* a third fit reaches this control without a second edit. */
|
|
92
|
+
const FIT_OPTIONS = wordOptions(TRACK_FITS);
|
|
30
93
|
|
|
31
94
|
export function ShotSection() {
|
|
32
95
|
const store = useStore();
|
|
33
96
|
const doc = useShotEditor((s) => s.doc);
|
|
34
|
-
const focused = useShotEditor((s) => s.
|
|
97
|
+
const focused = useShotEditor((s) => s.selectedMove);
|
|
35
98
|
const { sceneId, sceneFrame } = useInspectorFrame();
|
|
36
|
-
|
|
99
|
+
// A VIEWER'S PANEL LOCKS EVERY VERB THAT WRITES. `LockedControls` reaches
|
|
100
|
+
// the widgets; this panel's buttons are plain ones, so each reads the lock.
|
|
101
|
+
const locked = useLocked();
|
|
102
|
+
const scene = doc === null || sceneId === null ? undefined : doc.scenesById[sceneId];
|
|
103
|
+
const frameHeight = doc?.size.height;
|
|
104
|
+
// Memoised on the scene: the section repaints on every document identity
|
|
105
|
+
// change (once per rAF frame of any drag), and this walked every layer of the
|
|
106
|
+
// scene through `cameraSplineSourceIsSafe` each time. Above the early return
|
|
107
|
+
// because it is a hook.
|
|
108
|
+
const splineOptions = useMemo(
|
|
109
|
+
() =>
|
|
110
|
+
scene === undefined || frameHeight === undefined
|
|
111
|
+
? []
|
|
112
|
+
: Object.values(scene.layersById)
|
|
113
|
+
.filter(
|
|
114
|
+
(layer) =>
|
|
115
|
+
layer.kind === "mesh" &&
|
|
116
|
+
meshPrimitiveOf(layer.mesh)?.kind === "spline" &&
|
|
117
|
+
cameraSplineSourceIsSafe(scene, layer.id, frameHeight),
|
|
118
|
+
)
|
|
119
|
+
.map((layer) => ({ value: layer.id, label: layer.label })),
|
|
120
|
+
[scene, frameHeight],
|
|
121
|
+
);
|
|
122
|
+
if (doc === null || sceneId === null || focused === null || focused.sceneId !== sceneId) {
|
|
37
123
|
return null;
|
|
38
124
|
}
|
|
39
|
-
const
|
|
40
|
-
const shot = scene?.shots.find((s) => s.id === focused.blockId);
|
|
125
|
+
const shot = scene?.shots.find((s) => s.id === focused.shotId);
|
|
41
126
|
if (scene === undefined || shot === undefined) return null;
|
|
42
127
|
|
|
43
128
|
// LIVE state at the click, never the hook's coalesced frame: the hook holds
|
|
@@ -45,43 +130,115 @@ export function ShotSection() {
|
|
|
45
130
|
// framing" means the framing on SCREEN — writing the held frame's pose puts
|
|
46
131
|
// a camera the author never saw into the document. Same rule as
|
|
47
132
|
// useTransformWrite.
|
|
133
|
+
//
|
|
134
|
+
// STEADY, never the shaken `cameraAt`: this feeds the "set from current
|
|
135
|
+
// framing" buttons, which WRITE the pose into the document. Through the shaken
|
|
136
|
+
// read they would store one arbitrary sample of a sine sum as an authored
|
|
137
|
+
// pose, after which the shake applies on top of itself.
|
|
48
138
|
const onScreen = (): Framing => {
|
|
49
139
|
const s = store.getState();
|
|
50
|
-
if (s.doc === null || s.sceneId === null) return
|
|
140
|
+
if (s.doc === null || s.sceneId === null) return steadyCameraAt(scene, sceneFrame, doc.fps, doc.size);
|
|
51
141
|
const liveScene = s.doc.scenesById[s.sceneId] ?? scene;
|
|
52
|
-
return
|
|
142
|
+
return steadyCameraAt(liveScene, containerFrame(s.doc, s.sceneId, s.frame), s.doc.fps, s.doc.size);
|
|
53
143
|
};
|
|
54
|
-
|
|
55
144
|
return (
|
|
56
|
-
<InspectorSection title="Camera move" persistKey="shot:animate:shot" collapsible={false}>
|
|
145
|
+
<InspectorSection className="insp-object" title="Camera move" persistKey="shot:animate:shot" collapsible={false}>
|
|
57
146
|
{shot.kind === "path" ? (
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
147
|
+
<>
|
|
148
|
+
<InspectorRow label="Kind">
|
|
149
|
+
<SegmentedControl
|
|
150
|
+
ariaLabel="Move kind"
|
|
151
|
+
value={shot.kind}
|
|
152
|
+
options={KIND_OPTIONS}
|
|
153
|
+
onChange={(next) => store.setShotKind(shot.id, next as MoveShot["kind"])}
|
|
154
|
+
/>
|
|
155
|
+
</InspectorRow>
|
|
156
|
+
<p className="insp-note">
|
|
157
|
+
{`A camera path through ${shot.waypoints.length} waypoints, curved with a centripetal spline. ` +
|
|
158
|
+
`Its ends are the first and last waypoint.`}
|
|
159
|
+
</p>
|
|
160
|
+
{/* ── ONE GROUP PER WAYPOINT ────────────────────────────────────
|
|
161
|
+
The same `FramingRows` the two-pose ends use, so a pose is
|
|
162
|
+
edited the same way wherever it lives, plus the same "set from
|
|
163
|
+
current framing" button — which is what makes a path authorable
|
|
164
|
+
without a stage grip: park the camera, then press it. */}
|
|
165
|
+
{shot.waypoints.map((framing, index) => (
|
|
166
|
+
<WaypointGroup
|
|
167
|
+
key={index}
|
|
168
|
+
shotId={shot.id}
|
|
169
|
+
index={index}
|
|
170
|
+
framing={framing}
|
|
171
|
+
count={shot.waypoints.length}
|
|
172
|
+
read={onScreen}
|
|
173
|
+
/>
|
|
174
|
+
))}
|
|
175
|
+
</>
|
|
176
|
+
) : shot.kind === "track" ? (
|
|
177
|
+
<TrackShotRows shot={shot} scene={scene} />
|
|
178
|
+
) : shot.kind === "spline" ? (
|
|
179
|
+
<SplineShotRows shot={shot} options={splineOptions} />
|
|
67
180
|
) : (
|
|
68
181
|
<>
|
|
69
182
|
<InspectorRow label="Kind">
|
|
70
183
|
<SegmentedControl
|
|
71
184
|
ariaLabel="Move kind"
|
|
72
185
|
value={shot.kind}
|
|
73
|
-
options={
|
|
186
|
+
options={KIND_OPTIONS}
|
|
74
187
|
onChange={(next) => store.setShotKind(shot.id, next as MoveShot["kind"])}
|
|
75
188
|
/>
|
|
76
189
|
</InspectorRow>
|
|
190
|
+
<InspectorRow label="Camera rail">
|
|
191
|
+
<Combobox
|
|
192
|
+
options={splineOptions}
|
|
193
|
+
value=""
|
|
194
|
+
ariaLabel="Convert move to spline"
|
|
195
|
+
placeholder={splineOptions.length === 0 ? "No valid scene spline" : "Choose spline"}
|
|
196
|
+
onChange={(spline) => store.convertShotToSpline(shot.id, spline)}
|
|
197
|
+
/>
|
|
198
|
+
</InspectorRow>
|
|
199
|
+
{splineOptions.length === 0 ? (
|
|
200
|
+
<p className="insp-note">A camera rail cannot be below a cloner, and its authored base centreline must stay inside the safe camera distance range.</p>
|
|
201
|
+
) : (
|
|
202
|
+
<p className="insp-note">Choosing a rail explicitly replaces this move's x, y and zoom with spline progress from 0 to 1.</p>
|
|
203
|
+
)}
|
|
77
204
|
{/* The one line that makes the choice a choice. Both read as "get
|
|
78
205
|
closer"; only one of them moves the camera through the scene, and
|
|
79
206
|
a layer at a depth is the only place the difference shows. */}
|
|
80
|
-
<
|
|
207
|
+
<p className="insp-note">
|
|
81
208
|
{shot.kind === "dolly"
|
|
82
209
|
? "Dolly derives the distance from the live zoom, so the camera moves and layers at different depths shift against each other."
|
|
83
210
|
: "Zoom pins the distance at the move's start and narrows the lens instead, so nothing shifts."}
|
|
84
|
-
</
|
|
211
|
+
</p>
|
|
212
|
+
{/* ── AND WHAT THE PIN COSTS ON THE WAY OUT ─────────────────────
|
|
213
|
+
FIXED IN v18 — the fov became a held channel that carries the
|
|
214
|
+
pinned distance across the exit — so this only appears on a scene
|
|
215
|
+
still carrying `legacyZoomRig`, which every document authored
|
|
216
|
+
before the fix does until someone presses Upgrade. The advice is
|
|
217
|
+
no longer "work around it" but "take the fix", because there now
|
|
218
|
+
is one. The fix is a 28px button row of its own under the warning,
|
|
219
|
+
like every verb in this panel: inside the notice it was the 32px
|
|
220
|
+
house button in a section whose every other control is 28. */}
|
|
221
|
+
{scene.legacyZoomRig === true && isZoomRigBreak(shot) ? (
|
|
222
|
+
<>
|
|
223
|
+
<div className="insp-notice is-warn" data-testid="legacy-rig-notice">
|
|
224
|
+
{`This scene is on the old camera rig, where the distance pin ends WITH the move: at frame ` +
|
|
225
|
+
`${shot.frame + Math.max(1, Math.trunc(shot.durationFrames))} the distance and lens snap back, ` +
|
|
226
|
+
`so anything off the z=0 plane pops one frame.`}
|
|
227
|
+
</div>
|
|
228
|
+
<InspectorRow>
|
|
229
|
+
<div className="insp-buttons">
|
|
230
|
+
<button
|
|
231
|
+
type="button"
|
|
232
|
+
className="btn"
|
|
233
|
+
disabled={locked}
|
|
234
|
+
onClick={() => store.upgradeCameraRig(sceneId)}
|
|
235
|
+
>
|
|
236
|
+
Upgrade this shot's camera
|
|
237
|
+
</button>
|
|
238
|
+
</div>
|
|
239
|
+
</InspectorRow>
|
|
240
|
+
</>
|
|
241
|
+
) : null}
|
|
85
242
|
</>
|
|
86
243
|
)}
|
|
87
244
|
|
|
@@ -92,35 +249,392 @@ export function ShotSection() {
|
|
|
92
249
|
onScrub={(ease) => store.setShotEase(shot.id, ease)}
|
|
93
250
|
/>
|
|
94
251
|
|
|
95
|
-
{shot
|
|
252
|
+
<ShakeRows shot={shot} />
|
|
253
|
+
|
|
254
|
+
{shot.kind === "dolly" || shot.kind === "zoom" ? (
|
|
96
255
|
<>
|
|
97
256
|
<FramingGroup shot={shot} end="from" title="From" read={onScreen} />
|
|
98
257
|
<FramingGroup shot={shot} end="to" title="To" read={onScreen} />
|
|
99
258
|
</>
|
|
100
|
-
)}
|
|
259
|
+
) : null}
|
|
101
260
|
</InspectorSection>
|
|
102
261
|
);
|
|
103
262
|
}
|
|
104
263
|
|
|
264
|
+
/**
|
|
265
|
+
* A FOLLOW: which layers, how tight, and how much it smooths.
|
|
266
|
+
*
|
|
267
|
+
* No `from`/`to` rows, because there are none — the framing is derived from the
|
|
268
|
+
* targets on every frame. What this panel offers instead is the two things the
|
|
269
|
+
* derivation reads (targets and margin), the two that shape it (fit and
|
|
270
|
+
* smoothing), and the one way out (bake).
|
|
271
|
+
*/
|
|
272
|
+
function TrackShotRows({ shot, scene }: { shot: TrackShot; scene: Scene3D }) {
|
|
273
|
+
const store = useStore();
|
|
274
|
+
const selection = useShotEditor((s) => s.selection);
|
|
275
|
+
const locked = useLocked();
|
|
276
|
+
const labels = shot.targets.map((id) => scene.layersById[id]?.label ?? id);
|
|
277
|
+
const followed = labels.length === 0 ? "nothing" : labels.join(", ");
|
|
278
|
+
return (
|
|
279
|
+
<>
|
|
280
|
+
{/* A READOUT of names: the house readout's box (no hover, nothing to
|
|
281
|
+
type into), the names as words (`.is-text`) in the row's sans face
|
|
282
|
+
rather than the numeral face a number takes, on one line. The whole
|
|
283
|
+
list is the tooltip, for when it ellipsizes. */}
|
|
284
|
+
<InspectorRow label="Following" className="insp-readout">
|
|
285
|
+
<span className="insp-dim is-text" title={followed}>
|
|
286
|
+
{followed}
|
|
287
|
+
</span>
|
|
288
|
+
</InspectorRow>
|
|
289
|
+
<InspectorRow>
|
|
290
|
+
{/* THE SELECTION IS THE EDITOR. A layer picker here would be a second
|
|
291
|
+
outline in a 280px column; the outline is already on screen and
|
|
292
|
+
already the way this chrome names layers. */}
|
|
293
|
+
<div className="insp-buttons">
|
|
294
|
+
<button
|
|
295
|
+
type="button"
|
|
296
|
+
className="btn"
|
|
297
|
+
data-testid="track-set-targets"
|
|
298
|
+
disabled={locked || selection.length === 0}
|
|
299
|
+
title={
|
|
300
|
+
selection.length === 0
|
|
301
|
+
? "Select the layers in the outline first"
|
|
302
|
+
: "Follow the selected layers instead"
|
|
303
|
+
}
|
|
304
|
+
onClick={() => store.setTrackShot(shot.id, { targets: [...selection] })}
|
|
305
|
+
>
|
|
306
|
+
Follow selection
|
|
307
|
+
</button>
|
|
308
|
+
<button
|
|
309
|
+
type="button"
|
|
310
|
+
className="btn"
|
|
311
|
+
data-testid="track-bake"
|
|
312
|
+
title="Write the framings this follow derived into a plain dolly. The camera stops following."
|
|
313
|
+
disabled={locked}
|
|
314
|
+
onClick={() => store.bakeTrackShot(shot.id)}
|
|
315
|
+
>
|
|
316
|
+
Bake to dolly
|
|
317
|
+
</button>
|
|
318
|
+
</div>
|
|
319
|
+
</InspectorRow>
|
|
320
|
+
<InspectorRow label="Fit">
|
|
321
|
+
<SegmentedControl
|
|
322
|
+
ariaLabel="Track fit"
|
|
323
|
+
value={shot.fit}
|
|
324
|
+
options={FIT_OPTIONS}
|
|
325
|
+
onChange={(fit) => store.setTrackShot(shot.id, { fit: fit as TrackShot["fit"] })}
|
|
326
|
+
/>
|
|
327
|
+
</InspectorRow>
|
|
328
|
+
<p className="insp-note">
|
|
329
|
+
{shot.fit === "frame"
|
|
330
|
+
? "Frame derives x, y and zoom, so the targets keep filling the frame as they move or resize."
|
|
331
|
+
: "Position derives x and y only and keeps the zoom the camera arrived with, so a follow does not pump."}
|
|
332
|
+
</p>
|
|
333
|
+
{/* AN UNLABELLED PAIR, each box naming itself: a "Margin" pill holding
|
|
334
|
+
fields named Margin and Smooth printed Margin twice and nested two
|
|
335
|
+
boxes in a third. */}
|
|
336
|
+
<InspectorRow>
|
|
337
|
+
<div className="insp-vec">
|
|
338
|
+
<NumberField
|
|
339
|
+
value={shot.margin}
|
|
340
|
+
label="Margin"
|
|
341
|
+
ariaLabel="Track margin"
|
|
342
|
+
min={0}
|
|
343
|
+
max={MAX_TRACK_MARGIN}
|
|
344
|
+
step={0.01}
|
|
345
|
+
{...dragThrough(store, (margin: number) => store.setTrackShot(shot.id, { margin }))}
|
|
346
|
+
onCommit={(margin) => store.setTrackShot(shot.id, { margin })}
|
|
347
|
+
/>
|
|
348
|
+
<NumberField
|
|
349
|
+
value={shot.smoothFrames ?? 0}
|
|
350
|
+
label="Smooth"
|
|
351
|
+
ariaLabel="Track smoothing"
|
|
352
|
+
unit="f"
|
|
353
|
+
min={0}
|
|
354
|
+
max={MAX_TRACK_SMOOTH_FRAMES}
|
|
355
|
+
step={1}
|
|
356
|
+
{...dragThrough(store, (smoothFrames: number) => store.setTrackShot(shot.id, { smoothFrames }))}
|
|
357
|
+
onCommit={(smoothFrames) => store.setTrackShot(shot.id, { smoothFrames })}
|
|
358
|
+
/>
|
|
359
|
+
</div>
|
|
360
|
+
</InspectorRow>
|
|
361
|
+
{/* SAID, NOT HIDDEN. The fit is 2D, so a target off the design plane is
|
|
362
|
+
framed by its authored size rather than its apparent one. `validate`
|
|
363
|
+
carries the same sentence as a warning. */}
|
|
364
|
+
{shot.targets.some((id) => (scene.layersById[id]?.transform.position[2] ?? 0) !== 0) ? (
|
|
365
|
+
<div className="insp-notice">
|
|
366
|
+
A target sits off the design plane, and the fit uses each layer's authored size rather
|
|
367
|
+
than its apparent one, so the framing is close but not exact for it.
|
|
368
|
+
</div>
|
|
369
|
+
) : null}
|
|
370
|
+
</>
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* HANDHELD SHAKE.
|
|
376
|
+
*
|
|
377
|
+
* One switch row and three numbers, and the switch is the whole of "is there
|
|
378
|
+
* shake": the document says so by carrying the field or not, and there is no
|
|
379
|
+
* second way (a zeroed amplitude) to mean the same thing. See `CameraShake`.
|
|
380
|
+
* It is the house `Toggle`, like every other boolean in the inspector; it was
|
|
381
|
+
* a bare native checkbox.
|
|
382
|
+
*
|
|
383
|
+
* A REROLL rather than a seed field. The seed is an integer whose only job is
|
|
384
|
+
* to be different, so a control that asks the author to pick one is asking a
|
|
385
|
+
* question with no good answer; the button steps it and the number rides along
|
|
386
|
+
* for anyone who wants to write it down. Stepping rather than randomising, so
|
|
387
|
+
* pressing it twice from the same document lands on the same place.
|
|
388
|
+
*
|
|
389
|
+
* Every number is a box of its own in an unlabelled row: an "Amount" pill
|
|
390
|
+
* around Amp and Roll, and a "Rate" pill around Freq and a 32px button, nested
|
|
391
|
+
* boxes in boxes and made those rows 34px.
|
|
392
|
+
*/
|
|
393
|
+
function ShakeRows({ shot }: { shot: Shot }) {
|
|
394
|
+
const store = useStore();
|
|
395
|
+
// `DEFAULT_SHOT_FPS` and not a literal: the fallback is the timebase a fresh
|
|
396
|
+
// document is CREATED at, so a rehome of that value must move this with it.
|
|
397
|
+
// A module constant is also Object.is-stable, which a fallback minted inside
|
|
398
|
+
// the selector is not.
|
|
399
|
+
const fps = useShotEditor((s) => s.doc?.fps ?? DEFAULT_SHOT_FPS);
|
|
400
|
+
const locked = useLocked();
|
|
401
|
+
const shake = shot.shake;
|
|
402
|
+
const ceiling = maxShakeFreqHz(fps);
|
|
403
|
+
const patch = (next: Partial<NonNullable<Shot["shake"]>>): void => store.setShotShake(shot.id, next);
|
|
404
|
+
return (
|
|
405
|
+
<>
|
|
406
|
+
<InspectorRow label="Shake">
|
|
407
|
+
<Toggle
|
|
408
|
+
checked={shake !== undefined}
|
|
409
|
+
ariaLabel="Camera shake"
|
|
410
|
+
onChange={(on) => store.setShotShake(shot.id, on ? {} : null)}
|
|
411
|
+
/>
|
|
412
|
+
</InspectorRow>
|
|
413
|
+
{shake === undefined ? null : (
|
|
414
|
+
<>
|
|
415
|
+
<InspectorRow>
|
|
416
|
+
<div className="insp-vec">
|
|
417
|
+
<NumberField
|
|
418
|
+
value={shake.ampPx}
|
|
419
|
+
label="Amp"
|
|
420
|
+
ariaLabel="Amp, shake amplitude"
|
|
421
|
+
unit="px"
|
|
422
|
+
min={0}
|
|
423
|
+
max={MAX_SHAKE_PX}
|
|
424
|
+
step={1}
|
|
425
|
+
{...dragThrough(store, (ampPx: number) => patch({ ampPx }))}
|
|
426
|
+
onCommit={(ampPx) => patch({ ampPx })}
|
|
427
|
+
/>
|
|
428
|
+
<NumberField
|
|
429
|
+
value={shake.rollDeg ?? 0}
|
|
430
|
+
label="Roll"
|
|
431
|
+
ariaLabel="Roll, shake"
|
|
432
|
+
unit="°"
|
|
433
|
+
min={0}
|
|
434
|
+
max={MAX_SHAKE_ROLL_DEG}
|
|
435
|
+
step={0.5}
|
|
436
|
+
{...dragThrough(store, (rollDeg: number) => patch({ rollDeg }))}
|
|
437
|
+
onCommit={(rollDeg) => patch({ rollDeg })}
|
|
438
|
+
/>
|
|
439
|
+
</div>
|
|
440
|
+
</InspectorRow>
|
|
441
|
+
<InspectorRow>
|
|
442
|
+
<NumberField
|
|
443
|
+
value={shake.freqHz}
|
|
444
|
+
label="Freq"
|
|
445
|
+
ariaLabel="Freq, shake frequency"
|
|
446
|
+
unit="Hz"
|
|
447
|
+
min={MIN_SHAKE_FREQ_HZ}
|
|
448
|
+
max={ceiling}
|
|
449
|
+
step={0.1}
|
|
450
|
+
{...dragThrough(store, (freqHz: number) => patch({ freqHz }))}
|
|
451
|
+
onCommit={(freqHz) => patch({ freqHz })}
|
|
452
|
+
/>
|
|
453
|
+
</InspectorRow>
|
|
454
|
+
<InspectorRow>
|
|
455
|
+
<div className="insp-buttons">
|
|
456
|
+
<button
|
|
457
|
+
type="button"
|
|
458
|
+
className="btn"
|
|
459
|
+
data-testid="shake-reroll"
|
|
460
|
+
title={`Try another shake. The top octave runs at ${(ceiling * 4).toFixed(1)} Hz, which is this document's frame rate halved. Above it the shake aliases.`}
|
|
461
|
+
disabled={locked}
|
|
462
|
+
onClick={() => patch({ seed: shake.seed + 1 })}
|
|
463
|
+
>
|
|
464
|
+
Reroll {shake.seed}
|
|
465
|
+
</button>
|
|
466
|
+
</div>
|
|
467
|
+
</InspectorRow>
|
|
468
|
+
</>
|
|
469
|
+
)}
|
|
470
|
+
</>
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function SplineShotRows({ shot, options }: { shot: SplineShot; options: Array<{ value: string; label: string }> }) {
|
|
475
|
+
const store = useStore();
|
|
476
|
+
const choices = options.some((option) => option.value === shot.spline)
|
|
477
|
+
? options
|
|
478
|
+
: [{ value: shot.spline, label: shot.spline }, ...options];
|
|
479
|
+
return (
|
|
480
|
+
<>
|
|
481
|
+
<InspectorRow label="Spline">
|
|
482
|
+
<Combobox options={choices} value={shot.spline} ariaLabel="Camera spline" placeholder="Choose spline" onChange={(spline) => store.setSplineShot(shot.id, { spline })} />
|
|
483
|
+
</InspectorRow>
|
|
484
|
+
<InspectorRow label="Domain">
|
|
485
|
+
<SegmentedControl ariaLabel="Camera spline domain" value={shot.domain ?? "full"} options={wordOptions(SPLINE_DOMAINS)} onChange={(domain) => store.setSplineShot(shot.id, { domain: domain as SplineDomain })} />
|
|
486
|
+
</InspectorRow>
|
|
487
|
+
<SplineEndpointRows shot={shot} end="from" label="From" />
|
|
488
|
+
<SplineEndpointRows shot={shot} end="to" label="To" />
|
|
489
|
+
</>
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/** One end of a camera rail: where along the spline it sits, and how it is
|
|
494
|
+
* held there. Under its own heading, like a two-pose move's From and To, so
|
|
495
|
+
* each box names its field with one word ("Progress", "Roll", "Focus") and
|
|
496
|
+
* its accessible name says which end after that word ("Progress, From"),
|
|
497
|
+
* as a two-pose move's fields do (FramingRows). */
|
|
498
|
+
function SplineEndpointRows({ shot, end, label }: { shot: SplineShot; end: "from" | "to"; label: string }) {
|
|
499
|
+
const store = useStore();
|
|
500
|
+
const endpoint = shot[end];
|
|
501
|
+
// The endpoint as drawn, with the one field a box moves. A drag keeps the
|
|
502
|
+
// other two as they are, and each move names its absolute value, so the
|
|
503
|
+
// scrub's capture never compounds.
|
|
504
|
+
const patch = (next: Partial<SplineShot[typeof end]>): void =>
|
|
505
|
+
store.setSplineShot(shot.id, { [end]: { ...endpoint, ...next } });
|
|
506
|
+
return (
|
|
507
|
+
<>
|
|
508
|
+
<div className="insp-heading">{label}</div>
|
|
509
|
+
<InspectorRow>
|
|
510
|
+
<NumberField
|
|
511
|
+
value={endpoint.progress}
|
|
512
|
+
label="Progress"
|
|
513
|
+
ariaLabel={`Progress, ${label}`}
|
|
514
|
+
step={0.01}
|
|
515
|
+
{...dragThrough(store, (progress: number) => patch({ progress }))}
|
|
516
|
+
onCommit={(progress) => patch({ progress })}
|
|
517
|
+
/>
|
|
518
|
+
</InspectorRow>
|
|
519
|
+
<InspectorRow>
|
|
520
|
+
<div className="insp-vec">
|
|
521
|
+
<NumberField
|
|
522
|
+
value={endpoint.rollDeg ?? 0}
|
|
523
|
+
label="Roll"
|
|
524
|
+
ariaLabel={`Roll, ${label}`}
|
|
525
|
+
unit="°"
|
|
526
|
+
min={-MAX_SPLINE_ANGLE_DEG}
|
|
527
|
+
max={MAX_SPLINE_ANGLE_DEG}
|
|
528
|
+
step={1}
|
|
529
|
+
{...dragThrough(store, (rollDeg: number) => patch({ rollDeg }))}
|
|
530
|
+
onCommit={(rollDeg) => patch({ rollDeg })}
|
|
531
|
+
/>
|
|
532
|
+
<NumberField
|
|
533
|
+
value={endpoint.focusZ ?? 0}
|
|
534
|
+
label="Focus"
|
|
535
|
+
ariaLabel={`Focus, ${label}`}
|
|
536
|
+
unit="px"
|
|
537
|
+
step={1}
|
|
538
|
+
{...dragThrough(store, (focusZ: number) => patch({ focusZ }))}
|
|
539
|
+
onCommit={(focusZ) => patch({ focusZ })}
|
|
540
|
+
/>
|
|
541
|
+
</div>
|
|
542
|
+
</InspectorRow>
|
|
543
|
+
</>
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/** One waypoint of a path: its pose, a capture button, and the two structural
|
|
548
|
+
* verbs. `+` inserts AFTER this one so the list reads top to bottom in the
|
|
549
|
+
* order the camera travels; `−` is absent on the last two, because a path
|
|
550
|
+
* needs two and a button that always refuses is worse than no button.
|
|
551
|
+
*
|
|
552
|
+
* A POSE IS A GROUP, so its name is the group's heading and its buttons are
|
|
553
|
+
* one row of 28px buttons under it, like every inspector control: as a
|
|
554
|
+
* labelled pill holding a 32px button it was a 34px row with a box inside a
|
|
555
|
+
* box. */
|
|
556
|
+
function WaypointGroup(props: {
|
|
557
|
+
shotId: string;
|
|
558
|
+
index: number;
|
|
559
|
+
framing: Framing;
|
|
560
|
+
count: number;
|
|
561
|
+
read(): Framing;
|
|
562
|
+
}) {
|
|
563
|
+
const store = useStore();
|
|
564
|
+
const locked = useLocked();
|
|
565
|
+
const { shotId, index, framing, count } = props;
|
|
566
|
+
const title = `Waypoint ${index + 1}`;
|
|
567
|
+
const write = (patch: Partial<Framing>): void => store.setShotWaypoint(shotId, index, patch);
|
|
568
|
+
return (
|
|
569
|
+
<>
|
|
570
|
+
<div className="insp-heading">{title}</div>
|
|
571
|
+
<InspectorRow>
|
|
572
|
+
<div className="insp-buttons">
|
|
573
|
+
<CaptureButton pose={title} disabled={locked} onCapture={() => write(props.read())} />
|
|
574
|
+
<button
|
|
575
|
+
type="button"
|
|
576
|
+
className="btn is-icon"
|
|
577
|
+
title="Add a waypoint after this one"
|
|
578
|
+
aria-label={`Insert waypoint after ${title}`}
|
|
579
|
+
disabled={locked}
|
|
580
|
+
onClick={() => store.insertShotWaypoint(shotId, index)}
|
|
581
|
+
>
|
|
582
|
+
<IconPlus />
|
|
583
|
+
</button>
|
|
584
|
+
{count > MIN_PATH_WAYPOINTS ? (
|
|
585
|
+
<button
|
|
586
|
+
type="button"
|
|
587
|
+
className="btn is-icon"
|
|
588
|
+
title="Remove this waypoint"
|
|
589
|
+
aria-label={`Remove ${title}`}
|
|
590
|
+
disabled={locked}
|
|
591
|
+
onClick={() => store.removeShotWaypoint(shotId, index)}
|
|
592
|
+
>
|
|
593
|
+
<IconMinus />
|
|
594
|
+
</button>
|
|
595
|
+
) : null}
|
|
596
|
+
</div>
|
|
597
|
+
</InspectorRow>
|
|
598
|
+
<FramingRows framing={framing} name={title} scrub={poseScrub(store, write)} />
|
|
599
|
+
</>
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/** One end of a two-pose move, under its own heading: the capture button,
|
|
604
|
+
* then the pose, which drags live like every other field. */
|
|
105
605
|
function FramingGroup(props: { shot: MoveShot; end: "from" | "to"; title: string; read(): Framing }) {
|
|
106
606
|
const store = useStore();
|
|
607
|
+
const locked = useLocked();
|
|
107
608
|
const { shot, end, title } = props;
|
|
609
|
+
const write = (patch: Partial<Framing>): void => store.setShotFraming(shot.id, end, patch);
|
|
108
610
|
return (
|
|
109
611
|
<>
|
|
110
|
-
<
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
>
|
|
116
|
-
Set from current framing
|
|
117
|
-
</button>
|
|
612
|
+
<div className="insp-heading">{title}</div>
|
|
613
|
+
<InspectorRow>
|
|
614
|
+
<div className="insp-buttons">
|
|
615
|
+
<CaptureButton pose={title} disabled={locked} onCapture={() => write(props.read())} />
|
|
616
|
+
</div>
|
|
118
617
|
</InspectorRow>
|
|
119
|
-
<FramingRows
|
|
120
|
-
framing={shot[end]}
|
|
121
|
-
name={title}
|
|
122
|
-
onCommit={(patch) => store.setShotFraming(shot.id, end, patch)}
|
|
123
|
-
/>
|
|
618
|
+
<FramingRows framing={shot[end]} name={title} scrub={poseScrub(store, write)} />
|
|
124
619
|
</>
|
|
125
620
|
);
|
|
126
621
|
}
|
|
622
|
+
|
|
623
|
+
/** "Set from current framing" for one pose. Its NAME carries the pose after
|
|
624
|
+
* the visible words ("Set from current framing, From"), as the pose's fields
|
|
625
|
+
* ("X, From") and the waypoint verbs ("Remove Waypoint 2") carry it: the
|
|
626
|
+
* heading above is a plain line, so a screen reader moving by controls heard
|
|
627
|
+
* the same button once per pose with nothing saying which one it writes. */
|
|
628
|
+
function CaptureButton(props: { pose: string; disabled: boolean; onCapture(): void }) {
|
|
629
|
+
return (
|
|
630
|
+
<button
|
|
631
|
+
type="button"
|
|
632
|
+
className="btn"
|
|
633
|
+
aria-label={`Set from current framing, ${props.pose}`}
|
|
634
|
+
disabled={props.disabled}
|
|
635
|
+
onClick={props.onCapture}
|
|
636
|
+
>
|
|
637
|
+
Set from current framing
|
|
638
|
+
</button>
|
|
639
|
+
);
|
|
640
|
+
}
|