@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
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// It renders no conditional siblings, holds no error state and takes no
|
|
13
13
|
// children, because it is the shell's backdrop: `App` mounts it as the
|
|
14
14
|
// unconditional, unkeyed FIRST child of the shell root and the CSS pins it
|
|
15
|
-
// `position: fixed; inset: 0` behind the
|
|
15
|
+
// `position: fixed; inset: 0` behind the docked panels. Anything conditional
|
|
16
16
|
// living in here would share a parent with the iframe, and the whole point of
|
|
17
17
|
// the arrangement is that no shell state can reorder, reparent or remount it.
|
|
18
18
|
//
|
|
@@ -25,25 +25,86 @@
|
|
|
25
25
|
// The connect FAILURE therefore reports upward, into the shell's overlay strip,
|
|
26
26
|
// rather than rendering a sibling strip here.
|
|
27
27
|
//
|
|
28
|
+
// HOSTED, THE STAGE WAITS FOR THE CODE HOST (hostApi.ts). No Vite serves the
|
|
29
|
+
// project's modules there: a Service Worker does, from what the host compiled.
|
|
30
|
+
// So the iframe gets its src only once `__dailiesHost.ready` settles (the
|
|
31
|
+
// element itself is mounted from the start, so its identity never moves), and
|
|
32
|
+
// each stage load asks the host before connecting, which re-posts the module
|
|
33
|
+
// epoch and makes sure the Service Worker controls the new document. Locally
|
|
34
|
+
// there is no host and none of this runs.
|
|
35
|
+
//
|
|
28
36
|
// Note main.tsx mounts under StrictMode, so this effect double-runs in dev:
|
|
29
37
|
// connect, abort + attachStage(null), connect again. The abort path rejects
|
|
30
38
|
// with "stage connect aborted" and is swallowed. The iframe ELEMENT is not
|
|
31
39
|
// remounted by that — only the effect re-runs — and any future effect touching
|
|
32
40
|
// the bridge must survive the same double-invoke.
|
|
33
|
-
import { useEffect, useRef } from "react";
|
|
41
|
+
import { useEffect, useRef, useState } from "react";
|
|
42
|
+
import { useStudioTheme } from "@dailies-studio/ui/theme";
|
|
34
43
|
import { useStore } from "../../state/context.js";
|
|
35
|
-
import { connectStage } from "../../bridge.js";
|
|
44
|
+
import { connectStage, stageBridgeOf } from "../../bridge.js";
|
|
45
|
+
import { dailiesHost } from "../../hostApi.js";
|
|
46
|
+
import { chromePerfFlag } from "../../state/playbackPreparation.js";
|
|
47
|
+
import { bindBoardClipboard } from "../storyboard/boardClipboard.js";
|
|
48
|
+
import { PLAY_CONTROL } from "../timeline/PlayButton.js";
|
|
49
|
+
|
|
50
|
+
/** The author input a background preparing pass gives way to, in either
|
|
51
|
+
* window (`noteAuthorInput`). */
|
|
52
|
+
const AUTHOR_INPUT = ["pointerdown", "keydown", "wheel", "dragover"] as const;
|
|
36
53
|
|
|
37
|
-
/**
|
|
38
|
-
*
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
54
|
+
/** Input aimed at the Play control itself: its click, after the press,
|
|
55
|
+
* upgrades a background pass, which must still be running then. */
|
|
56
|
+
function atPlayControl(event: Event): boolean {
|
|
57
|
+
const target = event.target as Partial<Element> | null;
|
|
58
|
+
return typeof target?.closest === "function" && target.closest(PLAY_CONTROL) !== null;
|
|
59
|
+
}
|
|
60
|
+
/** How far a pointer over the stage moves from where it was last heard
|
|
61
|
+
* before the move counts as input, in CSS px. */
|
|
62
|
+
const POINTER_TRAVEL_PX = 3;
|
|
63
|
+
|
|
64
|
+
/** Whether a drag over the stage can land: only a file does. An asset tile,
|
|
65
|
+
* a board card, a planned shot and a Library card land on the board, never
|
|
66
|
+
* here. Types only: `getData` is empty until the drop. */
|
|
67
|
+
export function stageDropLands(dt: Pick<DataTransfer, "types">): boolean {
|
|
68
|
+
return Array.from(dt.types).includes("Files");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The stage URL is frozen by StagePanel's ref on its FIRST render. Only the
|
|
72
|
+
* perf flag, the gate's two A/B arms (`warmup`, `rasterDensity`), the heavy
|
|
73
|
+
* lanes' render-scale arm (`renderScale`) and the initial resolved theme
|
|
74
|
+
* cross the boundary, never the
|
|
75
|
+
* chrome's whole search string: the chrome rewrites ?shot/?scene via
|
|
76
|
+
* history.replaceState, and an iframe src interpolating live location.search
|
|
77
|
+
* would RELOAD the stage (fresh GL context, every raster redrawn) on every
|
|
78
|
+
* document switch or appearance change. */
|
|
79
|
+
export function stageSrc(theme: "light" | "dark"): string {
|
|
80
|
+
const search = new URLSearchParams();
|
|
81
|
+
// ALLOWLISTED, never forwarded verbatim (`chromePerfFlag`, which the
|
|
82
|
+
// preparing pass's diagnostics read too): this value is interpolated into
|
|
83
|
+
// the stage's URL, so the set of things it may be is closed on purpose. "1"
|
|
84
|
+
// is the HUD, "log" is the HUD plus a console echo of every sample.
|
|
85
|
+
const perf = chromePerfFlag();
|
|
86
|
+
if (perf !== null) search.set("perf", perf);
|
|
87
|
+
const own = typeof window === "undefined" ? null : new URLSearchParams(window.location.search);
|
|
88
|
+
// Same closed set for the warm-up arm (`ShotRuntimeProps.warmup`): the
|
|
89
|
+
// playback gate's A/B seam, absent from the URL in normal use.
|
|
90
|
+
const warmup = own?.get("warmup") ?? null;
|
|
91
|
+
if (warmup === "off" || warmup === "mount") {
|
|
92
|
+
search.set("warmup", warmup);
|
|
93
|
+
}
|
|
94
|
+
// And the raster-density arm (the stage's `STAGE_RASTER_DENSITY`): `on`
|
|
95
|
+
// cuts the layer rasters at the zoom times the density, one value and no
|
|
96
|
+
// other.
|
|
97
|
+
if (own?.get("rasterDensity") === "on") search.set("rasterDensity", "on");
|
|
98
|
+
// And the render-scale arm (the stage's `STAGE_RENDER_SCALE`): `off` mounts
|
|
99
|
+
// no playback scale at all, and a step forces that scale on every played
|
|
100
|
+
// frame. The heavy proofs' seam, never set in normal use.
|
|
101
|
+
const renderScale = own?.get("renderScale") ?? null;
|
|
102
|
+
if (renderScale !== null && ["off", "1", "0.85", "0.7", "0.5"].includes(renderScale)) {
|
|
103
|
+
search.set("renderScale", renderScale);
|
|
104
|
+
}
|
|
105
|
+
search.set("theme", theme);
|
|
106
|
+
return `/shot-stage.html?${search.toString()}`;
|
|
107
|
+
}
|
|
47
108
|
|
|
48
109
|
export function StagePanel({
|
|
49
110
|
/** Report a stage connect failure to the shell's overlay strip (null clears). */
|
|
@@ -52,42 +113,296 @@ export function StagePanel({
|
|
|
52
113
|
onConnectError: (message: string | null) => void;
|
|
53
114
|
}) {
|
|
54
115
|
const store = useStore();
|
|
116
|
+
const { resolved: theme } = useStudioTheme();
|
|
55
117
|
const iframeRef = useRef<HTMLIFrameElement | null>(null);
|
|
118
|
+
const srcRef = useRef(stageSrc(theme));
|
|
119
|
+
// Read once: the boot script installs it before the chrome runs, or never.
|
|
120
|
+
const [host] = useState(dailiesHost);
|
|
121
|
+
const [hostSettled, setHostSettled] = useState(host === undefined);
|
|
56
122
|
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
if (host === undefined) return;
|
|
125
|
+
let live = true;
|
|
126
|
+
let off: (() => void) | undefined;
|
|
127
|
+
host.ready.then(
|
|
128
|
+
() => {
|
|
129
|
+
if (live) setHostSettled(true);
|
|
130
|
+
},
|
|
131
|
+
(e: unknown) => {
|
|
132
|
+
if (!live) return;
|
|
133
|
+
// The stage still loads: a document with no project component draws,
|
|
134
|
+
// and each instance layer reports its own failure on top of this.
|
|
135
|
+
const message = `The hosted editor could not start its code host: ${e instanceof Error ? e.message : String(e)}`;
|
|
136
|
+
store.reportError(message);
|
|
137
|
+
// A later epoch means the host recovered: clear the error, unless
|
|
138
|
+
// something else has replaced it since.
|
|
139
|
+
off = host.subscribe(() => {
|
|
140
|
+
if (store.getState().error === message) store.reportError(null);
|
|
141
|
+
off?.();
|
|
142
|
+
off = undefined;
|
|
143
|
+
});
|
|
144
|
+
setHostSettled(true);
|
|
145
|
+
},
|
|
146
|
+
);
|
|
147
|
+
return () => {
|
|
148
|
+
live = false;
|
|
149
|
+
off?.();
|
|
150
|
+
};
|
|
151
|
+
}, [host, store]);
|
|
152
|
+
|
|
153
|
+
// The stage is a same-origin document, so editor furniture can follow a
|
|
154
|
+
// live appearance change by updating its namespaced root signal in place.
|
|
155
|
+
// Only furniture descendants consume it; authored content never receives a
|
|
156
|
+
// generic data-theme or changing root palette. The iframe URL stays frozen
|
|
157
|
+
// above, which keeps the document, bridge and GL context alive. Re-apply on
|
|
158
|
+
// load too because Vite HMR may replace the document without replacing this
|
|
159
|
+
// iframe element.
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
const iframe = iframeRef.current;
|
|
162
|
+
if (!iframe) return;
|
|
163
|
+
const apply = (): void => {
|
|
164
|
+
try {
|
|
165
|
+
const root = iframe.contentDocument?.documentElement;
|
|
166
|
+
if (root !== undefined && root !== null) root.dataset.dailiesStudioTheme = theme;
|
|
167
|
+
} catch {
|
|
168
|
+
// The shipped stage is same-origin. If a dev proxy temporarily serves
|
|
169
|
+
// an error document from elsewhere, appearance is cosmetic and must
|
|
170
|
+
// not tear down the bridge while it recovers.
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
iframe.addEventListener("load", apply);
|
|
174
|
+
apply();
|
|
175
|
+
return () => iframe.removeEventListener("load", apply);
|
|
176
|
+
}, [theme]);
|
|
177
|
+
|
|
178
|
+
// ── DROPPING A FILE ON THE PICTURE ──────────────────────────────────────
|
|
179
|
+
//
|
|
180
|
+
// The gesture every tool in this class has, and the one an author reaches for
|
|
181
|
+
// first with a screen recording. Without it the browser's own default wins and
|
|
182
|
+
// the drop NAVIGATES THE STAGE IFRAME to the file, which tears down the
|
|
183
|
+
// document, the bridge and the GL context: the app goes blank and the only way
|
|
184
|
+
// back is a reload.
|
|
185
|
+
//
|
|
186
|
+
// Listened for on the stage's own document rather than on the `<iframe>`
|
|
187
|
+
// element, because a drop lands INSIDE the frame and never reaches the parent
|
|
188
|
+
// element's handlers. That is the same same-origin reach the theme effect
|
|
189
|
+
// above makes, and the same `try` for the same reason.
|
|
190
|
+
//
|
|
191
|
+
// AT THE DROP POINT (F52). The stage owns its coordinate space, so the drop's
|
|
192
|
+
// client point crosses once through `designPointAt` (bridge v38), the same
|
|
193
|
+
// conversion the draw gesture gives its corners. Off the frame pane (the
|
|
194
|
+
// camera pane over a Shot, an open definition's one pane inside it) it is
|
|
195
|
+
// null and the drop lands centred, what the Add menu's row does.
|
|
57
196
|
useEffect(() => {
|
|
58
197
|
const iframe = iframeRef.current;
|
|
59
198
|
if (!iframe) return;
|
|
60
199
|
const aborter = new AbortController();
|
|
200
|
+
const bind = (): void => {
|
|
201
|
+
try {
|
|
202
|
+
const doc = iframe.contentDocument;
|
|
203
|
+
if (!doc) return;
|
|
204
|
+
// `dragover` MUST be prevented or `drop` never fires at all: the spec's
|
|
205
|
+
// default for dragover is "this is not a drop target". Prevented always,
|
|
206
|
+
// so the browser never navigates the frame to a file; `dropEffect`
|
|
207
|
+
// "none" is what tells the author nothing lands here.
|
|
208
|
+
doc.addEventListener(
|
|
209
|
+
"dragover",
|
|
210
|
+
(e) => {
|
|
211
|
+
e.preventDefault();
|
|
212
|
+
const dt = (e as DragEvent).dataTransfer;
|
|
213
|
+
if (dt !== null && dt !== undefined && !stageDropLands(dt)) {
|
|
214
|
+
dt.dropEffect = "none";
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
{ signal: aborter.signal },
|
|
218
|
+
);
|
|
219
|
+
doc.addEventListener(
|
|
220
|
+
"drop",
|
|
221
|
+
(e) => {
|
|
222
|
+
// Prevented FIRST and unconditionally, before any early return: the
|
|
223
|
+
// navigation this stops is destructive even for a file the store
|
|
224
|
+
// will refuse, and returning early without it is how a dropped .zip
|
|
225
|
+
// would still blank the stage.
|
|
226
|
+
e.preventDefault();
|
|
227
|
+
const dt = (e as DragEvent).dataTransfer;
|
|
228
|
+
const point = store.stage()?.designPointAt(e.clientX, e.clientY) ?? undefined;
|
|
229
|
+
const file = dt?.files?.[0];
|
|
230
|
+
if (file === undefined) return;
|
|
231
|
+
// Through the identical handshake the Frame tool and the Add menu
|
|
232
|
+
// use: park a click box at the point, then hand over the file. The
|
|
233
|
+
// kind is the SERVER's sniff, so an unsupported file is refused
|
|
234
|
+
// there with one message rather than guessed at here.
|
|
235
|
+
const at = point ?? store.insertOrigin();
|
|
236
|
+
store.stageHandlers.onDraw("image", [at, at]);
|
|
237
|
+
void store.completeImageDraw(file).catch((err: unknown) => {
|
|
238
|
+
store.reportError(err instanceof Error ? err.message : String(err));
|
|
239
|
+
});
|
|
240
|
+
},
|
|
241
|
+
{ signal: aborter.signal },
|
|
242
|
+
);
|
|
243
|
+
// The board's ⌘C ⌘V on a frame layer: the pointer on the frame focuses
|
|
244
|
+
// this document, so the native clipboard events fire here, not in the
|
|
245
|
+
// chrome's.
|
|
246
|
+
bindBoardClipboard(doc, store, aborter.signal);
|
|
247
|
+
} catch {
|
|
248
|
+
// Same as the theme effect: a dev proxy serving a cross-origin error
|
|
249
|
+
// document must not tear anything down. The cost is that dropping does
|
|
250
|
+
// nothing until it recovers, which is the pre-existing behaviour.
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
iframe.addEventListener("load", bind, { signal: aborter.signal });
|
|
254
|
+
bind();
|
|
255
|
+
return () => aborter.abort();
|
|
256
|
+
}, [store]);
|
|
257
|
+
|
|
258
|
+
// ── THE AUTHOR AT WORK, IN BOTH WINDOWS ────────────────────────────────
|
|
259
|
+
//
|
|
260
|
+
// What a background preparing pass gives way to, and what its idle wait
|
|
261
|
+
// starts again on (`noteAuthorInput`): a press, a key, a wheel or a drag
|
|
262
|
+
// over, in the chrome or on the stage, heard in the capture phase so a
|
|
263
|
+
// handler that stops the event cannot hide it. On the stage a pointer that
|
|
264
|
+
// TRAVELS counts too, but not one at rest or jittering in place, which is
|
|
265
|
+
// the author's usual state over the picture: pausing on it would starve the
|
|
266
|
+
// pass. Input aimed at the Play control only restarts the wait: its click is
|
|
267
|
+
// the press of Play that upgrades the pass. The stage's listeners go on
|
|
268
|
+
// again after each load, since a reminted document has none.
|
|
269
|
+
useEffect(() => {
|
|
270
|
+
const iframe = iframeRef.current;
|
|
271
|
+
if (!iframe) return;
|
|
272
|
+
const aborter = new AbortController();
|
|
273
|
+
const options = { capture: true, passive: true, signal: aborter.signal };
|
|
274
|
+
const note = (event: Event): void => store.noteAuthorInput(!atPlayControl(event));
|
|
275
|
+
const listen = (target: Window): void => {
|
|
276
|
+
for (const type of AUTHOR_INPUT) target.addEventListener(type, note, options);
|
|
277
|
+
};
|
|
278
|
+
let travelledFrom: { x: number; y: number } | null = null;
|
|
279
|
+
const travel = (e: PointerEvent): void => {
|
|
280
|
+
const from = travelledFrom;
|
|
281
|
+
if (from !== null && Math.hypot(e.clientX - from.x, e.clientY - from.y) < POINTER_TRAVEL_PX) return;
|
|
282
|
+
travelledFrom = { x: e.clientX, y: e.clientY };
|
|
283
|
+
// The first move only says where the pointer is.
|
|
284
|
+
if (from !== null) store.noteAuthorInput();
|
|
285
|
+
};
|
|
286
|
+
listen(window);
|
|
287
|
+
const bindStage = (): void => {
|
|
288
|
+
try {
|
|
289
|
+
const stage = iframe.contentWindow;
|
|
290
|
+
if (stage === null) return;
|
|
291
|
+
travelledFrom = null;
|
|
292
|
+
listen(stage);
|
|
293
|
+
stage.addEventListener("pointermove", travel, options);
|
|
294
|
+
} catch {
|
|
295
|
+
// The drop effect's reason: a dev proxy's cross-origin error document
|
|
296
|
+
// must not tear anything down. Its input goes unheard meanwhile.
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
iframe.addEventListener("load", bindStage, { signal: aborter.signal });
|
|
300
|
+
bindStage();
|
|
301
|
+
return () => aborter.abort();
|
|
302
|
+
}, [store]);
|
|
303
|
+
|
|
304
|
+
// THE PANEL GOING withdraws the stage it expected, settled or not: the
|
|
305
|
+
// connect effect below has no cleanup before the host settles, so an
|
|
306
|
+
// unmount then would leave a queued Play waiting for a stage that is gone.
|
|
307
|
+
// Its own effect, so the settle flip never runs it.
|
|
308
|
+
useEffect(() => () => store.expectStage(false), [store]);
|
|
309
|
+
|
|
310
|
+
useEffect(() => {
|
|
311
|
+
const iframe = iframeRef.current;
|
|
312
|
+
if (!iframe) return;
|
|
313
|
+
// A STAGE IS ON ITS WAY FROM THE FIRST COMMIT, hosted too: the code host's
|
|
314
|
+
// boot comes before the stage's src, and a press of Play in it is queued
|
|
315
|
+
// like one during the stage's own boot. Before the return below, and with
|
|
316
|
+
// no cleanup on that branch, so the expectation holds across the settle
|
|
317
|
+
// flip: only an unmount or a failed connect withdraws it.
|
|
318
|
+
store.expectStage(true);
|
|
319
|
+
if (!hostSettled) return;
|
|
320
|
+
// ONE CONTROLLER PER CONNECT ATTEMPT: a new attempt (the next load) ends
|
|
321
|
+
// the one before it, so a local boot, which connects at once and again at
|
|
322
|
+
// the iframe's load, attaches once.
|
|
323
|
+
let attempt: AbortController | null = null;
|
|
61
324
|
const connect = (): void => {
|
|
325
|
+
attempt?.abort();
|
|
326
|
+
const own = new AbortController();
|
|
327
|
+
attempt = own;
|
|
62
328
|
onConnectError(null);
|
|
63
|
-
|
|
329
|
+
store.setStageConnectError(null);
|
|
330
|
+
// A RELOAD IS A NEW STAGE. This runs for the reminted document too, and
|
|
331
|
+
// the bridge from the old one is dead by then, so dropping it HERE is
|
|
332
|
+
// what clears `stageInfo` and `stageBusy`. Without it the chrome went on
|
|
333
|
+
// holding a stale "the stage is ready" beside a stage that had not come
|
|
334
|
+
// up, which is a lit play button over a canvas that has never drawn.
|
|
335
|
+
// Paired with the expectation below, a press of Play is queued across a
|
|
336
|
+
// remint exactly as it is across the first boot.
|
|
337
|
+
store.attachStage(null);
|
|
338
|
+
store.expectStage(true);
|
|
339
|
+
void connectStage(iframe, store.stageHandlers, { signal: own.signal })
|
|
64
340
|
.then((bridge) => {
|
|
65
|
-
if (
|
|
341
|
+
if (own.signal.aborted) return;
|
|
342
|
+
store.attachStage(bridge);
|
|
343
|
+
// A RELOAD IS HEARD AS THE OLD DOCUMENT GOES, not only at the new
|
|
344
|
+
// one's load: the transport pauses (remembering its start) while
|
|
345
|
+
// the next stage boots, instead of running sound over a stage that
|
|
346
|
+
// is gone. A pagehide that keeps the page for the back/forward
|
|
347
|
+
// cache comes back with no load to reattach it, so it is ignored.
|
|
348
|
+
iframe.contentWindow?.addEventListener(
|
|
349
|
+
"pagehide",
|
|
350
|
+
(e: PageTransitionEvent) => {
|
|
351
|
+
if (e.persisted) return;
|
|
352
|
+
store.attachStage(null);
|
|
353
|
+
store.expectStage(true);
|
|
354
|
+
},
|
|
355
|
+
{ signal: own.signal },
|
|
356
|
+
);
|
|
66
357
|
})
|
|
67
358
|
.catch((e) => {
|
|
68
|
-
if (!
|
|
69
|
-
|
|
359
|
+
if (!own.signal.aborted) {
|
|
360
|
+
// WITHDRAWN ON FAILURE, so a bridge version mismatch or the poll
|
|
361
|
+
// deadline calls a queued Play off instead of leaving it waiting
|
|
362
|
+
// for a stage that will never draw. The refusal is the overlay
|
|
363
|
+
// strip's, which says what actually went wrong.
|
|
364
|
+
store.expectStage(false);
|
|
365
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
366
|
+
onConnectError(message);
|
|
367
|
+
// And for an agent, which cannot see the strip (stage health).
|
|
368
|
+
store.setStageConnectError(message);
|
|
70
369
|
}
|
|
71
370
|
});
|
|
72
371
|
};
|
|
73
|
-
|
|
74
|
-
|
|
372
|
+
const onLoad = (): void => {
|
|
373
|
+
const stage = iframe.contentWindow;
|
|
374
|
+
// False: the host is reloading the stage to get it controlled.
|
|
375
|
+
if (host !== undefined && stage !== null && !host.stageLoaded(stage)) return;
|
|
376
|
+
// The document already attached (its bridge booted before its load
|
|
377
|
+
// event): nothing new loaded, so the stage is not dropped and taken
|
|
378
|
+
// again, and a queued Play is not mistaken for a reload's.
|
|
379
|
+
const attached = store.stage();
|
|
380
|
+
if (attached !== null && stageBridgeOf(iframe) === attached) return;
|
|
381
|
+
connect();
|
|
382
|
+
};
|
|
383
|
+
iframe.addEventListener("load", onLoad);
|
|
384
|
+
// Locally the load event may already have fired. Hosted, the src was set
|
|
385
|
+
// in the commit this effect follows, so the stage's load is still ahead,
|
|
386
|
+
// and connecting before it would push a document the host has not seen.
|
|
387
|
+
if (host === undefined) connect();
|
|
75
388
|
return () => {
|
|
76
|
-
|
|
77
|
-
iframe.removeEventListener("load",
|
|
389
|
+
attempt?.abort();
|
|
390
|
+
iframe.removeEventListener("load", onLoad);
|
|
78
391
|
store.attachStage(null);
|
|
392
|
+
store.expectStage(false);
|
|
79
393
|
// The error belonged to the connection being torn down; it is the shell's
|
|
80
394
|
// state now, so it has to be cleared explicitly rather than dying with a
|
|
81
395
|
// local useState.
|
|
82
396
|
onConnectError(null);
|
|
397
|
+
store.setStageConnectError(null);
|
|
83
398
|
};
|
|
84
|
-
}, [store, onConnectError]);
|
|
399
|
+
}, [store, onConnectError, host, hostSettled]);
|
|
85
400
|
|
|
86
401
|
return (
|
|
87
402
|
<iframe
|
|
88
403
|
ref={iframeRef}
|
|
89
404
|
className="stage-frame"
|
|
90
|
-
src={
|
|
405
|
+
src={hostSettled ? srcRef.current : undefined}
|
|
91
406
|
title="Stage"
|
|
92
407
|
// The stage pins the cursor for the duration of a G / R / S transform,
|
|
93
408
|
// and an iframe cannot without the delegation. Without it
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// THE CELLS A MODE SHOWS ITS CONTENT IN.
|
|
2
|
+
//
|
|
3
|
+
// The four modes (the Storyboard, the material nodes, the Component Studio,
|
|
4
|
+
// Code Studio) used to cover the window. They now show their content in the
|
|
5
|
+
// docked grid's own cells: the left column, the canvas cell, the bottom row
|
|
6
|
+
// and the canvas bar. Each of those cells renders one SLOT, always, and a mode
|
|
7
|
+
// portals its parts into them.
|
|
8
|
+
//
|
|
9
|
+
// ── WHY PORTALS AND NOT FOUR COMPONENTS PER MODE ────────────────────────────
|
|
10
|
+
//
|
|
11
|
+
// A mode is one component with one state: the board's canvas API is shared by
|
|
12
|
+
// its rail and its canvas, the material graph's React Flow provider wraps the
|
|
13
|
+
// zoom its bar shows, the code studio's buffer feeds its editor and its save.
|
|
14
|
+
// Split into per-cell components, every one of those would have to move into
|
|
15
|
+
// the store or a new context. A portal keeps the mode one React subtree
|
|
16
|
+
// (context, refs and events all flow through a portal) and only moves where
|
|
17
|
+
// its pieces land in the DOM.
|
|
18
|
+
//
|
|
19
|
+
// The slots are rendered unconditionally, so a mode that opens finds them
|
|
20
|
+
// already registered and lands in the same commit, never a frame later.
|
|
21
|
+
import { createContext, useCallback, useContext, useMemo, useState } from "react";
|
|
22
|
+
import type { ReactNode } from "react";
|
|
23
|
+
import { createPortal } from "react-dom";
|
|
24
|
+
|
|
25
|
+
export type CellName = "left" | "centre" | "bottom" | "bar";
|
|
26
|
+
|
|
27
|
+
type Slots = Record<CellName, HTMLElement | null>;
|
|
28
|
+
|
|
29
|
+
interface CellSlotsValue {
|
|
30
|
+
slots: Slots;
|
|
31
|
+
register: (name: CellName, el: HTMLElement | null) => void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const CellSlotsContext = createContext<CellSlotsValue | null>(null);
|
|
35
|
+
|
|
36
|
+
const EMPTY: Slots = { left: null, centre: null, bottom: null, bar: null };
|
|
37
|
+
|
|
38
|
+
/** Around the shell. Renders no element of its own, so the stage iframe's
|
|
39
|
+
* place in the DOM is untouched. */
|
|
40
|
+
export function CellSlotsProvider({ children }: { children: ReactNode }) {
|
|
41
|
+
const [slots, setSlots] = useState<Slots>(EMPTY);
|
|
42
|
+
const register = useCallback((name: CellName, el: HTMLElement | null) => {
|
|
43
|
+
setSlots((was) => (was[name] === el ? was : { ...was, [name]: el }));
|
|
44
|
+
}, []);
|
|
45
|
+
const value = useMemo(() => ({ slots, register }), [slots, register]);
|
|
46
|
+
return <CellSlotsContext.Provider value={value}>{children}</CellSlotsContext.Provider>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** A cell's slot: an empty element a mode portals into. */
|
|
50
|
+
export function CellSlot({ name, className }: { name: CellName; className: string }) {
|
|
51
|
+
const ctx = useContext(CellSlotsContext);
|
|
52
|
+
const register = ctx?.register;
|
|
53
|
+
const ref = useCallback(
|
|
54
|
+
(el: HTMLDivElement | null) => {
|
|
55
|
+
register?.(name, el);
|
|
56
|
+
},
|
|
57
|
+
[register, name],
|
|
58
|
+
);
|
|
59
|
+
return <div ref={ref} className={className} data-cell-slot={name} />;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* A mode's part, shown in the named cell. Renders nothing until the cell's
|
|
64
|
+
* slot is on the page (outside the shell, as in a unit test that mounts a mode
|
|
65
|
+
* alone, there is no slot and nothing is shown).
|
|
66
|
+
*/
|
|
67
|
+
export function InCell({ name, children }: { name: CellName; children: ReactNode }) {
|
|
68
|
+
const node = useContext(CellSlotsContext)?.slots[name] ?? null;
|
|
69
|
+
return node === null ? null : createPortal(children, node);
|
|
70
|
+
}
|