@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,118 +0,0 @@
|
|
|
1
|
-
// The rail's icon set: hand-drawn 24-viewBox strokes at 1.5, tinted by the CSS
|
|
2
|
-
// `color` of whatever wraps them. Still no icon dependency — but a family of
|
|
3
|
-
// consistent strokes, where the old unicode glyphs never read as one voice.
|
|
4
|
-
//
|
|
5
|
-
// Sized by CSS (`.layer-kind svg`, `.layer-flag svg`), never by attributes, so
|
|
6
|
-
// one rule owns the scale everywhere the set appears.
|
|
7
|
-
import type { ReactElement, ReactNode } from "react";
|
|
8
|
-
|
|
9
|
-
function icon(children: ReactNode): ReactElement {
|
|
10
|
-
return (
|
|
11
|
-
<svg
|
|
12
|
-
viewBox="0 0 24 24"
|
|
13
|
-
fill="none"
|
|
14
|
-
stroke="currentColor"
|
|
15
|
-
strokeWidth={1.5}
|
|
16
|
-
strokeLinecap="round"
|
|
17
|
-
strokeLinejoin="round"
|
|
18
|
-
aria-hidden="true"
|
|
19
|
-
>
|
|
20
|
-
{children}
|
|
21
|
-
</svg>
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** One icon per layer kind — the same grammar Figma settled: a lone diamond is
|
|
26
|
-
* an instance, four diamonds are a definition. */
|
|
27
|
-
const KIND_ICONS: Record<string, ReactElement> = {
|
|
28
|
-
text: icon(<path d="M6 6h12M12 6v13" />),
|
|
29
|
-
image: icon(
|
|
30
|
-
<>
|
|
31
|
-
<rect x="3.5" y="5" width="17" height="14" rx="2" />
|
|
32
|
-
<circle cx="9" cy="9.5" r="1.5" />
|
|
33
|
-
<path d="m4.5 17 4-4 4 4 3-3 4 4" />
|
|
34
|
-
</>,
|
|
35
|
-
),
|
|
36
|
-
video: icon(
|
|
37
|
-
<>
|
|
38
|
-
<rect x="3.5" y="5" width="17" height="14" rx="2" />
|
|
39
|
-
<path d="M10.5 9.5v5l4-2.5z" />
|
|
40
|
-
</>,
|
|
41
|
-
),
|
|
42
|
-
rect: icon(<rect x="4.5" y="6.5" width="15" height="11" rx="1.5" />),
|
|
43
|
-
ellipse: icon(<circle cx="12" cy="12" r="7.5" />),
|
|
44
|
-
line: icon(<path d="M5.5 18.5 18.5 5.5" />),
|
|
45
|
-
arrow: icon(<path d="M6 18 18 6M9 6h9v9" />),
|
|
46
|
-
path: icon(<path d="M4.5 19.5c7 0 8-15 15-15" />),
|
|
47
|
-
// Angle brackets: the one glyph nobody has to be told means markup.
|
|
48
|
-
html: icon(<path d="m9 8-4 4 4 4M15 8l4 4-4 4" />),
|
|
49
|
-
group: icon(
|
|
50
|
-
<>
|
|
51
|
-
<rect x="4" y="4" width="12" height="12" rx="2" />
|
|
52
|
-
<path d="M20 9v9a2 2 0 0 1-2 2H9" />
|
|
53
|
-
</>,
|
|
54
|
-
),
|
|
55
|
-
device: icon(
|
|
56
|
-
<>
|
|
57
|
-
<rect x="7" y="3.5" width="10" height="17" rx="2" />
|
|
58
|
-
<path d="M10.5 17.5h3" />
|
|
59
|
-
</>,
|
|
60
|
-
),
|
|
61
|
-
mesh: icon(<path d="M12 3.5 19.5 8v8L12 20.5 4.5 16V8z" />),
|
|
62
|
-
cloner: icon(
|
|
63
|
-
<>
|
|
64
|
-
<rect x="4.5" y="4.5" width="6" height="6" rx="1" />
|
|
65
|
-
<rect x="13.5" y="4.5" width="6" height="6" rx="1" />
|
|
66
|
-
<rect x="4.5" y="13.5" width="6" height="6" rx="1" />
|
|
67
|
-
<rect x="13.5" y="13.5" width="6" height="6" rx="1" />
|
|
68
|
-
</>,
|
|
69
|
-
),
|
|
70
|
-
instance: icon(<path d="m12 4 8 8-8 8-8-8z" />),
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const FALLBACK = icon(<path d="m12 6 6 6-6 6-6-6z" />);
|
|
74
|
-
|
|
75
|
-
export function kindIcon(kind: string): ReactElement {
|
|
76
|
-
return KIND_ICONS[kind] ?? FALLBACK;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** A shot row: a clapperboard. */
|
|
80
|
-
export const SCENE_ICON = icon(
|
|
81
|
-
<>
|
|
82
|
-
<rect x="3.5" y="5" width="17" height="14" rx="2" />
|
|
83
|
-
<path d="M3.5 9.5h17M9 5.5l1.5 4M14 5.5l1.5 4" />
|
|
84
|
-
</>,
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
/** A component definition: the four-diamond glyph. */
|
|
88
|
-
export const COMPONENT_ICON = icon(
|
|
89
|
-
<path d="M12 3.5 15 6.5 12 9.5 9 6.5zM17.5 9l3 3-3 3-3-3zM12 14.5l3 3-3 3-3-3zM6.5 9l3 3-3 3-3-3z" />,
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
export const EYE_ICON = icon(
|
|
93
|
-
<>
|
|
94
|
-
<path d="M3.5 12S6.5 6 12 6s8.5 6 8.5 6-3 6-8.5 6S3.5 12 3.5 12z" />
|
|
95
|
-
<circle cx="12" cy="12" r="2.5" />
|
|
96
|
-
</>,
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
export const EYE_OFF_ICON = icon(
|
|
100
|
-
<>
|
|
101
|
-
<path d="M3.5 12S6.5 6 12 6s8.5 6 8.5 6-3 6-8.5 6S3.5 12 3.5 12z" />
|
|
102
|
-
<path d="m5 4.5 14 15" />
|
|
103
|
-
</>,
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
export const LOCK_ICON = icon(
|
|
107
|
-
<>
|
|
108
|
-
<rect x="5.5" y="10.5" width="13" height="9" rx="2" />
|
|
109
|
-
<path d="M8.5 10.5V8a3.5 3.5 0 0 1 7 0v2.5" />
|
|
110
|
-
</>,
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
export const UNLOCK_ICON = icon(
|
|
114
|
-
<>
|
|
115
|
-
<rect x="5.5" y="10.5" width="13" height="9" rx="2" />
|
|
116
|
-
<path d="M8.5 10.5V8a3.5 3.5 0 0 1 6.8-1.2" />
|
|
117
|
-
</>,
|
|
118
|
-
);
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
// Which scene is open, and how to open another one.
|
|
2
|
-
//
|
|
3
|
-
// A `<select>` and not a custom popover, deliberately: it is the one control in
|
|
4
|
-
// this chrome that has to work before anything else does (there is no document
|
|
5
|
-
// yet), and the native element brings keyboard navigation, type-ahead and a
|
|
6
|
-
// scrolling list that never leaves the viewport for free.
|
|
7
|
-
import { useState } from "react";
|
|
8
|
-
import { useShotEditor, useStore } from "../../state/context.js";
|
|
9
|
-
|
|
10
|
-
export function DocSwitcher() {
|
|
11
|
-
const store = useStore();
|
|
12
|
-
const shots = useShotEditor((s) => s.shots);
|
|
13
|
-
const name = useShotEditor((s) => s.name);
|
|
14
|
-
const [creating, setCreating] = useState(false);
|
|
15
|
-
const [draft, setDraft] = useState("");
|
|
16
|
-
|
|
17
|
-
const create = (): void => {
|
|
18
|
-
const trimmed = draft.trim();
|
|
19
|
-
setCreating(false);
|
|
20
|
-
setDraft("");
|
|
21
|
-
if (trimmed.length > 0) void store.createShot(trimmed);
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
if (creating) {
|
|
25
|
-
return (
|
|
26
|
-
<form
|
|
27
|
-
className="doc-switcher"
|
|
28
|
-
onSubmit={(e) => {
|
|
29
|
-
e.preventDefault();
|
|
30
|
-
create();
|
|
31
|
-
}}
|
|
32
|
-
>
|
|
33
|
-
<input
|
|
34
|
-
className="doc-switcher-input"
|
|
35
|
-
autoFocus
|
|
36
|
-
value={draft}
|
|
37
|
-
placeholder="scene name"
|
|
38
|
-
aria-label="New scene name"
|
|
39
|
-
onChange={(e) => setDraft(e.target.value)}
|
|
40
|
-
onKeyDown={(e) => {
|
|
41
|
-
if (e.key === "Escape") {
|
|
42
|
-
e.stopPropagation();
|
|
43
|
-
setCreating(false);
|
|
44
|
-
setDraft("");
|
|
45
|
-
}
|
|
46
|
-
}}
|
|
47
|
-
/>
|
|
48
|
-
</form>
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return (
|
|
53
|
-
<div className="doc-switcher">
|
|
54
|
-
<select
|
|
55
|
-
className="doc-switcher-select"
|
|
56
|
-
aria-label="Open scene"
|
|
57
|
-
value={name ?? ""}
|
|
58
|
-
onChange={(e) => {
|
|
59
|
-
const next = e.target.value;
|
|
60
|
-
// Closing DROPS the in-memory document, so the debounced autosave has
|
|
61
|
-
// to land first — the same order `openShot` uses. `flush` rejects
|
|
62
|
-
// when it could not write, which leaves the scene open with the
|
|
63
|
-
// failure already on screen instead of discarding it.
|
|
64
|
-
if (next === "") void store.flush().then(() => store.closeShot(), () => {});
|
|
65
|
-
else void store.openShot(next);
|
|
66
|
-
}}
|
|
67
|
-
>
|
|
68
|
-
<option value="">{shots.length === 0 ? "no scenes yet" : "choose a scene…"}</option>
|
|
69
|
-
{shots.map((s) => (
|
|
70
|
-
<option key={s.name} value={s.name}>
|
|
71
|
-
{s.name}
|
|
72
|
-
</option>
|
|
73
|
-
))}
|
|
74
|
-
</select>
|
|
75
|
-
<button
|
|
76
|
-
type="button"
|
|
77
|
-
className="doc-switcher-new"
|
|
78
|
-
title="New scene"
|
|
79
|
-
onClick={() => setCreating(true)}
|
|
80
|
-
>
|
|
81
|
-
+
|
|
82
|
-
</button>
|
|
83
|
-
</div>
|
|
84
|
-
);
|
|
85
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
// Play, pause, and the playhead.
|
|
2
|
-
//
|
|
3
|
-
// ── THE ONE THING WORTH READING ──────────────────────────────────────────
|
|
4
|
-
//
|
|
5
|
-
// Playback is a rAF loop that converts WALL TIME into an INTEGER FRAME and
|
|
6
|
-
// seeks to it. It never accumulates frames per tick.
|
|
7
|
-
//
|
|
8
|
-
// The difference matters: `frame++` per rAF makes playback speed a function of
|
|
9
|
-
// the monitor's refresh rate (a 120Hz laptop plays a 30fps shot at double
|
|
10
|
-
// speed) and makes a dropped frame permanently shift everything after it.
|
|
11
|
-
// Deriving the frame from elapsed time instead means the preview plays at the
|
|
12
|
-
// document's own rate on any display, drops frames under load rather than
|
|
13
|
-
// slowing down, and — because the seek target is `round(elapsed * fps)` — puts
|
|
14
|
-
// the same integer frame on screen that the export will render.
|
|
15
|
-
import { useEffect, useRef } from "react";
|
|
16
|
-
import { docDurationFrames } from "@dailies-studio/shot/doc";
|
|
17
|
-
import { useShotEditor, useStore } from "../../state/context.js";
|
|
18
|
-
|
|
19
|
-
/** The slider and the readout — the two things that show the frame — in their
|
|
20
|
-
* own component, so the per-frame tick during playback re-renders them alone
|
|
21
|
-
* and never the play button or the rAF loop's owner above. */
|
|
22
|
-
function TransportScrub({ total, playing }: { total: number; playing: boolean }) {
|
|
23
|
-
const store = useStore();
|
|
24
|
-
const frame = useShotEditor((s) => s.frame);
|
|
25
|
-
return (
|
|
26
|
-
<>
|
|
27
|
-
<input
|
|
28
|
-
className="transport-scrub"
|
|
29
|
-
type="range"
|
|
30
|
-
min={0}
|
|
31
|
-
max={Math.max(0, total - 1)}
|
|
32
|
-
step={1}
|
|
33
|
-
value={frame}
|
|
34
|
-
aria-label="Playhead"
|
|
35
|
-
onChange={(e) => {
|
|
36
|
-
// Scrubbing pauses. A transport that kept playing under the pointer
|
|
37
|
-
// would fight the drag on every rAF.
|
|
38
|
-
if (playing) store.setPlaying(false);
|
|
39
|
-
store.seek(Number(e.target.value));
|
|
40
|
-
}}
|
|
41
|
-
/>
|
|
42
|
-
<span className="transport-readout" data-testid="frame-readout">
|
|
43
|
-
{frame} / {Math.max(0, total - 1)}
|
|
44
|
-
</span>
|
|
45
|
-
</>
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function TransportBar() {
|
|
50
|
-
const store = useStore();
|
|
51
|
-
const playing = useShotEditor((s) => s.playing);
|
|
52
|
-
const doc = useShotEditor((s) => s.doc);
|
|
53
|
-
const total = doc === null ? 0 : docDurationFrames(doc);
|
|
54
|
-
const fps = doc === null ? 30 : doc.fps.num / doc.fps.den;
|
|
55
|
-
|
|
56
|
-
const startedAt = useRef(0);
|
|
57
|
-
const startedFrame = useRef(0);
|
|
58
|
-
useEffect(() => {
|
|
59
|
-
if (!playing || doc === null || total <= 0) return;
|
|
60
|
-
startedAt.current = performance.now();
|
|
61
|
-
startedFrame.current = store.getState().frame;
|
|
62
|
-
let raf = 0;
|
|
63
|
-
const tick = (): void => {
|
|
64
|
-
const elapsed = (performance.now() - startedAt.current) / 1000;
|
|
65
|
-
const target = startedFrame.current + Math.round(elapsed * fps);
|
|
66
|
-
store.seek(((target % total) + total) % total);
|
|
67
|
-
raf = requestAnimationFrame(tick);
|
|
68
|
-
};
|
|
69
|
-
raf = requestAnimationFrame(tick);
|
|
70
|
-
return () => cancelAnimationFrame(raf);
|
|
71
|
-
}, [playing, doc, total, fps, store]);
|
|
72
|
-
|
|
73
|
-
return (
|
|
74
|
-
<div className="transport">
|
|
75
|
-
<button
|
|
76
|
-
type="button"
|
|
77
|
-
className="transport-play"
|
|
78
|
-
title={playing ? "Pause (Space)" : "Play (Space)"}
|
|
79
|
-
onClick={() => store.setPlaying(!playing)}
|
|
80
|
-
>
|
|
81
|
-
{playing ? "❚❚" : "▶"}
|
|
82
|
-
</button>
|
|
83
|
-
<TransportScrub total={total} playing={playing} />
|
|
84
|
-
</div>
|
|
85
|
-
);
|
|
86
|
-
}
|
|
@@ -1,341 +0,0 @@
|
|
|
1
|
-
// The focused block's params, timing and easing.
|
|
2
|
-
//
|
|
3
|
-
// ── WHY THIS IS NOT `KeyframeEditor` ───────────────────────────────────
|
|
4
|
-
//
|
|
5
|
-
// `@dailies-studio/timeline`'s ease editor is a fine component and its INPUTS
|
|
6
|
-
// are keyframe-shaped: `refs: KeyframeAddr[]` and a `TimelineGesture` whose
|
|
7
|
-
// capture emits `{kind:"setEase", refs}`. There are no keyframes in this lane,
|
|
8
|
-
// so every one of those would be a fabricated address. What DOES transfer is
|
|
9
|
-
// everything pure: `EASE_PRESETS`, `matchPreset`, `SPRING_DEFAULT`, and the pad
|
|
10
|
-
// geometry `padToPx`/`pxToPad`. Those are imported, not re-derived, so the two
|
|
11
|
-
// chromes cannot disagree about what "Ease Out" means.
|
|
12
|
-
//
|
|
13
|
-
// ── THE SPRING WART, AND THE TAIL THAT ADMITS IT ───────────────────────
|
|
14
|
-
//
|
|
15
|
-
// A spring has an INTRINSIC duration in seconds. So dragging a spring block's
|
|
16
|
-
// right edge changes where its window CLIPS, not how fast it settles — which is
|
|
17
|
-
// correct physics and matches springs everywhere else in the engine, but is
|
|
18
|
-
// invisible unless the UI says so. It says so twice: the duration field is
|
|
19
|
-
// disabled with the reason attached, and the preview draws the part of the
|
|
20
|
-
// settle that falls OUTSIDE the block window as a dashed tail. If the tail is
|
|
21
|
-
// long, the block is cutting the spring off mid-flight, and the author can see
|
|
22
|
-
// exactly that.
|
|
23
|
-
import { useRef } from "react";
|
|
24
|
-
import {
|
|
25
|
-
blockProgressAt,
|
|
26
|
-
motionPresets,
|
|
27
|
-
presetsForSlot,
|
|
28
|
-
MIN_ANIMATE_FRAMES,
|
|
29
|
-
} from "@dailies-studio/shot/doc";
|
|
30
|
-
import type { AnimBlock } from "@dailies-studio/shot/doc";
|
|
31
|
-
import { EASE_PRESETS, SPRING_DEFAULT, easeSelection, padToPx, pxToPad } from "@dailies-studio/timeline";
|
|
32
|
-
import type { PadGeom } from "@dailies-studio/timeline";
|
|
33
|
-
import { InspectorRow, InspectorSection, NumberField, polylinePoints } from "@dailies-studio/ui";
|
|
34
|
-
import type { Ease, SpringEase, Value } from "@dailies-studio/engine";
|
|
35
|
-
import type { PointerEvent as ReactPointerEvent } from "react";
|
|
36
|
-
import { useShotEditor, useStore } from "../../../state/context.js";
|
|
37
|
-
import { ParamRow } from "../ParamRow.js";
|
|
38
|
-
import { useFocusedBlock } from "./useBlocks.js";
|
|
39
|
-
|
|
40
|
-
const PAD_GEOM: PadGeom = { width: 216, height: 132, padY: 30 };
|
|
41
|
-
const PREVIEW_W = 216;
|
|
42
|
-
const PREVIEW_H = 72;
|
|
43
|
-
const PREVIEW_PAD = 8;
|
|
44
|
-
const PREVIEW_SAMPLES = 96;
|
|
45
|
-
/** How far past the block's own window the settle tail is drawn, as a multiple
|
|
46
|
-
* of the window. Two windows is enough to show a spring that has not settled
|
|
47
|
-
* and short enough that a settled one draws a flat stub. */
|
|
48
|
-
const TAIL_FACTOR = 2;
|
|
49
|
-
|
|
50
|
-
export function BlockTiming() {
|
|
51
|
-
const store = useStore();
|
|
52
|
-
// A STATE SLICE OR NULL, never a fresh fallback object: a selector that
|
|
53
|
-
// mints `{num: 30, den: 1}` per call gives useSyncExternalStore a snapshot
|
|
54
|
-
// that is never Object.is-stable, which is an infinite re-render the moment
|
|
55
|
-
// this mounts with no document. Unreachable today only because the
|
|
56
|
-
// inspector early-returns on a null doc — this keeps it unreachable.
|
|
57
|
-
const fps = useShotEditor((s) => s.doc?.fps ?? null) ?? { num: 30, den: 1 };
|
|
58
|
-
const ref = useFocusedBlock();
|
|
59
|
-
if (ref === null) return null;
|
|
60
|
-
const { layer, block } = ref;
|
|
61
|
-
const preset = motionPresets[block.preset];
|
|
62
|
-
const duration = Math.max(1, Math.trunc(block.durationFrames));
|
|
63
|
-
const spring = block.ease?.type === "spring";
|
|
64
|
-
|
|
65
|
-
return (
|
|
66
|
-
<InspectorSection title="Timing" persistKey="shot:animate:timing">
|
|
67
|
-
<InspectorRow>
|
|
68
|
-
<NumberField
|
|
69
|
-
value={block.frame}
|
|
70
|
-
label="Start"
|
|
71
|
-
ariaLabel="Block start"
|
|
72
|
-
min={0}
|
|
73
|
-
step={1}
|
|
74
|
-
onCommit={(next) => store.setBlockTiming(layer.id, block.id, Math.max(0, next), duration)}
|
|
75
|
-
/>
|
|
76
|
-
<NumberField
|
|
77
|
-
value={duration}
|
|
78
|
-
label="Frames"
|
|
79
|
-
ariaLabel="Block frames"
|
|
80
|
-
min={MIN_ANIMATE_FRAMES}
|
|
81
|
-
step={1}
|
|
82
|
-
// A spring's duration is set by its physics. The field still SHOWS the
|
|
83
|
-
// window (it is real: it is where the block stops contributing), it
|
|
84
|
-
// just refuses to pretend that changing it retimes the motion.
|
|
85
|
-
disabled={spring}
|
|
86
|
-
{...(spring ? { title: "A spring settles in its own time — edit stiffness and damping." } : {})}
|
|
87
|
-
onCommit={(next) =>
|
|
88
|
-
store.setBlockTiming(layer.id, block.id, block.frame, Math.max(MIN_ANIMATE_FRAMES, next))
|
|
89
|
-
}
|
|
90
|
-
/>
|
|
91
|
-
</InspectorRow>
|
|
92
|
-
|
|
93
|
-
{preset !== undefined &&
|
|
94
|
-
preset.params.map((spec) => (
|
|
95
|
-
<ParamRow
|
|
96
|
-
key={spec.name}
|
|
97
|
-
spec={spec}
|
|
98
|
-
value={paramValue(block, spec.name, spec.default)}
|
|
99
|
-
onCommit={(next) => store.setBlockParams(layer.id, block.id, { [spec.name]: next })}
|
|
100
|
-
/>
|
|
101
|
-
))}
|
|
102
|
-
|
|
103
|
-
<EaseControl
|
|
104
|
-
{...(block.ease === undefined ? {} : { ease: block.ease })}
|
|
105
|
-
onEase={(ease) => store.setBlockEase(layer.id, block.id, ease)}
|
|
106
|
-
onScrub={(ease) => store.setBlockEase(layer.id, block.id, ease)}
|
|
107
|
-
/>
|
|
108
|
-
<SettlePreview block={block} fps={fps} />
|
|
109
|
-
|
|
110
|
-
{/* The preset itself is swappable in place, so "make that a slide instead"
|
|
111
|
-
keeps the block's timing and easing rather than deleting and redoing. */}
|
|
112
|
-
<InspectorRow label="Preset">
|
|
113
|
-
<select
|
|
114
|
-
className="select"
|
|
115
|
-
aria-label="Block preset"
|
|
116
|
-
value={block.preset}
|
|
117
|
-
onChange={(e) => store.setBlockPreset(layer.id, block.id, e.target.value)}
|
|
118
|
-
>
|
|
119
|
-
{presetsForSlot(block.slot).map((p) => (
|
|
120
|
-
<option key={p.id} value={p.id}>
|
|
121
|
-
{p.label}
|
|
122
|
-
</option>
|
|
123
|
-
))}
|
|
124
|
-
</select>
|
|
125
|
-
</InspectorRow>
|
|
126
|
-
</InspectorSection>
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function paramValue(block: AnimBlock, name: string, fallback: Value): Value {
|
|
131
|
-
const v = block.params[name];
|
|
132
|
-
return v === undefined ? fallback : v;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// ---- easing ------------------------------------------------------------------
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* The ease row, plus whichever editor the chosen ease needs.
|
|
139
|
-
*
|
|
140
|
-
* Takes an `Ease` and not a block, because the CAMERA's shots carry one too and
|
|
141
|
-
* `clipProgress01` reads it from both the same way. Two ease editors would be
|
|
142
|
-
* two places for "Ease Out" to mean something slightly different, which is the
|
|
143
|
-
* whole reason `EASE_PRESETS` is imported rather than re-declared.
|
|
144
|
-
*/
|
|
145
|
-
export function EaseControl(props: {
|
|
146
|
-
ease?: Ease;
|
|
147
|
-
/** What the select is called. The subject differs between the two callers and
|
|
148
|
-
* the accessible name has to say which one the author is looking at. */
|
|
149
|
-
ariaLabel?: string;
|
|
150
|
-
onEase(ease: Ease): void;
|
|
151
|
-
onScrub(ease: Ease): void;
|
|
152
|
-
}) {
|
|
153
|
-
const ease = props.ease;
|
|
154
|
-
// `easeSelection` takes a LIST because the shared editor is multi-select. One
|
|
155
|
-
// block is a list of one, and reusing it keeps "Custom" meaning exactly what
|
|
156
|
-
// it means in the other two chromes.
|
|
157
|
-
const sel = easeSelection(ease === undefined ? [] : [ease]);
|
|
158
|
-
|
|
159
|
-
return (
|
|
160
|
-
<>
|
|
161
|
-
<InspectorRow label="Ease">
|
|
162
|
-
<select
|
|
163
|
-
className="select"
|
|
164
|
-
aria-label={props.ariaLabel ?? "Block ease"}
|
|
165
|
-
value={sel.selectValue}
|
|
166
|
-
onChange={(e) => {
|
|
167
|
-
const preset = EASE_PRESETS.find((p) => p.id === e.target.value);
|
|
168
|
-
if (preset !== undefined) props.onEase(preset.ease);
|
|
169
|
-
}}
|
|
170
|
-
>
|
|
171
|
-
{EASE_PRESETS.map((p) => (
|
|
172
|
-
<option key={p.id} value={p.id}>
|
|
173
|
-
{p.label}
|
|
174
|
-
</option>
|
|
175
|
-
))}
|
|
176
|
-
{sel.selectValue === "custom" && (
|
|
177
|
-
<option value="custom" disabled>
|
|
178
|
-
Custom
|
|
179
|
-
</option>
|
|
180
|
-
)}
|
|
181
|
-
{sel.selectValue === "mixed" && (
|
|
182
|
-
<option value="mixed" disabled>
|
|
183
|
-
—
|
|
184
|
-
</option>
|
|
185
|
-
)}
|
|
186
|
-
</select>
|
|
187
|
-
</InspectorRow>
|
|
188
|
-
{sel.ease?.type === "bezier" && <BezierPad handles={sel.ease.handles} onEase={props.onScrub} />}
|
|
189
|
-
{sel.ease?.type === "spring" && <SpringFields spring={sel.ease} onEase={props.onEase} />}
|
|
190
|
-
</>
|
|
191
|
-
);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function BezierPad(props: {
|
|
195
|
-
handles: readonly [number, number, number, number];
|
|
196
|
-
onEase(ease: Ease): void;
|
|
197
|
-
}) {
|
|
198
|
-
const svgRef = useRef<SVGSVGElement | null>(null);
|
|
199
|
-
const drag = useRef<{ index: 0 | 1; base: [number, number, number, number] } | null>(null);
|
|
200
|
-
|
|
201
|
-
const down =
|
|
202
|
-
(index: 0 | 1) =>
|
|
203
|
-
(e: ReactPointerEvent<SVGCircleElement>): void => {
|
|
204
|
-
if (e.button !== 0) return;
|
|
205
|
-
e.currentTarget.setPointerCapture(e.pointerId);
|
|
206
|
-
const h = props.handles;
|
|
207
|
-
drag.current = { index, base: [h[0], h[1], h[2], h[3]] };
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
const move = (e: ReactPointerEvent<SVGCircleElement>): void => {
|
|
211
|
-
const d = drag.current;
|
|
212
|
-
const svg = svgRef.current;
|
|
213
|
-
if (d === null || svg === null) return;
|
|
214
|
-
const rect = svg.getBoundingClientRect();
|
|
215
|
-
const [x, y] = pxToPad(PAD_GEOM, e.clientX - rect.left, e.clientY - rect.top);
|
|
216
|
-
// Rebuilt from the DRAG-START handles, so the untouched one cannot creep as
|
|
217
|
-
// the live value it is being compared against changes underneath.
|
|
218
|
-
const handles: [number, number, number, number] =
|
|
219
|
-
d.index === 0 ? [x, y, d.base[2], d.base[3]] : [d.base[0], d.base[1], x, y];
|
|
220
|
-
props.onEase({ type: "bezier", handles });
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
const up = (e: ReactPointerEvent<SVGCircleElement>): void => {
|
|
224
|
-
if (drag.current === null) return;
|
|
225
|
-
drag.current = null;
|
|
226
|
-
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
const h = props.handles;
|
|
230
|
-
const [ax, ay] = padToPx(PAD_GEOM, 0, 0);
|
|
231
|
-
const [bx, by] = padToPx(PAD_GEOM, 1, 1);
|
|
232
|
-
const [x1, y1] = padToPx(PAD_GEOM, h[0], h[1]);
|
|
233
|
-
const [x2, y2] = padToPx(PAD_GEOM, h[2], h[3]);
|
|
234
|
-
const handle = (index: 0 | 1) => ({
|
|
235
|
-
className: "insp-pad-handle",
|
|
236
|
-
r: 4.5,
|
|
237
|
-
onPointerDown: down(index),
|
|
238
|
-
onPointerMove: move,
|
|
239
|
-
onPointerUp: up,
|
|
240
|
-
onPointerCancel: () => {
|
|
241
|
-
drag.current = null;
|
|
242
|
-
},
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
return (
|
|
246
|
-
<svg ref={svgRef} className="insp-pad" width={PAD_GEOM.width} height={PAD_GEOM.height}>
|
|
247
|
-
<rect
|
|
248
|
-
className="insp-pad-box"
|
|
249
|
-
x={0.5}
|
|
250
|
-
y={PAD_GEOM.padY + 0.5}
|
|
251
|
-
width={PAD_GEOM.width - 1}
|
|
252
|
-
height={PAD_GEOM.height - 2 * PAD_GEOM.padY - 1}
|
|
253
|
-
rx={3}
|
|
254
|
-
/>
|
|
255
|
-
<path className="insp-pad-curve" d={`M ${ax} ${ay} C ${x1} ${y1} ${x2} ${y2} ${bx} ${by}`} />
|
|
256
|
-
<line className="insp-pad-arm" x1={ax} y1={ay} x2={x1} y2={y1} />
|
|
257
|
-
<line className="insp-pad-arm" x1={bx} y1={by} x2={x2} y2={y2} />
|
|
258
|
-
<circle {...handle(0)} cx={x1} cy={y1} />
|
|
259
|
-
<circle {...handle(1)} cx={x2} cy={y2} />
|
|
260
|
-
</svg>
|
|
261
|
-
);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const SPRING_FIELDS = [
|
|
265
|
-
{ key: "stiffness", label: "Stiffness", min: 1, max: 1000, step: 1 },
|
|
266
|
-
{ key: "damping", label: "Damping", min: 0, max: 200, step: 0.5 },
|
|
267
|
-
{ key: "mass", label: "Mass", min: 0.05, max: 20, step: 0.05 },
|
|
268
|
-
] as const;
|
|
269
|
-
|
|
270
|
-
function SpringFields(props: { spring: SpringEase; onEase(ease: Ease): void }) {
|
|
271
|
-
return (
|
|
272
|
-
<InspectorRow>
|
|
273
|
-
{SPRING_FIELDS.map((f) => (
|
|
274
|
-
<NumberField
|
|
275
|
-
key={f.key}
|
|
276
|
-
value={props.spring[f.key]}
|
|
277
|
-
label={f.label}
|
|
278
|
-
ariaLabel={`Spring ${f.label.toLowerCase()}`}
|
|
279
|
-
min={f.min}
|
|
280
|
-
max={f.max}
|
|
281
|
-
step={f.step}
|
|
282
|
-
onCommit={(v) => props.onEase({ ...SPRING_DEFAULT, ...props.spring, [f.key]: v })}
|
|
283
|
-
/>
|
|
284
|
-
))}
|
|
285
|
-
</InspectorRow>
|
|
286
|
-
);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// ---- the preview, and the tail --------------------------------------------------
|
|
290
|
-
|
|
291
|
-
function SettlePreview(props: { block: AnimBlock; fps: { num: number; den: number } }) {
|
|
292
|
-
const { block, fps } = props;
|
|
293
|
-
const duration = Math.max(1, Math.trunc(block.durationFrames));
|
|
294
|
-
const spring = block.ease?.type === "spring";
|
|
295
|
-
// Sampled through `blockProgressAt` — the SAME function the renderer folds
|
|
296
|
-
// with. A preview drawn from a local re-implementation of the ease would
|
|
297
|
-
// eventually disagree with the pixels, which is the one thing a preview must
|
|
298
|
-
// never do.
|
|
299
|
-
const to = block.frame + (spring ? duration * (1 + TAIL_FACTOR) : duration);
|
|
300
|
-
const samples: Array<{ frame: number; value: number }> = [];
|
|
301
|
-
for (let i = 0; i < PREVIEW_SAMPLES; i += 1) {
|
|
302
|
-
const frame = block.frame + ((to - block.frame) * i) / (PREVIEW_SAMPLES - 1);
|
|
303
|
-
samples.push({ frame, value: blockProgressAt(block, frame, fps) ?? 0 });
|
|
304
|
-
}
|
|
305
|
-
const line = polylinePoints(samples, PREVIEW_W, PREVIEW_H, PREVIEW_PAD);
|
|
306
|
-
if (line === null) return null;
|
|
307
|
-
// The window's right edge in preview px, so the tail is drawn from exactly
|
|
308
|
-
// where the block stops contributing.
|
|
309
|
-
const edgeX = spring ? (duration / (to - block.frame)) * PREVIEW_W : PREVIEW_W;
|
|
310
|
-
|
|
311
|
-
return (
|
|
312
|
-
<div className="insp-preview animate-preview" data-testid="block-preview">
|
|
313
|
-
<svg width={PREVIEW_W} height={PREVIEW_H}>
|
|
314
|
-
{/* One polyline, clipped twice: solid inside the window, dashed after.
|
|
315
|
-
Two <polyline>s over the same points with opposite clips beats
|
|
316
|
-
splitting the sample array, because the split point then cannot land
|
|
317
|
-
between two samples and jitter as the duration changes. */}
|
|
318
|
-
<defs>
|
|
319
|
-
<clipPath id="blk-in">
|
|
320
|
-
<rect x={0} y={0} width={edgeX} height={PREVIEW_H} />
|
|
321
|
-
</clipPath>
|
|
322
|
-
<clipPath id="blk-out">
|
|
323
|
-
<rect x={edgeX} y={0} width={PREVIEW_W - edgeX} height={PREVIEW_H} />
|
|
324
|
-
</clipPath>
|
|
325
|
-
</defs>
|
|
326
|
-
<polyline className={spring ? "is-spring" : ""} points={line.points} clipPath="url(#blk-in)" />
|
|
327
|
-
{spring && (
|
|
328
|
-
<>
|
|
329
|
-
<polyline className="is-tail" points={line.points} clipPath="url(#blk-out)" />
|
|
330
|
-
<line className="animate-preview-edge" x1={edgeX} y1={0} x2={edgeX} y2={PREVIEW_H} />
|
|
331
|
-
</>
|
|
332
|
-
)}
|
|
333
|
-
</svg>
|
|
334
|
-
{spring && (
|
|
335
|
-
<span className="insp-dim animate-preview-note">
|
|
336
|
-
dashed = after the block ends; the spring holds at its clipped value
|
|
337
|
-
</span>
|
|
338
|
-
)}
|
|
339
|
-
</div>
|
|
340
|
-
);
|
|
341
|
-
}
|