@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
|
@@ -16,22 +16,20 @@
|
|
|
16
16
|
// the pull feels the same size at every zoom. A threshold in frames would get
|
|
17
17
|
// stronger as the author zoomed out until a whole scene was one sticky point;
|
|
18
18
|
// the store's layout snapping is divided by the view zoom for the same reason.
|
|
19
|
-
import { sceneWindows } from "@dailies-studio/shot/doc";
|
|
19
|
+
import { beatFrames, masterAudioClips, sceneWindows } from "@dailies-studio/shot/doc";
|
|
20
20
|
import type { Doc3D } from "@dailies-studio/shot/doc";
|
|
21
21
|
import { lifespanOf } from "./model.js";
|
|
22
22
|
|
|
23
23
|
/** How close a moving edge has to come, in SCREEN px. */
|
|
24
24
|
export const TIMELINE_SNAP_PX = 6;
|
|
25
25
|
|
|
26
|
-
export type SnapKind = "playhead" | "marker" | "scene" | "
|
|
26
|
+
export type SnapKind = "playhead" | "marker" | "scene" | "lifespan" | "audio" | "beat";
|
|
27
27
|
|
|
28
28
|
export interface SnapTarget {
|
|
29
29
|
/** MASTER frame. Every target in one space, because a gesture that crosses a
|
|
30
|
-
* scene boundary has to compare a scene-2
|
|
30
|
+
* scene boundary has to compare a scene-2 lifespan edge with a scene-1 one. */
|
|
31
31
|
frame: number;
|
|
32
32
|
kind: SnapKind;
|
|
33
|
-
/** What the guide would say if the UI ever labelled it. */
|
|
34
|
-
label: string;
|
|
35
33
|
}
|
|
36
34
|
|
|
37
35
|
export interface SnapHit {
|
|
@@ -56,28 +54,64 @@ export interface SnapScope {
|
|
|
56
54
|
*
|
|
57
55
|
* Without it every drag is pinned at zero travel: the bar under the pointer
|
|
58
56
|
* is always exactly on its own edge, so it wins every comparison and the
|
|
59
|
-
* gesture never leaves the ground.
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* and every block on it, so the whole layer really is in motion and none of its
|
|
63
|
-
* edges is a target. A block drag moves exactly one bar: its siblings and the
|
|
64
|
-
* layer's own lifespan stay put, and butting a During block against the In
|
|
65
|
-
* block's end — or onto the lifespan in, which is the pairing the lifespan row
|
|
66
|
-
* exists to make visible — is the most natural alignment in the lane. Excluding
|
|
67
|
-
* them buys nothing and disables it.
|
|
57
|
+
* gesture never leaves the ground. A lifespan drag moves the layer's bar and
|
|
58
|
+
* every key on it, so the whole layer is in motion and none of its edges is a
|
|
59
|
+
* target.
|
|
68
60
|
*/
|
|
69
61
|
exclude?: {
|
|
70
62
|
sceneId?: string;
|
|
71
63
|
layerId?: string;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
*
|
|
64
|
+
/**
|
|
65
|
+
* EVERY bar a gesture is moving an edge of, for the single-bar reason above
|
|
66
|
+
* read once per bar: a body drag over a selection of four clips has four
|
|
67
|
+
* bars sitting exactly on their own edges, and any one of them wins every
|
|
68
|
+
* comparison and pins the whole gesture at zero travel.
|
|
69
|
+
*
|
|
70
|
+
* A ROLL is the other list, and it is a PAIR rather than a selection: the
|
|
71
|
+
* seam is one layer's `out` and the next layer's `in` at the same frame, so
|
|
72
|
+
* the partner's edge sits exactly on the frame under the pointer and pins
|
|
73
|
+
* the drag just as surely as the pressed bar's own edge would. Naming both
|
|
74
|
+
* sides is what makes a seam drag able to leave the ground.
|
|
75
|
+
*
|
|
76
|
+
* Carries no scene, on the same ground `audioClipId` and `markerId` state:
|
|
77
|
+
* a layer id is document-unique (`usedIdsOf` mints against every home at
|
|
78
|
+
* once), so the id identifies the bar without one.
|
|
79
|
+
*/
|
|
80
|
+
layerIds?: readonly string[];
|
|
81
|
+
/** The audio clip a gesture is moving. Carries no scene even when a shot
|
|
82
|
+
* owns the clip: the id is document-unique across both homes (`usedIdsOf`
|
|
83
|
+
* mints against all of them), so it identifies the clip on its own. */
|
|
75
84
|
audioClipId?: string;
|
|
76
85
|
/** The marker a gesture is moving. Same reason as `audioClipId`, and it is
|
|
77
86
|
* not optional politeness: a marker sits exactly on its own target, so
|
|
78
87
|
* without this a marker drag is pinned at zero travel. */
|
|
79
88
|
markerId?: string;
|
|
89
|
+
/** A shot's out trim RIPPLES: every scene, lifespan and shot-owned
|
|
90
|
+
* audio edge at or past this master frame moves with the trim, so none of
|
|
91
|
+
* them is a target. The playhead, the markers and the Project's audio stay
|
|
92
|
+
* put. */
|
|
93
|
+
rippleFromFrame?: number;
|
|
80
94
|
};
|
|
95
|
+
/**
|
|
96
|
+
* WHERE THE MOVING EDGE MAY LEGALLY LAND, in master frames.
|
|
97
|
+
*
|
|
98
|
+
* Applied AT COLLECTION TIME, so an illegal target is simply not on the list.
|
|
99
|
+
* That is one code path rather than two: a trim bounded by `legalTrimRange`
|
|
100
|
+
* and a seam bounded by `legalRollRange` can only ever snap to a frame their
|
|
101
|
+
* op accepts, so an edge never appears to stick to a guide and then slip off
|
|
102
|
+
* it when the op clamps the same gesture back inside the range.
|
|
103
|
+
*
|
|
104
|
+
* An EMPTY range filters everything out, which is the right answer and needs
|
|
105
|
+
* no extra branch: `LegalRange` marks an empty intersection by carrying a
|
|
106
|
+
* `min` above its `max`, and no frame is both. An edge with nowhere legal to
|
|
107
|
+
* go has nothing legal to snap to.
|
|
108
|
+
*
|
|
109
|
+
* Omitted means unbounded above; frame 0 remains the floor either way.
|
|
110
|
+
*/
|
|
111
|
+
legal?: { min: number; max: number };
|
|
112
|
+
/** Offer the beat grid's frames too (gap 8), when the document has a
|
|
113
|
+
* `tempo`. The dock's per-tab toggle (`snapBeats`). */
|
|
114
|
+
beats?: boolean;
|
|
81
115
|
}
|
|
82
116
|
|
|
83
117
|
/**
|
|
@@ -89,53 +123,78 @@ export interface SnapScope {
|
|
|
89
123
|
*
|
|
90
124
|
* Deduped by frame, first writer wins, which makes the push order below a
|
|
91
125
|
* PRIORITY order: the playhead and the markers are what the author put there on
|
|
92
|
-
* purpose, so a
|
|
126
|
+
* purpose, so a hit on a frame that is also a scene boundary comes back as the
|
|
127
|
+
* marker.
|
|
93
128
|
*/
|
|
94
129
|
export function collectSnapTargets(doc: Doc3D, opts: SnapScope): SnapTarget[] {
|
|
95
130
|
const out: SnapTarget[] = [];
|
|
96
131
|
const seen = new Set<number>();
|
|
97
|
-
|
|
132
|
+
// Frame 0 is the floor whether or not a legal range was given, so the two
|
|
133
|
+
// bounds fold into one pair and `push` keeps one comparison per side.
|
|
134
|
+
const lo = Math.max(0, opts.legal?.min ?? 0);
|
|
135
|
+
const hi = opts.legal?.max ?? Infinity;
|
|
136
|
+
const ripple = opts.exclude?.rippleFromFrame ?? Infinity;
|
|
137
|
+
/** `rides`: the edge moves under a ripple trim (`exclude.rippleFromFrame`). */
|
|
138
|
+
const push = (frame: number, kind: SnapKind, rides = true): void => {
|
|
98
139
|
const at = Math.round(frame);
|
|
99
|
-
if (at <
|
|
140
|
+
if (at < lo || at > hi || seen.has(at) || (rides && at >= ripple)) return;
|
|
100
141
|
seen.add(at);
|
|
101
|
-
out.push({ frame: at, kind
|
|
142
|
+
out.push({ frame: at, kind });
|
|
102
143
|
};
|
|
144
|
+
// The bars a multi-bar drag is moving. Built once: `exclude.layerIds` is read
|
|
145
|
+
// inside the layer loop, and a list scan per layer per scene is the only part
|
|
146
|
+
// of this walk that would grow with the selection.
|
|
147
|
+
const moving = new Set(opts.exclude?.layerIds ?? []);
|
|
103
148
|
|
|
104
|
-
push(opts.playhead, "playhead",
|
|
149
|
+
push(opts.playhead, "playhead", false);
|
|
105
150
|
for (const marker of doc.markers ?? []) {
|
|
106
151
|
if (marker.id === opts.exclude?.markerId) continue;
|
|
107
|
-
push(marker.frame, "marker",
|
|
152
|
+
push(marker.frame, "marker", false);
|
|
108
153
|
}
|
|
109
154
|
|
|
110
|
-
// Audio edges
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
|
|
155
|
+
// Audio edges, THROUGH THE FLATTEN so a clip its shot owns is a target too —
|
|
156
|
+
// its scene-local frames arrive here already shifted onto the master base
|
|
157
|
+
// every other target on this list is in. They come before the scene loop
|
|
158
|
+
// because landing a layer on the start of the music is the alignment this
|
|
159
|
+
// lane is for.
|
|
160
|
+
for (const { clip, layerId, sceneId } of masterAudioClips(doc)) {
|
|
114
161
|
if (clip.id === opts.exclude?.audioClipId) continue;
|
|
115
|
-
|
|
116
|
-
|
|
162
|
+
// A RECORDING'S OWN SOUND IS THE OTHER HALF OF THE BAR BEING DRAGGED, and
|
|
163
|
+
// without this line it defeats the whole exclusion below. The flatten
|
|
164
|
+
// places `video.sound` at `max(lifetime.in, offsetFrames)` for the rest of
|
|
165
|
+
// the window, so a video layer contributes an "audio" target on the very
|
|
166
|
+
// lifespan edge the layer loop is careful to withhold: a clip drag then had
|
|
167
|
+
// a target at distance zero from its own anchor and was pinned at zero
|
|
168
|
+
// travel for the first frame of pointer travel.
|
|
169
|
+
if (layerId !== undefined && (moving.has(layerId) || layerId === opts.exclude?.layerId)) continue;
|
|
170
|
+
push(clip.frame, "audio", sceneId !== null);
|
|
171
|
+
push(clip.frame + clip.durationFrames, "audio", sceneId !== null);
|
|
117
172
|
}
|
|
118
173
|
|
|
119
174
|
for (const window of sceneWindows(doc)) {
|
|
120
175
|
const scene = doc.scenesById[window.id];
|
|
121
176
|
if (scene === undefined) continue;
|
|
122
|
-
push(window.start, "scene"
|
|
123
|
-
push(window.end, "scene"
|
|
177
|
+
push(window.start, "scene");
|
|
178
|
+
push(window.end, "scene");
|
|
124
179
|
for (const layer of Object.values(scene.layersById)) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (
|
|
129
|
-
const life = lifespanOf(layer, scene);
|
|
130
|
-
push(window.start + life.in, "lifespan"
|
|
131
|
-
push(window.start + life.out, "lifespan"
|
|
132
|
-
for (const block of layer.blocks) {
|
|
133
|
-
if (block.id === moving?.blockId) continue;
|
|
134
|
-
push(window.start + block.frame, "block", `${layer.label} ${block.preset}`);
|
|
135
|
-
push(window.start + block.frame + block.durationFrames, "block", `${layer.label} ${block.preset} end`);
|
|
136
|
-
}
|
|
180
|
+
// A BAR THIS GESTURE CARRIES offers neither of its edges: the whole
|
|
181
|
+
// layer is in motion (see SnapScope.exclude).
|
|
182
|
+
if (moving.has(layer.id)) continue;
|
|
183
|
+
if (opts.exclude?.sceneId === window.id && opts.exclude.layerId === layer.id) continue;
|
|
184
|
+
const life = lifespanOf(layer, scene.durationFrames);
|
|
185
|
+
push(window.start + life.in, "lifespan");
|
|
186
|
+
push(window.start + life.out, "lifespan");
|
|
137
187
|
}
|
|
138
188
|
}
|
|
189
|
+
// BEATS LAST, the lowest priority: a beat is a grid nobody placed on this
|
|
190
|
+
// frame, so a marker, a cut or a clip edge on the same frame wins it. The
|
|
191
|
+
// grid runs to the furthest edge already listed plus a bar, so a clip can
|
|
192
|
+
// still be dragged out past the film's end onto a beat.
|
|
193
|
+
if (opts.beats === true && doc.tempo !== undefined) {
|
|
194
|
+
const last = out.reduce((max, t) => Math.max(max, t.frame), 0);
|
|
195
|
+
const bar = Math.ceil((60 * doc.fps.num * (doc.tempo.beatsPerBar ?? 4)) / (doc.fps.den * doc.tempo.bpm));
|
|
196
|
+
for (const beat of beatFrames(doc.tempo, doc.fps, lo, Math.min(hi + 1, last + bar + 1))) push(beat.frame, "beat", false);
|
|
197
|
+
}
|
|
139
198
|
return out;
|
|
140
199
|
}
|
|
141
200
|
|
|
@@ -162,6 +221,16 @@ export function snapFrame(
|
|
|
162
221
|
return best === null ? { frame: rounded, target: null } : { frame: best.frame, target: best };
|
|
163
222
|
}
|
|
164
223
|
|
|
224
|
+
// ── ONE ANCHOR PER GESTURE ───────────────────────────────────────────────────
|
|
225
|
+
//
|
|
226
|
+
// There is no multi-edge pairing here, and a `snapDelta` that offered one was
|
|
227
|
+
// deleted rather than wired: nothing called it, and the rows do not work that
|
|
228
|
+
// way. A body drag picks ONE anchor at the press (`LifespanRow`: "WHAT THE
|
|
229
|
+
// POINTER IS CARRYING is what snaps") and moves the rest by the delta that
|
|
230
|
+
// anchor came back with, which is why `SnapPress` below is a one-candidate
|
|
231
|
+
// closure. Wanting several edges in play is a real change to that type and to
|
|
232
|
+
// every row that presses, not a missing call.
|
|
233
|
+
|
|
165
234
|
/**
|
|
166
235
|
* What a row is handed at the press: give it the frame you want and it gives
|
|
167
236
|
* back the frame you get, having told the dock where to draw the guide.
|
|
@@ -173,6 +242,19 @@ export function snapFrame(
|
|
|
173
242
|
export type SnapPress = (
|
|
174
243
|
exclude: SnapScope["exclude"],
|
|
175
244
|
suppressed: boolean,
|
|
245
|
+
/**
|
|
246
|
+
* WHERE THIS GESTURE MAY LEGALLY LAND, in MASTER frames — the row's own range
|
|
247
|
+
* already shifted onto the base every target is collected in.
|
|
248
|
+
*
|
|
249
|
+
* A third argument rather than something the dock could derive, because only
|
|
250
|
+
* the row knows which bound it is under: a clip's `in` edge is bounded by its
|
|
251
|
+
* footage and its own `out`, a seam by both sides' footage at once, and the
|
|
252
|
+
* dock sees a candidate frame and nothing else. Omitted by every gesture with
|
|
253
|
+
* no bound, which is every gesture but a clip's two edges and its seam. See
|
|
254
|
+
* `SnapScope.legal` for why it filters at collection rather than clamping
|
|
255
|
+
* after.
|
|
256
|
+
*/
|
|
257
|
+
legal?: SnapScope["legal"],
|
|
176
258
|
) => (candidateMasterFrame: number) => number;
|
|
177
259
|
|
|
178
260
|
/**
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// THE STAGGER'S ON-SCREEN HALF (Paper page 23, "8 · Stagger").
|
|
2
|
+
//
|
|
3
|
+
// A stagger moves several rows at once, and each row draws its own ghost, so the
|
|
4
|
+
// press-time picture has to reach rows the pressed one does not own. It is
|
|
5
|
+
// transient pointer state, not editor state: nothing is saved, undone or synced,
|
|
6
|
+
// so it lives here rather than in the store.
|
|
7
|
+
//
|
|
8
|
+
// ponytail: one module-level slot, because there is one pointer. Two editors
|
|
9
|
+
// dragging at once in one page would share it; scope it per store if that ever
|
|
10
|
+
// becomes a real case.
|
|
11
|
+
import { useSyncExternalStore } from "react";
|
|
12
|
+
import { frameToPx } from "@dailies-studio/timeline";
|
|
13
|
+
import { formatCombo } from "../../state/shortcuts.js";
|
|
14
|
+
|
|
15
|
+
export interface StaggerPreview {
|
|
16
|
+
/** Each moving bar's `in` at the press, in LANE frames (drawing clock). */
|
|
17
|
+
from: Readonly<Record<string, number>>;
|
|
18
|
+
/** The bar (or the key row) the pointer holds: it carries the readout. */
|
|
19
|
+
grabbed: string;
|
|
20
|
+
/** The step in frames, not the total: what the author is choosing. */
|
|
21
|
+
step: number;
|
|
22
|
+
/** Where a key row's readout sits, in lane pixels. A bar row places its own,
|
|
23
|
+
* beside the bar. */
|
|
24
|
+
anchor?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let preview: StaggerPreview | null = null;
|
|
28
|
+
const listeners = new Set<() => void>();
|
|
29
|
+
|
|
30
|
+
export function setStaggerPreview(next: StaggerPreview | null): void {
|
|
31
|
+
preview = next;
|
|
32
|
+
for (const listener of listeners) listener();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function subscribe(listener: () => void): () => void {
|
|
36
|
+
listeners.add(listener);
|
|
37
|
+
return () => listeners.delete(listener);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** This row's press-time `in`, or null when it is not in a running stagger.
|
|
41
|
+
* A primitive, so only the rows whose answer changed re-render. */
|
|
42
|
+
export function useStaggerGhost(id: string): number | null {
|
|
43
|
+
return useSyncExternalStore(subscribe, () => preview?.from[id] ?? null);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** The running stagger when `id` holds its readout, else null. The slot's own
|
|
47
|
+
* object, so the snapshot is stable between two sets. */
|
|
48
|
+
export function useStaggerReadout(id: string): StaggerPreview | null {
|
|
49
|
+
return useSyncExternalStore(subscribe, () => (preview?.grabbed === id ? preview : null));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** "Stagger +20f": the sign always shows, so a step left reads as one. */
|
|
53
|
+
export function staggerLabel(step: number): string {
|
|
54
|
+
return `Stagger ${step < 0 ? "−" : "+"}${Math.abs(step)}f`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** The dashed box where a bar started, and the accent rule as long as its shift. */
|
|
58
|
+
export function StaggerGhost({
|
|
59
|
+
fromFrame,
|
|
60
|
+
width,
|
|
61
|
+
shift,
|
|
62
|
+
ppf,
|
|
63
|
+
}: {
|
|
64
|
+
fromFrame: number;
|
|
65
|
+
width: number;
|
|
66
|
+
shift: number;
|
|
67
|
+
ppf: number;
|
|
68
|
+
}) {
|
|
69
|
+
const left = frameToPx(fromFrame, ppf);
|
|
70
|
+
const travel = frameToPx(Math.abs(shift), ppf);
|
|
71
|
+
return (
|
|
72
|
+
<span className="stagger-ghost" data-stagger-ghost="" style={{ left, width }} aria-hidden="true">
|
|
73
|
+
{shift === 0 ? null : (
|
|
74
|
+
// Leftward from the ghost's left edge for a step back, rightward otherwise.
|
|
75
|
+
<span
|
|
76
|
+
className="stagger-shift"
|
|
77
|
+
style={shift < 0 ? { right: "100%", width: travel } : { left: 0, width: travel }}
|
|
78
|
+
/>
|
|
79
|
+
)}
|
|
80
|
+
</span>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** The floating readout beside the grabbed bar. */
|
|
85
|
+
export function StaggerChip({ step, left }: { step: number; left: number }) {
|
|
86
|
+
return (
|
|
87
|
+
<span className="stagger-chip" data-testid="stagger-readout" role="status" style={{ left }}>
|
|
88
|
+
<span className="stagger-chip-step">{staggerLabel(step)}</span>
|
|
89
|
+
<span className="stagger-chip-key">{formatCombo("⌥")}</span>
|
|
90
|
+
</span>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Measuring a generated take in this tab, for the Generate drawer's choice
|
|
2
|
+
// between two: decode it, then the shared measuring functions over its PCM
|
|
3
|
+
// (`@dailies-studio/shot/measure`), into the facts `rankSfxTakes` reads.
|
|
4
|
+
//
|
|
5
|
+
// A CHUNK OF ITS OWN: the drawer imports this module with `import()` when two
|
|
6
|
+
// takes arrive, and the three functions are imported here BY NAME. A dynamic
|
|
7
|
+
// import of the measure entry itself keeps every export it has, the probe's
|
|
8
|
+
// lazy mediabunny among them, which put a 75 kB chunk in the build for code
|
|
9
|
+
// this never runs.
|
|
10
|
+
//
|
|
11
|
+
// It ADVISES a choice and nothing more: the decode is the browser's, whose
|
|
12
|
+
// resampler the spec leaves open, so these numbers are close to a sidecar's,
|
|
13
|
+
// not equal to it, and nothing stored or exported reads them. A take that
|
|
14
|
+
// cannot be measured here is shown unranked, and the drawer says to listen.
|
|
15
|
+
import { rankSfxTakes, takeFacts, toneCheck } from "@dailies-studio/shot/doc";
|
|
16
|
+
import type { TakeFacts } from "@dailies-studio/shot/doc";
|
|
17
|
+
import type { GenerationTake } from "../../api.js";
|
|
18
|
+
import { measureLoudness, rmsEnvelopeDb, spectralBalance } from "@dailies-studio/shot/measure";
|
|
19
|
+
|
|
20
|
+
const RATE = 48_000;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The takes as the drawer shows them: the one that measures better first,
|
|
24
|
+
* the reasons, and the tone check on it. A take this browser cannot measure
|
|
25
|
+
* leaves both unranked, with a word to listen instead.
|
|
26
|
+
*/
|
|
27
|
+
export async function judgeTakes(
|
|
28
|
+
takes: readonly GenerationTake[],
|
|
29
|
+
requestedSeconds: number,
|
|
30
|
+
): Promise<{ order: GenerationTake[]; reasons: string[]; tone: string | null }> {
|
|
31
|
+
const facts = await Promise.all(takes.map((take) => measureTake(take.url, requestedSeconds)));
|
|
32
|
+
if (facts.some((f) => f === null)) {
|
|
33
|
+
return { order: [...takes], reasons: takes.length > 1 ? ["These takes could not be measured in this browser, so listen to both."] : [], tone: null };
|
|
34
|
+
}
|
|
35
|
+
const measured = facts as TakeFacts[];
|
|
36
|
+
if (takes.length < 2) return { order: [...takes], reasons: [], tone: measured[0] === undefined ? null : toneCheck(measured[0]) };
|
|
37
|
+
const ranked = rankSfxTakes(measured);
|
|
38
|
+
const pick = takes[ranked.pick]!;
|
|
39
|
+
return { order: [pick, ...takes.filter((take) => take !== pick)], reasons: ranked.reasons, tone: toneCheck(measured[ranked.pick]!) };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** The take at `url` as the facts a ranking reads, or null when this browser
|
|
43
|
+
* cannot decode it. `requestedSeconds` is the length that was asked for. */
|
|
44
|
+
export async function measureTake(url: string, requestedSeconds: number): Promise<TakeFacts | null> {
|
|
45
|
+
try {
|
|
46
|
+
const Context = globalThis.OfflineAudioContext;
|
|
47
|
+
if (Context === undefined) return null;
|
|
48
|
+
const bytes = await (await fetch(url)).arrayBuffer();
|
|
49
|
+
const buffer = await new Context(2, 1, RATE).decodeAudioData(bytes);
|
|
50
|
+
const left = buffer.getChannelData(0);
|
|
51
|
+
const right = buffer.numberOfChannels > 1 ? buffer.getChannelData(1) : left;
|
|
52
|
+
const frames = buffer.length;
|
|
53
|
+
const samples = new Int16Array(frames * 2);
|
|
54
|
+
for (let i = 0; i < frames; i++) {
|
|
55
|
+
samples[i * 2] = Math.max(-32768, Math.min(32767, Math.round(left[i]! * 32768)));
|
|
56
|
+
samples[i * 2 + 1] = Math.max(-32768, Math.min(32767, Math.round(right[i]! * 32768)));
|
|
57
|
+
}
|
|
58
|
+
const wav = { sampleRate: buffer.sampleRate, channels: 2, frames, samples };
|
|
59
|
+
return takeFacts(
|
|
60
|
+
{
|
|
61
|
+
rmsDb: rmsEnvelopeDb(wav),
|
|
62
|
+
hopSamples: 960,
|
|
63
|
+
sampleRate: buffer.sampleRate,
|
|
64
|
+
loudness: measureLoudness(wav),
|
|
65
|
+
spectral: spectralBalance(wav),
|
|
66
|
+
},
|
|
67
|
+
requestedSeconds,
|
|
68
|
+
);
|
|
69
|
+
} catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// IS THE PLAYHEAD INSIDE THIS BAR RIGHT NOW.
|
|
2
|
+
//
|
|
3
|
+
// The timeline's one live state, and the reason `--bar-live` exists: a bar is
|
|
4
|
+
// green while the clock is in it and the hueless ink wash otherwise, so the
|
|
5
|
+
// strip says what is playing without the author tracing the playhead down forty
|
|
6
|
+
// rows to see which bars it crosses.
|
|
7
|
+
//
|
|
8
|
+
// ── WHY THIS IS A BOOLEAN SUBSCRIPTION AND NOT A FRAME ONE ─────────────────
|
|
9
|
+
//
|
|
10
|
+
// `useSyncExternalStore` notifies a component only when the SELECTED value
|
|
11
|
+
// changes, so selecting the predicate rather than the frame means a bar
|
|
12
|
+
// re-renders exactly twice per playhead pass-through — once on the way in and
|
|
13
|
+
// once on the way out — instead of once per playback frame. Forty bars on
|
|
14
|
+
// screen at 30fps is 80 renders over the length of a shot rather than 1200 a
|
|
15
|
+
// second, and the lane holding them never re-renders at all.
|
|
16
|
+
//
|
|
17
|
+
// `MarkerLane` found this shape first (`s.frame === marker.frame`); this is the
|
|
18
|
+
// same trick over a half-open range. The rule it protects is `TimelineDock`'s
|
|
19
|
+
// own header: THE FRAME STAYS OUT OF THAT COMPONENT. A dock root subscribed to
|
|
20
|
+
// the frame re-renders every scene group, camera row, track row and block bar
|
|
21
|
+
// thirty times a second, which is the performance hole this lane has already
|
|
22
|
+
// paid for once.
|
|
23
|
+
//
|
|
24
|
+
// The hot channel is right here and nowhere higher: `useShotEditorHot` is the
|
|
25
|
+
// one that fires on a frame-only `set`, and its own contract is that it lives
|
|
26
|
+
// "at the smallest leaf that must repaint every tick".
|
|
27
|
+
import { useShotEditorHot } from "../../state/context.js";
|
|
28
|
+
import { laneFrame } from "./model.js";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param startLane The bar's left edge in LANE frames — the same number its
|
|
32
|
+
* `left` is drawn from, which is what keeps the highlight and
|
|
33
|
+
* the pixels from disagreeing about where the bar is.
|
|
34
|
+
* @param endLane Its right edge, HALF-OPEN: a bar that ends at frame 60 is
|
|
35
|
+
* not live at 60, because the next one starts there and two
|
|
36
|
+
* adjacent bars must never both be lit.
|
|
37
|
+
* @param origin The lane's re-zero, as `laneFrame` means it (a shot's
|
|
38
|
+
* master start, or 0 in the scene and definition contexts).
|
|
39
|
+
* `laneFrame` picks the master clock or the definition's own,
|
|
40
|
+
* so a bar in a component reads `defFrame` with no extra
|
|
41
|
+
* argument here.
|
|
42
|
+
*/
|
|
43
|
+
export function useLive(startLane: number, endLane: number, origin: number): boolean {
|
|
44
|
+
return useShotEditorHot((s) => {
|
|
45
|
+
const frame = laneFrame(s, origin);
|
|
46
|
+
return frame >= startLane && frame < endLane;
|
|
47
|
+
});
|
|
48
|
+
}
|