@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,5 +1,7 @@
|
|
|
1
1
|
// Only for CLONER layers, and absent otherwise — the same rule MeshSection and
|
|
2
|
-
// TypographySection follow, for the same reason.
|
|
2
|
+
// TypographySection follow, for the same reason. `layout.ts` declares the
|
|
3
|
+
// ceiling; the LEAD-is-a-cloner withholding below is this file's, which is
|
|
4
|
+
// where `layout.ts` says a shape-of-selection rule belongs.
|
|
3
5
|
//
|
|
4
6
|
// Deliberately the SHORT list: how many, in what arrangement, and how they are
|
|
5
7
|
// staggered in time. The step and jitter channels, the per-clone override map
|
|
@@ -15,10 +17,26 @@
|
|
|
15
17
|
// about the frame budget, not a broken document. The hard cap IS a refusal, and
|
|
16
18
|
// that one is the validator's.
|
|
17
19
|
//
|
|
20
|
+
// ── EVERY NUMBER DRAGS LIVE ─────────────────────────────────────────────
|
|
21
|
+
//
|
|
22
|
+
// A number row is `ClonerNumber`: its typed value and its drag are ONE
|
|
23
|
+
// builder over each cloner's own block, so the clones move while the pointer
|
|
24
|
+
// is down, and a release lands the document its moves drew.
|
|
25
|
+
//
|
|
18
26
|
// ponytail: no override editor. Per-clone edits are agent-authored today; build
|
|
19
27
|
// the row when someone is actually art-directing one clone out of forty.
|
|
20
|
-
import { InspectorRow, InspectorSection, NumberField, SegmentedControl } from "@dailies-studio/ui";
|
|
21
|
-
import {
|
|
28
|
+
import { Combobox, InspectorRow, InspectorSection, NumberField, SegmentedControl, wordOptions } from "@dailies-studio/ui";
|
|
29
|
+
import {
|
|
30
|
+
CLONER_MODES,
|
|
31
|
+
CLONE_FROMS,
|
|
32
|
+
MAX_CLONES,
|
|
33
|
+
MAX_SPLINE_ANGLE_DEG,
|
|
34
|
+
SPLINE_DOMAINS,
|
|
35
|
+
SPLINE_FORWARD_AXES,
|
|
36
|
+
SPLINE_ORIENTATIONS,
|
|
37
|
+
cloneCountWarning,
|
|
38
|
+
meshPrimitiveOf,
|
|
39
|
+
} from "@dailies-studio/shot/doc";
|
|
22
40
|
import type {
|
|
23
41
|
CloneFrom,
|
|
24
42
|
ClonerConfig,
|
|
@@ -26,17 +44,33 @@ import type {
|
|
|
26
44
|
ClonerMode,
|
|
27
45
|
ClonerModeKind,
|
|
28
46
|
Layer3D,
|
|
47
|
+
SplineDomain,
|
|
48
|
+
SplineForwardAxis,
|
|
49
|
+
SplineOrientation,
|
|
29
50
|
} from "@dailies-studio/shot/doc";
|
|
30
|
-
import {
|
|
51
|
+
import { useShotEditor } from "../../../state/context.js";
|
|
52
|
+
import { useSelectedLayers, useSelectionScrub, useSelectionWrite } from "../useDocWrite.js";
|
|
53
|
+
import { KeyframeDot } from "../KeyframeDot.js";
|
|
54
|
+
import { splineSourceIsSafe } from "../../../state/splineRefs.js";
|
|
31
55
|
|
|
32
56
|
type ClonerBlock = ClonerLayer3D["cloner"];
|
|
57
|
+
type SplineClonerMode = Extract<ClonerMode, { kind: "spline" }>;
|
|
58
|
+
|
|
59
|
+
/** What a widget with no `mixed` prop of its own shows when the selection
|
|
60
|
+
* disagrees. `SegmentedControl` renders an unmatched value as "none of these
|
|
61
|
+
* selected" and `Combobox` falls back to its placeholder, which is the same
|
|
62
|
+
* statement in each one's own vocabulary. Both constants are
|
|
63
|
+
* `TypographySection`'s spelling; `StrokeSection` writes the empty value
|
|
64
|
+
* inline and has no combobox to place a placeholder in. */
|
|
65
|
+
const NO_SEGMENT = "";
|
|
66
|
+
const MIXED_PLACEHOLDER = "Mixed";
|
|
33
67
|
|
|
34
68
|
/** The mode a kind switch lands on, with its own required fields filled in.
|
|
35
69
|
* Switching arrangement must produce a VALID mode: a `{kind:"grid"}` with no
|
|
36
|
-
* columns is refused on save, so a
|
|
37
|
-
*
|
|
70
|
+
* columns is refused on save, so a picker that could write one is a control
|
|
71
|
+
* that only breaks documents. The numbers are the same defaults
|
|
38
72
|
* `add_layer` uses. */
|
|
39
|
-
function defaultMode(kind: ClonerModeKind): ClonerMode {
|
|
73
|
+
function defaultMode(kind: ClonerModeKind, spline?: string): ClonerMode {
|
|
40
74
|
switch (kind) {
|
|
41
75
|
case "grid":
|
|
42
76
|
return { kind, columns: 3, spacing: [240, 240] };
|
|
@@ -44,6 +78,8 @@ function defaultMode(kind: ClonerModeKind): ClonerMode {
|
|
|
44
78
|
return { kind, radiusPx: 320 };
|
|
45
79
|
case "scatter":
|
|
46
80
|
return { kind, extent: [320, 320, 0] };
|
|
81
|
+
case "spline":
|
|
82
|
+
return spline === undefined ? { kind: "linear", step: [240, 0, 0] } : { kind, spline };
|
|
47
83
|
default:
|
|
48
84
|
return { kind: "linear", step: [240, 0, 0] };
|
|
49
85
|
}
|
|
@@ -52,86 +88,229 @@ function defaultMode(kind: ClonerModeKind): ClonerMode {
|
|
|
52
88
|
export function ClonerSection() {
|
|
53
89
|
const write = useSelectionWrite();
|
|
54
90
|
const layers = useSelectedLayers();
|
|
55
|
-
const
|
|
56
|
-
|
|
91
|
+
const doc = useShotEditor((state) => state.doc);
|
|
92
|
+
const sceneId = useShotEditor((state) => state.sceneId);
|
|
93
|
+
const editingDef = useShotEditor((state) => state.editingDef);
|
|
94
|
+
// ── THE CLONER LAYERS THEMSELVES, NOT `useSelectionValue` ───────────────
|
|
95
|
+
//
|
|
96
|
+
// That hook compares members with `Object.is` and `layer.cloner` is an
|
|
97
|
+
// OBJECT, so two selected cloners were "Mixed" by identity — every row wore
|
|
98
|
+
// the placeholder over numbers the author could see agree, while the writes
|
|
99
|
+
// below still fanned out to both. The hook reports such a read on
|
|
100
|
+
// `console.error`, which is how this was found. Mixed is asked per FIELD by
|
|
101
|
+
// `mixedOn` instead, the shape `MeshSection` and `DeviceSection` already use.
|
|
102
|
+
//
|
|
103
|
+
// Data gate, not a kind gate: see layout.ts. The LEAD is the subject, so a
|
|
104
|
+
// selection led in document order by something that is not a cloner has no
|
|
105
|
+
// arrangement to show — `inspectorLayout.test.tsx` pins that withholding, and
|
|
106
|
+
// the rule survives the move off the hook because the lead is still what a
|
|
107
|
+
// row displays.
|
|
108
|
+
const lead = layers[0];
|
|
109
|
+
if (lead === undefined || lead.kind !== "cloner") return null;
|
|
57
110
|
|
|
58
|
-
const c = cloner
|
|
111
|
+
const c = lead.cloner;
|
|
112
|
+
const cloners = layers.filter((l): l is ClonerLayer3D => l.kind === "cloner");
|
|
59
113
|
const patch = (next: (block: ClonerBlock) => ClonerBlock): void =>
|
|
60
114
|
write((layer) => (layer.kind === "cloner" ? ({ ...layer, cloner: next(layer.cloner) } as Layer3D) : layer));
|
|
61
115
|
|
|
62
|
-
|
|
63
|
-
|
|
116
|
+
/** ONE ROW, ONE QUESTION: does THIS field disagree across the selected
|
|
117
|
+
* cloners? Every row reads the scalar it displays, so a row that agrees shows
|
|
118
|
+
* its value however many layers are selected. */
|
|
119
|
+
const mixedOn = <T,>(read: (block: ClonerBlock) => T): boolean =>
|
|
120
|
+
cloners.some((l) => !Object.is(read(l.cloner), read(c)));
|
|
121
|
+
|
|
122
|
+
/** The same question for a row only the SPLINE arrangement has. A selected
|
|
123
|
+
* cloner arranged some other way reads `undefined` here, which disagrees with
|
|
124
|
+
* any number — the honest answer, because that layer has no such field for
|
|
125
|
+
* the fan-out to land on either. */
|
|
126
|
+
const mixedOnSpline = <T,>(read: (mode: SplineClonerMode) => T): boolean =>
|
|
127
|
+
mixedOn((block) => (block.mode?.kind === "spline" ? read(block.mode) : undefined));
|
|
128
|
+
|
|
129
|
+
/** One spline-mode field, on each cloner's OWN base mode, and only on
|
|
130
|
+
* cloners arranged along a spline (`withSplineMode`). */
|
|
131
|
+
const setSpline = (edit: (mode: SplineClonerMode) => SplineClonerMode): void =>
|
|
132
|
+
patch((block) => withSplineMode(block, edit));
|
|
133
|
+
|
|
134
|
+
/** One number of the CURRENT arrangement (F16), written on each cloner's OWN
|
|
135
|
+
* mode and only on cloners arranged the same way, so a fan-out never copies
|
|
136
|
+
* the lead's arrangement onto another cloner. Mixed is per field, as the
|
|
137
|
+
* spline rows ask it. */
|
|
138
|
+
const modeNumber = <K extends ClonerModeKind>(
|
|
139
|
+
kind: K,
|
|
140
|
+
label: string,
|
|
141
|
+
read: (mode: Extract<ClonerMode, { kind: K }>) => number,
|
|
142
|
+
edit: (mode: Extract<ClonerMode, { kind: K }>, next: number) => ClonerMode,
|
|
143
|
+
field: { min?: number; unit?: string } = {},
|
|
144
|
+
) => {
|
|
145
|
+
const own = (block: ClonerBlock) =>
|
|
146
|
+
block.mode?.kind === kind ? (block.mode as Extract<ClonerMode, { kind: K }>) : undefined;
|
|
147
|
+
return (
|
|
148
|
+
<ClonerNumber
|
|
149
|
+
key={label}
|
|
150
|
+
value={read(own(c)!)}
|
|
151
|
+
mixed={mixedOn((block) => { const mode = own(block); return mode === undefined ? undefined : read(mode); })}
|
|
152
|
+
label={label}
|
|
153
|
+
ariaLabel={`Cloner ${label.toLowerCase()}`}
|
|
154
|
+
step={1}
|
|
155
|
+
{...(field.min === undefined ? {} : { min: field.min })}
|
|
156
|
+
{...(field.unit === undefined ? {} : { unit: field.unit })}
|
|
157
|
+
edit={(block, next) => { const mode = own(block); return mode === undefined ? block : { ...block, mode: edit(mode, next) }; }}
|
|
158
|
+
/>
|
|
159
|
+
);
|
|
160
|
+
};
|
|
64
161
|
|
|
65
162
|
const stagger = c.stagger;
|
|
66
|
-
|
|
163
|
+
// ── THE WARNING BELONGS TO A NUMBER THAT IS ON SCREEN ──────────────────
|
|
164
|
+
//
|
|
165
|
+
// It names a count ("120 clones is a lot to draw"), so it can only be said
|
|
166
|
+
// about a count the author can see. With two cloners selected the Count
|
|
167
|
+
// field reads "Mixed" and shows no number at all, and a sentence underneath
|
|
168
|
+
// naming the LEAD layer's 120 would be pointing at a figure nothing in the
|
|
169
|
+
// panel displays — worse than silence, because it reads as a fact about the
|
|
170
|
+
// selection rather than about one member of it.
|
|
171
|
+
const countMixed = mixedOn((block) => block.count);
|
|
172
|
+
const warning = countMixed ? null : cloneCountWarning(c.count);
|
|
173
|
+
const home = doc === null
|
|
174
|
+
? undefined
|
|
175
|
+
: editingDef === null
|
|
176
|
+
? (sceneId === null ? undefined : doc.scenesById[sceneId])
|
|
177
|
+
: doc.components?.[editingDef];
|
|
178
|
+
const selected = new Set(layers.map((layer) => layer.id));
|
|
179
|
+
const baseFollowOwnsPath = layers.some(
|
|
180
|
+
(layer) => layer.kind === "cloner" && layer.splineFollow !== undefined,
|
|
181
|
+
);
|
|
182
|
+
const splines = Object.values(home?.layersById ?? {})
|
|
183
|
+
.filter((layer) =>
|
|
184
|
+
layer.kind === "mesh" &&
|
|
185
|
+
meshPrimitiveOf(layer.mesh)?.kind === "spline" &&
|
|
186
|
+
!selected.has(layer.id) &&
|
|
187
|
+
splineSourceIsSafe(home?.layersById ?? {}, [...selected], layer.id, "cloner"),
|
|
188
|
+
)
|
|
189
|
+
.map((layer) => ({ value: layer.id, label: layer.label }));
|
|
190
|
+
const modeOptions = wordOptions(
|
|
191
|
+
CLONER_MODES.filter(
|
|
192
|
+
(kind) =>
|
|
193
|
+
kind !== "spline" ||
|
|
194
|
+
c.mode?.kind === "spline" ||
|
|
195
|
+
(!baseFollowOwnsPath && splines.length > 0),
|
|
196
|
+
),
|
|
197
|
+
);
|
|
198
|
+
const splineMode = c.mode?.kind === "spline" ? c.mode : null;
|
|
199
|
+
const splineMixed = splineMode !== null && mixedOnSpline((mode) => mode.spline);
|
|
200
|
+
const modeMixed = mixedOn((block) => block.mode?.kind ?? "linear");
|
|
201
|
+
const fromMixed = mixedOn((block) => block.stagger?.from ?? "start");
|
|
67
202
|
|
|
68
203
|
return (
|
|
69
|
-
<InspectorSection title="Cloner" persistKey="shot:cloner">
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
204
|
+
<InspectorSection className="insp-object" title="Cloner" persistKey="shot:cloner">
|
|
205
|
+
{/* The name is the FIELD's, not the row's: the label rides inside the
|
|
206
|
+
box, and the whole box is one drag surface. CLAMPED at the field
|
|
207
|
+
(`MAX_CLONES`), so the cap is a thing the control cannot exceed
|
|
208
|
+
rather than a save that fails after the fact. The validator still
|
|
209
|
+
refuses it, for every other way in. */}
|
|
210
|
+
<ClonerNumber
|
|
211
|
+
value={c.count}
|
|
212
|
+
mixed={countMixed}
|
|
213
|
+
label="Count"
|
|
214
|
+
ariaLabel="Clone count"
|
|
215
|
+
step={1}
|
|
216
|
+
min={1}
|
|
217
|
+
max={MAX_CLONES}
|
|
218
|
+
edit={(block, next) => ({ ...block, count: Math.round(next) })}
|
|
219
|
+
/>
|
|
84
220
|
{warning === null ? null : (
|
|
85
|
-
/*
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
<p className="inspector-empty" data-testid="cloner-count-warning">
|
|
221
|
+
/* A warning, in the panel's warning register: the save still goes
|
|
222
|
+
through, but the frame budget will feel it. */
|
|
223
|
+
<div className="insp-notice is-warn" data-testid="cloner-count-warning">
|
|
89
224
|
{warning}
|
|
90
|
-
</
|
|
225
|
+
</div>
|
|
91
226
|
)}
|
|
92
227
|
|
|
228
|
+
{/* A COMBOBOX, as the house line has it past four options: five
|
|
229
|
+
arrangements as segments left each word 9px at the inspector's floor
|
|
230
|
+
and 14px at its default width, run together into one string. */}
|
|
93
231
|
<InspectorRow label="Arrange">
|
|
94
|
-
<
|
|
232
|
+
<Combobox
|
|
95
233
|
ariaLabel="Cloner mode"
|
|
96
|
-
value={c.mode?.kind ?? "linear"}
|
|
97
|
-
|
|
98
|
-
|
|
234
|
+
value={modeMixed ? NO_SEGMENT : (c.mode?.kind ?? "linear")}
|
|
235
|
+
placeholder={modeMixed ? MIXED_PLACEHOLDER : "Arrange"}
|
|
236
|
+
options={modeOptions}
|
|
237
|
+
// A cloner already arranged this way keeps its own settings.
|
|
238
|
+
onChange={(next) =>
|
|
239
|
+
patch((block) =>
|
|
240
|
+
(block.mode?.kind ?? "linear") === next
|
|
241
|
+
? block
|
|
242
|
+
: { ...block, mode: defaultMode(next as ClonerModeKind, splines[0]?.value) },
|
|
243
|
+
)
|
|
244
|
+
}
|
|
99
245
|
/>
|
|
100
246
|
</InspectorRow>
|
|
247
|
+
{c.mode?.kind === "grid" ? [
|
|
248
|
+
modeNumber("grid", "Columns", (m) => m.columns, (m, n) => ({ ...m, columns: Math.max(1, Math.round(n)) }), { min: 1 }),
|
|
249
|
+
modeNumber("grid", "Gap X", (m) => m.spacing[0], (m, n) => ({ ...m, spacing: withAxis(m.spacing, 0, n) })),
|
|
250
|
+
modeNumber("grid", "Gap Y", (m) => m.spacing[1], (m, n) => ({ ...m, spacing: withAxis(m.spacing, 1, n) })),
|
|
251
|
+
] : null}
|
|
252
|
+
{c.mode?.kind === "radial" ? [
|
|
253
|
+
modeNumber("radial", "Radius", (m) => m.radiusPx, (m, n) => ({ ...m, radiusPx: n })),
|
|
254
|
+
modeNumber("radial", "Start", (m) => m.startDeg ?? 0, (m, n) => ({ ...m, startDeg: n }), { unit: "°" }),
|
|
255
|
+
modeNumber("radial", "Sweep", (m) => m.sweepDeg ?? 360, (m, n) => ({ ...m, sweepDeg: n }), { unit: "°" }),
|
|
256
|
+
] : null}
|
|
257
|
+
{c.mode?.kind === "scatter"
|
|
258
|
+
? AXES.map((axis, i) => modeNumber("scatter", `Extent ${axis}`, (m) => m.extent[i]!, (m, n) => ({ ...m, extent: withAxis(m.extent, i, n) })))
|
|
259
|
+
: null}
|
|
260
|
+
{c.mode?.kind === "linear"
|
|
261
|
+
? AXES.map((axis, i) => modeNumber("linear", `Step ${axis}`, (m) => m.step[i]!, (m, n) => ({ ...m, step: withAxis(m.step, i, n) })))
|
|
262
|
+
: null}
|
|
263
|
+
{baseFollowOwnsPath && splineMode === null ? (
|
|
264
|
+
<p className="insp-note">Remove spline follow before using spline distribution.</p>
|
|
265
|
+
) : null}
|
|
266
|
+
|
|
267
|
+
{splineMode === null ? null : (
|
|
268
|
+
<>
|
|
269
|
+
<InspectorRow label="Spline">
|
|
270
|
+
<Combobox options={splines} value={splineMixed ? NO_SEGMENT : splineMode.spline} ariaLabel="Cloner spline" placeholder={splineMixed ? MIXED_PLACEHOLDER : "Choose spline"} onChange={(spline) => setSpline((mode) => ({ ...mode, spline }))} />
|
|
271
|
+
</InspectorRow>
|
|
272
|
+
<InspectorRow label="Domain">
|
|
273
|
+
<SegmentedControl ariaLabel="Cloner spline domain" value={mixedOnSpline((mode) => mode.domain ?? "full") ? NO_SEGMENT : (splineMode.domain ?? "full")} options={wordOptions(SPLINE_DOMAINS)} onChange={(domain) => setSpline((mode) => ({ ...mode, domain: domain as SplineDomain }))} />
|
|
274
|
+
</InspectorRow>
|
|
275
|
+
<ClonerNumber path="cloner.mode.start" label="Start" value={splineMode.start ?? 0} mixed={mixedOnSpline((mode) => mode.start ?? 0)} min={0} max={1} step={0.01} edit={(block, start) => withSplineMode(block, (mode) => ({ ...mode, start }))} />
|
|
276
|
+
<ClonerNumber path="cloner.mode.end" label="End" value={splineMode.end ?? 1} mixed={mixedOnSpline((mode) => mode.end ?? 1)} min={0} max={1} step={0.01} edit={(block, end) => withSplineMode(block, (mode) => ({ ...mode, end }))} />
|
|
277
|
+
<ClonerNumber path="cloner.mode.progressOffset" label="Offset" value={splineMode.progressOffset ?? 0} mixed={mixedOnSpline((mode) => mode.progressOffset ?? 0)} step={0.01} edit={(block, progressOffset) => withSplineMode(block, (mode) => ({ ...mode, progressOffset }))} />
|
|
278
|
+
<InspectorRow label="Orient">
|
|
279
|
+
<SegmentedControl ariaLabel="Cloner spline orientation" value={mixedOnSpline((mode) => mode.orientation ?? "path") ? NO_SEGMENT : (splineMode.orientation ?? "path")} options={wordOptions(SPLINE_ORIENTATIONS)} onChange={(orientation) => setSpline((mode) => ({ ...mode, orientation: orientation as SplineOrientation }))} />
|
|
280
|
+
</InspectorRow>
|
|
281
|
+
{(splineMode.orientation ?? "path") === "path" ? (
|
|
282
|
+
<>
|
|
283
|
+
<InspectorRow label="Forward">
|
|
284
|
+
<Combobox options={SPLINE_FORWARD_AXES.map((value) => ({ value, label: value.toUpperCase() }))} value={mixedOnSpline((mode) => mode.forwardAxis ?? "+x") ? NO_SEGMENT : (splineMode.forwardAxis ?? "+x")} ariaLabel="Cloner forward axis" placeholder={mixedOnSpline((mode) => mode.forwardAxis ?? "+x") ? MIXED_PLACEHOLDER : "Axis"} onChange={(forwardAxis) => setSpline((mode) => ({ ...mode, forwardAxis: forwardAxis as SplineForwardAxis }))} />
|
|
285
|
+
</InspectorRow>
|
|
286
|
+
<ClonerNumber path="cloner.mode.bankDeg" label="Bank" value={splineMode.bankDeg ?? 0} mixed={mixedOnSpline((mode) => mode.bankDeg ?? 0)} min={-MAX_SPLINE_ANGLE_DEG} max={MAX_SPLINE_ANGLE_DEG} step={1} unit="°" edit={(block, bankDeg) => withSplineMode(block, (mode) => ({ ...mode, bankDeg }))} />
|
|
287
|
+
</>
|
|
288
|
+
) : null}
|
|
289
|
+
</>
|
|
290
|
+
)}
|
|
101
291
|
|
|
102
292
|
{/* The stagger's SPACING, as `each` — the gap between clones. `span`
|
|
103
293
|
expresses the same thing as a total and the two contradict each other
|
|
104
294
|
the moment the count changes, so the panel offers exactly one of them
|
|
105
295
|
and writing it CLEARS the other rather than saving a document the
|
|
106
296
|
validator refuses. */}
|
|
107
|
-
<
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const each = Math.round(next);
|
|
117
|
-
if (each === 0) {
|
|
118
|
-
// Absent is how "no stagger" is spelled everywhere in this
|
|
119
|
-
// lane. Writing `each: 0` would be a second way to say it.
|
|
120
|
-
const { stagger: _drop, ...rest } = block;
|
|
121
|
-
return rest as ClonerConfig;
|
|
122
|
-
}
|
|
123
|
-
const { span: _dropSpan, ...keep } = block.stagger ?? {};
|
|
124
|
-
return { ...block, stagger: { ...keep, each } };
|
|
125
|
-
})
|
|
126
|
-
}
|
|
127
|
-
/>
|
|
128
|
-
</InspectorRow>
|
|
297
|
+
<ClonerNumber
|
|
298
|
+
value={stagger?.each ?? 0}
|
|
299
|
+
mixed={mixedOn((block) => block.stagger?.each ?? 0)}
|
|
300
|
+
label="Stagger"
|
|
301
|
+
ariaLabel="Stagger each"
|
|
302
|
+
unit="f"
|
|
303
|
+
step={1}
|
|
304
|
+
edit={withStaggerEach}
|
|
305
|
+
/>
|
|
129
306
|
|
|
307
|
+
{/* Six orders: a combobox, as Arrange is. */}
|
|
130
308
|
<InspectorRow label="From">
|
|
131
|
-
<
|
|
309
|
+
<Combobox
|
|
132
310
|
ariaLabel="Stagger from"
|
|
133
|
-
value={stagger?.from ?? "start"}
|
|
134
|
-
|
|
311
|
+
value={fromMixed ? NO_SEGMENT : (stagger?.from ?? "start")}
|
|
312
|
+
placeholder={fromMixed ? MIXED_PLACEHOLDER : "From"}
|
|
313
|
+
options={wordOptions(CLONE_FROMS)}
|
|
135
314
|
onChange={(next) =>
|
|
136
315
|
patch((block) => ({ ...block, stagger: { ...block.stagger, from: next as CloneFrom } }))
|
|
137
316
|
}
|
|
@@ -140,3 +319,85 @@ export function ClonerSection() {
|
|
|
140
319
|
</InspectorSection>
|
|
141
320
|
);
|
|
142
321
|
}
|
|
322
|
+
|
|
323
|
+
const AXES = ["X", "Y", "Z"] as const;
|
|
324
|
+
|
|
325
|
+
/** A copy of a spacing, step or extent vector with one axis replaced. */
|
|
326
|
+
function withAxis<V extends number[]>(vec: V, axis: number, next: number): V {
|
|
327
|
+
return vec.map((value, i) => (i === axis ? next : value)) as V;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** A spline-mode edit on ONE cloner's own base mode, and only when it is
|
|
331
|
+
* arranged along a spline. Spreading the lead's painted mode instead moved
|
|
332
|
+
* every cloner onto the lead's spline and baked its sampled values. */
|
|
333
|
+
function withSplineMode(block: ClonerBlock, edit: (mode: SplineClonerMode) => SplineClonerMode): ClonerBlock {
|
|
334
|
+
return block.mode?.kind === "spline" ? { ...block, mode: edit(block.mode) } : block;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** The stagger's spacing, which CLEARS `span`: the two contradict each other
|
|
338
|
+
* the moment the count changes. 0 deletes the stagger: absent is how "no
|
|
339
|
+
* stagger" is spelled everywhere in this lane, and `each: 0` would be a
|
|
340
|
+
* second way to say it. */
|
|
341
|
+
function withStaggerEach(block: ClonerBlock, next: number): ClonerBlock {
|
|
342
|
+
const each = Math.round(next);
|
|
343
|
+
if (each === 0) {
|
|
344
|
+
const { stagger: _drop, ...rest } = block;
|
|
345
|
+
return rest as ClonerConfig;
|
|
346
|
+
}
|
|
347
|
+
const { span: _dropSpan, ...keep } = block.stagger ?? {};
|
|
348
|
+
return { ...block, stagger: { ...keep, each } };
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* One number of a cloner, as one box: the typed value and the drag are ONE
|
|
353
|
+
* builder (`edit`) over each cloner's own block, re-derived from the gesture's
|
|
354
|
+
* base on every capture, so a drag moves the clones live and never compounds.
|
|
355
|
+
* `path` is the track it keys, which puts its diamond in the box.
|
|
356
|
+
*/
|
|
357
|
+
function ClonerNumber({
|
|
358
|
+
label,
|
|
359
|
+
ariaLabel,
|
|
360
|
+
value,
|
|
361
|
+
mixed,
|
|
362
|
+
unit,
|
|
363
|
+
min,
|
|
364
|
+
max,
|
|
365
|
+
step,
|
|
366
|
+
path,
|
|
367
|
+
edit,
|
|
368
|
+
}: {
|
|
369
|
+
label: string;
|
|
370
|
+
ariaLabel?: string;
|
|
371
|
+
value: number;
|
|
372
|
+
mixed: boolean;
|
|
373
|
+
unit?: string;
|
|
374
|
+
min?: number;
|
|
375
|
+
max?: number;
|
|
376
|
+
step: number;
|
|
377
|
+
path?: string;
|
|
378
|
+
edit: (block: ClonerBlock, value: number) => ClonerBlock;
|
|
379
|
+
}) {
|
|
380
|
+
const write = useSelectionWrite();
|
|
381
|
+
const build = (layer: Layer3D, next: number): Layer3D =>
|
|
382
|
+
layer.kind === "cloner" ? ({ ...layer, cloner: edit(layer.cloner, next) } as Layer3D) : layer;
|
|
383
|
+
const scrub = useSelectionScrub((layer, raw) => build(layer, Number(raw)));
|
|
384
|
+
return (
|
|
385
|
+
<InspectorRow>
|
|
386
|
+
<NumberField
|
|
387
|
+
value={value}
|
|
388
|
+
mixed={mixed}
|
|
389
|
+
label={label}
|
|
390
|
+
ariaLabel={ariaLabel ?? label}
|
|
391
|
+
{...(unit === undefined ? {} : { unit })}
|
|
392
|
+
{...(min === undefined ? {} : { min })}
|
|
393
|
+
{...(max === undefined ? {} : { max })}
|
|
394
|
+
step={step}
|
|
395
|
+
trailing={path === undefined ? undefined : <KeyframeDot path={path} label={label} />}
|
|
396
|
+
onCommit={(next) => write((layer) => build(layer, next))}
|
|
397
|
+
onScrubStart={scrub.start}
|
|
398
|
+
onScrub={scrub.move}
|
|
399
|
+
onScrubEnd={scrub.end}
|
|
400
|
+
/>
|
|
401
|
+
</InspectorRow>
|
|
402
|
+
);
|
|
403
|
+
}
|
|
@@ -5,40 +5,64 @@
|
|
|
5
5
|
// a component a contract rather than a copy. A definition that exposes nothing
|
|
6
6
|
// gets a panel that says so, because "no controls" and "this component has no
|
|
7
7
|
// controls" read very differently to the author who just made one.
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
//
|
|
9
|
+
// ── ONE ROW FOR ONE USE'S PROP ──────────────────────────────────────────
|
|
10
|
+
//
|
|
11
|
+
// An override is this placement's value for one of the definition's props,
|
|
12
|
+
// which is exactly what Components' THIS USE panel shows for the same layer.
|
|
13
|
+
// So the rows ARE that panel's rows (`UsePropRow`): the control each prop's
|
|
14
|
+
// target describes (`exposedRowOf`, so a published 0..1 opacity is a bounded
|
|
15
|
+
// field and a published enum keeps its options), the override read the way
|
|
16
|
+
// the renderer writes it (`appliedOverride`: under a `renamedFrom` key too,
|
|
17
|
+
// and a value it quarantines read as none), the master read off what the
|
|
18
|
+
// definition draws (`masterValueOf`, the interior layer, never a stale
|
|
19
|
+
// published default), and one write (`writeUseValue`), by which typing the
|
|
20
|
+
// master's value stores nothing and the instance goes on following its
|
|
21
|
+
// definition.
|
|
22
|
+
import { InspectorRow, InspectorSection } from "@dailies-studio/ui";
|
|
23
|
+
import { appliedOverride, componentDefsOf, instancesOf } from "@dailies-studio/shot/doc";
|
|
10
24
|
import type { ComponentLayer3D } from "@dailies-studio/shot/doc";
|
|
11
|
-
import
|
|
12
|
-
import type { Value } from "@dailies-studio/engine";
|
|
25
|
+
import { useMemo } from "react";
|
|
13
26
|
import { useShotEditor, useStore } from "../../../state/context.js";
|
|
14
|
-
import {
|
|
27
|
+
import { layerUseKey, masterValueOf } from "../../../state/workspace.js";
|
|
28
|
+
import { UsePropRow } from "../../studio/ThisUsePanel.js";
|
|
29
|
+
import { exposedRowOf, projectAssetsOf } from "../propValue.js";
|
|
30
|
+
import { useSelectedLayers } from "../useDocWrite.js";
|
|
15
31
|
|
|
16
32
|
export function ComponentSection() {
|
|
17
33
|
const store = useStore();
|
|
18
34
|
const doc = useShotEditor((s) => s.doc);
|
|
35
|
+
const contracts = useShotEditor((s) => s.contracts);
|
|
36
|
+
// THE CONTAINER the layer lives in, the store's `layerContainer` rule: a
|
|
37
|
+
// component placed inside another definition is a use of that definition.
|
|
38
|
+
const container = useShotEditor((s) => s.editingDef ?? s.sceneId);
|
|
19
39
|
const layers = useSelectedLayers();
|
|
40
|
+
const assetTable = doc?.assets;
|
|
41
|
+
const assets = useMemo(() => projectAssetsOf(assetTable), [assetTable]);
|
|
20
42
|
// ONE instance, not a mixed selection: overrides are per definition, and two
|
|
21
43
|
// instances of two different definitions have no shared vocabulary at all.
|
|
22
|
-
const instance = layers.length === 1 && layers[0]?.kind === "component" ? layers[0] : null;
|
|
23
|
-
if (doc === null || instance === null) return null;
|
|
24
|
-
const def = componentDefsOf(doc)[
|
|
44
|
+
const instance = layers.length === 1 && layers[0]?.kind === "component" ? (layers[0] as ComponentLayer3D) : null;
|
|
45
|
+
if (doc === null || instance === null || container === null) return null;
|
|
46
|
+
const def = componentDefsOf(doc)[instance.component.def];
|
|
25
47
|
|
|
26
48
|
if (def === undefined) {
|
|
27
49
|
return (
|
|
28
|
-
<InspectorSection title="Component" persistKey="shot:component">
|
|
29
|
-
<p className="
|
|
50
|
+
<InspectorSection className="insp-object" title="Component" persistKey="shot:component">
|
|
51
|
+
<p className="insp-note">
|
|
30
52
|
This places a component that is not in this document. Its definition
|
|
31
|
-
was probably deleted
|
|
53
|
+
was probably deleted. Undo, or delete this layer.
|
|
32
54
|
</p>
|
|
33
55
|
</InspectorSection>
|
|
34
56
|
);
|
|
35
57
|
}
|
|
36
58
|
|
|
37
|
-
const overrides =
|
|
59
|
+
const overrides = instance.component.overrides;
|
|
38
60
|
const count = instancesOf(doc, def.id).length;
|
|
61
|
+
const useKey = layerUseKey(container, instance.id);
|
|
62
|
+
const use = { def: def.id };
|
|
39
63
|
|
|
40
64
|
return (
|
|
41
|
-
<InspectorSection title="Component" persistKey="shot:component">
|
|
65
|
+
<InspectorSection className="insp-object" title="Component" persistKey="shot:component">
|
|
42
66
|
<InspectorRow label="Definition">
|
|
43
67
|
<button
|
|
44
68
|
type="button"
|
|
@@ -51,43 +75,26 @@ export function ComponentSection() {
|
|
|
51
75
|
{def.name}
|
|
52
76
|
</button>
|
|
53
77
|
</InspectorRow>
|
|
54
|
-
<p className="
|
|
55
|
-
{count === 1 ? "The only one placed." : `${count} placed
|
|
78
|
+
<p className="insp-note">
|
|
79
|
+
{count === 1 ? "The only one placed." : `${count} placed. Editing it changes all of them.`}
|
|
56
80
|
</p>
|
|
57
81
|
|
|
58
82
|
{def.exposed.length === 0 ? (
|
|
59
|
-
<p className="
|
|
83
|
+
<p className="insp-empty">
|
|
60
84
|
Nothing is exposed yet. Open the definition and expose a field to make
|
|
61
85
|
it overridable here.
|
|
62
86
|
</p>
|
|
63
87
|
) : (
|
|
64
|
-
def.exposed.map((prop) =>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
{/* The override's own reset. An instance that says nothing FOLLOWS
|
|
75
|
-
its definition, so clearing is not "set it back to today's
|
|
76
|
-
default" — it is "stop having an opinion", and the two differ
|
|
77
|
-
the moment the definition changes. */}
|
|
78
|
-
<button
|
|
79
|
-
type="button"
|
|
80
|
-
className="layer-flag"
|
|
81
|
-
aria-label={`Reset ${prop.label}`}
|
|
82
|
-
title="Follow the component again"
|
|
83
|
-
disabled={value === undefined}
|
|
84
|
-
onClick={() => store.setComponentOverride(instance.id, prop.key, null)}
|
|
85
|
-
>
|
|
86
|
-
↺
|
|
87
|
-
</button>
|
|
88
|
-
</InspectorRow>
|
|
89
|
-
);
|
|
90
|
-
})
|
|
88
|
+
def.exposed.map((prop) => (
|
|
89
|
+
<UsePropRow
|
|
90
|
+
key={prop.key}
|
|
91
|
+
useKey={useKey}
|
|
92
|
+
row={exposedRowOf(prop, def, doc.materials, contracts)}
|
|
93
|
+
stored={appliedOverride(def, prop, overrides)?.value}
|
|
94
|
+
master={masterValueOf(doc, contracts, use, prop.key)}
|
|
95
|
+
assets={assets}
|
|
96
|
+
/>
|
|
97
|
+
))
|
|
91
98
|
)}
|
|
92
99
|
</InspectorSection>
|
|
93
100
|
);
|