@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,90 @@
|
|
|
1
|
+
// A BUDGET OF LIVE PREVIEWS, shared by every door that mounts one: the second
|
|
2
|
+
// shot stage (`ComponentStage`), on the board's component cards and in the
|
|
3
|
+
// component sheet. Each live preview is a document
|
|
4
|
+
// and, for anything WebGL, a context, so a module-level budget caps how many
|
|
5
|
+
// run at once. A preview takes a slot by its key and hands it on when it
|
|
6
|
+
// unmounts or scrolls away; one waiting wakes when a slot frees.
|
|
7
|
+
//
|
|
8
|
+
// ONE SLOT PER KEY, COUNTED. A card that holds the slot for its reference id
|
|
9
|
+
// mounts a preview that asks for the same id, so the two share one slot, and
|
|
10
|
+
// the slot frees only when the last holder lets go.
|
|
11
|
+
// ponytail: a fixed cap; make it LRU by last selection if more doors mount it.
|
|
12
|
+
import { useEffect, useState } from "react";
|
|
13
|
+
|
|
14
|
+
export const LIVE_CAP = 6;
|
|
15
|
+
const live = new Map<string, number>();
|
|
16
|
+
const waiters = new Set<() => void>();
|
|
17
|
+
|
|
18
|
+
function acquire(key: string): boolean {
|
|
19
|
+
const holders = live.get(key);
|
|
20
|
+
if (holders !== undefined) {
|
|
21
|
+
live.set(key, holders + 1);
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
if (live.size >= LIVE_CAP) return false;
|
|
25
|
+
live.set(key, 1);
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function release(key: string): void {
|
|
30
|
+
const holders = live.get(key);
|
|
31
|
+
if (holders === undefined) return;
|
|
32
|
+
if (holders > 1) {
|
|
33
|
+
live.set(key, holders - 1);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
live.delete(key);
|
|
37
|
+
for (const wake of [...waiters]) wake();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** For the suite, which must not inherit one file's live cards. */
|
|
41
|
+
export function resetLivePreviews(): void {
|
|
42
|
+
live.clear();
|
|
43
|
+
waiters.clear();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Whether `key` holds a live slot. Asks while `want` is true, waits while the
|
|
48
|
+
* budget is full, and releases the moment `want` turns false or the caller
|
|
49
|
+
* unmounts.
|
|
50
|
+
*/
|
|
51
|
+
export function useLiveSlot(key: string, want: boolean): boolean {
|
|
52
|
+
const [slot, setSlot] = useState(false);
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (!want) return;
|
|
55
|
+
let held = false;
|
|
56
|
+
const take = (): void => {
|
|
57
|
+
if (held || !acquire(key)) return;
|
|
58
|
+
held = true;
|
|
59
|
+
waiters.delete(take);
|
|
60
|
+
setSlot(true);
|
|
61
|
+
};
|
|
62
|
+
take();
|
|
63
|
+
if (!held) waiters.add(take);
|
|
64
|
+
return () => {
|
|
65
|
+
waiters.delete(take);
|
|
66
|
+
setSlot(false);
|
|
67
|
+
if (held) release(key);
|
|
68
|
+
};
|
|
69
|
+
}, [want, key]);
|
|
70
|
+
return slot && want;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Whether `el` is on screen. True where there is no `IntersectionObserver`
|
|
74
|
+
* (a suite), so a test sees the live branch. */
|
|
75
|
+
export function useInView(el: HTMLElement | null): boolean {
|
|
76
|
+
const [inView, setInView] = useState(false);
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
if (el === null) return;
|
|
79
|
+
if (typeof IntersectionObserver === "undefined") {
|
|
80
|
+
setInView(true);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const observer = new IntersectionObserver((entries) => {
|
|
84
|
+
for (const entry of entries) setInView(entry.isIntersecting);
|
|
85
|
+
});
|
|
86
|
+
observer.observe(el);
|
|
87
|
+
return () => observer.disconnect();
|
|
88
|
+
}, [el]);
|
|
89
|
+
return inView;
|
|
90
|
+
}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/* THE PLAN PANEL (PlanPanel.tsx, Paper SB1 and SB2). Wrapped in the
|
|
2
|
+
`component-sheets` cascade layer: the component imports it, and under the
|
|
3
|
+
/scene index's layer order it ranks below the house widgets, so it styles
|
|
4
|
+
its own classes and the layout around the widgets, never the widgets. The
|
|
5
|
+
rules it needs on a widget (the cut trigger's open caret, the bare fields'
|
|
6
|
+
inset) are in styles/modes/storyboard.css, in the chrome layer, where they
|
|
7
|
+
win. */
|
|
8
|
+
@layer component-sheets {
|
|
9
|
+
.plan-panel {
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: column;
|
|
12
|
+
min-width: 0;
|
|
13
|
+
min-height: 0;
|
|
14
|
+
height: 100%;
|
|
15
|
+
background: var(--surface-panel);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.plan-head-shot {
|
|
19
|
+
margin-left: auto;
|
|
20
|
+
font-size: var(--type-sm);
|
|
21
|
+
line-height: var(--leading-xs);
|
|
22
|
+
font-weight: var(--weight-medium);
|
|
23
|
+
color: var(--ink-3);
|
|
24
|
+
}
|
|
25
|
+
.plan-head-number {
|
|
26
|
+
font-family: var(--font-numeric);
|
|
27
|
+
font-variant-numeric: var(--numeric);
|
|
28
|
+
font-size: var(--type-md);
|
|
29
|
+
line-height: var(--leading-xs);
|
|
30
|
+
color: var(--ink-3);
|
|
31
|
+
}
|
|
32
|
+
/* The name is a button that opens its rename (`PartName`): it reads as the
|
|
33
|
+
title, ellipsized, and only its keyboard focus shows a ring. The heading
|
|
34
|
+
around it keeps the head's face, so the open rename's words are not set
|
|
35
|
+
in a heading's bold. */
|
|
36
|
+
.plan-head-title {
|
|
37
|
+
flex: 1;
|
|
38
|
+
min-width: 0;
|
|
39
|
+
margin: 0;
|
|
40
|
+
display: flex;
|
|
41
|
+
font: inherit;
|
|
42
|
+
}
|
|
43
|
+
.plan-head-name {
|
|
44
|
+
min-width: 0;
|
|
45
|
+
max-width: 100%;
|
|
46
|
+
padding: 0;
|
|
47
|
+
overflow: hidden;
|
|
48
|
+
text-align: left;
|
|
49
|
+
text-overflow: ellipsis;
|
|
50
|
+
white-space: nowrap;
|
|
51
|
+
font-size: var(--type-lg);
|
|
52
|
+
line-height: var(--leading-xs);
|
|
53
|
+
font-weight: var(--weight-medium);
|
|
54
|
+
color: var(--ink-1);
|
|
55
|
+
background: none;
|
|
56
|
+
border: 0;
|
|
57
|
+
border-radius: var(--radius-xs);
|
|
58
|
+
cursor: default;
|
|
59
|
+
}
|
|
60
|
+
.plan-head-name:focus-visible { outline: none; box-shadow: var(--focus-ring); }
|
|
61
|
+
/* The open rename takes the name's place and the width it had. */
|
|
62
|
+
.plan-rename {
|
|
63
|
+
flex: 1;
|
|
64
|
+
min-width: 0;
|
|
65
|
+
display: flex;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* THE BODY on the inspector's scale: 12 in from the column's edges and 12
|
|
69
|
+
between blocks, a block's caps label 6 over its control. It holds its
|
|
70
|
+
scrollbar's gutter, so a body that grows past the fold does not narrow
|
|
71
|
+
every control under the pointer. */
|
|
72
|
+
.plan-body {
|
|
73
|
+
display: flex;
|
|
74
|
+
flex-direction: column;
|
|
75
|
+
flex: 1;
|
|
76
|
+
gap: var(--space-6);
|
|
77
|
+
min-height: 0;
|
|
78
|
+
padding: var(--space-6);
|
|
79
|
+
overflow-y: auto;
|
|
80
|
+
scrollbar-gutter: stable;
|
|
81
|
+
}
|
|
82
|
+
.plan-field {
|
|
83
|
+
display: flex;
|
|
84
|
+
flex-direction: column;
|
|
85
|
+
gap: var(--space-3);
|
|
86
|
+
min-width: 0;
|
|
87
|
+
}
|
|
88
|
+
.plan-field-head {
|
|
89
|
+
display: flex;
|
|
90
|
+
align-items: center;
|
|
91
|
+
justify-content: space-between;
|
|
92
|
+
gap: var(--space-4);
|
|
93
|
+
}
|
|
94
|
+
.plan-count {
|
|
95
|
+
font-size: var(--type-sm);
|
|
96
|
+
line-height: var(--leading-xs);
|
|
97
|
+
font-weight: var(--weight-medium);
|
|
98
|
+
color: var(--ink-3);
|
|
99
|
+
}
|
|
100
|
+
.plan-count.is-numeric {
|
|
101
|
+
font-family: var(--font-numeric);
|
|
102
|
+
font-variant-numeric: var(--numeric);
|
|
103
|
+
font-weight: var(--weight-regular);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* How it moves: one row per beat, its number in a chip, then its words in
|
|
107
|
+
the house box with its remove (the house row verb, `.insp-key`) inside
|
|
108
|
+
it, so the open draft's box, which has no remove, ends on the same edge. */
|
|
109
|
+
.plan-beats {
|
|
110
|
+
display: flex;
|
|
111
|
+
flex-direction: column;
|
|
112
|
+
gap: var(--space-1);
|
|
113
|
+
margin: 0;
|
|
114
|
+
padding: 0;
|
|
115
|
+
list-style: none;
|
|
116
|
+
}
|
|
117
|
+
.plan-beat {
|
|
118
|
+
display: flex;
|
|
119
|
+
align-items: center;
|
|
120
|
+
gap: var(--space-3);
|
|
121
|
+
min-height: var(--row-h);
|
|
122
|
+
}
|
|
123
|
+
.plan-beat[draggable="true"] { cursor: grab; }
|
|
124
|
+
.plan-beat-number {
|
|
125
|
+
display: inline-flex;
|
|
126
|
+
align-items: center;
|
|
127
|
+
justify-content: center;
|
|
128
|
+
flex: none;
|
|
129
|
+
width: var(--badge-h);
|
|
130
|
+
height: var(--badge-h);
|
|
131
|
+
border-radius: var(--radius-xs);
|
|
132
|
+
background: var(--surface-secondary);
|
|
133
|
+
font-family: var(--font-numeric);
|
|
134
|
+
font-variant-numeric: var(--numeric);
|
|
135
|
+
font-size: var(--type-sm);
|
|
136
|
+
line-height: var(--leading-xs);
|
|
137
|
+
color: var(--ink-2);
|
|
138
|
+
}
|
|
139
|
+
.plan-add-beat {
|
|
140
|
+
display: inline-flex;
|
|
141
|
+
align-items: center;
|
|
142
|
+
align-self: flex-start;
|
|
143
|
+
gap: var(--space-4);
|
|
144
|
+
height: var(--row-h);
|
|
145
|
+
padding: 0;
|
|
146
|
+
font-size: var(--type-md);
|
|
147
|
+
line-height: var(--leading-xs);
|
|
148
|
+
font-weight: var(--weight-medium);
|
|
149
|
+
color: var(--ink-2);
|
|
150
|
+
background: none;
|
|
151
|
+
border: none;
|
|
152
|
+
border-radius: var(--radius-xs);
|
|
153
|
+
cursor: pointer;
|
|
154
|
+
}
|
|
155
|
+
.plan-add-beat:hover:not(:disabled) { color: var(--ink-1); }
|
|
156
|
+
.plan-add-beat-icon {
|
|
157
|
+
display: inline-flex;
|
|
158
|
+
align-items: center;
|
|
159
|
+
justify-content: center;
|
|
160
|
+
width: var(--badge-h);
|
|
161
|
+
height: var(--badge-h);
|
|
162
|
+
color: var(--ink-3);
|
|
163
|
+
}
|
|
164
|
+
.plan-add-beat-icon > svg { width: 16px; height: 16px; }
|
|
165
|
+
.plan-add-beat:disabled { opacity: var(--opacity-disabled); cursor: default; }
|
|
166
|
+
.plan-add-beat:focus-visible { outline: none; box-shadow: var(--focus-ring); }
|
|
167
|
+
|
|
168
|
+
/* Length and Cut to next share one row: a fixed Length, the cut the rest.
|
|
169
|
+
The cut is the house select trigger; the plan sets only its played
|
|
170
|
+
length, in the numeric face after the name. */
|
|
171
|
+
.plan-timing {
|
|
172
|
+
display: flex;
|
|
173
|
+
gap: var(--space-3);
|
|
174
|
+
}
|
|
175
|
+
.plan-length { flex: none; width: 96px; }
|
|
176
|
+
.plan-cut { flex: 1; }
|
|
177
|
+
.plan-cut-length {
|
|
178
|
+
flex: none;
|
|
179
|
+
font-family: var(--font-numeric);
|
|
180
|
+
font-variant-numeric: var(--numeric);
|
|
181
|
+
color: var(--ink-2);
|
|
182
|
+
}
|
|
183
|
+
.plan-cut-rows {
|
|
184
|
+
display: flex;
|
|
185
|
+
flex-direction: column;
|
|
186
|
+
gap: var(--space-3);
|
|
187
|
+
padding-top: var(--space-4);
|
|
188
|
+
border-top: 1px solid var(--line-soft);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/* Ground: a swatch before each word. */
|
|
192
|
+
.plan-swatch {
|
|
193
|
+
display: inline-block;
|
|
194
|
+
flex: none;
|
|
195
|
+
width: 10px;
|
|
196
|
+
height: 10px;
|
|
197
|
+
margin-inline-end: var(--space-3);
|
|
198
|
+
border-radius: var(--radius-2xs);
|
|
199
|
+
vertical-align: middle;
|
|
200
|
+
}
|
|
201
|
+
.plan-swatch.is-paper { background: var(--sketch-paper); }
|
|
202
|
+
.plan-swatch.is-dark { background: var(--surface-app); box-shadow: inset 0 0 0 1px var(--line-strong); }
|
|
203
|
+
.plan-swatch.is-brand { background: var(--accent); }
|
|
204
|
+
|
|
205
|
+
/* Pinned to this shot. */
|
|
206
|
+
.plan-pins {
|
|
207
|
+
display: flex;
|
|
208
|
+
flex-direction: column;
|
|
209
|
+
gap: var(--space-1);
|
|
210
|
+
margin: 0;
|
|
211
|
+
padding: 0;
|
|
212
|
+
list-style: none;
|
|
213
|
+
}
|
|
214
|
+
.plan-pin {
|
|
215
|
+
display: flex;
|
|
216
|
+
align-items: center;
|
|
217
|
+
gap: var(--space-4);
|
|
218
|
+
min-height: var(--row-h);
|
|
219
|
+
border-radius: var(--radius-sm);
|
|
220
|
+
}
|
|
221
|
+
.plan-pin[draggable="true"] { cursor: grab; }
|
|
222
|
+
.plan-pin:hover { background: var(--fill-rest); }
|
|
223
|
+
.plan-pin-thumb {
|
|
224
|
+
display: inline-flex;
|
|
225
|
+
align-items: center;
|
|
226
|
+
justify-content: center;
|
|
227
|
+
flex: none;
|
|
228
|
+
width: var(--badge-h);
|
|
229
|
+
height: var(--badge-h);
|
|
230
|
+
overflow: hidden;
|
|
231
|
+
border-radius: var(--radius-xs);
|
|
232
|
+
background: var(--surface-secondary);
|
|
233
|
+
font-size: var(--type-micro);
|
|
234
|
+
line-height: var(--leading-xs);
|
|
235
|
+
color: var(--ink-3);
|
|
236
|
+
}
|
|
237
|
+
.plan-pin-thumb > img { width: 100%; height: 100%; object-fit: cover; }
|
|
238
|
+
.plan-pin-name {
|
|
239
|
+
flex: 1;
|
|
240
|
+
min-width: 0;
|
|
241
|
+
overflow: hidden;
|
|
242
|
+
text-overflow: ellipsis;
|
|
243
|
+
white-space: nowrap;
|
|
244
|
+
font-size: var(--type-md);
|
|
245
|
+
line-height: var(--leading-xs);
|
|
246
|
+
color: var(--ink-1);
|
|
247
|
+
}
|
|
248
|
+
.plan-pin-on {
|
|
249
|
+
display: inline-flex;
|
|
250
|
+
align-items: center;
|
|
251
|
+
gap: var(--space-2);
|
|
252
|
+
flex: none;
|
|
253
|
+
font-size: var(--type-sm);
|
|
254
|
+
line-height: var(--leading-xs);
|
|
255
|
+
font-weight: var(--weight-medium);
|
|
256
|
+
color: var(--ink-2);
|
|
257
|
+
}
|
|
258
|
+
.plan-pin-on::before {
|
|
259
|
+
content: "";
|
|
260
|
+
width: 6px;
|
|
261
|
+
height: 6px;
|
|
262
|
+
border-radius: var(--radius-full);
|
|
263
|
+
background: var(--success);
|
|
264
|
+
}
|
|
265
|
+
.plan-pin-kind {
|
|
266
|
+
flex: none;
|
|
267
|
+
width: 64px;
|
|
268
|
+
text-align: right;
|
|
269
|
+
font-size: var(--type-sm);
|
|
270
|
+
line-height: var(--leading-xs);
|
|
271
|
+
color: var(--ink-3);
|
|
272
|
+
}
|
|
273
|
+
.plan-none {
|
|
274
|
+
margin: 0;
|
|
275
|
+
font-size: var(--type-md);
|
|
276
|
+
line-height: var(--leading-sm);
|
|
277
|
+
color: var(--ink-3);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/* Style: the count, under a soft rule. */
|
|
281
|
+
.plan-style {
|
|
282
|
+
display: flex;
|
|
283
|
+
flex-direction: column;
|
|
284
|
+
gap: var(--space-3);
|
|
285
|
+
padding-top: var(--space-6);
|
|
286
|
+
border-top: 1px solid var(--line-soft);
|
|
287
|
+
}
|
|
288
|
+
.plan-style-text {
|
|
289
|
+
margin: 0;
|
|
290
|
+
min-width: 0;
|
|
291
|
+
font-size: var(--type-md);
|
|
292
|
+
line-height: var(--leading-xs);
|
|
293
|
+
color: var(--ink-2);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// A COMPONENT CARD'S POSTER: the still a live preview shows at rest (S3).
|
|
2
|
+
//
|
|
3
|
+
// The board draws every component card from this picture and mounts a live
|
|
4
|
+
// stage only on hover, focus or selection (`ComponentStage`, `LIVE_CAP`), so
|
|
5
|
+
// a wall of cards costs pictures, not documents. A live stage captures the
|
|
6
|
+
// poster once it is ready (`bridge.captureFrame`) and writes it here.
|
|
7
|
+
//
|
|
8
|
+
// KEYED ON WHAT THE PICTURE SHOWS: the component (or Studio definition), its
|
|
9
|
+
// content hash and the card's own props, so a tweak or a code change asks for
|
|
10
|
+
// a new poster rather than showing a stale one.
|
|
11
|
+
//
|
|
12
|
+
// MEMORY FIRST, THEN INDEXEDDB, so a reload shows the posters at once. Every
|
|
13
|
+
// IndexedDB call is wrapped: a private window, blocked site data or a suite
|
|
14
|
+
// with no IndexedDB leaves the memory cache, and the live stage still draws.
|
|
15
|
+
// ponytail: the store grows with every key; prune by age if it ever matters.
|
|
16
|
+
import { useEffect, useState } from "react";
|
|
17
|
+
import { canonicalJSON, fnv1a64 } from "@dailies-studio/contract";
|
|
18
|
+
|
|
19
|
+
const DB_NAME = "dailies-component-posters";
|
|
20
|
+
const STORE = "posters";
|
|
21
|
+
|
|
22
|
+
const memory = new Map<string, string>();
|
|
23
|
+
const listeners = new Set<(key: string) => void>();
|
|
24
|
+
|
|
25
|
+
/** `${component|def}|${contentHash}|${fnv1a64(canonicalJSON(props))}`. A
|
|
26
|
+
* props value canonicalJSON refuses (a non-finite number) keys as "props". */
|
|
27
|
+
export function posterKey(source: string, contentHash: string, props: Record<string, unknown>): string {
|
|
28
|
+
let hash: string;
|
|
29
|
+
try {
|
|
30
|
+
hash = fnv1a64(canonicalJSON(props));
|
|
31
|
+
} catch {
|
|
32
|
+
hash = "props";
|
|
33
|
+
}
|
|
34
|
+
return `${source}|${contentHash}|${hash}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let opened: Promise<IDBDatabase | null> | null = null;
|
|
38
|
+
function openDb(): Promise<IDBDatabase | null> {
|
|
39
|
+
opened ??= new Promise<IDBDatabase | null>((resolve) => {
|
|
40
|
+
try {
|
|
41
|
+
if (typeof indexedDB === "undefined") {
|
|
42
|
+
resolve(null);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const request = indexedDB.open(DB_NAME, 1);
|
|
46
|
+
request.onupgradeneeded = () => request.result.createObjectStore(STORE);
|
|
47
|
+
request.onsuccess = () => resolve(request.result);
|
|
48
|
+
request.onerror = () => resolve(null);
|
|
49
|
+
request.onblocked = () => resolve(null);
|
|
50
|
+
} catch {
|
|
51
|
+
resolve(null);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return opened;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function readStored(key: string): Promise<Blob | null> {
|
|
58
|
+
const db = await openDb();
|
|
59
|
+
if (db === null) return null;
|
|
60
|
+
return new Promise<Blob | null>((resolve) => {
|
|
61
|
+
try {
|
|
62
|
+
const request = db.transaction(STORE, "readonly").objectStore(STORE).get(key);
|
|
63
|
+
request.onsuccess = () => resolve(request.result instanceof Blob ? request.result : null);
|
|
64
|
+
request.onerror = () => resolve(null);
|
|
65
|
+
} catch {
|
|
66
|
+
resolve(null);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The poster's object URL, from memory, else from IndexedDB. Null for none. */
|
|
72
|
+
export async function readPoster(key: string): Promise<string | null> {
|
|
73
|
+
const known = memory.get(key);
|
|
74
|
+
if (known !== undefined) return known;
|
|
75
|
+
const blob = await readStored(key);
|
|
76
|
+
if (blob === null) return null;
|
|
77
|
+
// A capture may have landed while IndexedDB answered: it is newer.
|
|
78
|
+
const raced = memory.get(key);
|
|
79
|
+
if (raced !== undefined) return raced;
|
|
80
|
+
const url = URL.createObjectURL(blob);
|
|
81
|
+
memory.set(key, url);
|
|
82
|
+
return url;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Keeps `blob` as the poster for `key`, replacing any older one. */
|
|
86
|
+
export function writePoster(key: string, blob: Blob): string {
|
|
87
|
+
const old = memory.get(key);
|
|
88
|
+
if (old !== undefined) URL.revokeObjectURL(old);
|
|
89
|
+
const url = URL.createObjectURL(blob);
|
|
90
|
+
memory.set(key, url);
|
|
91
|
+
for (const listen of [...listeners]) listen(key);
|
|
92
|
+
void openDb().then((db) => {
|
|
93
|
+
try {
|
|
94
|
+
db?.transaction(STORE, "readwrite").objectStore(STORE).put(blob, key);
|
|
95
|
+
} catch {
|
|
96
|
+
// The memory copy stands; the next session captures again.
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
return url;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** The poster for `key` as an object URL, following later writes. Null while
|
|
103
|
+
* there is none (or `key` is null). */
|
|
104
|
+
export function usePoster(key: string | null): string | null {
|
|
105
|
+
const [url, setUrl] = useState<string | null>(() => (key === null ? null : (memory.get(key) ?? null)));
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
if (key === null) {
|
|
108
|
+
setUrl(null);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
let alive = true;
|
|
112
|
+
setUrl(memory.get(key) ?? null);
|
|
113
|
+
void readPoster(key).then((next) => {
|
|
114
|
+
if (alive && next !== null) setUrl(next);
|
|
115
|
+
});
|
|
116
|
+
const listen = (written: string): void => {
|
|
117
|
+
if (written === key) setUrl(memory.get(key) ?? null);
|
|
118
|
+
};
|
|
119
|
+
listeners.add(listen);
|
|
120
|
+
return () => {
|
|
121
|
+
alive = false;
|
|
122
|
+
listeners.delete(listen);
|
|
123
|
+
};
|
|
124
|
+
}, [key]);
|
|
125
|
+
return url;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** For the suite: forget every poster in memory. */
|
|
129
|
+
export function resetPosters(): void {
|
|
130
|
+
for (const url of memory.values()) URL.revokeObjectURL(url);
|
|
131
|
+
memory.clear();
|
|
132
|
+
listeners.clear();
|
|
133
|
+
}
|