@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,109 @@
|
|
|
1
|
+
// THE ICON SET the Insert drawer's Icons section lists: every Lucide icon,
|
|
2
|
+
// under its canonical kebab name, with its aliases kept for search.
|
|
3
|
+
//
|
|
4
|
+
// LAZY. Lucide is about 1850 icons, and none of them is worth a byte of the
|
|
5
|
+
// editor's first load: `loadIconSet` imports the package (and the raw text of
|
|
6
|
+
// its alias index) the first time the Icons section is asked for, and Vite
|
|
7
|
+
// splits both into their own chunk.
|
|
8
|
+
//
|
|
9
|
+
// WHY THE RAW INDEX. `lucide`'s `icons` object keys every icon by its PASCAL
|
|
10
|
+
// name, aliases included, and an alias points at the very same node array as
|
|
11
|
+
// its canonical icon, so identity alone cannot say which name is the real one.
|
|
12
|
+
// The index file can: each line re-exports one icon FILE, whose kebab name is
|
|
13
|
+
// the canonical name, under every Pascal name that means it.
|
|
14
|
+
import type { IconNode } from "@dailies-studio/shot/doc";
|
|
15
|
+
|
|
16
|
+
export interface IconEntry {
|
|
17
|
+
/** Lucide's canonical kebab name, the file name (`arrow-up-right`). */
|
|
18
|
+
name: string;
|
|
19
|
+
/** Every other name Lucide accepts for it, kebab-cased. */
|
|
20
|
+
aliases: readonly string[];
|
|
21
|
+
/** The drawing, element by element, on Lucide's 24×24 grid. */
|
|
22
|
+
node: readonly IconNode[];
|
|
23
|
+
/** Name and aliases in one lowercase string, the thing a query is matched
|
|
24
|
+
* against. */
|
|
25
|
+
search: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** `export { default as A, default as B } from './icons/a-b.mjs';` */
|
|
29
|
+
const EXPORT_LINE = /export \{ ([^}]+) \} from '\.\/icons\/([a-z0-9-]+)\.mjs'/g;
|
|
30
|
+
|
|
31
|
+
/** `ArrowUpRight` to `arrow-up-right`, `Grid2x2` to `grid-2x2`: a dash
|
|
32
|
+
* before each inner capital, and before a digit run that follows a letter,
|
|
33
|
+
* except the `x` of a size like `2x2`. Used only for ALIASES, which have no
|
|
34
|
+
* file to read the real kebab name from. */
|
|
35
|
+
export function kebabOf(pascal: string): string {
|
|
36
|
+
return pascal
|
|
37
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1-$2")
|
|
38
|
+
.replace(/(?<![0-9]x)(?<=[a-zA-Z])(?=[0-9])/g, "-")
|
|
39
|
+
.toLowerCase();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The set, from the alias index's text and the package's `icons` object.
|
|
44
|
+
* Sorted by name. A line whose icon the object does not have is skipped
|
|
45
|
+
* rather than listed as a tile that would draw nothing.
|
|
46
|
+
*/
|
|
47
|
+
export function buildIconSet(raw: string, icons: Readonly<Record<string, unknown>>): IconEntry[] {
|
|
48
|
+
const out: IconEntry[] = [];
|
|
49
|
+
for (const match of raw.matchAll(EXPORT_LINE)) {
|
|
50
|
+
const names = (match[1] ?? "")
|
|
51
|
+
.split(",")
|
|
52
|
+
.map((part) => part.trim().replace(/^default as /, ""))
|
|
53
|
+
.filter((part) => part !== "");
|
|
54
|
+
const name = match[2] ?? "";
|
|
55
|
+
const first = names[0];
|
|
56
|
+
const node = first === undefined ? undefined : icons[first];
|
|
57
|
+
if (name === "" || !Array.isArray(node)) continue;
|
|
58
|
+
const aliases = [...new Set(names.map(kebabOf))].filter((alias) => alias !== name);
|
|
59
|
+
out.push({
|
|
60
|
+
name,
|
|
61
|
+
aliases,
|
|
62
|
+
node: node as readonly IconNode[],
|
|
63
|
+
search: [name, ...aliases].join(" "),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
out.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The icons a query finds, best first.
|
|
72
|
+
*
|
|
73
|
+
* Every whitespace-separated word must appear in the name or an alias. Ties
|
|
74
|
+
* keep the set's alphabetical order. Ranked: the exact name, then a name that
|
|
75
|
+
* starts with the query, then everything else that matched. An empty query
|
|
76
|
+
* answers the whole set.
|
|
77
|
+
*/
|
|
78
|
+
export function searchIcons(set: readonly IconEntry[], query: string): readonly IconEntry[] {
|
|
79
|
+
const q = query.trim().toLowerCase();
|
|
80
|
+
if (q === "") return set;
|
|
81
|
+
const words = q.split(/\s+/);
|
|
82
|
+
const rank = (entry: IconEntry): number => (entry.name === q ? 0 : entry.name.startsWith(q) ? 1 : 2);
|
|
83
|
+
return set
|
|
84
|
+
.filter((entry) => words.every((word) => entry.search.includes(word)))
|
|
85
|
+
.map((entry, index) => ({ entry, index, rank: rank(entry) }))
|
|
86
|
+
.sort((a, b) => a.rank - b.rank || a.index - b.index)
|
|
87
|
+
.map(({ entry }) => entry);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let pending: Promise<readonly IconEntry[]> | null = null;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The whole set, loaded once. A failed load (a chunk the network dropped)
|
|
94
|
+
* clears the memo, so the next open tries again instead of answering the
|
|
95
|
+
* same rejection forever.
|
|
96
|
+
*/
|
|
97
|
+
export function loadIconSet(): Promise<readonly IconEntry[]> {
|
|
98
|
+
pending ??= Promise.all([
|
|
99
|
+
import("lucide/dist/esm/iconsAndAliases.mjs?raw"),
|
|
100
|
+
import("lucide"),
|
|
101
|
+
]).then(
|
|
102
|
+
([raw, lucide]) => buildIconSet(raw.default, lucide.icons as Readonly<Record<string, unknown>>),
|
|
103
|
+
(error: unknown) => {
|
|
104
|
+
pending = null;
|
|
105
|
+
throw error;
|
|
106
|
+
},
|
|
107
|
+
);
|
|
108
|
+
return pending;
|
|
109
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// THE EXPOSE PIN: the inspector's one way to publish a property, and the only
|
|
2
|
+
// place in the chrome an `ExposedProp3D` is created.
|
|
3
|
+
//
|
|
4
|
+
// It is `KeyframeDot`'s twin on purpose (the same accessory lane inside the
|
|
5
|
+
// field's box, the same 16px button, one dotted PATH and nothing else),
|
|
6
|
+
// because the two verbs are the same shape of decision about the same row:
|
|
7
|
+
// the diamond says "this moves over time", the pin says "whoever places this
|
|
8
|
+
// decides". An author who has learnt one has learnt both.
|
|
9
|
+
//
|
|
10
|
+
// ── ABSENT, NEVER DEAD ──────────────────────────────────────────────────
|
|
11
|
+
//
|
|
12
|
+
// The diamond's discipline, for the diamond's reason: what the chrome offers
|
|
13
|
+
// and what the document accepts must be the same set by construction, or the
|
|
14
|
+
// author presses a control and the save gate refuses them afterwards. Every
|
|
15
|
+
// condition below is asked of the same function the gate asks. The per-layer
|
|
16
|
+
// ones live in the doc plane as `exposeOffered` (component.ts), which
|
|
17
|
+
// `suggestControls` asks too.
|
|
18
|
+
//
|
|
19
|
+
// a DEFINITION is open an exposed prop belongs to one, and only one
|
|
20
|
+
// exactly ONE layer a prop targets one layer, so a fan-out over a
|
|
21
|
+
// multi-selection would be several props from one
|
|
22
|
+
// press with one key between them
|
|
23
|
+
// not an identity path `identityPathProblem`, `exposeProp`'s first refusal
|
|
24
|
+
// not already published `exposedPropWriting`, the predicate `KeyframeDot`
|
|
25
|
+
// also consults — a second prop on the same field
|
|
26
|
+
// is two absolute writers
|
|
27
|
+
// no track on the path `exposeProp`'s own refusal, asked BEFORE the
|
|
28
|
+
// offer rather than after the press
|
|
29
|
+
// a default is capturable `exposableValueOf` reads the layer and falls back
|
|
30
|
+
// to the path table's, or for a component prop to
|
|
31
|
+
// the LIVE contract's default; with none, the prop
|
|
32
|
+
// would start at "" and change the picture the
|
|
33
|
+
// moment it is made. The press captures the same
|
|
34
|
+
// value (the store's `exposeProp`)
|
|
35
|
+
// its container exists `writeAtPath` REFUSES to create one, so a prop
|
|
36
|
+
// aimed into an absent `paint.stroke` would be
|
|
37
|
+
// permanently inert — the table's `needs`, which is
|
|
38
|
+
// the same clause a first keyframe honours
|
|
39
|
+
//
|
|
40
|
+
// Under `LockedControls` (a viewer) it is disabled, like every other control
|
|
41
|
+
// that writes.
|
|
42
|
+
import { useLocked } from "@dailies-studio/ui";
|
|
43
|
+
import { exposeOffered, trackablePath } from "@dailies-studio/shot/doc";
|
|
44
|
+
import { useShotEditor, useStore } from "../../state/context.js";
|
|
45
|
+
import { useSelectedLayers } from "./useDocWrite.js";
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Whether the pin for `path` is offered here: the clauses above the import,
|
|
49
|
+
* asked of the doc plane's one predicate so "Make component"'s suggested
|
|
50
|
+
* controls offer exactly what this pin does. A row that draws the pin outside
|
|
51
|
+
* a box (a ControlSpec vector's one pin, in PropRow's label column) asks
|
|
52
|
+
* first, because a slot handed a pin that renders nothing still draws, empty.
|
|
53
|
+
*/
|
|
54
|
+
export function useExposeOffered(path: string): boolean {
|
|
55
|
+
const doc = useShotEditor((s) => s.doc);
|
|
56
|
+
const editingDef = useShotEditor((s) => s.editingDef);
|
|
57
|
+
const contracts = useShotEditor((s) => s.contracts);
|
|
58
|
+
const layers = useSelectedLayers();
|
|
59
|
+
if (doc === null || editingDef === null || layers.length !== 1) return false;
|
|
60
|
+
const layer = layers[0]!;
|
|
61
|
+
// The registry's contract types an `instance.props.<key>` path, as it does
|
|
62
|
+
// for the diamond beside this one.
|
|
63
|
+
const live = layer.kind === "instance" ? contracts[layer.instance.component] : undefined;
|
|
64
|
+
return exposeOffered(doc, editingDef, layer, path, live);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The pin for one property: render it as the `trailing` of the box that shows
|
|
69
|
+
* the value. `KeyframeDot` already does it beside every diamond over one
|
|
70
|
+
* path, so a section only mounts this itself for a path with no diamond.
|
|
71
|
+
*
|
|
72
|
+
* `label` and `kind` override the path table's, and exist for the same reason
|
|
73
|
+
* `KeyframeDot`'s `label` does: a contract-driven path is in no table, and a
|
|
74
|
+
* row's wording is sometimes the section's rather than the table's.
|
|
75
|
+
*/
|
|
76
|
+
export function ExposeDot({
|
|
77
|
+
path,
|
|
78
|
+
label,
|
|
79
|
+
kind,
|
|
80
|
+
}: {
|
|
81
|
+
path: string;
|
|
82
|
+
label?: string | undefined;
|
|
83
|
+
kind?: string | undefined;
|
|
84
|
+
}) {
|
|
85
|
+
const store = useStore();
|
|
86
|
+
const editingDef = useShotEditor((s) => s.editingDef);
|
|
87
|
+
const layers = useSelectedLayers();
|
|
88
|
+
const locked = useLocked();
|
|
89
|
+
const offered = useExposeOffered(path);
|
|
90
|
+
|
|
91
|
+
// Offered means a definition is open and exactly one layer is selected.
|
|
92
|
+
if (!offered || editingDef === null) return null;
|
|
93
|
+
const layer = layers[0]!;
|
|
94
|
+
const tabled = trackablePath(path);
|
|
95
|
+
|
|
96
|
+
const name = label ?? tabled?.label ?? path;
|
|
97
|
+
// Spread, not `{label, kind}`: under `exactOptionalPropertyTypes` an explicit
|
|
98
|
+
// `label: undefined` is not the same as an absent one, and the store reads
|
|
99
|
+
// absence as "take the table's word".
|
|
100
|
+
const opts = {
|
|
101
|
+
...(label === undefined ? {} : { label }),
|
|
102
|
+
...(kind === undefined ? {} : { kind }),
|
|
103
|
+
};
|
|
104
|
+
return (
|
|
105
|
+
<button
|
|
106
|
+
type="button"
|
|
107
|
+
className="insp-key insp-expose"
|
|
108
|
+
data-testid={`expose-dot-${path}`}
|
|
109
|
+
aria-label={`Publish ${name} as a component property`}
|
|
110
|
+
title={`Publish ${name}: every instance of this component can then override it`}
|
|
111
|
+
disabled={locked}
|
|
112
|
+
onClick={() => store.exposeProp(editingDef, layer.id, path, opts)}
|
|
113
|
+
>
|
|
114
|
+
⊕
|
|
115
|
+
</button>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
@@ -1,69 +1,339 @@
|
|
|
1
|
-
// A `Framing` as
|
|
2
|
-
// scene's own camera, and a move's `from
|
|
1
|
+
// A `Framing` as rows of fields. ONE definition and ONE layout, three subjects:
|
|
2
|
+
// the scene's own camera (`CameraSection`), and a camera move's `from`, `to`
|
|
3
|
+
// and path waypoints (`animate/ShotSection.tsx`).
|
|
3
4
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
5
|
+
// ONE LAYOUT, so a pose reads the same wherever it is edited: the same words
|
|
6
|
+
// in the same places, the same units. The move editor's poses used to be a
|
|
7
|
+
// second grid in another order, naming the orbit "Yaw" and "Pitch" while the
|
|
8
|
+
// Camera section above it said "Pan X" and "Pan Y" and hid every unit, so an
|
|
9
|
+
// author comparing a move's From with the camera read two vocabularies for one
|
|
10
|
+
// set of numbers.
|
|
11
|
+
//
|
|
12
|
+
// The one difference is the zoom's SLIDER, which the Camera section asks for
|
|
13
|
+
// (`zoomSlider`) and a pose does not. A built-in row is a field, Figma's
|
|
14
|
+
// posture, and the camera's Zoom is the one built-in row that already had a
|
|
15
|
+
// slider; a pose's Zoom never did, and a move editor holding a slider per
|
|
16
|
+
// pose would make the exception the rule.
|
|
17
|
+
//
|
|
18
|
+
// It is shared rather than copied because the list IS the schema. A new
|
|
19
|
+
// framing field fails to compile in `FIELDS` until it is described, and lands
|
|
20
|
+
// in a row only when `LAYOUT` places it, so no subject can keep an authored
|
|
21
|
+
// value the author could never see or reach.
|
|
8
22
|
//
|
|
9
23
|
// The write is a PATCH and not a whole framing, so a caller can route it
|
|
10
24
|
// straight at whichever seam owns its subject: `setSceneCamera` goes through
|
|
11
25
|
// the refusal fork, `setShotFraming` does not, and this component knows about
|
|
12
26
|
// neither.
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
27
|
+
import { Fragment } from "react";
|
|
28
|
+
import type { ReactNode } from "react";
|
|
29
|
+
import { FieldLabels, IconDiamond, InspectorRow, NumberField, RangeField, formatValue, useLocked } from "@dailies-studio/ui";
|
|
30
|
+
import { MAX_CAMERA_PITCH_DEG, MAX_FRAMING_ZOOM, MIN_FRAMING_ZOOM } from "@dailies-studio/shot/doc";
|
|
15
31
|
import type { Framing } from "@dailies-studio/shot/doc";
|
|
16
32
|
|
|
17
|
-
/**
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
33
|
+
/** A framing field, by its schema name. */
|
|
34
|
+
export type CameraKey = keyof Framing;
|
|
35
|
+
|
|
36
|
+
interface FramingField {
|
|
37
|
+
/** The visible word, the prefix inside the box. The accessible name starts
|
|
38
|
+
* with it and then names the subject ("Pan X, Camera"), the house "Label,
|
|
39
|
+
* detail", so a speech command can say the word on the box and a screen
|
|
40
|
+
* reader hears it before which framing it belongs to. */
|
|
21
41
|
label: string;
|
|
22
42
|
unit?: string;
|
|
23
43
|
step: number;
|
|
44
|
+
/** HARD limits: what the document accepts. */
|
|
24
45
|
min?: number;
|
|
25
46
|
max?: number;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
47
|
+
/** The span the zoom's slider draws (see below). */
|
|
48
|
+
range?: readonly [number, number];
|
|
49
|
+
/** What the word means, when the visible word is a film word. */
|
|
50
|
+
title?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Every framing field, described once.
|
|
55
|
+
*
|
|
56
|
+
* ── THE WORDS ARE FILM WORDS ────────────────────────────────────────────
|
|
57
|
+
*
|
|
58
|
+
* "Pan X" and "Pan Y" are what a camera operator calls turning the camera
|
|
59
|
+
* across and up: that is `yawDeg` and `pitchDeg`, the orbit around the point
|
|
60
|
+
* the camera looks at. The schema word stays in the tooltip.
|
|
61
|
+
*
|
|
62
|
+
* ── AND THERE IS NO CAMERA Z ────────────────────────────────────────────
|
|
63
|
+
*
|
|
64
|
+
* `Framing` has no camera position on z: the distance is DERIVED from the
|
|
65
|
+
* lens and the zoom. The only z it carries is `focusZ`, the depth of the focus
|
|
66
|
+
* plane, which nothing but depth of field reads. So the field is labelled
|
|
67
|
+
* "Focus", for what it does, rather than "Z", which would promise a dolly the
|
|
68
|
+
* value does not perform.
|
|
69
|
+
*
|
|
70
|
+
* ── AND `range` IS NOT `min`/`max` ─────────────────────────────────────
|
|
71
|
+
*
|
|
72
|
+
* `min`/`max` are the HARD limits, what the document accepts, and they go to
|
|
73
|
+
* the field. `range` is the span Zoom's slider draws, and only Zoom has one:
|
|
74
|
+
* X, Y, Roll and Focus are open and signed (`checkFraming` in validate.ts asks
|
|
75
|
+
* each for nothing but a finite number, and nothing wraps `rollDeg` mod 360, so
|
|
76
|
+
* a roll of 720 is two turns rather than a seam).
|
|
77
|
+
*/
|
|
78
|
+
const FIELDS: Record<CameraKey, FramingField> = {
|
|
79
|
+
x: { label: "X", step: 1, title: "Where the camera looks, across the design plane, in design px" },
|
|
80
|
+
y: { label: "Y", step: 1, title: "Where the camera looks, down the design plane, in design px" },
|
|
81
|
+
focusZ: {
|
|
82
|
+
label: "Focus",
|
|
83
|
+
step: 1,
|
|
84
|
+
title: "The depth of the focus plane, in design px. Only depth of field reads it",
|
|
85
|
+
},
|
|
29
86
|
// Clamped at the widget as well as in the store: the store's clamp is the
|
|
30
87
|
// boundary that must hold, and this one is what stops the author typing a
|
|
31
88
|
// value that silently becomes a different one.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
89
|
+
//
|
|
90
|
+
// …but 0.05..50 is a VALIDATOR BAND (cameraLimits.ts), not a scale anybody
|
|
91
|
+
// authors across, so it cannot also be the slider. 0.25..4 is two octaves
|
|
92
|
+
// either side of 1: of the 199 authored zooms in the (since deleted)
|
|
93
|
+
// machineage example, 182 sit inside it (the working cluster is 1..1.5: 1,
|
|
94
|
+
// 1.035, 1.075, 1.13, over a body running 0.24 to 2.95), so 1 sits at 20%
|
|
95
|
+
// and 1.5 at 33%. The 17 that do not are one 0.24 and the dive ladder 4.4,
|
|
96
|
+
// 6.6, 10.2, 13.5, 18, 24, 26.4. The slider widens to hold them, and typing,
|
|
97
|
+
// the arrow keys and a scrub all still move them from where they are.
|
|
98
|
+
zoom: { label: "Zoom", step: 0.01, min: MIN_FRAMING_ZOOM, max: MAX_FRAMING_ZOOM, range: [0.25, 4] },
|
|
99
|
+
// THE ORBIT POSE (schema v26): where the camera stands around the point it
|
|
100
|
+
// looks at. Pan X is open like roll; Pan Y stops short of the poles.
|
|
101
|
+
yawDeg: {
|
|
102
|
+
label: "Pan X",
|
|
103
|
+
unit: "°",
|
|
104
|
+
step: 0.5,
|
|
105
|
+
title: "Pan X turns the camera across: its yaw, an orbit around the point it looks at",
|
|
106
|
+
},
|
|
107
|
+
pitchDeg: {
|
|
108
|
+
label: "Pan Y",
|
|
109
|
+
unit: "°",
|
|
110
|
+
step: 0.5,
|
|
111
|
+
min: -MAX_CAMERA_PITCH_DEG,
|
|
112
|
+
max: MAX_CAMERA_PITCH_DEG,
|
|
113
|
+
title: "Pan Y turns the camera up and down: its pitch, an orbit around the point it looks at",
|
|
114
|
+
},
|
|
115
|
+
rollDeg: { label: "Roll", unit: "°", step: 0.5 },
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/** Every framing field, in `FIELDS` order: the camera's key states read each. */
|
|
119
|
+
export const FRAMING_KEYS = Object.keys(FIELDS) as CameraKey[];
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The rows, top to bottom, in Paper's order: where it looks, what is sharp
|
|
123
|
+
* beside the lens, how much it sees, how it is turned, how it is held.
|
|
124
|
+
*
|
|
125
|
+
* ── THE ROW RULE, WHICH IS A WIDTH RULE ────────────────────────────────
|
|
126
|
+
*
|
|
127
|
+
* An inspector row holds AT MOST TWO labelled boxes, or three only when every
|
|
128
|
+
* label in it is a single character. The label is the half that gives way
|
|
129
|
+
* last, after the value and the unit, and at the rail's narrow end three word
|
|
130
|
+
* labels in one row ("Zoom", "Roll", "Focus") rendered as "Z…", "R…", "F…".
|
|
131
|
+
*
|
|
132
|
+
* A row holding ONE field stretches it full width, which is what Zoom wants
|
|
133
|
+
* for its slider, and what Focus gets when there is no lens beside it (a
|
|
134
|
+
* move's pose has none: the lens is the scene's, not the pose's).
|
|
135
|
+
*/
|
|
136
|
+
const LAYOUT: ReadonlyArray<ReadonlyArray<CameraKey | "fov">> = [
|
|
137
|
+
["x", "y"],
|
|
138
|
+
["focusZ", "fov"],
|
|
139
|
+
["zoom"],
|
|
140
|
+
["yawDeg", "pitchDeg"],
|
|
141
|
+
["rollDeg"],
|
|
35
142
|
];
|
|
36
143
|
|
|
144
|
+
/**
|
|
145
|
+
* A camera scrub, as the callbacks the rows drive. The OWNER of the subject
|
|
146
|
+
* implements it (`CameraSection` over the stage's framing lane, the move editor
|
|
147
|
+
* over the panel scrub), so this file still knows about no seam.
|
|
148
|
+
*
|
|
149
|
+
* `start`/`move`/`end` bracket one gesture. `commit` is a discrete value: the
|
|
150
|
+
* end of a gesture when one is open, and a plain one-entry write when none is
|
|
151
|
+
* (a typed value, an arrow nudge, a slider key).
|
|
152
|
+
*/
|
|
153
|
+
export interface FramingScrub {
|
|
154
|
+
start(): void;
|
|
155
|
+
move(patch: Partial<Framing>): void;
|
|
156
|
+
end(commit: boolean): void;
|
|
157
|
+
commit(patch: Partial<Framing>): void;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* What a field's diamond says about the camera's keys, which are its POSES
|
|
162
|
+
* (`cameraMarks`): every end of every camera move in the shot.
|
|
163
|
+
*
|
|
164
|
+
* keyed a pose sits at the playhead. A pose holds the whole framing, so
|
|
165
|
+
* every field lights together.
|
|
166
|
+
* animated this field's value differs between the shot's poses.
|
|
167
|
+
* static it does not, or there are no poses.
|
|
168
|
+
*/
|
|
169
|
+
export type CameraKeyState = "keyed" | "animated" | "static";
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* The diamonds' data, worked out by the section that owns the store. This file
|
|
173
|
+
* stays store-free: it draws a state and calls back.
|
|
174
|
+
*/
|
|
175
|
+
export interface CameraKeys {
|
|
176
|
+
stateOf(key: CameraKey): CameraKeyState;
|
|
177
|
+
/** This field's value differs between the shot's poses, at EVERY frame,
|
|
178
|
+
* a pose's own included. Apart from `stateOf` because "keyed" wins the
|
|
179
|
+
* diamond's state on a pose while the value still changes across the
|
|
180
|
+
* shot, and the value's accent ink reads this, so it holds steady as the
|
|
181
|
+
* playhead crosses a pose. */
|
|
182
|
+
animatedOf(key: CameraKey): boolean;
|
|
183
|
+
/** What a click does, as a sentence ("Click to add a camera move ending at
|
|
184
|
+
* the playhead."), or the reason it does nothing. Said in every diamond's
|
|
185
|
+
* tooltip. */
|
|
186
|
+
action: string;
|
|
187
|
+
/** Null when a click cannot do anything here (a path waypoint, the
|
|
188
|
+
* transport playing): the diamond then reads its state and is disabled. */
|
|
189
|
+
toggle: (() => void) | null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const KEY_WORDS: Record<CameraKeyState, string> = {
|
|
193
|
+
keyed: "a camera key sits at the playhead",
|
|
194
|
+
animated: "animated: it changes between the camera keys",
|
|
195
|
+
static: "not animated",
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* One field's diamond, INSIDE the field: the house in-field key (`.insp-key`,
|
|
200
|
+
* the property diamond's box and glyph) in the camera's own signal.
|
|
201
|
+
*
|
|
202
|
+
* `is-camera` and `data-camera-state` rather than the property diamond's
|
|
203
|
+
* `is-keyed` / `is-tracked`, because those two mean "a keyframe on a track"
|
|
204
|
+
* and set the value in `--kf` (ui widgets.css). A camera pose is not a
|
|
205
|
+
* keyframe on a track (`model.ts` says the same of its marks), so its states
|
|
206
|
+
* draw in the ACCENT, the camera's documented exception (inspector-shot.css).
|
|
207
|
+
*
|
|
208
|
+
* Disabled, at full strength, when a click can do nothing here: a viewer's
|
|
209
|
+
* locked panel, a path waypoint, or the transport playing (the state is read
|
|
210
|
+
* off the held inspector frame while a press would act on the live one).
|
|
211
|
+
*
|
|
212
|
+
* `data-camera-animated` marks a field whose value changes across the shot,
|
|
213
|
+
* whatever the state: the value's accent ink keys on it (inspector-shot.css).
|
|
214
|
+
*/
|
|
215
|
+
function CameraKeyDiamond(props: { name: string; field: CameraKey; keys: CameraKeys }) {
|
|
216
|
+
const locked = useLocked();
|
|
217
|
+
const toggle = props.keys.toggle;
|
|
218
|
+
const live = toggle !== null && !locked;
|
|
219
|
+
const state = props.keys.stateOf(props.field);
|
|
220
|
+
return (
|
|
221
|
+
<button
|
|
222
|
+
type="button"
|
|
223
|
+
className="insp-key is-camera"
|
|
224
|
+
data-camera-state={state}
|
|
225
|
+
{...(props.keys.animatedOf(props.field) ? { "data-camera-animated": "" } : {})}
|
|
226
|
+
data-testid={`camera-field-key-${props.field}`}
|
|
227
|
+
aria-label={`${props.name} key`}
|
|
228
|
+
aria-pressed={state === "keyed"}
|
|
229
|
+
// A viewer's panel says only what the diamond shows: the action it
|
|
230
|
+
// cannot take is not offered, and the store's refusal names VIEW_ONLY.
|
|
231
|
+
title={`${props.name}: ${KEY_WORDS[state]}.${locked ? "" : ` ${props.keys.action}`}`}
|
|
232
|
+
disabled={!live}
|
|
233
|
+
onClick={() => {
|
|
234
|
+
if (live) toggle();
|
|
235
|
+
}}
|
|
236
|
+
>
|
|
237
|
+
<IconDiamond filled={state === "keyed"} />
|
|
238
|
+
</button>
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* The framing's rows: X | Y, Focus beside the lens, Zoom across (with its
|
|
244
|
+
* slider in the Camera section), Pan X | Pan Y, then Roll.
|
|
245
|
+
*
|
|
246
|
+
* ── EVERY FIELD IS A STANDARD FIELD ─────────────────────────────────────
|
|
247
|
+
*
|
|
248
|
+
* The house NumberField with its name inside (and, with `zoomSlider`, Zoom the
|
|
249
|
+
* house RangeField, the field plus its slider in one box), the units shown,
|
|
250
|
+
* and, where the subject has keys, the diamond INSIDE the box as its
|
|
251
|
+
* `trailing`. A subject with no keys (a move's pose: its keys ARE the move)
|
|
252
|
+
* draws no diamond at all, rather than a dead one, and its fields are exactly
|
|
253
|
+
* as wide.
|
|
254
|
+
*
|
|
255
|
+
* `fov` is the one field that is not a framing key: it is `Scene3D.fovDeg`,
|
|
256
|
+
* slotted in by the Camera section, which owns the write. It has no diamond:
|
|
257
|
+
* the lens is not part of a pose.
|
|
258
|
+
*
|
|
259
|
+
* The names stay INSIDE the boxes whatever the panel's field-label
|
|
260
|
+
* preference: these are words already (one letter only for X and Y), and a
|
|
261
|
+
* name above each field would double the rows for words the box already says.
|
|
262
|
+
*/
|
|
37
263
|
export function FramingRows(props: {
|
|
38
264
|
framing: Framing;
|
|
39
|
-
/**
|
|
40
|
-
*
|
|
265
|
+
/** Follows the visible word in every field's accessible name ("X, Camera",
|
|
266
|
+
* "Pan X, From"), and so in its key's ("X, Camera key") and its slider's.
|
|
267
|
+
* Three framings can be on screen at once, so the word alone is not a
|
|
268
|
+
* name. */
|
|
41
269
|
name: string;
|
|
42
|
-
|
|
270
|
+
scrub: FramingScrub;
|
|
271
|
+
/** The camera's key diamonds. Absent, the fields carry none. */
|
|
272
|
+
keys?: CameraKeys;
|
|
273
|
+
/** The lens field, owned by the caller because it writes the scene. */
|
|
274
|
+
fov?: ReactNode;
|
|
275
|
+
/** Zoom as the field plus its slider: the Camera section's, and only its
|
|
276
|
+
* (see the header). Absent, Zoom is a field like every other. */
|
|
277
|
+
zoomSlider?: boolean;
|
|
43
278
|
}) {
|
|
279
|
+
const { scrub, keys } = props;
|
|
280
|
+
const field = (key: CameraKey): ReactNode => {
|
|
281
|
+
const f = FIELDS[key];
|
|
282
|
+
const name = `${f.label}, ${props.name}`;
|
|
283
|
+
const value = props.framing[key] ?? 0;
|
|
284
|
+
const shared = {
|
|
285
|
+
value,
|
|
286
|
+
step: f.step,
|
|
287
|
+
...(f.unit === undefined ? {} : { unit: f.unit }),
|
|
288
|
+
...(f.title === undefined ? {} : { title: f.title }),
|
|
289
|
+
...(keys === undefined ? {} : { trailing: <CameraKeyDiamond name={name} field={key} keys={keys} /> }),
|
|
290
|
+
onScrubStart: scrub.start,
|
|
291
|
+
onScrub: (next: number) => scrub.move({ [key]: next }),
|
|
292
|
+
onScrubEnd: scrub.end,
|
|
293
|
+
onCommit: (next: number) => scrub.commit({ [key]: next }),
|
|
294
|
+
};
|
|
295
|
+
if (props.zoomSlider === true && f.range !== undefined && f.min !== undefined && f.max !== undefined) {
|
|
296
|
+
// The slider spans the authoring range, widened by the field to hold the
|
|
297
|
+
// value, so a dive past 4 still shows its thumb at the end of a scale
|
|
298
|
+
// that contains it rather than pinned to a lie. It announces the value
|
|
299
|
+
// at its own quantum: mid-move the effective zoom is an interpolated
|
|
300
|
+
// float, and "1.051664760227515×" is noise to a listener.
|
|
301
|
+
return (
|
|
302
|
+
<RangeField
|
|
303
|
+
key={key}
|
|
304
|
+
label={f.label}
|
|
305
|
+
ariaLabel={name}
|
|
306
|
+
min={f.min}
|
|
307
|
+
max={f.max}
|
|
308
|
+
range={f.range}
|
|
309
|
+
valueText={`${formatValue(value, f.step)}×`}
|
|
310
|
+
{...shared}
|
|
311
|
+
/>
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
return (
|
|
315
|
+
<NumberField
|
|
316
|
+
key={key}
|
|
317
|
+
label={f.label}
|
|
318
|
+
ariaLabel={name}
|
|
319
|
+
{...(f.min === undefined ? {} : { min: f.min })}
|
|
320
|
+
{...(f.max === undefined ? {} : { max: f.max })}
|
|
321
|
+
{...shared}
|
|
322
|
+
/>
|
|
323
|
+
);
|
|
324
|
+
};
|
|
44
325
|
return (
|
|
45
|
-
|
|
46
|
-
{
|
|
47
|
-
|
|
48
|
-
{
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
ariaLabel={`${props.name} ${f.label.toLowerCase()}`}
|
|
58
|
-
{...(f.unit === undefined ? {} : { unit: f.unit })}
|
|
59
|
-
{...limits}
|
|
60
|
-
step={f.step}
|
|
61
|
-
onCommit={(next) => props.onCommit({ [f.key]: next })}
|
|
62
|
-
/>
|
|
63
|
-
);
|
|
64
|
-
})}
|
|
65
|
-
</InspectorRow>
|
|
66
|
-
))}
|
|
67
|
-
</>
|
|
326
|
+
<FieldLabels value="compact">
|
|
327
|
+
{LAYOUT.map((row) => {
|
|
328
|
+
const cells = row.flatMap((cell): ReactNode[] =>
|
|
329
|
+
cell === "fov" ? (props.fov === undefined ? [] : [<Fragment key="fov">{props.fov}</Fragment>]) : [field(cell)],
|
|
330
|
+
);
|
|
331
|
+
return (
|
|
332
|
+
<InspectorRow key={row.join("|")}>
|
|
333
|
+
{cells.length === 1 ? cells[0] : <div className="insp-vec">{cells}</div>}
|
|
334
|
+
</InspectorRow>
|
|
335
|
+
);
|
|
336
|
+
})}
|
|
337
|
+
</FieldLabels>
|
|
68
338
|
);
|
|
69
339
|
}
|