@dailies-studio/shot-editor 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.vite/manifest.json +382 -0
- package/dist/assets/AgentPanel-CQejLbiX.js +2 -0
- package/dist/assets/AgentPanel-DJEphah0.css +1 -0
- package/dist/assets/AgentQuestionDialog-BqkD0kXF.js +1 -0
- package/dist/assets/CodeStudio-Dv4ihHpz.js +23 -0
- package/dist/assets/CommentsPanel-B4yhhT1T.js +1 -0
- package/dist/assets/ComponentLibraryRail-Ch_dsAru.js +1 -0
- package/dist/assets/ComponentStage-BRhRotyH.js +1 -0
- package/dist/assets/CurvePane-BjFVabKX.css +1 -0
- package/dist/assets/CurvePane-R22WzllJ.js +1 -0
- package/dist/assets/ExportDialog-CWLQWTmG.js +2 -0
- package/dist/assets/FontPicker-DWKaYL7F.js +1 -0
- package/dist/assets/FontPicker-v0IOIArM.css +1 -0
- package/dist/assets/GenerateDrawer-Bh3SqDKt.js +2 -0
- package/dist/assets/InsertDrawer-Ce1fod1L.js +1 -0
- package/dist/assets/LibraryBrowser-D9sEGz5w.js +2 -0
- package/dist/assets/MakeItYoursSection-CMO4319_.js +2 -0
- package/dist/assets/MaterialGraphCanvas-BFzl4wWR.js +23 -0
- package/dist/assets/MaterialGraphCanvas-BIUD59ik.css +1 -0
- package/dist/assets/MaterialWorkspace-CqTWqaRA.js +14 -0
- package/dist/assets/PreferencesDialog-CBf9_OoD.js +1 -0
- package/dist/assets/PresetsSection-CUEaoCVU.js +1 -0
- package/dist/assets/SoundHistory-BeS8EW1h.js +1 -0
- package/dist/assets/SoundMode-BmcZru3i.js +3 -0
- package/dist/assets/StockSounds-DdAUGRYK.js +1 -0
- package/dist/assets/TemplateFieldSection-CaeXpLFo.js +1 -0
- package/dist/assets/VersionHistoryPanel-CxG6XhNP.js +1 -0
- package/dist/assets/board-B88EPuLO.css +1 -0
- package/dist/assets/board-CL-he7SL.js +3 -0
- package/dist/assets/components-0oOSPN0l.js +1 -0
- package/dist/assets/googleFontRanges-gp32wJMo.js +1 -0
- package/dist/assets/googleFonts-BNon6M7X.js +1 -0
- package/dist/assets/iconsAndAliases-DL7gXP5j.js +1863 -0
- package/dist/assets/index-4F5NQmI8.css +1 -0
- package/dist/assets/index-BGHQGV5z.js +117 -0
- package/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/dist/assets/libraryModel-CpIwfFha.js +1 -0
- package/dist/assets/lucide-6iK64tTB.js +9319 -0
- package/dist/assets/motionStamp-CH-Hew4E.js +1 -0
- package/dist/assets/takeMeasure-Tdg0TxEi.js +1 -0
- package/dist/assets/webmcp-BsB9gxWC.js +2 -0
- package/dist/index.html +34 -3
- package/package.json +12 -9
- package/src/addFamilies.ts +66 -0
- package/src/api.ts +1631 -55
- package/src/app/App.tsx +977 -198
- package/src/app/lazyPanel.tsx +137 -0
- package/src/app/lazySurfaces.tsx +203 -0
- package/src/audio/index.tsx +41 -10
- package/src/audio/meter.ts +485 -0
- package/src/audio/meterMath.ts +273 -0
- package/src/audio/meterStore.ts +155 -0
- package/src/audio/offlineMix.ts +289 -0
- package/src/audio/preview.ts +928 -118
- package/src/bridge.ts +15 -5
- package/src/bridgeTypes.ts +1611 -36
- package/src/channels.ts +17 -32
- package/src/fonts/googleFonts.ts +4 -0
- package/src/hostApi.ts +174 -0
- package/src/icons.tsx +770 -0
- package/src/layerWords.ts +61 -0
- package/src/main.tsx +56 -4
- package/src/panels/ResizeHandle.tsx +33 -43
- package/src/panels/agent/AgentDots.tsx +36 -0
- package/src/panels/agent/AgentPanel.tsx +119 -43
- package/src/panels/agent/AgentQuestionDialog.tsx +202 -0
- package/src/panels/agent/agent.css +113 -97
- package/src/panels/agent/agentGate.ts +35 -0
- package/src/panels/code/CodeDefaultsPanel.tsx +345 -0
- package/src/panels/code/CodeStudio.tsx +373 -0
- package/src/panels/code/FileTree.tsx +265 -0
- package/src/panels/code/codeBufferGuard.ts +52 -0
- package/src/panels/code/codeParts.tsx +321 -0
- package/src/panels/comments/CommentComposer.tsx +239 -0
- package/src/panels/comments/CommentThread.tsx +346 -0
- package/src/panels/comments/CommentsPanel.tsx +336 -0
- package/src/panels/comments/InboxButton.tsx +131 -0
- package/src/panels/comments/MentionMenu.tsx +98 -0
- package/src/panels/comments/parts.tsx +171 -0
- package/src/panels/export/ExportDialog.tsx +919 -96
- package/src/panels/export/ExportScrim.tsx +57 -0
- package/src/panels/files/AssetsTab.tsx +656 -0
- package/src/panels/files/FilePanel.tsx +1466 -216
- package/src/panels/files/LayerMarks.tsx +40 -0
- package/src/panels/files/LeftColumn.tsx +37 -0
- package/src/panels/files/McpSection.tsx +690 -47
- package/src/panels/files/MissingAssetsNotice.tsx +60 -0
- package/src/panels/files/assetDrag.ts +79 -0
- package/src/panels/files/resourceParts.tsx +255 -0
- package/src/panels/fonts/FontPicker.tsx +762 -0
- package/src/panels/fonts/faceCache.ts +207 -0
- package/src/panels/fonts/fontPicker.css +271 -0
- package/src/panels/fonts/pickerModel.ts +265 -0
- package/src/panels/fonts/pickerRequest.ts +46 -0
- package/src/panels/header/AccountAvatar.tsx +76 -0
- package/src/panels/header/AgentChip.tsx +77 -0
- package/src/panels/header/AppMenu.tsx +213 -0
- package/src/panels/header/Header.tsx +564 -84
- package/src/panels/header/HistoryControls.tsx +31 -5
- package/src/panels/header/PresenceStack.tsx +329 -0
- package/src/panels/header/ProjectTitle.tsx +232 -0
- package/src/panels/header/ShareButton.tsx +59 -0
- package/src/panels/header/StatusBadges.tsx +358 -27
- package/src/panels/header/SyncDeletedDialog.tsx +88 -0
- package/src/panels/header/ZoomControls.tsx +46 -15
- package/src/panels/header/agentHandoff.ts +52 -0
- package/src/panels/header/leave.ts +26 -0
- package/src/panels/help/HelpSection.tsx +189 -163
- package/src/panels/insert/InsertDrawer.tsx +723 -0
- package/src/panels/insert/iconSet.ts +109 -0
- package/src/panels/inspector/ExposeDot.tsx +117 -0
- package/src/panels/inspector/FramingRows.tsx +314 -44
- package/src/panels/inspector/InspectorFooter.tsx +91 -0
- package/src/panels/inspector/InspectorHead.tsx +425 -0
- package/src/panels/inspector/InspectorPanel.tsx +408 -124
- package/src/panels/inspector/KeyframeDot.tsx +373 -0
- package/src/panels/inspector/KeyframeSection.tsx +257 -0
- package/src/panels/inspector/MarkerSection.tsx +55 -11
- package/src/panels/inspector/ParamRow.tsx +84 -53
- package/src/panels/inspector/RightColumn.tsx +152 -0
- package/src/panels/inspector/TimeSection.tsx +866 -0
- package/src/panels/inspector/animate/EaseControl.tsx +244 -0
- package/src/panels/inspector/animate/PresetsSection.tsx +135 -0
- package/src/panels/inspector/animate/ShotSection.tsx +562 -48
- package/src/panels/inspector/design/AlignRow.tsx +95 -31
- package/src/panels/inspector/design/AppearanceSection.tsx +118 -9
- package/src/panels/inspector/design/ClonerSection.tsx +323 -62
- package/src/panels/inspector/design/ComponentSection.tsx +50 -43
- package/src/panels/inspector/design/CornerSection.tsx +45 -14
- package/src/panels/inspector/design/DefinitionProps.tsx +315 -0
- package/src/panels/inspector/design/DefinitionSection.tsx +136 -0
- package/src/panels/inspector/design/DeviceSection.tsx +210 -0
- package/src/panels/inspector/design/FillSection.tsx +83 -51
- package/src/panels/inspector/design/FontField.tsx +196 -0
- package/src/panels/inspector/design/HtmlSection.tsx +23 -12
- package/src/panels/inspector/design/InstanceSection.tsx +373 -154
- package/src/panels/inspector/design/MaskSection.tsx +84 -0
- package/src/panels/inspector/design/MaterialSection.tsx +177 -0
- package/src/panels/inspector/design/MediaSection.tsx +273 -120
- package/src/panels/inspector/design/MeshSection.tsx +910 -82
- package/src/panels/inspector/design/PaintRows.tsx +550 -0
- package/src/panels/inspector/design/PlateStyleSection.tsx +177 -0
- package/src/panels/inspector/design/ShadowSection.tsx +221 -48
- package/src/panels/inspector/design/SplineFollowSection.tsx +235 -0
- package/src/panels/inspector/design/StrokeSection.tsx +228 -43
- package/src/panels/inspector/design/TemplateFieldSection.tsx +57 -0
- package/src/panels/inspector/design/TransformSection.tsx +244 -50
- package/src/panels/inspector/design/TypographySection.tsx +679 -110
- package/src/panels/inspector/layout.ts +295 -0
- package/src/panels/inspector/propValue.ts +199 -0
- package/src/panels/inspector/scene/CameraSection.tsx +232 -22
- package/src/panels/inspector/scene/EffectsSection.tsx +722 -71
- package/src/panels/inspector/scene/MakeItYoursSection.tsx +180 -0
- package/src/panels/inspector/scene/SceneDocSection.tsx +191 -0
- package/src/panels/inspector/scene/SceneSection.tsx +219 -44
- package/src/panels/inspector/scene/TransitionSection.tsx +523 -72
- package/src/panels/inspector/storyboard/StoryboardReferenceSection.tsx +593 -0
- package/src/panels/inspector/storyboard/VideoStill.tsx +120 -0
- package/src/panels/inspector/timeModel.ts +190 -0
- package/src/panels/inspector/useDocWrite.ts +438 -146
- package/src/panels/library/LibraryBrowser.tsx +435 -0
- package/src/panels/library/LibraryGrid.tsx +190 -0
- package/src/panels/library/LibraryPreviewPanel.tsx +349 -0
- package/src/panels/library/LibraryRail.tsx +84 -0
- package/src/panels/library/LibrarySoundPanel.tsx +293 -0
- package/src/panels/library/SoundHistory.tsx +245 -0
- package/src/panels/library/StockSounds.tsx +390 -0
- package/src/panels/library/libraryModel.ts +147 -0
- package/src/panels/library/stockSearch.ts +77 -0
- package/src/panels/maskMarks.tsx +117 -0
- package/src/panels/material/MaterialCommentNode.tsx +54 -0
- package/src/panels/material/MaterialGraphCanvas.tsx +861 -0
- package/src/panels/material/MaterialGraphMenu.tsx +45 -0
- package/src/panels/material/MaterialGroupNode.tsx +56 -0
- package/src/panels/material/MaterialLibrary.tsx +253 -0
- package/src/panels/material/MaterialNode.tsx +453 -0
- package/src/panels/material/MaterialNodePalette.tsx +349 -0
- package/src/panels/material/MaterialParameterControls.tsx +525 -0
- package/src/panels/material/MaterialRerouteNode.tsx +42 -0
- package/src/panels/material/MaterialWorkspace.tsx +501 -0
- package/src/panels/material/NodePreview.tsx +70 -0
- package/src/panels/material/flowModel.ts +441 -0
- package/src/panels/material/parameterValues.ts +231 -0
- package/src/panels/material/previewScheduler.ts +235 -0
- package/src/panels/material/reactFlow.css +6 -0
- package/src/panels/material/socketGlyph.ts +85 -0
- package/src/panels/material/viewportMemory.ts +58 -0
- package/src/panels/preferences/PreferencesDialog.tsx +215 -0
- package/src/panels/share/ShareComponentDialog.tsx +247 -0
- package/src/panels/stage/StageMenu.tsx +35 -5
- package/src/panels/stage/StagePanel.tsx +338 -23
- package/src/panels/stage/cellSlots.tsx +70 -0
- package/src/panels/storyboard/BoardCanvas.tsx +739 -0
- package/src/panels/storyboard/BoardCursors.tsx +158 -0
- package/src/panels/storyboard/BoardDropDialog.tsx +161 -0
- package/src/panels/storyboard/BoardPlan.tsx +42 -0
- package/src/panels/storyboard/ComponentSheet.tsx +187 -0
- package/src/panels/storyboard/ComponentStage.tsx +235 -0
- package/src/panels/storyboard/MiniSketch.tsx +173 -0
- package/src/panels/storyboard/PartPicker.tsx +64 -0
- package/src/panels/storyboard/PlanPanel.tsx +617 -0
- package/src/panels/storyboard/ReferencePreview.tsx +217 -0
- package/src/panels/storyboard/ShotBrief.tsx +360 -0
- package/src/panels/storyboard/ShotCard.tsx +251 -0
- package/src/panels/storyboard/StagePreview.tsx +348 -0
- package/src/panels/storyboard/StillThumbs.tsx +162 -0
- package/src/panels/storyboard/StoryboardView.tsx +79 -0
- package/src/panels/storyboard/board.css +1065 -0
- package/src/panels/storyboard/board.ts +12 -0
- package/src/panels/storyboard/boardClipboard.ts +111 -0
- package/src/panels/storyboard/boardOrder.ts +68 -0
- package/src/panels/storyboard/boardRows.tsx +425 -0
- package/src/panels/storyboard/liveCap.ts +90 -0
- package/src/panels/storyboard/plan.css +295 -0
- package/src/panels/storyboard/posterCache.ts +133 -0
- package/src/panels/storyboard/shotboard/ComponentPicker.tsx +373 -0
- package/src/panels/storyboard/shotboard/ImagePicker.tsx +145 -0
- package/src/panels/storyboard/shotboard/ShotBoard.tsx +292 -0
- package/src/panels/storyboard/shotboard/SketchCanvas.tsx +443 -0
- package/src/panels/storyboard/shotboard/SketchInspector.tsx +471 -0
- package/src/panels/storyboard/shotboard/SketchItem.tsx +240 -0
- package/src/panels/storyboard/shotboard/SketchLayers.tsx +130 -0
- package/src/panels/storyboard/shotboard/SketchPlay.tsx +108 -0
- package/src/panels/storyboard/shotboard/SketchStrip.tsx +178 -0
- package/src/panels/storyboard/shotboard/SketchToolFooter.tsx +99 -0
- package/src/panels/storyboard/shotboard/sketch.css +1392 -0
- package/src/panels/storyboard/shotboard/sketchModel.ts +219 -0
- package/src/panels/storyboard/storyboardDrop.ts +164 -0
- package/src/panels/studio/ComponentLibraryRail.tsx +346 -0
- package/src/panels/studio/ComponentSidePanel.tsx +355 -0
- package/src/panels/studio/ComponentStudio.tsx +243 -0
- package/src/panels/studio/ComponentsHome.tsx +318 -0
- package/src/panels/studio/ComponentsPreview.tsx +179 -0
- package/src/panels/studio/EveryUse.tsx +244 -0
- package/src/panels/studio/ThisUsePanel.tsx +679 -0
- package/src/panels/studio/componentParts.tsx +341 -0
- package/src/panels/studio/components.ts +6 -0
- package/src/panels/studio/newCodeComponent.ts +66 -0
- package/src/panels/timeline/AudioFxButton.tsx +290 -0
- package/src/panels/timeline/AudioLane.tsx +674 -85
- package/src/panels/timeline/BlockRow.tsx +154 -85
- package/src/panels/timeline/ChannelRow.tsx +378 -129
- package/src/panels/timeline/CurvePane.tsx +508 -0
- package/src/panels/timeline/Filmstrip.tsx +361 -0
- package/src/panels/timeline/GenerateDrawer.tsx +407 -0
- package/src/panels/timeline/KeyStretch.tsx +115 -0
- package/src/panels/timeline/LifespanRow.tsx +730 -47
- package/src/panels/timeline/MarkerLane.tsx +46 -8
- package/src/panels/timeline/PlayButton.tsx +91 -0
- package/src/panels/timeline/PropertyPicker.tsx +254 -0
- package/src/panels/timeline/ShotRow.tsx +400 -0
- package/src/panels/timeline/SoundMode.tsx +1406 -0
- package/src/panels/timeline/TimelineDock.tsx +2338 -272
- package/src/panels/timeline/TransportVolume.tsx +163 -0
- package/src/panels/timeline/beatRuler.ts +74 -0
- package/src/panels/timeline/curveModel.ts +500 -0
- package/src/panels/timeline/duckUnderVoice.ts +68 -0
- package/src/panels/timeline/keyframeMenu.ts +138 -0
- package/src/panels/timeline/labelWidth.ts +49 -0
- package/src/panels/timeline/marquee.ts +155 -0
- package/src/panels/timeline/model.ts +1860 -199
- package/src/panels/timeline/snap.ts +125 -43
- package/src/panels/timeline/staggerPreview.tsx +92 -0
- package/src/panels/timeline/takeMeasure.ts +72 -0
- package/src/panels/timeline/useLive.ts +48 -0
- package/src/panels/timeline/viewport.ts +234 -0
- package/src/panels/toolbar/BackChip.tsx +116 -0
- package/src/panels/toolbar/CameraViewBar.tsx +118 -49
- package/src/panels/toolbar/CanvasBar.tsx +117 -0
- package/src/panels/toolbar/InsertButton.tsx +38 -0
- package/src/panels/toolbar/IsolationTrail.tsx +156 -0
- package/src/panels/toolbar/PanelToggles.tsx +86 -0
- package/src/panels/toolbar/ShadingBar.tsx +119 -26
- package/src/panels/toolbar/ToolDockBar.tsx +329 -80
- package/src/panels/toolbar/ToolFooter.tsx +102 -0
- package/src/panels/toolbar/ViewModeBar.tsx +247 -38
- package/src/panels/toolbar/WorkPlaneBar.tsx +94 -0
- package/src/panels/toolbar/useAddRows.tsx +326 -0
- package/src/panels/toolbar/useDismiss.ts +122 -0
- package/src/panels/versions/VersionHistoryPanel.tsx +274 -0
- package/src/panels/versions/versionModel.ts +103 -0
- package/src/panels/virtualWindow.ts +271 -0
- package/src/shotTokens.css +151 -60
- package/src/stage/FrustumGizmo.tsx +504 -84
- package/src/stage/GridPlane.tsx +225 -16
- package/src/stage/NodePreviews.tsx +628 -0
- package/src/stage/PerfHud.tsx +437 -47
- package/src/stage/SplitRenderer.tsx +816 -169
- package/src/stage/StageApp.tsx +4274 -486
- package/src/stage/StageRootBoundary.tsx +114 -0
- package/src/stage/StageTools.tsx +651 -151
- package/src/stage/TransformGizmo.tsx +696 -0
- package/src/stage/adaptivePresentation.ts +145 -0
- package/src/stage/bridgeClient.ts +2522 -41
- package/src/stage/exportWorker.ts +9 -0
- package/src/stage/feed.ts +305 -14
- package/src/stage/fontPreview.ts +176 -0
- package/src/stage/frustumGeometry.ts +319 -1
- package/src/stage/furnitureTokens.ts +33 -0
- package/src/stage/gizmoMath.ts +508 -16
- package/src/stage/gpuTimer.ts +276 -0
- package/src/stage/instanceModules.ts +194 -33
- package/src/stage/interactions/move.ts +24 -5
- package/src/stage/isolation.ts +291 -33
- package/src/stage/jsSelfProfiler.ts +325 -0
- package/src/stage/main.tsx +116 -16
- package/src/stage/modalState.ts +222 -23
- package/src/stage/moduleEpoch.ts +60 -0
- package/src/stage/motionPathStage.ts +279 -0
- package/src/stage/motionPreview.ts +74 -0
- package/src/stage/overlays/Overlays.tsx +1112 -159
- package/src/stage/overlays/Rulers.tsx +495 -0
- package/src/stage/panes.ts +159 -10
- package/src/stage/project.ts +68 -3
- package/src/stage/renderScaleController.ts +730 -0
- package/src/stage/rulers.ts +129 -0
- package/src/stage/sceneExtent.ts +75 -0
- package/src/stage/shading.ts +20 -0
- package/src/stage/splineStage.ts +632 -0
- package/src/stage/stagePerformanceTelemetry.ts +717 -0
- package/src/stage/stageTheme.ts +36 -0
- package/src/stage/stageWarmup.ts +11 -0
- package/src/stage/textAutoSize.ts +124 -0
- package/src/stage/tier1Export.ts +641 -0
- package/src/stage/tokenColor.ts +14 -9
- package/src/stage/view.ts +56 -9
- package/src/stage/workingView.ts +277 -33
- package/src/state/accountPrefs.ts +261 -0
- package/src/state/agentActions.ts +173 -15
- package/src/state/agentActivity.ts +198 -0
- package/src/state/agentQuestion.ts +239 -0
- package/src/state/bootOpen.ts +42 -0
- package/src/state/cameraVerbs.ts +139 -0
- package/src/state/clientId.ts +6 -1
- package/src/state/comments.ts +1099 -0
- package/src/state/context.tsx +19 -1
- package/src/state/editorPrefs.ts +220 -0
- package/src/state/errorReports.ts +121 -0
- package/src/state/fieldWrite.ts +161 -0
- package/src/state/fontRecents.ts +70 -0
- package/src/state/fonts.ts +656 -0
- package/src/state/graphClipboard.ts +414 -0
- package/src/state/imageThumbs.ts +216 -0
- package/src/state/insert.ts +444 -67
- package/src/state/isolationTrail.ts +92 -0
- package/src/state/keyMoves.ts +134 -0
- package/src/state/keyframeClipboard.ts +177 -0
- package/src/state/keymap.ts +1153 -117
- package/src/state/layoutPrefs.ts +272 -0
- package/src/state/layouts.ts +205 -0
- package/src/state/measureMedia.ts +167 -0
- package/src/state/menu.ts +2341 -28
- package/src/state/motionStamp.ts +7 -0
- package/src/state/panelSize.ts +375 -5
- package/src/state/pendingChanges.ts +119 -0
- package/src/state/playHold.ts +78 -0
- package/src/state/playbackPreparation.ts +562 -0
- package/src/state/posterCapture.ts +242 -0
- package/src/state/presence.ts +254 -0
- package/src/state/projectComponents.ts +160 -0
- package/src/state/renderEvents.ts +298 -18
- package/src/state/runtimeErrors.ts +46 -0
- package/src/state/selection.ts +12 -0
- package/src/state/shortcuts.ts +1606 -0
- package/src/state/sketchGuide.ts +51 -0
- package/src/state/splineRefs.ts +87 -0
- package/src/state/stageHealth.ts +259 -0
- package/src/state/stageWatchdog.ts +47 -0
- package/src/state/stillStage.ts +330 -0
- package/src/state/stillThumbs.ts +474 -0
- package/src/state/store.ts +23591 -2982
- package/src/state/storyboardModel.ts +528 -0
- package/src/state/sync.ts +1257 -0
- package/src/state/syncCache.ts +159 -0
- package/src/state/triangles.ts +13 -0
- package/src/state/types.ts +1698 -55
- package/src/state/unsavedGuard.ts +49 -0
- package/src/state/viewMode.ts +200 -0
- package/src/state/webmcp.ts +1128 -0
- package/src/state/workspace.ts +1147 -0
- package/src/styles/assets-tab.css +304 -0
- package/src/styles/canvas.css +545 -0
- package/src/styles/comments.css +1435 -0
- package/src/styles/controls.css +162 -0
- package/src/styles/dialogs.css +390 -0
- package/src/styles/insert-drawer.css +586 -0
- package/src/styles/inspector-board.css +207 -0
- package/src/styles/inspector-object.css +486 -0
- package/src/styles/inspector-shot.css +252 -0
- package/src/styles/inspector.css +360 -0
- package/src/styles/left-column.css +1628 -0
- package/src/styles/library-preview.css +575 -0
- package/src/styles/library.css +512 -0
- package/src/styles/modes/code.css +569 -0
- package/src/styles/modes/component.css +1561 -0
- package/src/styles/modes/material.css +1201 -0
- package/src/styles/modes/storyboard.css +518 -0
- package/src/styles/share-component.css +49 -0
- package/src/styles/shell.css +653 -0
- package/src/styles/shot-brief.css +360 -0
- package/src/styles/states.css +76 -0
- package/src/styles/sync.css +380 -0
- package/src/styles/timeline-graph.css +384 -0
- package/src/styles/timeline-meters.css +89 -0
- package/src/styles/timeline-rows.css +1959 -0
- package/src/styles/timeline-sound.css +1003 -0
- package/src/styles/timeline.css +754 -0
- package/src/styles/tool-footer.css +525 -0
- package/src/styles/topbar.css +886 -0
- package/src/styles/versions.css +262 -0
- package/src/styles.css +72 -2921
- package/src/tools.ts +108 -23
- package/dist/assets/index-B7notge7.css +0 -1
- package/dist/assets/index-BBE6Ta9L.js +0 -52
- package/src/panels/files/icons.tsx +0 -118
- package/src/panels/header/DocSwitcher.tsx +0 -85
- package/src/panels/header/TransportBar.tsx +0 -86
- package/src/panels/inspector/animate/BlockTiming.tsx +0 -341
- package/src/panels/inspector/animate/DuringBlocks.tsx +0 -85
- package/src/panels/inspector/animate/InOutPresets.tsx +0 -57
- package/src/panels/inspector/animate/ResolvedChannels.tsx +0 -150
- package/src/panels/inspector/animate/useBlocks.ts +0 -43
- package/src/panels/material/MaterialPicker.tsx +0 -172
- package/src/panels/timeline/SceneStrip.tsx +0 -145
- package/src/panels/toolbar/AddMenu.tsx +0 -230
- package/src/stage/MoveGizmo.tsx +0 -411
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
// Which frames the stage draws once, before playback starts, so that playback
|
|
2
|
+
// never pays a first-use cost.
|
|
3
|
+
//
|
|
4
|
+
// ── WHY PLAYBACK NEEDS THIS AT ALL ─────────────────────────────────────────
|
|
5
|
+
//
|
|
6
|
+
// The first time a frame is really drawn, the GPU process does work the
|
|
7
|
+
// warm-up cannot do ahead of it: the first submit of a new pipeline, of a
|
|
8
|
+
// transition's render targets, of a caption's rasters. Measured on
|
|
9
|
+
// examples/training-season-reel (1080p, WebGPU, RTX 3060): the first loop
|
|
10
|
+
// stalled 3.0 s in total with gaps up to 530 ms, and every later loop about
|
|
11
|
+
// 1.1 s with none over 110 ms. The warm-up compiles STAND-INS of the next
|
|
12
|
+
// scene while the transport runs; a stand-in builds the program but not the
|
|
13
|
+
// first real draw, and it only gets the frames before the cut to do it in.
|
|
14
|
+
//
|
|
15
|
+
// Drawing these frames once through the ordinary seek path, with the stage in
|
|
16
|
+
// its playing presentation, pays that cost under a frozen picture (bridge
|
|
17
|
+
// v51): in the background while the author sits paused, or for a queued Play.
|
|
18
|
+
// Measured end to end on the landscape cut: the first loop's stalls fell from
|
|
19
|
+
// 3.2 s to about 0.3 s and its worst gap from 284 ms to under 100 ms, for a
|
|
20
|
+
// pass of about 3 s. HyperFrames makes the same trade for its shader
|
|
21
|
+
// transitions: it holds `play()` while it renders every transition frame once.
|
|
22
|
+
//
|
|
23
|
+
// ── WHICH FRAMES, AND IN WHICH ORDER ───────────────────────────────────────
|
|
24
|
+
//
|
|
25
|
+
// The frames where something can appear that the GPU has not drawn yet:
|
|
26
|
+
//
|
|
27
|
+
// - a Shot's first frame, where its scene mounts;
|
|
28
|
+
// - a transition window's first and last frames and the first frame after
|
|
29
|
+
// it, where the held Shot leaves (the stalls sat on both ends). Under
|
|
30
|
+
// `hold` the outgoing Shot is one frozen picture for the whole window, so
|
|
31
|
+
// the frames between buy nothing but the keys inside it of the incoming
|
|
32
|
+
// Shot's fades, blurs and component props: that Shot plays on into the TO
|
|
33
|
+
// operand, a target keyed apart from the canvas when the Shot has no post
|
|
34
|
+
// chain, and the one frame per state the rule below picks can land outside
|
|
35
|
+
// the window. Under `run` the
|
|
36
|
+
// outgoing Shot plays on past its own end, and every window frame is a
|
|
37
|
+
// picture of it drawn nowhere else, so all of them;
|
|
38
|
+
// - a layer's first frame;
|
|
39
|
+
// - the frames inside the Shot where one of its tracks can change what the
|
|
40
|
+
// renderer builds (`trackEventFrames`): one frame of each blending a fade
|
|
41
|
+
// draws (blending is part of a pipeline) and of each side of a blur's 0,
|
|
42
|
+
// both ends of a component prop, a move's last key, each step of a raster
|
|
43
|
+
// input. A tweened colour, raster input or geometry input builds nothing it
|
|
44
|
+
// can keep. Every frame of an animation was measured and bought nothing
|
|
45
|
+
// over its two ends (154 frames against 250, the same stalls).
|
|
46
|
+
//
|
|
47
|
+
// In playing order from the playhead, with the playhead's own Shot LAST: a
|
|
48
|
+
// Play pass then ends inside the Shot it hands over in, so the hand-over's
|
|
49
|
+
// start frame needs no remount. With the loop off, only the frames playback
|
|
50
|
+
// can reach from the playhead in its direction.
|
|
51
|
+
//
|
|
52
|
+
// ── WHAT IS REMEMBERED ────────────────────────────────────────────────────
|
|
53
|
+
//
|
|
54
|
+
// Each frame carries a KEY: what its pixels depend on, as content, because a
|
|
55
|
+
// save replaces the whole document object. The parts, each memoised by the
|
|
56
|
+
// identity of what it reads:
|
|
57
|
+
//
|
|
58
|
+
// - the document-wide inputs every Shot draws with: size, rate, the assets
|
|
59
|
+
// that are not audio, materials, material lighting, component definitions;
|
|
60
|
+
// - the Shot as it draws: `drawnScene`, less its name, its audio and its
|
|
61
|
+
// guides, so a still, a resource, a rename, a sound or a guide re-keys
|
|
62
|
+
// nothing;
|
|
63
|
+
// - the Shot before it, on the incoming transition window's frames only (the
|
|
64
|
+
// only frames that draw both);
|
|
65
|
+
// - the placed code components' module epoch (`preparationKey().modules`), on
|
|
66
|
+
// the Shots that place one;
|
|
67
|
+
// - the isolated layer, on the frames of the Shot that holds it;
|
|
68
|
+
// - the Shot-local frame.
|
|
69
|
+
//
|
|
70
|
+
// The store adds what the stage shows (the layout, the shading, the board) in
|
|
71
|
+
// front of every key, and `PreparedFrames` drops everything when the stage's
|
|
72
|
+
// base moves (`preparationKey().base`). A frame is credited only for what the
|
|
73
|
+
// stage reported of it (`credit`), so a timed-out frame is never prepared.
|
|
74
|
+
//
|
|
75
|
+
// ── WHY A FRAME WAS PENDING (the perf flag only) ──────────────────────────
|
|
76
|
+
//
|
|
77
|
+
// Under the chrome's `?perf=1` or `?perf=log`, each frame also carries its key
|
|
78
|
+
// in parts, `PreparedFrames` remembers the parts it last credited for each
|
|
79
|
+
// frame in each view, and every pass names why each of its frames is pending:
|
|
80
|
+
// the first part that moved, a first timeout, the clear that dropped it, or
|
|
81
|
+
// never drawn (`PreparedFrames.explain`, `window.__dailiesPrep`). With the
|
|
82
|
+
// flag off none of it runs.
|
|
83
|
+
import {
|
|
84
|
+
INSTANCE_PROP_PREFIX,
|
|
85
|
+
drawnScene,
|
|
86
|
+
sceneWindows,
|
|
87
|
+
shotsPlacing,
|
|
88
|
+
trackEventFrames,
|
|
89
|
+
transitionAt,
|
|
90
|
+
} from "@dailies-studio/shot/doc";
|
|
91
|
+
import type { Doc3D, Layer3D, Scene3D } from "@dailies-studio/shot/doc";
|
|
92
|
+
import type { ShotPreparedOutcome } from "../bridgeTypes.js";
|
|
93
|
+
|
|
94
|
+
/** The chrome's perf flag as its URL set it: the two values the stage URL
|
|
95
|
+
* forwards (`stageSrc`), or null. */
|
|
96
|
+
export type PreparationPerf = "1" | "log" | null;
|
|
97
|
+
|
|
98
|
+
/** Read once at load, like the stage's `PERF_HUD_ENABLED`, and written again
|
|
99
|
+
* only by the test seam below. */
|
|
100
|
+
let perf: PreparationPerf = chromePerfFlag();
|
|
101
|
+
|
|
102
|
+
/** The chrome's `?perf`, ALLOWLISTED: the one reading of it, for this file and
|
|
103
|
+
* for the stage URL StagePanel builds, which must never carry anything else. */
|
|
104
|
+
export function chromePerfFlag(): PreparationPerf {
|
|
105
|
+
const value = typeof window === "undefined" ? null : new URLSearchParams(window.location.search).get("perf");
|
|
106
|
+
return value === "1" || value === "log" ? value : null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Test seam: the perf flag as the URL would set it. A `PreparedFrames` reads
|
|
110
|
+
* it when it is made, `playbackPreparation` on every call. Drops the last
|
|
111
|
+
* report. */
|
|
112
|
+
export function setPreparationPerf(next: PreparationPerf): void {
|
|
113
|
+
perf = next;
|
|
114
|
+
report = null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface PreparationFrame {
|
|
118
|
+
/** MASTER frame to seek. */
|
|
119
|
+
frame: number;
|
|
120
|
+
/** What this frame's pixels depend on, for `PreparedFrames`. */
|
|
121
|
+
key: string;
|
|
122
|
+
/** The key in parts, under the perf flag only. */
|
|
123
|
+
parts?: PreparationKeyParts;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** A frame's key in parts, which `PreparedFrames.explain` compares. */
|
|
127
|
+
export interface PreparationKeyParts {
|
|
128
|
+
/** Which frame this is across edits: the Shot's id and the Shot-local frame. */
|
|
129
|
+
id: string;
|
|
130
|
+
/** What the stage shows: the prefix the store adds, so empty as
|
|
131
|
+
* `playbackPreparation` lists it. */
|
|
132
|
+
view: string;
|
|
133
|
+
/** The document-wide inputs. */
|
|
134
|
+
shared: string;
|
|
135
|
+
/** The Shot before, on this Shot's incoming transition window (empty
|
|
136
|
+
* elsewhere and for the first Shot). */
|
|
137
|
+
prev: string;
|
|
138
|
+
/** This Shot. */
|
|
139
|
+
shot: string;
|
|
140
|
+
/** The placed modules' epoch, or empty for a Shot that places no code. */
|
|
141
|
+
modules: string;
|
|
142
|
+
/** The isolated layer, or empty when it is not in this Shot. */
|
|
143
|
+
isolation: string;
|
|
144
|
+
/** The Shot-local frame. */
|
|
145
|
+
local: number;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Where the transport starts, which orders the list. */
|
|
149
|
+
export interface PreparationStart {
|
|
150
|
+
/** The playhead, a MASTER frame. */
|
|
151
|
+
frame: number;
|
|
152
|
+
/** The transport wraps its window (`playbackLoops`). */
|
|
153
|
+
loop: boolean;
|
|
154
|
+
/** The sign of the rate it starts at. */
|
|
155
|
+
direction: 1 | -1;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** What the stage shows besides the document, per frame. */
|
|
159
|
+
export interface PreparationInputs {
|
|
160
|
+
/** The layer the stage isolates, or null. */
|
|
161
|
+
isolation: string | null;
|
|
162
|
+
/** The placed code components' module epoch (`preparationKey().modules`). */
|
|
163
|
+
modules: string;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* The frames to prepare inside a playback window, in the order to draw them.
|
|
168
|
+
*
|
|
169
|
+
* `window` is the transport's loop (`playbackWindow`): only frames playback
|
|
170
|
+
* will reach are prepared.
|
|
171
|
+
*/
|
|
172
|
+
export function playbackPreparation(
|
|
173
|
+
doc: Doc3D,
|
|
174
|
+
window: { start: number; frames: number },
|
|
175
|
+
start: PreparationStart,
|
|
176
|
+
inputs: PreparationInputs,
|
|
177
|
+
): PreparationFrame[] {
|
|
178
|
+
const end = window.start + window.frames;
|
|
179
|
+
const shared = sharedHashOf(doc);
|
|
180
|
+
const placing = placingOf(doc);
|
|
181
|
+
const withParts = perf !== null;
|
|
182
|
+
const shots: ShotFrames[] = [];
|
|
183
|
+
let previousHash = "";
|
|
184
|
+
for (const scene of sceneWindows(doc)) {
|
|
185
|
+
const body = doc.scenesById[scene.id];
|
|
186
|
+
const length = scene.end - scene.start;
|
|
187
|
+
if (body === undefined || length <= 0) continue;
|
|
188
|
+
const { hash, events, stateKeys } = shotFactsOf(body, doc.fps);
|
|
189
|
+
const blendsFrom = previousHash;
|
|
190
|
+
previousHash = hash;
|
|
191
|
+
if (scene.end <= window.start || scene.start >= end) continue;
|
|
192
|
+
// The blend into this Shot, and its window's last local frame (the first
|
|
193
|
+
// after the blend), or -1 when nothing blends into it.
|
|
194
|
+
const into = transitionAt(doc, scene.start);
|
|
195
|
+
const blend = into?.incoming === scene.id ? into : null;
|
|
196
|
+
const blended = blend === null ? -1 : blend.durationFrames;
|
|
197
|
+
const local = new Set<number>(events);
|
|
198
|
+
if (blend !== null) {
|
|
199
|
+
// `run` (`outgoingSceneFrameAt`): every window frame; `hold`, or none
|
|
200
|
+
// set: the window's first and last frames, the one after it, and this
|
|
201
|
+
// Shot's state keys between them (see the header).
|
|
202
|
+
if (doc.scenesById[blend.outgoing]?.transition?.outgoing === "run") {
|
|
203
|
+
for (let at = 0; at <= blended; at += 1) local.add(at);
|
|
204
|
+
} else {
|
|
205
|
+
local.add(0).add(blended - 1).add(blended);
|
|
206
|
+
// ponytail: listed for a Shot with a post chain too, whose operand
|
|
207
|
+
// draws its scene through a source keyed as its presenter's, so they
|
|
208
|
+
// buy nothing there; the chain test (`hasPostChain`) lives with three.
|
|
209
|
+
// Skip them once a three-free one exists.
|
|
210
|
+
for (const at of stateKeys) if (at >= 1 && at <= blended - 2) local.add(at);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const modules = placing.has(scene.id) ? inputs.modules : "";
|
|
214
|
+
const isolation = inputs.isolation !== null && body.layersById[inputs.isolation] !== undefined ? inputs.isolation : "";
|
|
215
|
+
const frames: PreparationFrame[] = [];
|
|
216
|
+
for (const at of [...local].sort((a, b) => a - b)) {
|
|
217
|
+
if (at < 0 || at >= length) continue;
|
|
218
|
+
const frame = scene.start + at;
|
|
219
|
+
if (frame < window.start || frame >= end) continue;
|
|
220
|
+
const prev = at <= blended ? blendsFrom : "";
|
|
221
|
+
const key = `${shared}|${prev}>${hash}${modules === "" ? "" : `~${modules}`}@${at}${isolation === "" ? "" : `!${isolation}`}`;
|
|
222
|
+
frames.push(
|
|
223
|
+
withParts
|
|
224
|
+
? { frame, key, parts: { id: `${scene.id}@${at}`, view: "", shared, prev, shot: hash, modules, isolation, local: at } }
|
|
225
|
+
: { frame, key },
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
shots.push({ start: scene.start, end: scene.end, frames });
|
|
229
|
+
}
|
|
230
|
+
return inPlayingOrder(shots, window, start);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** One Shot's frames inside the window, ascending. */
|
|
234
|
+
interface ShotFrames {
|
|
235
|
+
start: number;
|
|
236
|
+
end: number;
|
|
237
|
+
frames: PreparationFrame[];
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** The Shots in playing order from the playhead's, which comes LAST, each
|
|
241
|
+
* ascending; with the loop off, only the frames reachable in `direction`. */
|
|
242
|
+
function inPlayingOrder(
|
|
243
|
+
shots: readonly ShotFrames[],
|
|
244
|
+
window: { start: number; frames: number },
|
|
245
|
+
{ frame, loop, direction }: PreparationStart,
|
|
246
|
+
): PreparationFrame[] {
|
|
247
|
+
const count = shots.length;
|
|
248
|
+
if (count === 0) return [];
|
|
249
|
+
// Where the transport's first frame is, by `playbackTargetFrame`'s rule
|
|
250
|
+
// (Header.tsx): outside the window it starts at the in point, and with the
|
|
251
|
+
// loop off a start on the far edge plays again from the near one.
|
|
252
|
+
const last = window.start + window.frames - 1;
|
|
253
|
+
let from = frame >= window.start && frame <= last ? frame : window.start;
|
|
254
|
+
if (!loop && from === (direction < 0 ? window.start : last)) from = direction < 0 ? last : window.start;
|
|
255
|
+
const home = Math.max(
|
|
256
|
+
0,
|
|
257
|
+
shots.findIndex((shot) => from >= shot.start && from < shot.end),
|
|
258
|
+
);
|
|
259
|
+
const order: number[] = [];
|
|
260
|
+
for (let step = 1; step < count; step += 1) {
|
|
261
|
+
const at = home + step * direction;
|
|
262
|
+
if (at >= 0 && at < count) order.push(at);
|
|
263
|
+
else if (loop) order.push(((at % count) + count) % count);
|
|
264
|
+
}
|
|
265
|
+
order.push(home);
|
|
266
|
+
const out: PreparationFrame[] = [];
|
|
267
|
+
for (const at of order) {
|
|
268
|
+
for (const entry of shots[at]!.frames) {
|
|
269
|
+
if (!loop && (direction > 0 ? entry.frame < from : entry.frame > from)) continue;
|
|
270
|
+
out.push(entry);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return out;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** What a Shot's frames key on, where its events are, and its state keys
|
|
277
|
+
* (`stateKeyFrames`): once per Shot object, which an edit elsewhere in the
|
|
278
|
+
* document keeps, and per rate, which a spring's frames move with. */
|
|
279
|
+
interface ShotFacts {
|
|
280
|
+
hash: string;
|
|
281
|
+
events: readonly number[];
|
|
282
|
+
stateKeys: readonly number[];
|
|
283
|
+
}
|
|
284
|
+
const shotFacts = new WeakMap<Scene3D, ShotFacts & { fps: Doc3D["fps"] }>();
|
|
285
|
+
|
|
286
|
+
function shotFactsOf(body: Scene3D, fps: Doc3D["fps"]): ShotFacts {
|
|
287
|
+
let facts = shotFacts.get(body);
|
|
288
|
+
if (facts === undefined || facts.fps.num !== fps.num || facts.fps.den !== fps.den) {
|
|
289
|
+
const { name: _name, audio: _audio, guides: _guides, ...drawn } = drawnScene(body);
|
|
290
|
+
facts = { hash: contentHash(drawn), events: sceneEventFrames(body, fps), stateKeys: stateKeyFrames(body), fps };
|
|
291
|
+
shotFacts.set(body, facts);
|
|
292
|
+
}
|
|
293
|
+
return facts;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** The last shared hash and the six fields it was made of, by identity. */
|
|
297
|
+
let sharedMemo: { of: readonly unknown[]; hash: string } | null = null;
|
|
298
|
+
|
|
299
|
+
function sharedHashOf(doc: Doc3D): string {
|
|
300
|
+
const of = [doc.size, doc.fps, doc.assets, doc.materials, doc.materialLighting, doc.components];
|
|
301
|
+
const memo = sharedMemo;
|
|
302
|
+
if (memo !== null && of.every((value, at) => value === memo.of[at])) return memo.hash;
|
|
303
|
+
// Audio draws no pixel: an import of a sound re-keys nothing.
|
|
304
|
+
const assets = Object.fromEntries(Object.entries(doc.assets).filter(([, asset]) => asset.kind !== "audio"));
|
|
305
|
+
const hash = contentHash({
|
|
306
|
+
size: doc.size,
|
|
307
|
+
fps: doc.fps,
|
|
308
|
+
assets,
|
|
309
|
+
materials: doc.materials,
|
|
310
|
+
materialLighting: doc.materialLighting,
|
|
311
|
+
components: doc.components,
|
|
312
|
+
});
|
|
313
|
+
sharedMemo = { of, hash };
|
|
314
|
+
return hash;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/** The Shots that place a code component (`shotsPlacing`), per document. */
|
|
318
|
+
const placingMemo = new WeakMap<Doc3D, ReadonlySet<string>>();
|
|
319
|
+
|
|
320
|
+
function placingOf(doc: Doc3D): ReadonlySet<string> {
|
|
321
|
+
let placing = placingMemo.get(doc);
|
|
322
|
+
if (placing === undefined) {
|
|
323
|
+
placing = new Set(shotsPlacing(doc, []));
|
|
324
|
+
placingMemo.set(doc, placing);
|
|
325
|
+
}
|
|
326
|
+
return placing;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** The Shot-local frames where a layer starts, or one of its tracks can
|
|
330
|
+
* change what the renderer builds (`trackEventFrames`). */
|
|
331
|
+
function sceneEventFrames(scene: Scene3D, fps: Doc3D["fps"]): number[] {
|
|
332
|
+
const length = Math.max(0, Math.trunc(scene.durationFrames));
|
|
333
|
+
const out = [0];
|
|
334
|
+
for (const layer of Object.values(scene.layersById)) {
|
|
335
|
+
if (layer.lifetime !== undefined) out.push(layer.lifetime.in);
|
|
336
|
+
for (const [path, track] of Object.entries(layer.tracks ?? {})) {
|
|
337
|
+
out.push(...trackEventFrames(path, track, fps, lifeInShot(layer, length)));
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return out;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/** The frames of every key a fade, a blur or a component prop track has where
|
|
344
|
+
* its layer draws: what a held window's interior adds (see the header). */
|
|
345
|
+
function stateKeyFrames(scene: Scene3D): number[] {
|
|
346
|
+
const length = Math.max(0, Math.trunc(scene.durationFrames));
|
|
347
|
+
const out: number[] = [];
|
|
348
|
+
for (const layer of Object.values(scene.layersById)) {
|
|
349
|
+
const life = lifeInShot(layer, length);
|
|
350
|
+
for (const [path, track] of Object.entries(layer.tracks ?? {})) {
|
|
351
|
+
if (path !== "transform.opacity" && path !== "blurPx" && !path.startsWith(INSTANCE_PROP_PREFIX)) continue;
|
|
352
|
+
for (const key of track.keyframes) if (key.frame >= life.in && key.frame < life.out) out.push(key.frame);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return out;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/** A layer's lifetime inside its Shot: no lifetime is the whole Shot, so a key
|
|
359
|
+
* past the Shot's end is no frame the layer draws, and the state it reaches
|
|
360
|
+
* inside is found on the curve instead. */
|
|
361
|
+
function lifeInShot(layer: Layer3D, length: number): { in: number; out: number } {
|
|
362
|
+
return { in: layer.lifetime?.in ?? 0, out: Math.min(layer.lifetime?.out ?? length, length) };
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* FNV-1a over a value's JSON. Not a security hash: two inputs colliding costs
|
|
367
|
+
* one skipped preparation, which is a stall, never a wrong picture.
|
|
368
|
+
*/
|
|
369
|
+
function contentHash(value: unknown): string {
|
|
370
|
+
const text = JSON.stringify(value) ?? "";
|
|
371
|
+
let hash = 0x811c9dc5;
|
|
372
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
373
|
+
hash ^= text.charCodeAt(i);
|
|
374
|
+
hash = Math.imul(hash, 0x01000193);
|
|
375
|
+
}
|
|
376
|
+
return (hash >>> 0).toString(36) + text.length.toString(36);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/** Why the prepared set was last cleared: a new renderer (`onReady`), the
|
|
380
|
+
* stage's base moved (`preparationKey().base`), another Project opened, or
|
|
381
|
+
* the set ran past `PREPARED_LIMIT`. */
|
|
382
|
+
export type PreparedClear = "new stage" | "base" | "Project" | "overflow";
|
|
383
|
+
|
|
384
|
+
/** Why a frame is pending, in the order `explain` asks: drawn only in another
|
|
385
|
+
* view, else the first part of its key that differs from the parts this view
|
|
386
|
+
* last credited it with, else one timeout of that very key, else the clear
|
|
387
|
+
* that dropped it, else never drawn. */
|
|
388
|
+
const PREPARATION_REASONS = [
|
|
389
|
+
"view",
|
|
390
|
+
"shared",
|
|
391
|
+
"previous Shot",
|
|
392
|
+
"Shot",
|
|
393
|
+
"modules",
|
|
394
|
+
"isolation",
|
|
395
|
+
"timed out",
|
|
396
|
+
"cleared: new stage",
|
|
397
|
+
"cleared: base",
|
|
398
|
+
"cleared: Project",
|
|
399
|
+
"cleared: overflow",
|
|
400
|
+
"never drawn",
|
|
401
|
+
] as const;
|
|
402
|
+
export type PreparationReason = (typeof PREPARATION_REASONS)[number];
|
|
403
|
+
|
|
404
|
+
/** What `window.__dailiesPrep` holds under the perf flag. */
|
|
405
|
+
export interface PreparationReport {
|
|
406
|
+
/** The passes begun: for a queued Play, and while paused. */
|
|
407
|
+
passes: { foreground: number; background: number };
|
|
408
|
+
/** Why the prepared set was last cleared; null: never. */
|
|
409
|
+
cleared: PreparedClear | null;
|
|
410
|
+
/** The last pass begun. */
|
|
411
|
+
last: PreparationBreakdown | null;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/** One pass: per reason, how many of its frames were pending for it, and the
|
|
415
|
+
* first few as `<Shot id>@<local frame>`. */
|
|
416
|
+
export interface PreparationBreakdown {
|
|
417
|
+
background: boolean;
|
|
418
|
+
frames: number;
|
|
419
|
+
reasons: Partial<Record<PreparationReason, { count: number; examples: string[] }>>;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/** Examples kept per reason. */
|
|
423
|
+
const EXAMPLES = 3;
|
|
424
|
+
|
|
425
|
+
/** The report of the latest `PreparedFrames` made under the perf flag. */
|
|
426
|
+
let report: PreparationReport | null = null;
|
|
427
|
+
|
|
428
|
+
/** That report, or null with the perf flag off. */
|
|
429
|
+
export function preparationReport(): PreparationReport | null {
|
|
430
|
+
return report;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/** What a `PreparedFrames` keeps under the perf flag. */
|
|
434
|
+
interface PreparationDiagnostics {
|
|
435
|
+
/** The parts each frame was last credited with in each view, by
|
|
436
|
+
* `drawnKey`. */
|
|
437
|
+
drawn: Map<string, PreparationKeyParts>;
|
|
438
|
+
/** Every `PreparationKeyParts.id` credited in any view. */
|
|
439
|
+
drawnAnywhere: Set<string>;
|
|
440
|
+
report: PreparationReport;
|
|
441
|
+
/** `?perf=log`: one console line per pass as well. */
|
|
442
|
+
log: boolean;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/** ponytail: the record is bounded by a whole clear, like the runtime's raster
|
|
446
|
+
* key history. It serves the perf report alone, and what a clear costs is a
|
|
447
|
+
* few frames reading "never drawn" until they are drawn again. */
|
|
448
|
+
const DRAWN_LIMIT = 4096;
|
|
449
|
+
|
|
450
|
+
/** How many keys a `PreparedFrames` holds, credited, timed out once and given
|
|
451
|
+
* up together, before it starts over: a long session of edits mints new keys
|
|
452
|
+
* and never retires old ones. */
|
|
453
|
+
const PREPARED_LIMIT = 20_000;
|
|
454
|
+
|
|
455
|
+
const drawnKey = (parts: PreparationKeyParts): string => `${parts.view}#${parts.id}`;
|
|
456
|
+
|
|
457
|
+
/** A new report, which `preparationReport` and `window.__dailiesPrep` show
|
|
458
|
+
* from now on: the page has one store. */
|
|
459
|
+
function startDiagnostics(log: boolean): PreparationDiagnostics {
|
|
460
|
+
const own: PreparationReport = { passes: { foreground: 0, background: 0 }, cleared: null, last: null };
|
|
461
|
+
report = own;
|
|
462
|
+
if (typeof window !== "undefined") (window as unknown as { __dailiesPrep?: PreparationReport }).__dailiesPrep = own;
|
|
463
|
+
return { drawn: new Map(), drawnAnywhere: new Set(), report: own, log };
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function reasonOf(
|
|
467
|
+
parts: PreparationKeyParts,
|
|
468
|
+
sameView: PreparationKeyParts | undefined,
|
|
469
|
+
drawnElsewhere: boolean,
|
|
470
|
+
timedOut: boolean,
|
|
471
|
+
cleared: PreparedClear | null,
|
|
472
|
+
): PreparationReason {
|
|
473
|
+
if (timedOut) return "timed out";
|
|
474
|
+
// Never drawn in this view: the view is what moved, if any other drew it.
|
|
475
|
+
if (sameView === undefined) return drawnElsewhere ? "view" : "never drawn";
|
|
476
|
+
if (parts.shared !== sameView.shared) return "shared";
|
|
477
|
+
if (parts.prev !== sameView.prev) return "previous Shot";
|
|
478
|
+
if (parts.shot !== sameView.shot) return "Shot";
|
|
479
|
+
if (parts.modules !== sameView.modules) return "modules";
|
|
480
|
+
if (parts.isolation !== sameView.isolation) return "isolation";
|
|
481
|
+
// The very key it was credited with: only a clear drops a credited key.
|
|
482
|
+
return cleared === null ? "never drawn" : `cleared: ${cleared}`;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* The keys this stage has drawn, the ones it gave up on, and the ones that
|
|
487
|
+
* timed out once. Owned by the store, which clears it whenever the GPU state
|
|
488
|
+
* the keys stand for is replaced: a new renderer, a moved base, another
|
|
489
|
+
* Project.
|
|
490
|
+
*/
|
|
491
|
+
export class PreparedFrames {
|
|
492
|
+
/** Presented in the playing presentation by their own publication, or
|
|
493
|
+
* drawn whole by the transport. */
|
|
494
|
+
private readonly keys = new Set<string>();
|
|
495
|
+
/** Timed out twice: not asked for again until the key or the base moves. */
|
|
496
|
+
private readonly gaveUp = new Set<string>();
|
|
497
|
+
/** Timed out once, with no external claim pending at the deadline. */
|
|
498
|
+
private readonly timedOut = new Set<string>();
|
|
499
|
+
/** Under the perf flag only, read when the store makes this. */
|
|
500
|
+
private readonly diagnostics: PreparationDiagnostics | null = perf === null ? null : startDiagnostics(perf === "log");
|
|
501
|
+
|
|
502
|
+
/** `frames` less what is prepared or given up. */
|
|
503
|
+
pending(frames: readonly PreparationFrame[]): PreparationFrame[] {
|
|
504
|
+
return frames.filter((entry) => !this.keys.has(entry.key) && !this.gaveUp.has(entry.key));
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/** One frame, by how the stage said it ended (bridge v51). `"presented"`
|
|
508
|
+
* prepares its key alone; a second `"timeout"` gives the key up; a
|
|
509
|
+
* `"timeout-pending"` (a load still out at the deadline) counts nothing, so
|
|
510
|
+
* the frame is asked for again. */
|
|
511
|
+
credit(entry: PreparationFrame, outcome: ShotPreparedOutcome): void {
|
|
512
|
+
if (outcome === "timeout-pending") return;
|
|
513
|
+
if (this.keys.size + this.gaveUp.size + this.timedOut.size >= PREPARED_LIMIT) this.clear("overflow");
|
|
514
|
+
if (outcome === "timeout") {
|
|
515
|
+
if (this.timedOut.delete(entry.key)) this.gaveUp.add(entry.key);
|
|
516
|
+
else this.timedOut.add(entry.key);
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
this.timedOut.delete(entry.key);
|
|
520
|
+
this.keys.add(entry.key);
|
|
521
|
+
const diagnostics = this.diagnostics;
|
|
522
|
+
if (diagnostics === null || entry.parts === undefined) return;
|
|
523
|
+
if (diagnostics.drawn.size >= DRAWN_LIMIT) {
|
|
524
|
+
diagnostics.drawn.clear();
|
|
525
|
+
diagnostics.drawnAnywhere.clear();
|
|
526
|
+
}
|
|
527
|
+
diagnostics.drawn.set(drawnKey(entry.parts), entry.parts);
|
|
528
|
+
diagnostics.drawnAnywhere.add(entry.parts.id);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
clear(reason: PreparedClear): void {
|
|
532
|
+
this.keys.clear();
|
|
533
|
+
this.gaveUp.clear();
|
|
534
|
+
this.timedOut.clear();
|
|
535
|
+
if (this.diagnostics !== null) this.diagnostics.report.cleared = reason;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/** Under the perf flag only: why each frame of a pass that begins is
|
|
539
|
+
* pending, kept as the report's `last`, and under `?perf=log` one console
|
|
540
|
+
* line. */
|
|
541
|
+
explain(frames: readonly PreparationFrame[], background: boolean): void {
|
|
542
|
+
const diagnostics = this.diagnostics;
|
|
543
|
+
if (diagnostics === null) return;
|
|
544
|
+
const { drawn, drawnAnywhere, report: own } = diagnostics;
|
|
545
|
+
const reasons: PreparationBreakdown["reasons"] = {};
|
|
546
|
+
for (const { key, parts } of frames) {
|
|
547
|
+
if (parts === undefined) continue;
|
|
548
|
+
const reason = reasonOf(parts, drawn.get(drawnKey(parts)), drawnAnywhere.has(parts.id), this.timedOut.has(key), own.cleared);
|
|
549
|
+
const row = (reasons[reason] ??= { count: 0, examples: [] });
|
|
550
|
+
row.count += 1;
|
|
551
|
+
if (row.examples.length < EXAMPLES) row.examples.push(parts.id);
|
|
552
|
+
}
|
|
553
|
+
own.passes[background ? "background" : "foreground"] += 1;
|
|
554
|
+
own.last = { background, frames: frames.length, reasons };
|
|
555
|
+
if (!diagnostics.log) return;
|
|
556
|
+
const words = PREPARATION_REASONS.flatMap((reason) => {
|
|
557
|
+
const row = reasons[reason];
|
|
558
|
+
return row === undefined ? [] : [`${reason} ${row.count} (${row.examples.join(", ")})`];
|
|
559
|
+
});
|
|
560
|
+
console.debug(`[prep] ${background ? "background" : "play"} pass, ${frames.length} frames: ${words.join("; ")}`);
|
|
561
|
+
}
|
|
562
|
+
}
|