@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,85 +0,0 @@
|
|
|
1
|
-
// The During list: the blocks the Animate gesture writes, plus a palette to add
|
|
2
|
-
// one without a gesture.
|
|
3
|
-
//
|
|
4
|
-
// Each row FOCUSES rather than expanding inline. `state.selectedBlock` is the
|
|
5
|
-
// same bit the dock's bars write, so clicking a row here highlights the bar
|
|
6
|
-
// there and puts its timing in one editor below — instead of N accordion
|
|
7
|
-
// editors that each claim to be "the" timing controls.
|
|
8
|
-
import { motionPresets, presetsForSlot } from "@dailies-studio/shot/doc";
|
|
9
|
-
import { InspectorSection } from "@dailies-studio/ui";
|
|
10
|
-
import { useShotEditor, useStore } from "../../../state/context.js";
|
|
11
|
-
import { useSelectedLayers } from "../useDocWrite.js";
|
|
12
|
-
import { useBlocksInSlot } from "./useBlocks.js";
|
|
13
|
-
|
|
14
|
-
const DEFAULT_FRAMES = 24;
|
|
15
|
-
|
|
16
|
-
export function DuringBlocks() {
|
|
17
|
-
const store = useStore();
|
|
18
|
-
const layers = useSelectedLayers();
|
|
19
|
-
const blocks = useBlocksInSlot("during");
|
|
20
|
-
const focused = useShotEditor((s) => s.selectedBlock);
|
|
21
|
-
// The inspector's subject scene. Its layers are the selection's, so this is
|
|
22
|
-
// the scene every row here belongs to by construction.
|
|
23
|
-
const sceneId = useShotEditor((s) => s.sceneId);
|
|
24
|
-
const multi = layers.length > 1;
|
|
25
|
-
if (layers.length === 0 || sceneId === null) return null;
|
|
26
|
-
|
|
27
|
-
return (
|
|
28
|
-
<InspectorSection title="During" persistKey="shot:animate:during">
|
|
29
|
-
{blocks.length === 0 ? (
|
|
30
|
-
<p className="inspector-empty">
|
|
31
|
-
Press ⇧A, then drag on the canvas. The edit becomes a block ending at the playhead.
|
|
32
|
-
</p>
|
|
33
|
-
) : (
|
|
34
|
-
<ul className="animate-blocks" aria-label="During blocks">
|
|
35
|
-
{blocks.map(({ layer, block }) => {
|
|
36
|
-
const on = focused?.layerId === layer.id && focused.blockId === block.id;
|
|
37
|
-
const label = motionPresets[block.preset]?.label ?? block.preset;
|
|
38
|
-
return (
|
|
39
|
-
<li key={`${layer.id}:${block.id}`}>
|
|
40
|
-
<button
|
|
41
|
-
type="button"
|
|
42
|
-
className="animate-block-row"
|
|
43
|
-
aria-pressed={on}
|
|
44
|
-
aria-label={`Block ${label} on ${layer.label}`}
|
|
45
|
-
onClick={() => store.selectBlock({ sceneId, layerId: layer.id, blockId: block.id })}
|
|
46
|
-
>
|
|
47
|
-
<span className="animate-block-name">
|
|
48
|
-
{label}
|
|
49
|
-
{/* Which layer, but only when there IS more than one — on a
|
|
50
|
-
single selection the suffix would be noise on every row. */}
|
|
51
|
-
{multi && <em className="animate-block-owner"> · {layer.label}</em>}
|
|
52
|
-
</span>
|
|
53
|
-
<span className="animate-block-span">
|
|
54
|
-
f{block.frame}–{block.frame + Math.max(1, Math.trunc(block.durationFrames))}
|
|
55
|
-
</span>
|
|
56
|
-
</button>
|
|
57
|
-
<button
|
|
58
|
-
type="button"
|
|
59
|
-
className="animate-block-remove"
|
|
60
|
-
aria-label={`Remove ${label} on ${layer.label}`}
|
|
61
|
-
onClick={() => store.removeBlock(layer.id, block.id)}
|
|
62
|
-
>
|
|
63
|
-
✕
|
|
64
|
-
</button>
|
|
65
|
-
</li>
|
|
66
|
-
);
|
|
67
|
-
})}
|
|
68
|
-
</ul>
|
|
69
|
-
)}
|
|
70
|
-
<div className="animate-palette" role="group" aria-label="During presets">
|
|
71
|
-
{presetsForSlot("during").map((preset) => (
|
|
72
|
-
<button
|
|
73
|
-
key={preset.id}
|
|
74
|
-
type="button"
|
|
75
|
-
className="animate-preset"
|
|
76
|
-
aria-label={`Add ${preset.label}`}
|
|
77
|
-
onClick={() => store.addBlock("during", preset.id, DEFAULT_FRAMES)}
|
|
78
|
-
>
|
|
79
|
-
+ {preset.label}
|
|
80
|
-
</button>
|
|
81
|
-
))}
|
|
82
|
-
</div>
|
|
83
|
-
</InspectorSection>
|
|
84
|
-
);
|
|
85
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
// The In and Out palettes: click a preset, every selected layer gets that block.
|
|
2
|
-
//
|
|
3
|
-
// ── WHY THEY STACK RATHER THAN REPLACE ─────────────────────────────────
|
|
4
|
-
//
|
|
5
|
-
// Clicking Fade then Slide gives a layer that fades AND slides, because the
|
|
6
|
-
// channel combinators commute — the fold has no ordering rule to get wrong. So
|
|
7
|
-
// the palette is a set of buttons, not a radio group, and a preset already on
|
|
8
|
-
// the layer shows as active and REMOVES on a second click. A replace-only
|
|
9
|
-
// palette would make "fade and slide in" unauthorable, which is the single most
|
|
10
|
-
// common entrance in the entire category.
|
|
11
|
-
import { presetsForSlot } from "@dailies-studio/shot/doc";
|
|
12
|
-
import type { BlockSlot } from "@dailies-studio/shot/doc";
|
|
13
|
-
import { InspectorSection } from "@dailies-studio/ui";
|
|
14
|
-
import { useStore } from "../../../state/context.js";
|
|
15
|
-
import { useSelectedLayers } from "../useDocWrite.js";
|
|
16
|
-
import { useBlocksInSlot } from "./useBlocks.js";
|
|
17
|
-
|
|
18
|
-
/** The default window for a palette-added entrance or exit. */
|
|
19
|
-
const DEFAULT_FRAMES = 12;
|
|
20
|
-
|
|
21
|
-
export function InOutPresets(props: { slot: BlockSlot; title: string }) {
|
|
22
|
-
const store = useStore();
|
|
23
|
-
const layers = useSelectedLayers();
|
|
24
|
-
const present = useBlocksInSlot(props.slot);
|
|
25
|
-
if (layers.length === 0) return null;
|
|
26
|
-
|
|
27
|
-
// A preset counts as "on" when EVERY selected layer carries it. Partial
|
|
28
|
-
// coverage reads as off, so one more click completes the set rather than
|
|
29
|
-
// clearing the layers that already had it.
|
|
30
|
-
const coverage = new Map<string, number>();
|
|
31
|
-
for (const { block } of present) coverage.set(block.preset, (coverage.get(block.preset) ?? 0) + 1);
|
|
32
|
-
|
|
33
|
-
return (
|
|
34
|
-
<InspectorSection title={props.title} persistKey={`shot:animate:${props.slot}`}>
|
|
35
|
-
<div className="animate-palette" role="group" aria-label={`${props.title} presets`}>
|
|
36
|
-
{presetsForSlot(props.slot).map((preset) => {
|
|
37
|
-
const on = (coverage.get(preset.id) ?? 0) >= layers.length;
|
|
38
|
-
return (
|
|
39
|
-
<button
|
|
40
|
-
key={preset.id}
|
|
41
|
-
type="button"
|
|
42
|
-
className="animate-preset"
|
|
43
|
-
aria-label={`${props.title} ${preset.label}`}
|
|
44
|
-
aria-pressed={on}
|
|
45
|
-
onClick={() => {
|
|
46
|
-
if (on) store.removeBlocksByPreset(props.slot, preset.id);
|
|
47
|
-
else store.addBlock(props.slot, preset.id, DEFAULT_FRAMES);
|
|
48
|
-
}}
|
|
49
|
-
>
|
|
50
|
-
{preset.label}
|
|
51
|
-
</button>
|
|
52
|
-
);
|
|
53
|
-
})}
|
|
54
|
-
</div>
|
|
55
|
-
</InspectorSection>
|
|
56
|
-
);
|
|
57
|
-
}
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
// What the block stack ADDS UP TO on this layer, at this frame.
|
|
2
|
-
//
|
|
3
|
-
// ── WHY A READOUT AND NOT SEVEN MORE FIELDS ─────────────────────────────
|
|
4
|
-
//
|
|
5
|
-
// Blocks are DELTA writers and they compose (resolve.ts). So "what is this
|
|
6
|
-
// layer's opacity right now" has no single author: it is the base times every
|
|
7
|
-
// active block's contribution, and there is nothing for a field to write into
|
|
8
|
-
// that would not have to pick one of the contributors and hide the rest. The
|
|
9
|
-
// Design tab already solves the writable half — `setEffectiveField` compensates
|
|
10
|
-
// a typed value back through the fold — and this is the other half: a stacked
|
|
11
|
-
// delta model where the author can see the sum is legible, and one where they
|
|
12
|
-
// cannot is a pile of numbers that do not match the screen.
|
|
13
|
-
//
|
|
14
|
-
// ── IT CANNOT DISAGREE WITH THE STAGE, BY CONSTRUCTION ─────────────────
|
|
15
|
-
//
|
|
16
|
-
// Every number here comes out of `resolveLayerAt`, which is the same function
|
|
17
|
-
// the runtime writes onto the mesh and the same one the renderer samples. There
|
|
18
|
-
// is no second fold in this file and there must never be one: a readout that
|
|
19
|
-
// re-derived the composition would be a second answer to a question that has
|
|
20
|
-
// exactly one, and it would drift the first time a channel gained a combinator.
|
|
21
|
-
//
|
|
22
|
-
// ── "WRITING" MEANS DECLARED, NOT DIFFERENT ────────────────────────────
|
|
23
|
-
//
|
|
24
|
-
// A channel is marked when a live block's delta CONTAINS it, not when the
|
|
25
|
-
// folded value happens to differ from identity. The two come apart constantly:
|
|
26
|
-
// an out-fade at its first frame writes opacity 1, and an in-fade that finished
|
|
27
|
-
// forty frames ago still writes opacity 1 and still owns the channel. Marking
|
|
28
|
-
// on "differs from identity" would say "nothing is animating this" one frame
|
|
29
|
-
// before a fade visibly starts, and would need a second copy of resolve.ts's
|
|
30
|
-
// identity table to say it. Declared is both cheaper and truer: the mark means
|
|
31
|
-
// "a block governs this channel", which is exactly what an author about to type
|
|
32
|
-
// in the Design tab needs to know.
|
|
33
|
-
import { InspectorSection, formatValue } from "@dailies-studio/ui";
|
|
34
|
-
import {
|
|
35
|
-
blockProgressAt,
|
|
36
|
-
deltaFor,
|
|
37
|
-
layerDeltaAt,
|
|
38
|
-
motionPresets,
|
|
39
|
-
resolveLayerAt,
|
|
40
|
-
} from "@dailies-studio/shot/doc";
|
|
41
|
-
import type { Layer3D, LayerDelta } from "@dailies-studio/shot/doc";
|
|
42
|
-
import type { Rational } from "@dailies-studio/engine";
|
|
43
|
-
import { CHANNELS } from "../../../channels.js";
|
|
44
|
-
import type { Channel } from "../../../channels.js";
|
|
45
|
-
import { useShotEditor } from "../../../state/context.js";
|
|
46
|
-
import { useInspectorFrame, useSelectedLayers } from "../useDocWrite.js";
|
|
47
|
-
|
|
48
|
-
export function ResolvedChannels() {
|
|
49
|
-
const layers = useSelectedLayers();
|
|
50
|
-
const { sceneFrame } = useInspectorFrame();
|
|
51
|
-
const fps = useShotEditor((s) => s.doc?.fps ?? null);
|
|
52
|
-
// THE FIRST selected layer. A fan-out would have to answer "what is the
|
|
53
|
-
// resolved opacity of four layers", which is either four readouts or a
|
|
54
|
-
// "Mixed" that tells the author nothing about the composition they came here
|
|
55
|
-
// to understand. One subject, named in the header when there is a choice.
|
|
56
|
-
const layer = layers[0];
|
|
57
|
-
if (layer === undefined || fps === null) return null;
|
|
58
|
-
|
|
59
|
-
const state = resolveLayerAt(layer, sceneFrame, fps);
|
|
60
|
-
const delta = layerDeltaAt(layer, sceneFrame, fps);
|
|
61
|
-
const writers = writersAt(layer, sceneFrame, fps);
|
|
62
|
-
|
|
63
|
-
return (
|
|
64
|
-
<InspectorSection
|
|
65
|
-
title="Resolved"
|
|
66
|
-
persistKey="shot:animate:resolved"
|
|
67
|
-
action={
|
|
68
|
-
<span className="insp-dim">
|
|
69
|
-
{layers.length > 1 ? `${layer.label} · ` : ""}f{sceneFrame}
|
|
70
|
-
</span>
|
|
71
|
-
}
|
|
72
|
-
>
|
|
73
|
-
<dl className="resolved-list" aria-label="Resolved channels">
|
|
74
|
-
{CHANNELS.map((channel) => {
|
|
75
|
-
const by = writers.get(channel.key);
|
|
76
|
-
return (
|
|
77
|
-
<div
|
|
78
|
-
key={channel.key}
|
|
79
|
-
className="resolved-row"
|
|
80
|
-
data-channel={channel.key}
|
|
81
|
-
data-live={by !== undefined}
|
|
82
|
-
>
|
|
83
|
-
<dt className="resolved-label">{channel.label}</dt>
|
|
84
|
-
<dd className="resolved-value">{channel.read(state)}</dd>
|
|
85
|
-
<dd
|
|
86
|
-
className="resolved-by"
|
|
87
|
-
title={by === undefined ? undefined : `${by.join(", ")} → ${deltaText(channel.key, delta)}`}
|
|
88
|
-
>
|
|
89
|
-
{by?.join(", ") ?? ""}
|
|
90
|
-
</dd>
|
|
91
|
-
</div>
|
|
92
|
-
);
|
|
93
|
-
})}
|
|
94
|
-
</dl>
|
|
95
|
-
</InspectorSection>
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Which live blocks declare each channel, by preset label.
|
|
101
|
-
*
|
|
102
|
-
* `blockProgressAt` + `deltaFor` is the same pair `layerDeltaAt` folds with, so
|
|
103
|
-
* a block counted here is exactly a block that contributed to the numbers
|
|
104
|
-
* beside it — including the outside-the-window holds, which are the cases a
|
|
105
|
-
* naive "is the playhead inside the bar" test gets wrong in both directions.
|
|
106
|
-
*/
|
|
107
|
-
function writersAt(layer: Layer3D, sceneFrame: number, fps: Rational): Map<Channel, string[]> {
|
|
108
|
-
const out = new Map<Channel, string[]>();
|
|
109
|
-
for (const block of layer.blocks) {
|
|
110
|
-
const p = blockProgressAt(block, sceneFrame, fps);
|
|
111
|
-
if (p === null) continue;
|
|
112
|
-
const label = motionPresets[block.preset]?.label ?? block.preset;
|
|
113
|
-
for (const key of Object.keys(deltaFor(block.preset, block.slot, block.params, p)) as Channel[]) {
|
|
114
|
-
const at = out.get(key);
|
|
115
|
-
if (at === undefined) out.set(key, [label]);
|
|
116
|
-
// Two Fades on one channel are one name: the row says WHAT governs the
|
|
117
|
-
// channel, and the count is the dock's job.
|
|
118
|
-
else if (!at.includes(label)) at.push(label);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
return out;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/** The fold's own contribution to one channel, in that channel's combinator's
|
|
125
|
-
* terms — a multiply reads "×", an add reads signed. It is the answer to "how
|
|
126
|
-
* much of this number is the blocks", which the resolved value alone hides. */
|
|
127
|
-
function deltaText(key: Channel, d: Required<LayerDelta>): string {
|
|
128
|
-
switch (key) {
|
|
129
|
-
case "opacity":
|
|
130
|
-
return `×${formatValue(d.opacity, 0.01)}`;
|
|
131
|
-
case "offset":
|
|
132
|
-
return d.offset.map(signed).join(", ");
|
|
133
|
-
case "scale":
|
|
134
|
-
return d.scale.map((v) => `×${formatValue(v, 0.01)}`).join(", ");
|
|
135
|
-
case "tilt":
|
|
136
|
-
return d.tilt.map((v) => `${signed(v)}°`).join(", ");
|
|
137
|
-
case "roll":
|
|
138
|
-
return `${signed(d.roll)}°`;
|
|
139
|
-
case "blurPx":
|
|
140
|
-
return `${formatValue(d.blurPx, 0.1)} px`;
|
|
141
|
-
case "reveal":
|
|
142
|
-
return `${formatValue(d.reveal * 100, 1)}%`;
|
|
143
|
-
case "textPhase":
|
|
144
|
-
return `${formatValue(d.textPhase * 100, 1)}%`;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function signed(v: number): string {
|
|
149
|
-
return `${v >= 0 ? "+" : ""}${formatValue(v, 0.1)}`;
|
|
150
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
// What the Animate panel reads: the selection's blocks, and the focused one.
|
|
2
|
-
//
|
|
3
|
-
// The focused block is `state.selectedBlock`, which the timeline dock ALSO
|
|
4
|
-
// writes when a bar is clicked. One bit, two surfaces: clicking a bar in the
|
|
5
|
-
// dock puts its timing and easing in the inspector, and adding a block from the
|
|
6
|
-
// inspector focuses its bar. Two independent "which block" states would drift
|
|
7
|
-
// apart within one session.
|
|
8
|
-
import { useShotEditor } from "../../../state/context.js";
|
|
9
|
-
import { useSelectedLayers } from "../useDocWrite.js";
|
|
10
|
-
import type { AnimBlock, BlockSlot, Layer3D } from "@dailies-studio/shot/doc";
|
|
11
|
-
|
|
12
|
-
export interface BlockRef {
|
|
13
|
-
layer: Layer3D;
|
|
14
|
-
block: AnimBlock;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/** Every block on the selection in one slot, layer-major then frame-major.
|
|
18
|
-
* Sorted, because a fan-out across four layers otherwise lists in whatever
|
|
19
|
-
* order each layer happened to accumulate its blocks. */
|
|
20
|
-
export function useBlocksInSlot(slot: BlockSlot): BlockRef[] {
|
|
21
|
-
const layers = useSelectedLayers();
|
|
22
|
-
const out: BlockRef[] = [];
|
|
23
|
-
for (const layer of layers) {
|
|
24
|
-
for (const block of layer.blocks) {
|
|
25
|
-
if (block.slot === slot) out.push({ layer, block });
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
out.sort((a, b) => a.block.frame - b.block.frame || a.block.id.localeCompare(b.block.id));
|
|
29
|
-
return out;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** The block the timing and easing controls are about, or null. */
|
|
33
|
-
export function useFocusedBlock(): BlockRef | null {
|
|
34
|
-
const focused = useShotEditor((s) => s.selectedBlock);
|
|
35
|
-
const layers = useSelectedLayers();
|
|
36
|
-
if (focused === null) return null;
|
|
37
|
-
const layer = layers.find((l) => l.id === focused.layerId);
|
|
38
|
-
const block = layer?.blocks.find((b) => b.id === focused.blockId);
|
|
39
|
-
// A block whose layer left the selection is NOT shown: the panel's subject is
|
|
40
|
-
// the selection, and showing a stray block would let the author edit
|
|
41
|
-
// something the canvas is not highlighting.
|
|
42
|
-
return layer !== undefined && block !== undefined ? { layer, block } : null;
|
|
43
|
-
}
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
// Pick a TSL generator to paint over a model's albedo.
|
|
2
|
-
//
|
|
3
|
-
// ── NO GL IN HERE, AND THAT IS NOT A COMPROMISE ─────────────────────────
|
|
4
|
-
//
|
|
5
|
-
// The shot chrome is three-free by test, so a live preview tile would have to
|
|
6
|
-
// live in the stage iframe and be pushed across the bridge. It would also buy
|
|
7
|
-
// nothing: the studio already SHIPS a rendered PNG for every catalog entry
|
|
8
|
-
// (`packages/effects/thumbs`, served at the server's own thumbs route), made by
|
|
9
|
-
// the same generators on the same renderer. A live tile would show the same
|
|
10
|
-
// image for more machinery and one GL context per card.
|
|
11
|
-
//
|
|
12
|
-
// ── WHY IT IS A DIALOG AND NOT A PANEL ──────────────────────────────────
|
|
13
|
-
//
|
|
14
|
-
// Picking a material is a MODE the author enters from one place and leaves the
|
|
15
|
-
// moment they choose — the same shape `ExportDialog` has. A permanent panel
|
|
16
|
-
// would need a permanent home in a shell whose three columns are all spoken
|
|
17
|
-
// for, and would ask "which layer is this for?" on every render instead of
|
|
18
|
-
// once at the point the author asked.
|
|
19
|
-
import { useEffect, useMemo, useState } from "react";
|
|
20
|
-
import { useShotEditor, useStore } from "../../state/context.js";
|
|
21
|
-
import type { ComponentRow } from "../../api.js";
|
|
22
|
-
|
|
23
|
-
/** The one module whose builtins are TSL generators. Paper's shaders are DOM
|
|
24
|
-
* background layers and particles are emitters; neither is a surface a mesh
|
|
25
|
-
* can wear, and both would be a card that produced a document the runtime
|
|
26
|
-
* cannot resolve. */
|
|
27
|
-
const EFFECTS_MODULE = "@dailies-studio/effects";
|
|
28
|
-
|
|
29
|
-
/** Fixed shelf order, mirroring the 2D lane's Library. A category this chrome
|
|
30
|
-
* does not know (a newer server) appends after, alphabetically, rather than
|
|
31
|
-
* vanishing. */
|
|
32
|
-
const CATEGORY_ORDER = ["noise", "patterns", "organic", "space", "material", "decor"];
|
|
33
|
-
|
|
34
|
-
const CATEGORY_LABELS: Record<string, string> = {
|
|
35
|
-
noise: "Noise",
|
|
36
|
-
patterns: "Patterns",
|
|
37
|
-
organic: "Organic",
|
|
38
|
-
space: "Space",
|
|
39
|
-
material: "Material",
|
|
40
|
-
decor: "Decor",
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
function categoryLabel(category: string): string {
|
|
44
|
-
return CATEGORY_LABELS[category] ?? category.charAt(0).toUpperCase() + category.slice(1);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/** A stable colour per id, for a card whose thumbnail has not been generated.
|
|
48
|
-
* Deterministic so the same generator always wears the same placeholder. */
|
|
49
|
-
function thumbGradient(id: string): string {
|
|
50
|
-
let h = 2166136261;
|
|
51
|
-
for (let i = 0; i < id.length; i += 1) {
|
|
52
|
-
h ^= id.charCodeAt(i);
|
|
53
|
-
h = Math.imul(h, 16777619);
|
|
54
|
-
}
|
|
55
|
-
h = h >>> 0;
|
|
56
|
-
const hue1 = h % 360;
|
|
57
|
-
const hue2 = (hue1 + 40 + ((h >>> 9) % 160)) % 360;
|
|
58
|
-
return `linear-gradient(${(h >>> 17) % 360}deg, hsl(${hue1} 45% 30%), hsl(${hue2} 60% 55%))`;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function MaterialPicker() {
|
|
62
|
-
const store = useStore();
|
|
63
|
-
const request = useShotEditor((s) => s.materialPicker);
|
|
64
|
-
const library = useShotEditor((s) => s.library);
|
|
65
|
-
const libraryLoaded = useShotEditor((s) => s.libraryLoaded);
|
|
66
|
-
const [query, setQuery] = useState("");
|
|
67
|
-
|
|
68
|
-
useEffect(() => {
|
|
69
|
-
if (!libraryLoaded) void store.refreshLibrary();
|
|
70
|
-
}, [libraryLoaded, store]);
|
|
71
|
-
|
|
72
|
-
const shelves = useMemo(() => {
|
|
73
|
-
const needle = query.trim().toLowerCase();
|
|
74
|
-
const rows = library.filter(
|
|
75
|
-
(row): row is ComponentRow & { effect: NonNullable<ComponentRow["effect"]> } =>
|
|
76
|
-
row.builtin === true && row.host === "three" && row.rel === EFFECTS_MODULE && row.effect !== undefined,
|
|
77
|
-
);
|
|
78
|
-
const matching =
|
|
79
|
-
needle === ""
|
|
80
|
-
? rows
|
|
81
|
-
: rows.filter(
|
|
82
|
-
(row) =>
|
|
83
|
-
row.name.toLowerCase().includes(needle) ||
|
|
84
|
-
row.effect.tags.some((t) => t.toLowerCase().includes(needle)),
|
|
85
|
-
);
|
|
86
|
-
const byCategory = new Map<string, typeof matching>();
|
|
87
|
-
for (const row of matching) {
|
|
88
|
-
const list = byCategory.get(row.effect.category);
|
|
89
|
-
if (list) list.push(row);
|
|
90
|
-
else byCategory.set(row.effect.category, [row]);
|
|
91
|
-
}
|
|
92
|
-
return [...byCategory.entries()].sort((a, b) => {
|
|
93
|
-
const ai = CATEGORY_ORDER.indexOf(a[0]);
|
|
94
|
-
const bi = CATEGORY_ORDER.indexOf(b[0]);
|
|
95
|
-
if (ai === bi) return a[0].localeCompare(b[0]);
|
|
96
|
-
// An unknown category sorts after every known one rather than at the top.
|
|
97
|
-
return (ai < 0 ? CATEGORY_ORDER.length : ai) - (bi < 0 ? CATEGORY_ORDER.length : bi);
|
|
98
|
-
});
|
|
99
|
-
}, [library, query]);
|
|
100
|
-
|
|
101
|
-
if (request === null) return null;
|
|
102
|
-
const count = request.layerIds.length;
|
|
103
|
-
|
|
104
|
-
return (
|
|
105
|
-
<div className="dialog-overlay" onPointerDown={() => store.closeMaterialPicker()}>
|
|
106
|
-
<div
|
|
107
|
-
className="dialog material-picker"
|
|
108
|
-
role="dialog"
|
|
109
|
-
aria-label="Choose a material"
|
|
110
|
-
onPointerDown={(e) => e.stopPropagation()}
|
|
111
|
-
>
|
|
112
|
-
<div className="dialog-title">Material</div>
|
|
113
|
-
<div className="dialog-field">
|
|
114
|
-
<span className="dialog-label">{count === 1 ? "1 mesh layer" : `${count} mesh layers`}</span>
|
|
115
|
-
<input
|
|
116
|
-
className="material-search"
|
|
117
|
-
type="search"
|
|
118
|
-
placeholder="Search materials"
|
|
119
|
-
value={query}
|
|
120
|
-
onChange={(e) => setQuery(e.target.value)}
|
|
121
|
-
autoFocus
|
|
122
|
-
/>
|
|
123
|
-
</div>
|
|
124
|
-
<div className="dialog-body">
|
|
125
|
-
{!libraryLoaded ? (
|
|
126
|
-
<p className="dialog-message">Loading…</p>
|
|
127
|
-
) : shelves.length === 0 ? (
|
|
128
|
-
<p className="dialog-message">No material matches “{query}”.</p>
|
|
129
|
-
) : (
|
|
130
|
-
shelves.map(([category, rows]) => (
|
|
131
|
-
<section key={category} className="material-shelf">
|
|
132
|
-
<h3>{categoryLabel(category)}</h3>
|
|
133
|
-
<div className="material-grid">
|
|
134
|
-
{rows.map((row) => (
|
|
135
|
-
<button
|
|
136
|
-
key={row.id}
|
|
137
|
-
type="button"
|
|
138
|
-
className="material-card"
|
|
139
|
-
title={
|
|
140
|
-
row.effect.softwareRaster === "unfinished"
|
|
141
|
-
? `${row.name} — may not finish a software-rasterized render (--gl swangle); use --gl angle.`
|
|
142
|
-
: row.name
|
|
143
|
-
}
|
|
144
|
-
onClick={() => store.applyMeshMaterial(request.layerIds, row.id)}
|
|
145
|
-
>
|
|
146
|
-
{row.effect.thumb === undefined ? (
|
|
147
|
-
<span className="material-thumb" style={{ background: thumbGradient(row.id) }} />
|
|
148
|
-
) : (
|
|
149
|
-
<img className="material-thumb" src={row.effect.thumb} alt="" />
|
|
150
|
-
)}
|
|
151
|
-
<span className="material-name">{row.name}</span>
|
|
152
|
-
{row.effect.softwareRaster === "unfinished" ? (
|
|
153
|
-
<span className="material-badge" aria-hidden="true">
|
|
154
|
-
slow
|
|
155
|
-
</span>
|
|
156
|
-
) : null}
|
|
157
|
-
</button>
|
|
158
|
-
))}
|
|
159
|
-
</div>
|
|
160
|
-
</section>
|
|
161
|
-
))
|
|
162
|
-
)}
|
|
163
|
-
</div>
|
|
164
|
-
<div className="dialog-actions">
|
|
165
|
-
<button type="button" onClick={() => store.closeMaterialPicker()}>
|
|
166
|
-
Cancel
|
|
167
|
-
</button>
|
|
168
|
-
</div>
|
|
169
|
-
</div>
|
|
170
|
-
</div>
|
|
171
|
-
);
|
|
172
|
-
}
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
// The SCENE LANE: scenes back to back over the master strip.
|
|
2
|
-
//
|
|
3
|
-
// Slim and secondary, and the ONLY home for the three verbs that are about
|
|
4
|
-
// scenes rather than about time — create, reorder, trim. The tracks below show
|
|
5
|
-
// every scene at once now, so this lane no longer decides what is on screen; it
|
|
6
|
-
// is the handle on the document's coarsest structure, sitting directly above the
|
|
7
|
-
// boundaries it draws.
|
|
8
|
-
//
|
|
9
|
-
// A bar's WIDTH is `scene.durationFrames` and its POSITION is a prefix sum over
|
|
10
|
-
// `doc.scenes` — derived, never stored. So trimming a bar writes one field and
|
|
11
|
-
// every bar after it moves for free, and reordering is a splice. Storing
|
|
12
|
-
// per-scene offsets would desync on both, and the desync only shows up in an
|
|
13
|
-
// export.
|
|
14
|
-
import { frameToPx, startDrag } from "@dailies-studio/timeline";
|
|
15
|
-
import type { PointerEvent as ReactPointerEvent } from "react";
|
|
16
|
-
import { useShotEditor, useStore } from "../../state/context.js";
|
|
17
|
-
import { frameDelta, sceneBars } from "./model.js";
|
|
18
|
-
|
|
19
|
-
/** Its own component so the per-frame tick re-renders one absolutely
|
|
20
|
-
* positioned div, not every scene bar above it. */
|
|
21
|
-
function ScenePlayhead({ ppf }: { ppf: number }) {
|
|
22
|
-
const frame = useShotEditor((s) => s.frame);
|
|
23
|
-
return <div className="scene-playhead" style={{ left: frameToPx(frame, ppf) }} />;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function SceneStrip({ ppf, labelWidth }: { ppf: number; labelWidth: number }) {
|
|
27
|
-
const store = useStore();
|
|
28
|
-
const doc = useShotEditor((s) => s.doc);
|
|
29
|
-
const sceneId = useShotEditor((s) => s.sceneId);
|
|
30
|
-
if (doc === null) return null;
|
|
31
|
-
const bars = sceneBars(doc);
|
|
32
|
-
|
|
33
|
-
return (
|
|
34
|
-
<div className="scene-strip" data-testid="scene-strip">
|
|
35
|
-
<div className="scene-strip-label" style={{ width: labelWidth }}>
|
|
36
|
-
<span>Shots</span>
|
|
37
|
-
<button
|
|
38
|
-
type="button"
|
|
39
|
-
className="scene-strip-add"
|
|
40
|
-
aria-label="Add shot"
|
|
41
|
-
title="Add a shot at the end, and go to it"
|
|
42
|
-
onClick={() => store.addScene()}
|
|
43
|
-
>
|
|
44
|
-
+
|
|
45
|
-
</button>
|
|
46
|
-
</div>
|
|
47
|
-
<div className="scene-strip-lane">
|
|
48
|
-
{bars.map((bar, index) => (
|
|
49
|
-
<div
|
|
50
|
-
key={bar.id}
|
|
51
|
-
className={`scene-bar${bar.id === sceneId ? " is-current" : ""}`}
|
|
52
|
-
style={{ left: frameToPx(bar.start, ppf), width: Math.max(8, frameToPx(bar.end - bar.start, ppf)) }}
|
|
53
|
-
data-scene={bar.id}
|
|
54
|
-
draggable
|
|
55
|
-
onDragStart={(e) => e.dataTransfer.setData("text/plain", bar.id)}
|
|
56
|
-
onDragOver={(e) => e.preventDefault()}
|
|
57
|
-
onDrop={(e) => {
|
|
58
|
-
e.preventDefault();
|
|
59
|
-
const from = e.dataTransfer.getData("text/plain");
|
|
60
|
-
if (from.length > 0 && from !== bar.id) store.reorderScene(from, index);
|
|
61
|
-
}}
|
|
62
|
-
onClick={() => {
|
|
63
|
-
// Clicking a scene both SELECTS it and moves the playhead into
|
|
64
|
-
// it: the micro lane follows the playhead, so selecting without
|
|
65
|
-
// seeking would show one scene's tracks under another's ruler.
|
|
66
|
-
// The frame is read AT THE CLICK, not subscribed: a handler
|
|
67
|
-
// needs the current value once, and the subscription was what
|
|
68
|
-
// re-rendered every bar on every playback frame.
|
|
69
|
-
store.setScene(bar.id);
|
|
70
|
-
const frame = store.getState().frame;
|
|
71
|
-
if (frame < bar.start || frame >= bar.end) store.seek(bar.start);
|
|
72
|
-
}}
|
|
73
|
-
// Zoom the dock to this shot. A COMPOUND on purpose: the single
|
|
74
|
-
// click above fires first on both clicks, so the shot is selected
|
|
75
|
-
// and the playhead is inside it before the lane reframes around
|
|
76
|
-
// it. `\` (fit) is the way back out.
|
|
77
|
-
onDoubleClick={() => store.fitDock(bar.id)}
|
|
78
|
-
title="Double-click to zoom the timeline to this shot"
|
|
79
|
-
>
|
|
80
|
-
<span className="scene-bar-name">{bar.name}</span>
|
|
81
|
-
<SceneTrimHandle
|
|
82
|
-
onDrag={(deltaPx) => store.trimScene(bar.id, bar.end - bar.start + frameDelta(deltaPx, ppf))}
|
|
83
|
-
onStart={() => store.beginBlockDrag()}
|
|
84
|
-
onEnd={(commit) => store.endBlockDrag(commit)}
|
|
85
|
-
/>
|
|
86
|
-
</div>
|
|
87
|
-
))}
|
|
88
|
-
{/* The boundaries themselves. A diamond per cut, filled when the shot
|
|
89
|
-
before it blends into the one after — strip geometry rather than bar
|
|
90
|
-
geometry, because a transition is about the SEAM and the bars stay
|
|
91
|
-
scene-local. Nothing after the last bar: there is no seam there. */}
|
|
92
|
-
{bars.slice(0, -1).map((bar) => (
|
|
93
|
-
<button
|
|
94
|
-
key={`cut-${bar.id}`}
|
|
95
|
-
type="button"
|
|
96
|
-
className={`scene-cut${doc.scenesById[bar.id]?.transition !== undefined ? " is-blend" : ""}`}
|
|
97
|
-
style={{ left: frameToPx(bar.end, ppf) }}
|
|
98
|
-
aria-label={`Transition after ${bar.name}`}
|
|
99
|
-
title={
|
|
100
|
-
doc.scenesById[bar.id]?.transition === undefined
|
|
101
|
-
? `Cut after ${bar.name}`
|
|
102
|
-
: `${doc.scenesById[bar.id]!.transition!.kind} after ${bar.name}`
|
|
103
|
-
}
|
|
104
|
-
onClick={(e) => {
|
|
105
|
-
e.stopPropagation();
|
|
106
|
-
// Select the OUTGOING shot: that is where the transition is
|
|
107
|
-
// stored and where the inspector edits it. The playhead goes to
|
|
108
|
-
// the last frame it owns, so the stage shows the frame the blend
|
|
109
|
-
// starts from.
|
|
110
|
-
store.setScene(bar.id);
|
|
111
|
-
store.seek(Math.max(bar.start, bar.end - 1));
|
|
112
|
-
}}
|
|
113
|
-
/>
|
|
114
|
-
))}
|
|
115
|
-
{/* One playhead across the whole master timeline, so the macro lane
|
|
116
|
-
always shows where the export is. */}
|
|
117
|
-
<ScenePlayhead ppf={ppf} />
|
|
118
|
-
</div>
|
|
119
|
-
</div>
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function SceneTrimHandle({
|
|
124
|
-
onStart,
|
|
125
|
-
onDrag,
|
|
126
|
-
onEnd,
|
|
127
|
-
}: {
|
|
128
|
-
onStart(): void;
|
|
129
|
-
onDrag(deltaPx: number): void;
|
|
130
|
-
onEnd(commit: boolean): void;
|
|
131
|
-
}) {
|
|
132
|
-
const onPointerDown = (e: ReactPointerEvent<HTMLSpanElement>): void => {
|
|
133
|
-
e.preventDefault();
|
|
134
|
-
e.stopPropagation();
|
|
135
|
-
const startX = e.clientX;
|
|
136
|
-
onStart();
|
|
137
|
-
// Absolute from the PRESS, never cumulative — the same rule every drag in
|
|
138
|
-
// this lane follows, and the reason a coalesced move cannot make it drift.
|
|
139
|
-
startDrag(e, {
|
|
140
|
-
onMove: (ev) => onDrag(ev.clientX - startX),
|
|
141
|
-
onEnd: (commit) => onEnd(commit),
|
|
142
|
-
});
|
|
143
|
-
};
|
|
144
|
-
return <span className="scene-trim" data-handle="scene-out" onPointerDown={onPointerDown} aria-hidden="true" />;
|
|
145
|
-
}
|