@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,525 @@
|
|
|
1
|
+
// A REUSABLE GRAPH'S EXPOSED CONTROLS, in the Material section of the mesh
|
|
2
|
+
// that wears it (MaterialSection.tsx): first the controls themselves, each a
|
|
3
|
+
// card editing its definition on the graph, then the per-mesh rows, which edit
|
|
4
|
+
// THIS mesh's value and keyframes.
|
|
5
|
+
//
|
|
6
|
+
// Both are `PropRow`s, the one row every ControlSpec surface renders, so a
|
|
7
|
+
// float with a range is the range field, a colour is the colour field, and a
|
|
8
|
+
// vector's axes are named boxes, exactly as a component's props are. Listed in
|
|
9
|
+
// the graph's own order (its declaration), never alphabetically, the rule
|
|
10
|
+
// every ControlSpec list follows.
|
|
11
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
12
|
+
import { IconClose, PropRow, useLocked } from "@dailies-studio/ui";
|
|
13
|
+
import type { PropRowReset, ScrubHandlers } from "@dailies-studio/ui";
|
|
14
|
+
import type { ControlSpec } from "@dailies-studio/contract";
|
|
15
|
+
import { valueEquals, type Value } from "@dailies-studio/engine";
|
|
16
|
+
import {
|
|
17
|
+
containerFrame,
|
|
18
|
+
homeOf,
|
|
19
|
+
isGraphMaterial,
|
|
20
|
+
isMaterialGraphValueForType,
|
|
21
|
+
materialBindingForSlot,
|
|
22
|
+
materialGraphParamPath,
|
|
23
|
+
materialGraphParameterValue,
|
|
24
|
+
resolveTracksAt,
|
|
25
|
+
} from "@dailies-studio/shot/doc";
|
|
26
|
+
import type {
|
|
27
|
+
MaterialGraph3D,
|
|
28
|
+
MaterialGraphParameter,
|
|
29
|
+
MaterialGraphValue,
|
|
30
|
+
} from "@dailies-studio/shot/doc";
|
|
31
|
+
import { useShotEditor, useStore } from "../../state/context.js";
|
|
32
|
+
import { writeMeshGraphOverride } from "../../state/store.js";
|
|
33
|
+
import { useExposeOffered } from "../inspector/ExposeDot.js";
|
|
34
|
+
import { KeyframeDot } from "../inspector/KeyframeDot.js";
|
|
35
|
+
import { isVectorSpec } from "../inspector/propValue.js";
|
|
36
|
+
import { useScrub } from "../inspector/useDocWrite.js";
|
|
37
|
+
import {
|
|
38
|
+
clampParameterValues,
|
|
39
|
+
valuesOutsideRange,
|
|
40
|
+
withParameterRange,
|
|
41
|
+
writeParameterDefault,
|
|
42
|
+
} from "./parameterValues.js";
|
|
43
|
+
import type { ParameterRange } from "./parameterValues.js";
|
|
44
|
+
|
|
45
|
+
export function MaterialParameterControls({
|
|
46
|
+
graph,
|
|
47
|
+
containerId,
|
|
48
|
+
layerId,
|
|
49
|
+
slot,
|
|
50
|
+
}: {
|
|
51
|
+
graph: MaterialGraph3D;
|
|
52
|
+
containerId: string;
|
|
53
|
+
layerId: string;
|
|
54
|
+
/** The binding slot `graph` is bound on (`governingMaterialSlot`): a
|
|
55
|
+
* submesh with its own graph, or null for the whole model. Values, keys and
|
|
56
|
+
* writes all address that binding. */
|
|
57
|
+
slot: number | null;
|
|
58
|
+
}) {
|
|
59
|
+
const parameters = Object.values(graph.parameters);
|
|
60
|
+
if (parameters.length === 0) {
|
|
61
|
+
return <p className="material-empty-copy">No exposed controls yet. Add one to animate a graph value per mesh.</p>;
|
|
62
|
+
}
|
|
63
|
+
return (
|
|
64
|
+
<div className="material-parameter-list">
|
|
65
|
+
{parameters.map((parameter) => (
|
|
66
|
+
<MaterialParameterRow
|
|
67
|
+
key={parameter.id}
|
|
68
|
+
graph={graph}
|
|
69
|
+
parameter={parameter}
|
|
70
|
+
containerId={containerId}
|
|
71
|
+
layerId={layerId}
|
|
72
|
+
slot={slot}
|
|
73
|
+
/>
|
|
74
|
+
))}
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Definition-side lifecycle. These values belong to the reusable graph;
|
|
80
|
+
* MaterialParameterControls above edits this mesh's binding and keyframes. */
|
|
81
|
+
export function MaterialParameterDefinitions({ graph }: { graph: MaterialGraph3D }) {
|
|
82
|
+
const parameters = Object.values(graph.parameters);
|
|
83
|
+
if (parameters.length === 0) return null;
|
|
84
|
+
return (
|
|
85
|
+
<div className="material-parameter-definitions">
|
|
86
|
+
{parameters.map((parameter) => (
|
|
87
|
+
<ParameterDefinition key={parameter.id} graph={graph} parameter={parameter} />
|
|
88
|
+
))}
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** A float parameter's range, as `ParameterRange` reads it. */
|
|
94
|
+
function rangeOf(parameter: MaterialGraphParameter): ParameterRange {
|
|
95
|
+
return {
|
|
96
|
+
...(parameter.min === undefined ? {} : { min: parameter.min }),
|
|
97
|
+
...(parameter.max === undefined ? {} : { max: parameter.max }),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** A range in words, for the clamp offer: "0 to 1", "at least 0". */
|
|
102
|
+
function rangeWords(range: ParameterRange): string {
|
|
103
|
+
if (range.min !== undefined && range.max !== undefined) return `${range.min} to ${range.max}`;
|
|
104
|
+
return range.min !== undefined ? `at least ${range.min}` : `at most ${range.max ?? 0}`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** A number field's spec for one of the definition's own numbers (a bound, the
|
|
108
|
+
* step): always a plain field, never a slider, since these numbers have no
|
|
109
|
+
* scale of their own. */
|
|
110
|
+
function fieldSpec(key: string, label: string, limits: ParameterRange, step: number): ControlSpec {
|
|
111
|
+
return {
|
|
112
|
+
key,
|
|
113
|
+
propName: key,
|
|
114
|
+
label,
|
|
115
|
+
kind: "number",
|
|
116
|
+
animatable: false,
|
|
117
|
+
as: "field",
|
|
118
|
+
default: Number.NaN,
|
|
119
|
+
step,
|
|
120
|
+
...limits,
|
|
121
|
+
} as ControlSpec;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* A DRAG ON A NUMBER WITH NO PICTURE TO PREVIEW (a bound, the step): the field
|
|
126
|
+
* shows the dragged value live, and the release commits it ONCE, through the
|
|
127
|
+
* same commit a typed value takes, so a bound dragged past a mesh's value
|
|
128
|
+
* still asks the clamp question rather than writing into a refusal. A row's
|
|
129
|
+
* field always routes its drag through its scrub halves, so without these a
|
|
130
|
+
* drag would move nothing at all.
|
|
131
|
+
*/
|
|
132
|
+
function useReleaseDrag(commit: (value: number) => void): { scrub: ScrubHandlers; live: number | null } {
|
|
133
|
+
const [live, setLive] = useState<number | null>(null);
|
|
134
|
+
const last = useRef<number | null>(null);
|
|
135
|
+
const latest = useRef(commit);
|
|
136
|
+
latest.current = commit;
|
|
137
|
+
return {
|
|
138
|
+
live,
|
|
139
|
+
scrub: {
|
|
140
|
+
start: () => {
|
|
141
|
+
last.current = null;
|
|
142
|
+
},
|
|
143
|
+
move: (raw) => {
|
|
144
|
+
if (typeof raw !== "number" || !Number.isFinite(raw)) return;
|
|
145
|
+
last.current = raw;
|
|
146
|
+
setLive(raw);
|
|
147
|
+
},
|
|
148
|
+
end: (keep) => {
|
|
149
|
+
const value = last.current;
|
|
150
|
+
last.current = null;
|
|
151
|
+
setLive(null);
|
|
152
|
+
if (keep && value !== null) latest.current(value);
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* ONE CONTROL'S DEFINITION: its label, its default, and for a float its range
|
|
160
|
+
* and step, in a card under the parameter's name.
|
|
161
|
+
*
|
|
162
|
+
* ── A RANGE THAT TELLS THE TRUTH ────────────────────────────────────────
|
|
163
|
+
*
|
|
164
|
+
* An absent bound shows EMPTY (the field's dash), never a 0 or a 1 the graph
|
|
165
|
+
* does not hold, and a set one carries a Clear in its box that opens it
|
|
166
|
+
* again. Minimum is held under Maximum and Maximum over Minimum by their own
|
|
167
|
+
* limits, so the pair cannot cross. A new bound moves the default inside it in
|
|
168
|
+
* the same step (the graph refuses a default outside its range), and when a
|
|
169
|
+
* mesh already holds a value or a keyframe outside it, the bound is not
|
|
170
|
+
* written blind into a refusal: the card says how many and offers to clamp
|
|
171
|
+
* them with it, in one undo step, or to keep the range as it was.
|
|
172
|
+
*/
|
|
173
|
+
function ParameterDefinition({ graph, parameter }: { graph: MaterialGraph3D; parameter: MaterialGraphParameter }) {
|
|
174
|
+
const store = useStore();
|
|
175
|
+
const doc = useShotEditor((s) => s.doc);
|
|
176
|
+
// The card's own buttons write, so a viewer's locked panel disables them
|
|
177
|
+
// with every field in it.
|
|
178
|
+
const locked = useLocked();
|
|
179
|
+
const [pending, setPending] = useState<{ range: ParameterRange; count: number } | null>(null);
|
|
180
|
+
const float = parameter.type === "float";
|
|
181
|
+
const range = rangeOf(parameter);
|
|
182
|
+
const name = parameter.label;
|
|
183
|
+
// An offer is about the range it was asked against: once the bounds move
|
|
184
|
+
// under it (an undo, another tab), it is a question nobody is asking.
|
|
185
|
+
useEffect(() => setPending(null), [parameter.min, parameter.max]);
|
|
186
|
+
|
|
187
|
+
const patch = (
|
|
188
|
+
change: (found: MaterialGraphParameter) => MaterialGraphParameter,
|
|
189
|
+
alsoDoc?: Parameters<typeof store.updateProjectMaterial>[2],
|
|
190
|
+
): void => {
|
|
191
|
+
store.updateProjectMaterial(
|
|
192
|
+
graph.id,
|
|
193
|
+
(current) => {
|
|
194
|
+
const found = current.parameters[parameter.id];
|
|
195
|
+
if (found === undefined) return current;
|
|
196
|
+
const next = change(found);
|
|
197
|
+
return next === found ? current : { ...current, parameters: { ...current.parameters, [parameter.id]: next } };
|
|
198
|
+
},
|
|
199
|
+
alsoDoc,
|
|
200
|
+
);
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const applyRange = (next: ParameterRange, clamp: boolean): void => {
|
|
204
|
+
setPending(null);
|
|
205
|
+
patch(
|
|
206
|
+
(found) => withParameterRange(found, next),
|
|
207
|
+
clamp ? (d) => clampParameterValues(d, graph.id, parameter.id, next) : undefined,
|
|
208
|
+
);
|
|
209
|
+
};
|
|
210
|
+
const setBound = (which: "min" | "max", value: number | null): void => {
|
|
211
|
+
const next: ParameterRange = { ...range };
|
|
212
|
+
if (value === null) delete next[which];
|
|
213
|
+
else next[which] = value;
|
|
214
|
+
// Opening a bound leaves every value inside; narrowing one may not.
|
|
215
|
+
const outside = value === null || doc === null ? 0 : valuesOutsideRange(doc, graph.id, parameter.id, next);
|
|
216
|
+
if (outside > 0) setPending({ range: next, count: outside });
|
|
217
|
+
else applyRange(next, false);
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
// A drag on the default moves the graph's default while the pointer is down,
|
|
221
|
+
// one undo entry, through the narrow write a scrub may make. The bounds and
|
|
222
|
+
// the step commit on release (`useReleaseDrag`): they change no picture to
|
|
223
|
+
// preview, and a new bound has the clamp question to ask first.
|
|
224
|
+
const defaultScrub = useScrub((next, raw) => writeParameterDefault(next, graph.id, parameter.id, raw));
|
|
225
|
+
const defaultSpec = useMemo(() => controlSpec(parameter), [parameter]);
|
|
226
|
+
const setStep = (raw: unknown): void => {
|
|
227
|
+
if (typeof raw === "number" && Number.isFinite(raw) && raw > 0) {
|
|
228
|
+
patch((found) => (found.step === raw ? found : { ...found, step: raw }));
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
const minDrag = useReleaseDrag((value) => setBound("min", value));
|
|
232
|
+
const maxDrag = useReleaseDrag((value) => setBound("max", value));
|
|
233
|
+
const stepDrag = useReleaseDrag(setStep);
|
|
234
|
+
|
|
235
|
+
// A CLEAR GOES WITH THE NUMBER IT CLEARS, so it unmounts under the press,
|
|
236
|
+
// and the focus fell to <body>: no ring, nothing announced, and the next
|
|
237
|
+
// keys went to the window's keymap. It hands the focus to the field it sat
|
|
238
|
+
// in, which stays, as a PropRow's reset does.
|
|
239
|
+
const clear = (words: string, title: string, run: () => void) => (
|
|
240
|
+
<button
|
|
241
|
+
type="button"
|
|
242
|
+
className="insp-key"
|
|
243
|
+
aria-label={`Clear ${name} ${words}`}
|
|
244
|
+
title={title}
|
|
245
|
+
disabled={locked}
|
|
246
|
+
onClick={(e) => {
|
|
247
|
+
const field = e.currentTarget.closest(".numfield")?.querySelector<HTMLElement>(".numfield-input");
|
|
248
|
+
run();
|
|
249
|
+
field?.focus();
|
|
250
|
+
}}
|
|
251
|
+
>
|
|
252
|
+
<IconClose />
|
|
253
|
+
</button>
|
|
254
|
+
);
|
|
255
|
+
const clearStep = (): void =>
|
|
256
|
+
patch((found) => {
|
|
257
|
+
if (found.step === undefined) return found;
|
|
258
|
+
const { step: _step, ...rest } = found;
|
|
259
|
+
return rest;
|
|
260
|
+
});
|
|
261
|
+
// The label is typed, never dragged.
|
|
262
|
+
const noScrub: ScrubHandlers = { start: () => {}, move: () => {}, end: () => {} };
|
|
263
|
+
// A field's accessible name starts with the words its row shows ("Minimum,
|
|
264
|
+
// Roughness"), the house rule, so speech input can say what it sees, and the
|
|
265
|
+
// control's name tells the cards apart.
|
|
266
|
+
const named = (words: string): string => `${words}, ${name}`;
|
|
267
|
+
|
|
268
|
+
return (
|
|
269
|
+
<details className="material-parameter-definition">
|
|
270
|
+
<summary>
|
|
271
|
+
<span>{parameter.label}</span>
|
|
272
|
+
<small>{parameter.type}</small>
|
|
273
|
+
</summary>
|
|
274
|
+
<div className="material-parameter-definition-body">
|
|
275
|
+
<PropRow
|
|
276
|
+
spec={{ key: "label", propName: "label", label: "Label", kind: "string", animatable: false, default: "" } as ControlSpec}
|
|
277
|
+
value={parameter.label}
|
|
278
|
+
name={named("Label")}
|
|
279
|
+
commit={(raw) => {
|
|
280
|
+
if (typeof raw === "string") patch((found) => (found.label === raw.trim() ? found : { ...found, label: raw.trim() }));
|
|
281
|
+
}}
|
|
282
|
+
scrub={noScrub}
|
|
283
|
+
/>
|
|
284
|
+
<PropRow
|
|
285
|
+
spec={defaultSpec}
|
|
286
|
+
label="Default"
|
|
287
|
+
name={named("Default")}
|
|
288
|
+
value={parameter.default}
|
|
289
|
+
// Graph colours are `#rrggbb`: the picker offers no alpha.
|
|
290
|
+
{...(parameter.type === "color" ? { opaqueColor: true } : {})}
|
|
291
|
+
commit={(raw) => {
|
|
292
|
+
if (isMaterialGraphValueForType(raw, parameter.type)) {
|
|
293
|
+
patch((found) => (valueEquals(found.default as Value, raw as Value) ? found : { ...found, default: raw }));
|
|
294
|
+
}
|
|
295
|
+
}}
|
|
296
|
+
scrub={defaultScrub}
|
|
297
|
+
/>
|
|
298
|
+
{float ? (
|
|
299
|
+
<>
|
|
300
|
+
<PropRow
|
|
301
|
+
spec={fieldSpec("min", "Minimum", parameter.max === undefined ? {} : { max: parameter.max }, parameter.step ?? 0.01)}
|
|
302
|
+
name={named("Minimum")}
|
|
303
|
+
value={minDrag.live ?? parameter.min ?? Number.NaN}
|
|
304
|
+
commit={(raw) => {
|
|
305
|
+
if (typeof raw === "number" && Number.isFinite(raw)) setBound("min", raw);
|
|
306
|
+
}}
|
|
307
|
+
scrub={minDrag.scrub}
|
|
308
|
+
{...(parameter.min === undefined
|
|
309
|
+
? {}
|
|
310
|
+
: { trailing: clear("minimum", "Clear the minimum: the control is open below", () => setBound("min", null)) })}
|
|
311
|
+
/>
|
|
312
|
+
<PropRow
|
|
313
|
+
spec={fieldSpec("max", "Maximum", parameter.min === undefined ? {} : { min: parameter.min }, parameter.step ?? 0.01)}
|
|
314
|
+
name={named("Maximum")}
|
|
315
|
+
value={maxDrag.live ?? parameter.max ?? Number.NaN}
|
|
316
|
+
commit={(raw) => {
|
|
317
|
+
if (typeof raw === "number" && Number.isFinite(raw)) setBound("max", raw);
|
|
318
|
+
}}
|
|
319
|
+
scrub={maxDrag.scrub}
|
|
320
|
+
{...(parameter.max === undefined
|
|
321
|
+
? {}
|
|
322
|
+
: { trailing: clear("maximum", "Clear the maximum: the control is open above", () => setBound("max", null)) })}
|
|
323
|
+
/>
|
|
324
|
+
{pending === null ? null : (
|
|
325
|
+
<div className="insp-notice is-warn" role="status" data-testid="material-parameter-clamp">
|
|
326
|
+
<span>
|
|
327
|
+
{pending.count === 1
|
|
328
|
+
? `A mesh holds a value outside ${rangeWords(pending.range)}.`
|
|
329
|
+
: `Meshes hold ${pending.count} values outside ${rangeWords(pending.range)}.`}
|
|
330
|
+
</span>
|
|
331
|
+
{/* The two answers on the house row of buttons: 28px house
|
|
332
|
+
buttons, 6px apart, sharing the row by their words. */}
|
|
333
|
+
<div className="insp-buttons material-parameter-clamp-actions">
|
|
334
|
+
<button
|
|
335
|
+
type="button"
|
|
336
|
+
className="btn"
|
|
337
|
+
data-testid="material-parameter-clamp-apply"
|
|
338
|
+
disabled={locked}
|
|
339
|
+
title="Set the range and move those values to its nearest end, in one undo step"
|
|
340
|
+
onClick={() => applyRange(pending.range, true)}
|
|
341
|
+
>
|
|
342
|
+
Clamp them
|
|
343
|
+
</button>
|
|
344
|
+
<button
|
|
345
|
+
type="button"
|
|
346
|
+
className="btn"
|
|
347
|
+
data-testid="material-parameter-clamp-cancel"
|
|
348
|
+
onClick={() => setPending(null)}
|
|
349
|
+
>
|
|
350
|
+
Keep the range
|
|
351
|
+
</button>
|
|
352
|
+
</div>
|
|
353
|
+
</div>
|
|
354
|
+
)}
|
|
355
|
+
<PropRow
|
|
356
|
+
spec={fieldSpec("step", "Step", { min: 0.0001 }, 0.01)}
|
|
357
|
+
name={named("Step")}
|
|
358
|
+
value={stepDrag.live ?? parameter.step ?? Number.NaN}
|
|
359
|
+
commit={setStep}
|
|
360
|
+
scrub={stepDrag.scrub}
|
|
361
|
+
{...(parameter.step === undefined
|
|
362
|
+
? {}
|
|
363
|
+
: { trailing: clear("step", "Clear the step: the field moves by its range's own quantum", clearStep) })}
|
|
364
|
+
/>
|
|
365
|
+
</>
|
|
366
|
+
) : null}
|
|
367
|
+
<div className="insp-buttons material-delete-parameter">
|
|
368
|
+
<button
|
|
369
|
+
type="button"
|
|
370
|
+
className="btn is-danger"
|
|
371
|
+
disabled={locked}
|
|
372
|
+
onClick={() => store.deleteProjectMaterialParameter(graph.id, parameter.id)}
|
|
373
|
+
>
|
|
374
|
+
Delete control
|
|
375
|
+
</button>
|
|
376
|
+
</div>
|
|
377
|
+
</div>
|
|
378
|
+
</details>
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* One control's value ON THIS MESH: the row, its diamond, its reset.
|
|
384
|
+
*
|
|
385
|
+
* The value shown is the binding's AT THE FRAME (an animated control shows its
|
|
386
|
+
* sampled value); whether the mesh stores a value of its own, and whether a
|
|
387
|
+
* track drives it, are read off the AUTHORED binding.
|
|
388
|
+
*/
|
|
389
|
+
function MaterialParameterRow({
|
|
390
|
+
graph,
|
|
391
|
+
parameter,
|
|
392
|
+
containerId,
|
|
393
|
+
layerId,
|
|
394
|
+
slot,
|
|
395
|
+
}: {
|
|
396
|
+
graph: MaterialGraph3D;
|
|
397
|
+
parameter: MaterialGraphParameter;
|
|
398
|
+
containerId: string;
|
|
399
|
+
layerId: string;
|
|
400
|
+
slot: number | null;
|
|
401
|
+
}) {
|
|
402
|
+
const store = useStore();
|
|
403
|
+
const doc = useShotEditor((state) => state.doc);
|
|
404
|
+
const frame = useShotEditor((state) => state.frame);
|
|
405
|
+
const defFrame = useShotEditor((state) => state.defFrame);
|
|
406
|
+
const editingDef = useShotEditor((state) => state.editingDef);
|
|
407
|
+
const layer = doc === null ? undefined : homeOf(doc, containerId)?.layersById[layerId];
|
|
408
|
+
const localFrame =
|
|
409
|
+
doc === null ? 0 : containerFrame(doc, containerId, editingDef === containerId ? defFrame : frame);
|
|
410
|
+
const effective =
|
|
411
|
+
doc === null || layer === undefined ? undefined : resolveTracksAt(layer, localFrame, doc.fps);
|
|
412
|
+
const binding = effective?.kind === "mesh" && isGraphMaterial(effective.mesh.material)
|
|
413
|
+
? materialBindingForSlot(effective.mesh.material, slot)
|
|
414
|
+
: undefined;
|
|
415
|
+
const value = materialGraphParameterValue(graph, binding?.overrides, parameter.id) ?? parameter.default;
|
|
416
|
+
const authored =
|
|
417
|
+
layer?.kind === "mesh" && isGraphMaterial(layer.mesh.material)
|
|
418
|
+
? materialBindingForSlot(layer.mesh.material, slot)
|
|
419
|
+
: undefined;
|
|
420
|
+
const override = authored?.overrides?.[parameter.id];
|
|
421
|
+
const path = materialGraphParamPath(parameter.id, slot);
|
|
422
|
+
const tracked = layer?.tracks?.[path] !== undefined;
|
|
423
|
+
const spec = controlSpec(parameter);
|
|
424
|
+
const vector = isVectorSpec(spec);
|
|
425
|
+
// The column draws a slot for whatever it is handed, even a pin that
|
|
426
|
+
// renders nothing, so the row asks before it hands one over.
|
|
427
|
+
const pinOffered = useExposeOffered(path);
|
|
428
|
+
const scrub = useScrub((base, raw) => {
|
|
429
|
+
const current = store.getState();
|
|
430
|
+
const next = graphValue(raw, parameter);
|
|
431
|
+
if (next === null) return base;
|
|
432
|
+
return writeMeshGraphOverride(
|
|
433
|
+
base,
|
|
434
|
+
containerId,
|
|
435
|
+
layerId,
|
|
436
|
+
parameter.id,
|
|
437
|
+
next,
|
|
438
|
+
containerFrame(base, containerId, current.editingDef === containerId ? current.defFrame : current.frame),
|
|
439
|
+
slot,
|
|
440
|
+
);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
// THE RESET, in the label column, so the box and its diamond never move
|
|
444
|
+
// when it appears. Shown while this mesh holds a value of its own or a track
|
|
445
|
+
// drives the control, the rule a component's rows follow (InstanceSection,
|
|
446
|
+
// THIS USE): drawn on every row, it took a 16px lane and a gap from every
|
|
447
|
+
// untouched mesh's name, which then read "Roughn…" at the default width.
|
|
448
|
+
// Under a track it is refused with the way out: a reset would take the
|
|
449
|
+
// keyframes with it (clearing a tracked value reads to the router as
|
|
450
|
+
// "remove its animation").
|
|
451
|
+
const reset: PropRowReset | undefined =
|
|
452
|
+
override === undefined && !tracked
|
|
453
|
+
? undefined
|
|
454
|
+
: {
|
|
455
|
+
onReset: () => store.setMeshGraphOverride(layerId, parameter.id, null, slot),
|
|
456
|
+
title: "Use the material's default on this mesh",
|
|
457
|
+
...(tracked
|
|
458
|
+
? {
|
|
459
|
+
refused: `${parameter.label} is animated on this mesh: its keyframes drive it. Remove them on the timeline to use the material's default.`,
|
|
460
|
+
}
|
|
461
|
+
: {}),
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
return (
|
|
465
|
+
<PropRow
|
|
466
|
+
spec={spec}
|
|
467
|
+
value={value}
|
|
468
|
+
commit={(raw) => {
|
|
469
|
+
const next = graphValue(raw, parameter);
|
|
470
|
+
if (next !== null) store.setMeshGraphOverride(layerId, parameter.id, next, slot);
|
|
471
|
+
}}
|
|
472
|
+
scrub={scrub}
|
|
473
|
+
// Every box of a vec2 or vec3 parameter carries its one track's
|
|
474
|
+
// diamond, naming the axes that key together. Its one expose pin sits
|
|
475
|
+
// in the label column instead, once per row (KeyframeDot's header):
|
|
476
|
+
// beside the first box's diamond it overflowed that third of the row.
|
|
477
|
+
// A one-box parameter keeps both in its box.
|
|
478
|
+
trailing={({ axes }) => <KeyframeDot path={path} label={parameter.label} pin={!vector} parts={axes} />}
|
|
479
|
+
{...(vector && pinOffered ? { actions: <KeyframeDot path={path} label={parameter.label} diamond={false} /> } : {})}
|
|
480
|
+
{...(reset === undefined ? {} : { reset })}
|
|
481
|
+
// THE BLUE DOT: this mesh holds its own value, not the material's.
|
|
482
|
+
changed={override !== undefined && !valueEquals(override as Value, parameter.default as Value)}
|
|
483
|
+
changedLabel="Changed on this mesh"
|
|
484
|
+
inherited={override === undefined && !tracked}
|
|
485
|
+
// A graph colour is `#rrggbb` (`isMaterialGraphValueForType`): the
|
|
486
|
+
// picker offers no alpha, so a drag cannot write a value it refuses.
|
|
487
|
+
{...(parameter.type === "color" ? { opaqueColor: true } : {})}
|
|
488
|
+
description={`Material parameter ${parameter.id}`}
|
|
489
|
+
rowProps={{ "data-material-parameter": parameter.id }}
|
|
490
|
+
/>
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function controlSpec(parameter: MaterialGraphParameter): ControlSpec {
|
|
495
|
+
const base = {
|
|
496
|
+
key: parameter.id,
|
|
497
|
+
propName: parameter.id,
|
|
498
|
+
label: parameter.label,
|
|
499
|
+
animatable: true as const,
|
|
500
|
+
};
|
|
501
|
+
if (parameter.type === "float") {
|
|
502
|
+
return {
|
|
503
|
+
...base,
|
|
504
|
+
kind: "number",
|
|
505
|
+
default: parameter.default as number,
|
|
506
|
+
...(parameter.min === undefined ? {} : { min: parameter.min }),
|
|
507
|
+
...(parameter.max === undefined ? {} : { max: parameter.max }),
|
|
508
|
+
...(parameter.step === undefined ? {} : { step: parameter.step }),
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
if (parameter.type === "color") return { ...base, kind: "color", default: parameter.default as string };
|
|
512
|
+
if (parameter.type === "vec2") {
|
|
513
|
+
return { ...base, kind: "vec2", default: parameter.default as [number, number], step: parameter.step ?? 0.01 };
|
|
514
|
+
}
|
|
515
|
+
return {
|
|
516
|
+
...base,
|
|
517
|
+
kind: "vec3",
|
|
518
|
+
default: parameter.default as [number, number, number],
|
|
519
|
+
step: parameter.step ?? 0.01,
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function graphValue(raw: unknown, parameter: MaterialGraphParameter): MaterialGraphValue | null {
|
|
524
|
+
return isMaterialGraphValueForType(raw, parameter.type) ? raw : null;
|
|
525
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// A wire's elbow: one dot, one input, one output.
|
|
2
|
+
//
|
|
3
|
+
// The registry types it `passthrough`, so it is genuinely transparent — the
|
|
4
|
+
// compiler emits the upstream expression and the dot costs nothing. It exists
|
|
5
|
+
// because a graph with a wire crossing the whole canvas is a graph nobody can
|
|
6
|
+
// read, and dragging a card sideways to fix that changes the layout of the
|
|
7
|
+
// thing rather than the route of the wire.
|
|
8
|
+
//
|
|
9
|
+
// Drawn small on purpose. It is punctuation, not a card, and giving it a title
|
|
10
|
+
// bar would make an author look for a value in it.
|
|
11
|
+
import { memo } from "react";
|
|
12
|
+
import { Handle, Position } from "@xyflow/react";
|
|
13
|
+
import type { NodeProps } from "@xyflow/react";
|
|
14
|
+
import { socketGlyphClass, socketTitle } from "./socketGlyph.js";
|
|
15
|
+
import { useSocketDrop } from "./MaterialNode.js";
|
|
16
|
+
import type { MaterialFlowNode } from "./flowModel.js";
|
|
17
|
+
|
|
18
|
+
export const MaterialRerouteNode = memo(function MaterialRerouteNode({ data }: NodeProps<MaterialFlowNode>) {
|
|
19
|
+
const { node, connected, outputType } = data;
|
|
20
|
+
// The dot wears the type it is CARRYING once one is resolved, so a chain of
|
|
21
|
+
// reroutes still says what is flowing through it. Unresolved (nothing wired
|
|
22
|
+
// in yet) falls back to the generic dashed circle its spec declares.
|
|
23
|
+
const type = outputType === null || outputType === "texture" ? "numeric" : outputType;
|
|
24
|
+
const inDrop = useSocketDrop(node.id, "value", "target");
|
|
25
|
+
const outDrop = useSocketDrop(node.id, "value", "source");
|
|
26
|
+
return (
|
|
27
|
+
<div className="material-reroute" data-testid={`material-node-${node.id}`} title={socketTitle("Reroute", type)}>
|
|
28
|
+
<Handle
|
|
29
|
+
type="target"
|
|
30
|
+
position={Position.Left}
|
|
31
|
+
id="value"
|
|
32
|
+
className={socketGlyphClass(type, { connected: connected.has("in:value"), drop: inDrop })}
|
|
33
|
+
/>
|
|
34
|
+
<Handle
|
|
35
|
+
type="source"
|
|
36
|
+
position={Position.Right}
|
|
37
|
+
id="value"
|
|
38
|
+
className={socketGlyphClass(type, { connected: connected.has("out:value"), drop: outDrop })}
|
|
39
|
+
/>
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
});
|