@dailies-studio/shot-editor 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.vite/manifest.json +382 -0
- package/dist/assets/AgentPanel-CQejLbiX.js +2 -0
- package/dist/assets/AgentPanel-DJEphah0.css +1 -0
- package/dist/assets/AgentQuestionDialog-BqkD0kXF.js +1 -0
- package/dist/assets/CodeStudio-Dv4ihHpz.js +23 -0
- package/dist/assets/CommentsPanel-B4yhhT1T.js +1 -0
- package/dist/assets/ComponentLibraryRail-Ch_dsAru.js +1 -0
- package/dist/assets/ComponentStage-BRhRotyH.js +1 -0
- package/dist/assets/CurvePane-BjFVabKX.css +1 -0
- package/dist/assets/CurvePane-R22WzllJ.js +1 -0
- package/dist/assets/ExportDialog-CWLQWTmG.js +2 -0
- package/dist/assets/FontPicker-DWKaYL7F.js +1 -0
- package/dist/assets/FontPicker-v0IOIArM.css +1 -0
- package/dist/assets/GenerateDrawer-Bh3SqDKt.js +2 -0
- package/dist/assets/InsertDrawer-Ce1fod1L.js +1 -0
- package/dist/assets/LibraryBrowser-D9sEGz5w.js +2 -0
- package/dist/assets/MakeItYoursSection-CMO4319_.js +2 -0
- package/dist/assets/MaterialGraphCanvas-BFzl4wWR.js +23 -0
- package/dist/assets/MaterialGraphCanvas-BIUD59ik.css +1 -0
- package/dist/assets/MaterialWorkspace-CqTWqaRA.js +14 -0
- package/dist/assets/PreferencesDialog-CBf9_OoD.js +1 -0
- package/dist/assets/PresetsSection-CUEaoCVU.js +1 -0
- package/dist/assets/SoundHistory-BeS8EW1h.js +1 -0
- package/dist/assets/SoundMode-BmcZru3i.js +3 -0
- package/dist/assets/StockSounds-DdAUGRYK.js +1 -0
- package/dist/assets/TemplateFieldSection-CaeXpLFo.js +1 -0
- package/dist/assets/VersionHistoryPanel-CxG6XhNP.js +1 -0
- package/dist/assets/board-B88EPuLO.css +1 -0
- package/dist/assets/board-CL-he7SL.js +3 -0
- package/dist/assets/components-0oOSPN0l.js +1 -0
- package/dist/assets/googleFontRanges-gp32wJMo.js +1 -0
- package/dist/assets/googleFonts-BNon6M7X.js +1 -0
- package/dist/assets/iconsAndAliases-DL7gXP5j.js +1863 -0
- package/dist/assets/index-4F5NQmI8.css +1 -0
- package/dist/assets/index-BGHQGV5z.js +117 -0
- package/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/dist/assets/libraryModel-CpIwfFha.js +1 -0
- package/dist/assets/lucide-6iK64tTB.js +9319 -0
- package/dist/assets/motionStamp-CH-Hew4E.js +1 -0
- package/dist/assets/takeMeasure-Tdg0TxEi.js +1 -0
- package/dist/assets/webmcp-BsB9gxWC.js +2 -0
- package/dist/index.html +34 -3
- package/package.json +12 -9
- package/src/addFamilies.ts +66 -0
- package/src/api.ts +1631 -55
- package/src/app/App.tsx +977 -198
- package/src/app/lazyPanel.tsx +137 -0
- package/src/app/lazySurfaces.tsx +203 -0
- package/src/audio/index.tsx +41 -10
- package/src/audio/meter.ts +485 -0
- package/src/audio/meterMath.ts +273 -0
- package/src/audio/meterStore.ts +155 -0
- package/src/audio/offlineMix.ts +289 -0
- package/src/audio/preview.ts +928 -118
- package/src/bridge.ts +15 -5
- package/src/bridgeTypes.ts +1611 -36
- package/src/channels.ts +17 -32
- package/src/fonts/googleFonts.ts +4 -0
- package/src/hostApi.ts +174 -0
- package/src/icons.tsx +770 -0
- package/src/layerWords.ts +61 -0
- package/src/main.tsx +56 -4
- package/src/panels/ResizeHandle.tsx +33 -43
- package/src/panels/agent/AgentDots.tsx +36 -0
- package/src/panels/agent/AgentPanel.tsx +119 -43
- package/src/panels/agent/AgentQuestionDialog.tsx +202 -0
- package/src/panels/agent/agent.css +113 -97
- package/src/panels/agent/agentGate.ts +35 -0
- package/src/panels/code/CodeDefaultsPanel.tsx +345 -0
- package/src/panels/code/CodeStudio.tsx +373 -0
- package/src/panels/code/FileTree.tsx +265 -0
- package/src/panels/code/codeBufferGuard.ts +52 -0
- package/src/panels/code/codeParts.tsx +321 -0
- package/src/panels/comments/CommentComposer.tsx +239 -0
- package/src/panels/comments/CommentThread.tsx +346 -0
- package/src/panels/comments/CommentsPanel.tsx +336 -0
- package/src/panels/comments/InboxButton.tsx +131 -0
- package/src/panels/comments/MentionMenu.tsx +98 -0
- package/src/panels/comments/parts.tsx +171 -0
- package/src/panels/export/ExportDialog.tsx +919 -96
- package/src/panels/export/ExportScrim.tsx +57 -0
- package/src/panels/files/AssetsTab.tsx +656 -0
- package/src/panels/files/FilePanel.tsx +1466 -216
- package/src/panels/files/LayerMarks.tsx +40 -0
- package/src/panels/files/LeftColumn.tsx +37 -0
- package/src/panels/files/McpSection.tsx +690 -47
- package/src/panels/files/MissingAssetsNotice.tsx +60 -0
- package/src/panels/files/assetDrag.ts +79 -0
- package/src/panels/files/resourceParts.tsx +255 -0
- package/src/panels/fonts/FontPicker.tsx +762 -0
- package/src/panels/fonts/faceCache.ts +207 -0
- package/src/panels/fonts/fontPicker.css +271 -0
- package/src/panels/fonts/pickerModel.ts +265 -0
- package/src/panels/fonts/pickerRequest.ts +46 -0
- package/src/panels/header/AccountAvatar.tsx +76 -0
- package/src/panels/header/AgentChip.tsx +77 -0
- package/src/panels/header/AppMenu.tsx +213 -0
- package/src/panels/header/Header.tsx +564 -84
- package/src/panels/header/HistoryControls.tsx +31 -5
- package/src/panels/header/PresenceStack.tsx +329 -0
- package/src/panels/header/ProjectTitle.tsx +232 -0
- package/src/panels/header/ShareButton.tsx +59 -0
- package/src/panels/header/StatusBadges.tsx +358 -27
- package/src/panels/header/SyncDeletedDialog.tsx +88 -0
- package/src/panels/header/ZoomControls.tsx +46 -15
- package/src/panels/header/agentHandoff.ts +52 -0
- package/src/panels/header/leave.ts +26 -0
- package/src/panels/help/HelpSection.tsx +189 -163
- package/src/panels/insert/InsertDrawer.tsx +723 -0
- package/src/panels/insert/iconSet.ts +109 -0
- package/src/panels/inspector/ExposeDot.tsx +117 -0
- package/src/panels/inspector/FramingRows.tsx +314 -44
- package/src/panels/inspector/InspectorFooter.tsx +91 -0
- package/src/panels/inspector/InspectorHead.tsx +425 -0
- package/src/panels/inspector/InspectorPanel.tsx +408 -124
- package/src/panels/inspector/KeyframeDot.tsx +373 -0
- package/src/panels/inspector/KeyframeSection.tsx +257 -0
- package/src/panels/inspector/MarkerSection.tsx +55 -11
- package/src/panels/inspector/ParamRow.tsx +84 -53
- package/src/panels/inspector/RightColumn.tsx +152 -0
- package/src/panels/inspector/TimeSection.tsx +866 -0
- package/src/panels/inspector/animate/EaseControl.tsx +244 -0
- package/src/panels/inspector/animate/PresetsSection.tsx +135 -0
- package/src/panels/inspector/animate/ShotSection.tsx +562 -48
- package/src/panels/inspector/design/AlignRow.tsx +95 -31
- package/src/panels/inspector/design/AppearanceSection.tsx +118 -9
- package/src/panels/inspector/design/ClonerSection.tsx +323 -62
- package/src/panels/inspector/design/ComponentSection.tsx +50 -43
- package/src/panels/inspector/design/CornerSection.tsx +45 -14
- package/src/panels/inspector/design/DefinitionProps.tsx +315 -0
- package/src/panels/inspector/design/DefinitionSection.tsx +136 -0
- package/src/panels/inspector/design/DeviceSection.tsx +210 -0
- package/src/panels/inspector/design/FillSection.tsx +83 -51
- package/src/panels/inspector/design/FontField.tsx +196 -0
- package/src/panels/inspector/design/HtmlSection.tsx +23 -12
- package/src/panels/inspector/design/InstanceSection.tsx +373 -154
- package/src/panels/inspector/design/MaskSection.tsx +84 -0
- package/src/panels/inspector/design/MaterialSection.tsx +177 -0
- package/src/panels/inspector/design/MediaSection.tsx +273 -120
- package/src/panels/inspector/design/MeshSection.tsx +910 -82
- package/src/panels/inspector/design/PaintRows.tsx +550 -0
- package/src/panels/inspector/design/PlateStyleSection.tsx +177 -0
- package/src/panels/inspector/design/ShadowSection.tsx +221 -48
- package/src/panels/inspector/design/SplineFollowSection.tsx +235 -0
- package/src/panels/inspector/design/StrokeSection.tsx +228 -43
- package/src/panels/inspector/design/TemplateFieldSection.tsx +57 -0
- package/src/panels/inspector/design/TransformSection.tsx +244 -50
- package/src/panels/inspector/design/TypographySection.tsx +679 -110
- package/src/panels/inspector/layout.ts +295 -0
- package/src/panels/inspector/propValue.ts +199 -0
- package/src/panels/inspector/scene/CameraSection.tsx +232 -22
- package/src/panels/inspector/scene/EffectsSection.tsx +722 -71
- package/src/panels/inspector/scene/MakeItYoursSection.tsx +180 -0
- package/src/panels/inspector/scene/SceneDocSection.tsx +191 -0
- package/src/panels/inspector/scene/SceneSection.tsx +219 -44
- package/src/panels/inspector/scene/TransitionSection.tsx +523 -72
- package/src/panels/inspector/storyboard/StoryboardReferenceSection.tsx +593 -0
- package/src/panels/inspector/storyboard/VideoStill.tsx +120 -0
- package/src/panels/inspector/timeModel.ts +190 -0
- package/src/panels/inspector/useDocWrite.ts +438 -146
- package/src/panels/library/LibraryBrowser.tsx +435 -0
- package/src/panels/library/LibraryGrid.tsx +190 -0
- package/src/panels/library/LibraryPreviewPanel.tsx +349 -0
- package/src/panels/library/LibraryRail.tsx +84 -0
- package/src/panels/library/LibrarySoundPanel.tsx +293 -0
- package/src/panels/library/SoundHistory.tsx +245 -0
- package/src/panels/library/StockSounds.tsx +390 -0
- package/src/panels/library/libraryModel.ts +147 -0
- package/src/panels/library/stockSearch.ts +77 -0
- package/src/panels/maskMarks.tsx +117 -0
- package/src/panels/material/MaterialCommentNode.tsx +54 -0
- package/src/panels/material/MaterialGraphCanvas.tsx +861 -0
- package/src/panels/material/MaterialGraphMenu.tsx +45 -0
- package/src/panels/material/MaterialGroupNode.tsx +56 -0
- package/src/panels/material/MaterialLibrary.tsx +253 -0
- package/src/panels/material/MaterialNode.tsx +453 -0
- package/src/panels/material/MaterialNodePalette.tsx +349 -0
- package/src/panels/material/MaterialParameterControls.tsx +525 -0
- package/src/panels/material/MaterialRerouteNode.tsx +42 -0
- package/src/panels/material/MaterialWorkspace.tsx +501 -0
- package/src/panels/material/NodePreview.tsx +70 -0
- package/src/panels/material/flowModel.ts +441 -0
- package/src/panels/material/parameterValues.ts +231 -0
- package/src/panels/material/previewScheduler.ts +235 -0
- package/src/panels/material/reactFlow.css +6 -0
- package/src/panels/material/socketGlyph.ts +85 -0
- package/src/panels/material/viewportMemory.ts +58 -0
- package/src/panels/preferences/PreferencesDialog.tsx +215 -0
- package/src/panels/share/ShareComponentDialog.tsx +247 -0
- package/src/panels/stage/StageMenu.tsx +35 -5
- package/src/panels/stage/StagePanel.tsx +338 -23
- package/src/panels/stage/cellSlots.tsx +70 -0
- package/src/panels/storyboard/BoardCanvas.tsx +739 -0
- package/src/panels/storyboard/BoardCursors.tsx +158 -0
- package/src/panels/storyboard/BoardDropDialog.tsx +161 -0
- package/src/panels/storyboard/BoardPlan.tsx +42 -0
- package/src/panels/storyboard/ComponentSheet.tsx +187 -0
- package/src/panels/storyboard/ComponentStage.tsx +235 -0
- package/src/panels/storyboard/MiniSketch.tsx +173 -0
- package/src/panels/storyboard/PartPicker.tsx +64 -0
- package/src/panels/storyboard/PlanPanel.tsx +617 -0
- package/src/panels/storyboard/ReferencePreview.tsx +217 -0
- package/src/panels/storyboard/ShotBrief.tsx +360 -0
- package/src/panels/storyboard/ShotCard.tsx +251 -0
- package/src/panels/storyboard/StagePreview.tsx +348 -0
- package/src/panels/storyboard/StillThumbs.tsx +162 -0
- package/src/panels/storyboard/StoryboardView.tsx +79 -0
- package/src/panels/storyboard/board.css +1065 -0
- package/src/panels/storyboard/board.ts +12 -0
- package/src/panels/storyboard/boardClipboard.ts +111 -0
- package/src/panels/storyboard/boardOrder.ts +68 -0
- package/src/panels/storyboard/boardRows.tsx +425 -0
- package/src/panels/storyboard/liveCap.ts +90 -0
- package/src/panels/storyboard/plan.css +295 -0
- package/src/panels/storyboard/posterCache.ts +133 -0
- package/src/panels/storyboard/shotboard/ComponentPicker.tsx +373 -0
- package/src/panels/storyboard/shotboard/ImagePicker.tsx +145 -0
- package/src/panels/storyboard/shotboard/ShotBoard.tsx +292 -0
- package/src/panels/storyboard/shotboard/SketchCanvas.tsx +443 -0
- package/src/panels/storyboard/shotboard/SketchInspector.tsx +471 -0
- package/src/panels/storyboard/shotboard/SketchItem.tsx +240 -0
- package/src/panels/storyboard/shotboard/SketchLayers.tsx +130 -0
- package/src/panels/storyboard/shotboard/SketchPlay.tsx +108 -0
- package/src/panels/storyboard/shotboard/SketchStrip.tsx +178 -0
- package/src/panels/storyboard/shotboard/SketchToolFooter.tsx +99 -0
- package/src/panels/storyboard/shotboard/sketch.css +1392 -0
- package/src/panels/storyboard/shotboard/sketchModel.ts +219 -0
- package/src/panels/storyboard/storyboardDrop.ts +164 -0
- package/src/panels/studio/ComponentLibraryRail.tsx +346 -0
- package/src/panels/studio/ComponentSidePanel.tsx +355 -0
- package/src/panels/studio/ComponentStudio.tsx +243 -0
- package/src/panels/studio/ComponentsHome.tsx +318 -0
- package/src/panels/studio/ComponentsPreview.tsx +179 -0
- package/src/panels/studio/EveryUse.tsx +244 -0
- package/src/panels/studio/ThisUsePanel.tsx +679 -0
- package/src/panels/studio/componentParts.tsx +341 -0
- package/src/panels/studio/components.ts +6 -0
- package/src/panels/studio/newCodeComponent.ts +66 -0
- package/src/panels/timeline/AudioFxButton.tsx +290 -0
- package/src/panels/timeline/AudioLane.tsx +674 -85
- package/src/panels/timeline/BlockRow.tsx +154 -85
- package/src/panels/timeline/ChannelRow.tsx +378 -129
- package/src/panels/timeline/CurvePane.tsx +508 -0
- package/src/panels/timeline/Filmstrip.tsx +361 -0
- package/src/panels/timeline/GenerateDrawer.tsx +407 -0
- package/src/panels/timeline/KeyStretch.tsx +115 -0
- package/src/panels/timeline/LifespanRow.tsx +730 -47
- package/src/panels/timeline/MarkerLane.tsx +46 -8
- package/src/panels/timeline/PlayButton.tsx +91 -0
- package/src/panels/timeline/PropertyPicker.tsx +254 -0
- package/src/panels/timeline/ShotRow.tsx +400 -0
- package/src/panels/timeline/SoundMode.tsx +1406 -0
- package/src/panels/timeline/TimelineDock.tsx +2338 -272
- package/src/panels/timeline/TransportVolume.tsx +163 -0
- package/src/panels/timeline/beatRuler.ts +74 -0
- package/src/panels/timeline/curveModel.ts +500 -0
- package/src/panels/timeline/duckUnderVoice.ts +68 -0
- package/src/panels/timeline/keyframeMenu.ts +138 -0
- package/src/panels/timeline/labelWidth.ts +49 -0
- package/src/panels/timeline/marquee.ts +155 -0
- package/src/panels/timeline/model.ts +1860 -199
- package/src/panels/timeline/snap.ts +125 -43
- package/src/panels/timeline/staggerPreview.tsx +92 -0
- package/src/panels/timeline/takeMeasure.ts +72 -0
- package/src/panels/timeline/useLive.ts +48 -0
- package/src/panels/timeline/viewport.ts +234 -0
- package/src/panels/toolbar/BackChip.tsx +116 -0
- package/src/panels/toolbar/CameraViewBar.tsx +118 -49
- package/src/panels/toolbar/CanvasBar.tsx +117 -0
- package/src/panels/toolbar/InsertButton.tsx +38 -0
- package/src/panels/toolbar/IsolationTrail.tsx +156 -0
- package/src/panels/toolbar/PanelToggles.tsx +86 -0
- package/src/panels/toolbar/ShadingBar.tsx +119 -26
- package/src/panels/toolbar/ToolDockBar.tsx +329 -80
- package/src/panels/toolbar/ToolFooter.tsx +102 -0
- package/src/panels/toolbar/ViewModeBar.tsx +247 -38
- package/src/panels/toolbar/WorkPlaneBar.tsx +94 -0
- package/src/panels/toolbar/useAddRows.tsx +326 -0
- package/src/panels/toolbar/useDismiss.ts +122 -0
- package/src/panels/versions/VersionHistoryPanel.tsx +274 -0
- package/src/panels/versions/versionModel.ts +103 -0
- package/src/panels/virtualWindow.ts +271 -0
- package/src/shotTokens.css +151 -60
- package/src/stage/FrustumGizmo.tsx +504 -84
- package/src/stage/GridPlane.tsx +225 -16
- package/src/stage/NodePreviews.tsx +628 -0
- package/src/stage/PerfHud.tsx +437 -47
- package/src/stage/SplitRenderer.tsx +816 -169
- package/src/stage/StageApp.tsx +4274 -486
- package/src/stage/StageRootBoundary.tsx +114 -0
- package/src/stage/StageTools.tsx +651 -151
- package/src/stage/TransformGizmo.tsx +696 -0
- package/src/stage/adaptivePresentation.ts +145 -0
- package/src/stage/bridgeClient.ts +2522 -41
- package/src/stage/exportWorker.ts +9 -0
- package/src/stage/feed.ts +305 -14
- package/src/stage/fontPreview.ts +176 -0
- package/src/stage/frustumGeometry.ts +319 -1
- package/src/stage/furnitureTokens.ts +33 -0
- package/src/stage/gizmoMath.ts +508 -16
- package/src/stage/gpuTimer.ts +276 -0
- package/src/stage/instanceModules.ts +194 -33
- package/src/stage/interactions/move.ts +24 -5
- package/src/stage/isolation.ts +291 -33
- package/src/stage/jsSelfProfiler.ts +325 -0
- package/src/stage/main.tsx +116 -16
- package/src/stage/modalState.ts +222 -23
- package/src/stage/moduleEpoch.ts +60 -0
- package/src/stage/motionPathStage.ts +279 -0
- package/src/stage/motionPreview.ts +74 -0
- package/src/stage/overlays/Overlays.tsx +1112 -159
- package/src/stage/overlays/Rulers.tsx +495 -0
- package/src/stage/panes.ts +159 -10
- package/src/stage/project.ts +68 -3
- package/src/stage/renderScaleController.ts +730 -0
- package/src/stage/rulers.ts +129 -0
- package/src/stage/sceneExtent.ts +75 -0
- package/src/stage/shading.ts +20 -0
- package/src/stage/splineStage.ts +632 -0
- package/src/stage/stagePerformanceTelemetry.ts +717 -0
- package/src/stage/stageTheme.ts +36 -0
- package/src/stage/stageWarmup.ts +11 -0
- package/src/stage/textAutoSize.ts +124 -0
- package/src/stage/tier1Export.ts +641 -0
- package/src/stage/tokenColor.ts +14 -9
- package/src/stage/view.ts +56 -9
- package/src/stage/workingView.ts +277 -33
- package/src/state/accountPrefs.ts +261 -0
- package/src/state/agentActions.ts +173 -15
- package/src/state/agentActivity.ts +198 -0
- package/src/state/agentQuestion.ts +239 -0
- package/src/state/bootOpen.ts +42 -0
- package/src/state/cameraVerbs.ts +139 -0
- package/src/state/clientId.ts +6 -1
- package/src/state/comments.ts +1099 -0
- package/src/state/context.tsx +19 -1
- package/src/state/editorPrefs.ts +220 -0
- package/src/state/errorReports.ts +121 -0
- package/src/state/fieldWrite.ts +161 -0
- package/src/state/fontRecents.ts +70 -0
- package/src/state/fonts.ts +656 -0
- package/src/state/graphClipboard.ts +414 -0
- package/src/state/imageThumbs.ts +216 -0
- package/src/state/insert.ts +444 -67
- package/src/state/isolationTrail.ts +92 -0
- package/src/state/keyMoves.ts +134 -0
- package/src/state/keyframeClipboard.ts +177 -0
- package/src/state/keymap.ts +1153 -117
- package/src/state/layoutPrefs.ts +272 -0
- package/src/state/layouts.ts +205 -0
- package/src/state/measureMedia.ts +167 -0
- package/src/state/menu.ts +2341 -28
- package/src/state/motionStamp.ts +7 -0
- package/src/state/panelSize.ts +375 -5
- package/src/state/pendingChanges.ts +119 -0
- package/src/state/playHold.ts +78 -0
- package/src/state/playbackPreparation.ts +562 -0
- package/src/state/posterCapture.ts +242 -0
- package/src/state/presence.ts +254 -0
- package/src/state/projectComponents.ts +160 -0
- package/src/state/renderEvents.ts +298 -18
- package/src/state/runtimeErrors.ts +46 -0
- package/src/state/selection.ts +12 -0
- package/src/state/shortcuts.ts +1606 -0
- package/src/state/sketchGuide.ts +51 -0
- package/src/state/splineRefs.ts +87 -0
- package/src/state/stageHealth.ts +259 -0
- package/src/state/stageWatchdog.ts +47 -0
- package/src/state/stillStage.ts +330 -0
- package/src/state/stillThumbs.ts +474 -0
- package/src/state/store.ts +23591 -2982
- package/src/state/storyboardModel.ts +528 -0
- package/src/state/sync.ts +1257 -0
- package/src/state/syncCache.ts +159 -0
- package/src/state/triangles.ts +13 -0
- package/src/state/types.ts +1698 -55
- package/src/state/unsavedGuard.ts +49 -0
- package/src/state/viewMode.ts +200 -0
- package/src/state/webmcp.ts +1128 -0
- package/src/state/workspace.ts +1147 -0
- package/src/styles/assets-tab.css +304 -0
- package/src/styles/canvas.css +545 -0
- package/src/styles/comments.css +1435 -0
- package/src/styles/controls.css +162 -0
- package/src/styles/dialogs.css +390 -0
- package/src/styles/insert-drawer.css +586 -0
- package/src/styles/inspector-board.css +207 -0
- package/src/styles/inspector-object.css +486 -0
- package/src/styles/inspector-shot.css +252 -0
- package/src/styles/inspector.css +360 -0
- package/src/styles/left-column.css +1628 -0
- package/src/styles/library-preview.css +575 -0
- package/src/styles/library.css +512 -0
- package/src/styles/modes/code.css +569 -0
- package/src/styles/modes/component.css +1561 -0
- package/src/styles/modes/material.css +1201 -0
- package/src/styles/modes/storyboard.css +518 -0
- package/src/styles/share-component.css +49 -0
- package/src/styles/shell.css +653 -0
- package/src/styles/shot-brief.css +360 -0
- package/src/styles/states.css +76 -0
- package/src/styles/sync.css +380 -0
- package/src/styles/timeline-graph.css +384 -0
- package/src/styles/timeline-meters.css +89 -0
- package/src/styles/timeline-rows.css +1959 -0
- package/src/styles/timeline-sound.css +1003 -0
- package/src/styles/timeline.css +754 -0
- package/src/styles/tool-footer.css +525 -0
- package/src/styles/topbar.css +886 -0
- package/src/styles/versions.css +262 -0
- package/src/styles.css +72 -2921
- package/src/tools.ts +108 -23
- package/dist/assets/index-B7notge7.css +0 -1
- package/dist/assets/index-BBE6Ta9L.js +0 -52
- package/src/panels/files/icons.tsx +0 -118
- package/src/panels/header/DocSwitcher.tsx +0 -85
- package/src/panels/header/TransportBar.tsx +0 -86
- package/src/panels/inspector/animate/BlockTiming.tsx +0 -341
- package/src/panels/inspector/animate/DuringBlocks.tsx +0 -85
- package/src/panels/inspector/animate/InOutPresets.tsx +0 -57
- package/src/panels/inspector/animate/ResolvedChannels.tsx +0 -150
- package/src/panels/inspector/animate/useBlocks.ts +0 -43
- package/src/panels/material/MaterialPicker.tsx +0 -172
- package/src/panels/timeline/SceneStrip.tsx +0 -145
- package/src/panels/toolbar/AddMenu.tsx +0 -230
- package/src/stage/MoveGizmo.tsx +0 -411
|
@@ -1,49 +1,380 @@
|
|
|
1
|
-
// What the chrome knows and the author cannot see
|
|
2
|
-
// and which GL backend actually came up.
|
|
1
|
+
// What the chrome knows and the author cannot see, in two places.
|
|
3
2
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// the
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
// `SaveBadges` sit in the top bar: save state and a codegen refusal, which are
|
|
4
|
+
// facts about the DOCUMENT. `StageBadges` sit in the canvas bar: the frame rate
|
|
5
|
+
// and the GL backend, which are facts about the PICTURE under that bar.
|
|
6
|
+
//
|
|
7
|
+
// The backend chip was once removed under "a badge is an event, never a
|
|
8
|
+
// setting": the shot lane pinned preview and export to one backend
|
|
9
|
+
// (`SHOT_STAGE_BACKEND`), so the chip said "webgl" in every session that has
|
|
10
|
+
// ever run. That pin now covers the EXPORT only — the preview takes the
|
|
11
|
+
// WebGPU-first default and lands on whichever backend the host browser has
|
|
12
|
+
// (packages/shot/src/stageBackend.ts) — so the word varies per session again
|
|
13
|
+
// and is worth a chip. `stageInfo.backend` is the stage's own onReady report,
|
|
14
|
+
// read off the live renderer after three's silent fallback has or has not
|
|
15
|
+
// happened, which is exactly what an import-time constant cannot tell you.
|
|
16
|
+
//
|
|
17
|
+
// THE FPS CHIP IS AN EVENT TOO, and playback is the event: it appears when the
|
|
18
|
+
// transport starts and goes when it stops, so it never becomes a permanent
|
|
19
|
+
// number decorating an idle bar. What it reports is what the STAGE presented,
|
|
20
|
+
// measured stage-side over one-second windows and sent across the bridge — not
|
|
21
|
+
// the chrome's own seek rate, which is what the transport asked for and reads
|
|
22
|
+
// ~60 while the picture runs at 18, because the stage coalesces requests and
|
|
23
|
+
// drops publications exactly when the two come apart. While the playback
|
|
24
|
+
// render scale holds the picture below the display's density (bridge v52), the
|
|
25
|
+
// chip says by how much ("24 fps · 85%"), and its title says why.
|
|
26
|
+
//
|
|
27
|
+
// A BROKEN PIECE IS AN EVENT THAT STANDS. When the stage isolates a layer, a
|
|
28
|
+
// background, a post stack or a transition that threw (bridge v34), the
|
|
29
|
+
// danger chip says how many, first in the row so it is the last to wrap away,
|
|
30
|
+
// and its title names each one and what it threw. It is not on the View >
|
|
31
|
+
// Status badges switch: a picture with a hole in it is not a preference.
|
|
32
|
+
//
|
|
33
|
+
// A LIVE-SYNC PROJECT SWAPS THE SAVE CHIP FOR THE SYNC CHIP (R5, Paper page
|
|
34
|
+
// 21 "Sync status"): one quiet line that only speaks up when work is not on
|
|
35
|
+
// the server yet. Saved and Syncing stay grey; Offline turns warn and counts
|
|
36
|
+
// what waits on this device. It opens a popover naming every state, with
|
|
37
|
+
// "Try now" on the reconnect row. A full disk adds `SyncQuotaBanner`.
|
|
38
|
+
import { useEffect, useRef, useState } from "react";
|
|
39
|
+
import type { ReactNode } from "react";
|
|
40
|
+
import { Popover } from "@dailies-studio/ui";
|
|
41
|
+
import { describePieceFailure, pieceLayerLabel } from "@dailies-studio/shot/doc";
|
|
42
|
+
import type { PlaybackResolution, StagePerformanceReport } from "../../bridgeTypes.js";
|
|
43
|
+
import { CHECK_ICON } from "../../icons.js";
|
|
44
|
+
import { useShotEditor, useShotEditorHot, useStore } from "../../state/context.js";
|
|
45
|
+
import type { SyncStatus } from "../../state/sync.js";
|
|
46
|
+
import { SYNC_HOLD_REASON } from "../../state/workspace.js";
|
|
47
|
+
|
|
48
|
+
/** The document's state: a codegen refusal and the save chip. Never on the
|
|
49
|
+
* View > Status badges switch, see `showFpsBadge` in types.ts. */
|
|
50
|
+
export function SaveBadges() {
|
|
14
51
|
const saveState = useShotEditor((s) => s.saveState);
|
|
15
52
|
const dirty = useShotEditor((s) => s.dirty);
|
|
16
53
|
const emitWarning = useShotEditor((s) => s.emitWarning);
|
|
17
|
-
const stageInfo = useShotEditor((s) => s.stageInfo);
|
|
18
54
|
const name = useShotEditor((s) => s.name);
|
|
55
|
+
const sync = useShotEditor((s) => s.syncStatus);
|
|
56
|
+
const save = saveChip(saveState, dirty);
|
|
19
57
|
|
|
20
58
|
return (
|
|
21
59
|
<div className="status-badges">
|
|
22
|
-
{stageInfo === null ? null : (
|
|
23
|
-
<span className="badge badge-quiet" title={`${stageInfo.width}x${stageInfo.height}`}>
|
|
24
|
-
{stageInfo.backend}
|
|
25
|
-
</span>
|
|
26
|
-
)}
|
|
27
60
|
{emitWarning === null ? null : (
|
|
28
|
-
<span className="
|
|
61
|
+
<span className="status-chip status-chip-warn" title={emitWarning}>
|
|
29
62
|
codegen
|
|
30
63
|
</span>
|
|
31
64
|
)}
|
|
32
|
-
{name === null ? null : (
|
|
33
|
-
<
|
|
34
|
-
|
|
65
|
+
{name === null ? null : sync !== null ? (
|
|
66
|
+
<SyncChip status={sync} />
|
|
67
|
+
) : save === null ? null : (
|
|
68
|
+
<span className={`status-chip status-chip-${save.tone}`} title={save.text}>
|
|
69
|
+
{/* Saved wears Paper's check: the quiet state, read at a glance. */}
|
|
70
|
+
{save.tone === "saved" ? CHECK_ICON : null}
|
|
71
|
+
{save.text}
|
|
35
72
|
</span>
|
|
36
73
|
)}
|
|
37
74
|
</div>
|
|
38
75
|
);
|
|
39
76
|
}
|
|
40
77
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
78
|
+
const changes = (n: number): string => (n === 1 ? "1 change" : `${n} changes`);
|
|
79
|
+
|
|
80
|
+
function glyph(size: 14 | 16, children: ReactNode): ReactNode {
|
|
81
|
+
return (
|
|
82
|
+
<svg
|
|
83
|
+
viewBox={`0 0 ${size} ${size}`}
|
|
84
|
+
fill="none"
|
|
85
|
+
stroke="currentColor"
|
|
86
|
+
strokeWidth={1.4}
|
|
87
|
+
strokeLinecap="round"
|
|
88
|
+
strokeLinejoin="round"
|
|
89
|
+
aria-hidden="true"
|
|
90
|
+
>
|
|
91
|
+
{children}
|
|
92
|
+
</svg>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
const SAVED_ICON = glyph(14, <path d="M3 7.4L5.8 10L11 4.5" />);
|
|
96
|
+
const CLOUD_OFF_ICON = glyph(
|
|
97
|
+
14,
|
|
98
|
+
<>
|
|
99
|
+
<path d="M4 11h6.2a2.6 2.6 0 0 0 .4-5.17A3.6 3.6 0 0 0 3.7 5.2 2.9 2.9 0 0 0 4 11Z" />
|
|
100
|
+
<path d="M2 2l10 10" />
|
|
101
|
+
</>,
|
|
102
|
+
);
|
|
103
|
+
const SYNCING_ICON = glyph(
|
|
104
|
+
14,
|
|
105
|
+
<>
|
|
106
|
+
<circle cx="7" cy="7" r="4.8" className="sync-ring" />
|
|
107
|
+
<path d="M7 2.2a4.8 4.8 0 0 1 4.8 4.8" className="sync-arc" />
|
|
108
|
+
</>,
|
|
109
|
+
);
|
|
110
|
+
const RECONNECT_ICON = glyph(
|
|
111
|
+
14,
|
|
112
|
+
<>
|
|
113
|
+
<path d="M11.5 6.5A4.6 4.6 0 0 0 3.2 4.4M2.5 7.5a4.6 4.6 0 0 0 8.3 2.1" />
|
|
114
|
+
<path d="M3 2v2.6h2.6M11 12V9.4H8.4" />
|
|
115
|
+
</>,
|
|
116
|
+
);
|
|
117
|
+
const CHEVRON = (
|
|
118
|
+
<svg viewBox="0 0 10 10" fill="none" stroke="currentColor" strokeWidth={1.3} strokeLinecap="round" aria-hidden="true" className="sync-chevron">
|
|
119
|
+
<path d="M2.5 4l2.5 2.5L7.5 4" />
|
|
120
|
+
</svg>
|
|
121
|
+
);
|
|
122
|
+
const WARN_ICON = glyph(
|
|
123
|
+
16,
|
|
124
|
+
<>
|
|
125
|
+
<path d="M8 2.2L14.2 13H1.8L8 2.2Z" />
|
|
126
|
+
<path d="M8 6.5v3" />
|
|
127
|
+
<circle cx="8" cy="11.2" r="0.8" fill="currentColor" stroke="none" />
|
|
128
|
+
</>,
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
type SyncState = "saved" | "syncing" | "offline" | "reconnecting" | "held";
|
|
132
|
+
|
|
133
|
+
/** The one state the sync chip says. */
|
|
134
|
+
export function syncState(s: SyncStatus): SyncState {
|
|
135
|
+
if (s.hold !== null) return "held";
|
|
136
|
+
if (!s.connected) return s.waiting > 0 ? "offline" : "reconnecting";
|
|
137
|
+
return s.waiting > 0 ? "syncing" : "saved";
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Whole seconds until `at`, ticking each second while `live`. */
|
|
141
|
+
function useSecondsUntil(at: number | null, live: boolean): number | null {
|
|
142
|
+
const [now, setNow] = useState(() => Date.now());
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
if (at === null || !live) return;
|
|
145
|
+
setNow(Date.now());
|
|
146
|
+
const id = setInterval(() => setNow(Date.now()), 1000);
|
|
147
|
+
return () => clearInterval(id);
|
|
148
|
+
}, [at, live]);
|
|
149
|
+
return at === null ? null : Math.max(0, Math.ceil((at - now) / 1000));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function SyncChip({ status }: { status: SyncStatus }) {
|
|
153
|
+
const store = useStore();
|
|
154
|
+
const anchor = useRef<HTMLButtonElement>(null);
|
|
155
|
+
const [open, setOpen] = useState(false);
|
|
156
|
+
const seconds = useSecondsUntil(status.retryAt, open);
|
|
157
|
+
const state = syncState(status);
|
|
158
|
+
const { waiting, hold } = status;
|
|
159
|
+
|
|
160
|
+
const chip: Record<SyncState, { tone: string; icon: ReactNode; text: string }> = {
|
|
161
|
+
saved: { tone: "saved", icon: SAVED_ICON, text: "saved" },
|
|
162
|
+
syncing: { tone: "saved", icon: SYNCING_ICON, text: "syncing…" },
|
|
163
|
+
offline: { tone: "offline", icon: CLOUD_OFF_ICON, text: `Offline, ${changes(waiting)} saved on this device` },
|
|
164
|
+
reconnecting: { tone: "saved", icon: RECONNECT_ICON, text: "reconnecting…" },
|
|
165
|
+
held: { tone: "error", icon: null, text: hold === "quota" ? "not stored" : "read only" },
|
|
166
|
+
};
|
|
167
|
+
const rows: Array<{ id: SyncState; icon: ReactNode; title: ReactNode; text: string }> = [
|
|
168
|
+
{ id: "saved", icon: SAVED_ICON, title: "Saved", text: "Every change is on the server." },
|
|
169
|
+
{
|
|
170
|
+
id: "syncing",
|
|
171
|
+
icon: SYNCING_ICON,
|
|
172
|
+
title: "Syncing",
|
|
173
|
+
text:
|
|
174
|
+
state === "syncing"
|
|
175
|
+
? `Sending ${changes(waiting)}. Peers see them as they land.`
|
|
176
|
+
: "Changes send as you make them. Peers see them as they land.",
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
id: "offline",
|
|
180
|
+
icon: CLOUD_OFF_ICON,
|
|
181
|
+
title: "Offline",
|
|
182
|
+
text: "Keep working. They send when you are back online. Closing the tab is safe.",
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
id: "reconnecting",
|
|
186
|
+
icon: RECONNECT_ICON,
|
|
187
|
+
title: (
|
|
188
|
+
<>
|
|
189
|
+
<span>{seconds === null ? "Reconnecting" : `Reconnecting in ${seconds} s`}</span>
|
|
190
|
+
{status.connected || hold !== null ? null : (
|
|
191
|
+
<button type="button" className="sync-try" data-testid="sync-try-now" onClick={() => store.syncRetryNow()}>
|
|
192
|
+
Try now
|
|
193
|
+
</button>
|
|
194
|
+
)}
|
|
195
|
+
</>
|
|
196
|
+
),
|
|
197
|
+
text: "Retries wait longer each time, with a little jitter.",
|
|
198
|
+
},
|
|
199
|
+
];
|
|
200
|
+
const c = chip[state];
|
|
201
|
+
|
|
202
|
+
return (
|
|
203
|
+
<>
|
|
204
|
+
<button
|
|
205
|
+
ref={anchor}
|
|
206
|
+
type="button"
|
|
207
|
+
className={`status-chip status-chip-${c.tone} sync-chip`}
|
|
208
|
+
data-testid="sync-chip"
|
|
209
|
+
data-state={state}
|
|
210
|
+
aria-expanded={open}
|
|
211
|
+
title={hold === null ? "Sync status" : SYNC_HOLD_REASON[hold]}
|
|
212
|
+
onClick={() => setOpen((v) => !v)}
|
|
213
|
+
>
|
|
214
|
+
{c.icon}
|
|
215
|
+
{c.text}
|
|
216
|
+
{state === "offline" ? CHEVRON : null}
|
|
217
|
+
</button>
|
|
218
|
+
<Popover open={open} anchor={anchor} onClose={() => setOpen(false)} align="right" className="sync-pop">
|
|
219
|
+
<div className="sync-panel" data-testid="sync-panel">
|
|
220
|
+
<div className="sync-head">
|
|
221
|
+
<span className="sync-title">Sync</span>
|
|
222
|
+
<span className="sync-device">this device · {status.tabs === 1 ? "1 tab" : `${status.tabs} tabs`}</span>
|
|
223
|
+
</div>
|
|
224
|
+
{hold === null ? null : (
|
|
225
|
+
<div className="sync-row is-current is-held">
|
|
226
|
+
<span className="sync-icon">{WARN_ICON}</span>
|
|
227
|
+
<span className="sync-body">
|
|
228
|
+
<span className="sync-row-title">{hold === "quota" ? "Not stored" : "Read only"}</span>
|
|
229
|
+
<span className="sync-row-text">{SYNC_HOLD_REASON[hold]}</span>
|
|
230
|
+
</span>
|
|
231
|
+
</div>
|
|
232
|
+
)}
|
|
233
|
+
{rows.map((row) => (
|
|
234
|
+
<div key={row.id} className={`sync-row${row.id === state ? " is-current" : ""}`} data-row={row.id}>
|
|
235
|
+
<span className="sync-icon">{row.icon}</span>
|
|
236
|
+
<span className="sync-body">
|
|
237
|
+
<span className="sync-row-title">{row.title}</span>
|
|
238
|
+
<span className="sync-row-text">{row.text}</span>
|
|
239
|
+
</span>
|
|
240
|
+
</div>
|
|
241
|
+
))}
|
|
242
|
+
</div>
|
|
243
|
+
</Popover>
|
|
244
|
+
</>
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** "Edits cannot be stored on this device" (Paper page 21): editing is held
|
|
249
|
+
* until the browser takes the waiting changes again. Sticky, no dismiss. */
|
|
250
|
+
export function SyncQuotaBanner() {
|
|
251
|
+
const store = useStore();
|
|
252
|
+
const status = useShotEditor((s) => s.syncStatus);
|
|
253
|
+
const [busy, setBusy] = useState(false);
|
|
254
|
+
if (status?.hold !== "quota") return null;
|
|
255
|
+
const retry = (prune: boolean): void => {
|
|
256
|
+
setBusy(true);
|
|
257
|
+
void store.syncRetryStorage(prune).finally(() => setBusy(false));
|
|
258
|
+
};
|
|
259
|
+
const n = status.waiting;
|
|
260
|
+
return (
|
|
261
|
+
<div className="sync-banner" role="alert" data-testid="sync-quota">
|
|
262
|
+
<span className="sync-banner-icon">{WARN_ICON}</span>
|
|
263
|
+
<span className="sync-banner-body">
|
|
264
|
+
<span className="sync-banner-title">Edits cannot be stored on this device</span>
|
|
265
|
+
<span className="sync-banner-text">
|
|
266
|
+
The browser is out of space. Editing is paused so nothing is lost.
|
|
267
|
+
{n > 0 ? ` Your ${changes(n)} saved ${n === 1 ? "is" : "are"} safe.` : ""}
|
|
268
|
+
</span>
|
|
269
|
+
</span>
|
|
270
|
+
<button type="button" className="sync-banner-free" disabled={busy} onClick={() => retry(true)}>
|
|
271
|
+
Free up space
|
|
272
|
+
</button>
|
|
273
|
+
<button type="button" className="sync-banner-retry" disabled={busy} onClick={() => retry(false)}>
|
|
274
|
+
Try again
|
|
275
|
+
</button>
|
|
276
|
+
</div>
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** The picture's state: the presented rate while playing, and the backend.
|
|
281
|
+
* Both answer to View > Status badges. */
|
|
282
|
+
export function StageBadges() {
|
|
283
|
+
const backend = useShotEditor((s) => s.stageInfo?.backend ?? null);
|
|
284
|
+
const playing = useShotEditor((s) => s.playing);
|
|
285
|
+
// Hot: a report written while the transport plays wakes no cold reader.
|
|
286
|
+
const perf = useShotEditorHot((s) => s.stagePerf);
|
|
287
|
+
const showFps = useShotEditor((s) => s.showFpsBadge);
|
|
288
|
+
const resolution = useShotEditor((s) => s.playbackResolution);
|
|
289
|
+
const showBackend = useShotEditor((s) => s.showBackendBadge);
|
|
290
|
+
const failures = useShotEditor((s) => s.stagePieceFailures);
|
|
291
|
+
const renderErrors = useShotEditor((s) => s.stageRenderErrors);
|
|
292
|
+
const doc = useShotEditor((s) => s.doc);
|
|
293
|
+
// ONE chip for everything the stage could not draw: an isolated piece and a
|
|
294
|
+
// render fallback (an undecodable html layer, a font, a texture) are the
|
|
295
|
+
// same news to the author, and the same refusal at export.
|
|
296
|
+
const broken = failures.length + renderErrors.length;
|
|
297
|
+
|
|
298
|
+
return (
|
|
299
|
+
<div className="stage-badges" data-testid="stage-badges">
|
|
300
|
+
{broken === 0 ? null : (
|
|
301
|
+
<span
|
|
302
|
+
className="status-chip status-chip-error"
|
|
303
|
+
data-testid="piece-failures-badge"
|
|
304
|
+
role="status"
|
|
305
|
+
title={[
|
|
306
|
+
...failures.map((failure) => describePieceFailure(failure, pieceLayerLabel(doc, failure))),
|
|
307
|
+
...renderErrors,
|
|
308
|
+
].join("\n")}
|
|
309
|
+
>
|
|
310
|
+
{broken === 1 ? "1 broken piece" : `${broken} broken pieces`}
|
|
311
|
+
</span>
|
|
312
|
+
)}
|
|
313
|
+
{showFps && playing && perf !== null ? (
|
|
314
|
+
<span
|
|
315
|
+
className="status-chip status-chip-quiet status-chip-fps"
|
|
316
|
+
data-testid="fps-badge"
|
|
317
|
+
data-render-scale={perf.renderScale}
|
|
318
|
+
title={fpsTitle(perf, resolution)}
|
|
319
|
+
>
|
|
320
|
+
{Math.round(perf.presentedFps)} fps
|
|
321
|
+
{perf.renderScale < 1 ? ` · ${Math.round(perf.renderScale * 100)}%` : null}
|
|
322
|
+
</span>
|
|
323
|
+
) : null}
|
|
324
|
+
{backend === null || !showBackend ? null : (
|
|
325
|
+
<span
|
|
326
|
+
className="status-chip status-chip-quiet"
|
|
327
|
+
data-testid="backend-badge"
|
|
328
|
+
title={`preview backend: ${backend}. Exports always render on webgl.`}
|
|
329
|
+
>
|
|
330
|
+
{backend}
|
|
331
|
+
</span>
|
|
332
|
+
)}
|
|
333
|
+
</div>
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** The fps chip's title: the presented rate, the GPU's time when the stage
|
|
338
|
+
* measured it, and while the picture renders below the display's density,
|
|
339
|
+
* how far and why (bridge v52). */
|
|
340
|
+
export function fpsTitle(perf: StagePerformanceReport, resolution: PlaybackResolution): string {
|
|
341
|
+
const rate =
|
|
342
|
+
`Presented ${Math.round(perf.presentedFps)} of ${Math.round(perf.expectedFps)} expected frames ` +
|
|
343
|
+
`in the last second, ${perf.droppedPublications} dropped.`;
|
|
344
|
+
const gpu = perf.gpuMs === null ? "" : ` The GPU took ${perf.gpuMs.toFixed(1)} ms a frame (p95).`;
|
|
345
|
+
if (!(perf.renderScale < 1)) return rate + gpu;
|
|
346
|
+
const why = resolution === "fast" ? "Playback resolution is Fast" : "the GPU could not keep up";
|
|
347
|
+
return (
|
|
348
|
+
`${rate}${gpu} Rendering at ${Math.round(perf.renderScale * 100)}% of the display's resolution while ` +
|
|
349
|
+
`playing, because ${why}. Paused frames and exports are always full resolution.`
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/** The chip's word AND its tone, or NULL when there is nothing to say.
|
|
354
|
+
*
|
|
355
|
+
* ONE FUNCTION FOR BOTH, because they disagreed. The word has always come
|
|
356
|
+
* from `dirty` first and the class was written straight off `saveState`, so
|
|
357
|
+
* the ordinary state after a first autosave — `saveState: "saved"` with a
|
|
358
|
+
* pending edit — rendered the word "unsaved" wearing `.status-chip-saved`. That
|
|
359
|
+
* cost nothing while `.status-chip-saved` had no rule; the moment it got the quiet
|
|
360
|
+
* treatment it deserves, the one chip an author must not miss became the
|
|
361
|
+
* faintest thing on the bar. The tone now falls out of the same branch that
|
|
362
|
+
* picks the word, so the two cannot drift again.
|
|
363
|
+
*
|
|
364
|
+
* Null is a state the chip used to spell "—": a freshly opened, untouched
|
|
365
|
+
* document sat behind a bordered pill containing an em-dash, which is a
|
|
366
|
+
* control-shaped object that reads as information and carries none. Silence
|
|
367
|
+
* is the honest rendering of "saved, and nothing has happened since" — the
|
|
368
|
+
* chip returns the moment there is a word for it, which is what makes its
|
|
369
|
+
* appearance mean something. */
|
|
370
|
+
type SaveChip = { tone: "error" | "saving" | "idle" | "saved"; text: string };
|
|
371
|
+
|
|
372
|
+
function saveChip(saveState: string, dirty: boolean): SaveChip | null {
|
|
373
|
+
if (saveState === "error") return { tone: "error", text: "save failed" };
|
|
374
|
+
if (saveState === "saving") return { tone: "saving", text: "saving…" };
|
|
44
375
|
// "saved" plus pending edits is not saved — the badge follows the DOCUMENT,
|
|
45
376
|
// not the last request, or an autosave debounce reads as "everything is on
|
|
46
377
|
// disk" for the whole 400ms an author might close the tab in.
|
|
47
|
-
if (dirty) return "unsaved";
|
|
48
|
-
return saveState === "saved" ? "saved" : "
|
|
378
|
+
if (dirty) return { tone: "idle", text: "unsaved" };
|
|
379
|
+
return saveState === "saved" ? { tone: "saved", text: "saved" } : null;
|
|
49
380
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// A Shot deleted under live sync while this tab had changes the server never
|
|
2
|
+
// got (R10, Paper page 21 "Renamed or deleted"). The editor is read only
|
|
3
|
+
// behind it, and the two ways on are the only ways out: "Restore" brings the
|
|
4
|
+
// Shot back and its session replays the changes, "Save as copy" makes them a
|
|
5
|
+
// new Shot. With nothing pending there is no dialog: the Shot closes and the
|
|
6
|
+
// error row says who deleted it.
|
|
7
|
+
import type { ReactNode } from "react";
|
|
8
|
+
import { Dialog, initialsOf } from "@dailies-studio/ui";
|
|
9
|
+
import { useShotEditor, useStore } from "../../state/context.js";
|
|
10
|
+
|
|
11
|
+
const plural = (n: number, one: string): string => `${n} ${one}${n === 1 ? "" : "s"}`;
|
|
12
|
+
|
|
13
|
+
export function SyncDeletedDialog(): ReactNode {
|
|
14
|
+
const store = useStore();
|
|
15
|
+
const d = useShotEditor((s) => s.syncDeleted);
|
|
16
|
+
if (d === null) return null;
|
|
17
|
+
const changes = plural(d.count, "change");
|
|
18
|
+
return (
|
|
19
|
+
<Dialog
|
|
20
|
+
open
|
|
21
|
+
className="sync-deleted"
|
|
22
|
+
dismissOnOverlay={false}
|
|
23
|
+
dismissOnEscape={false}
|
|
24
|
+
onClose={() => {}}
|
|
25
|
+
title={
|
|
26
|
+
<>
|
|
27
|
+
{d.by === null ? null : (
|
|
28
|
+
<span className="sync-face is-lg" style={{ background: d.color ?? undefined }} aria-hidden="true">
|
|
29
|
+
{initialsOf(d.by)}
|
|
30
|
+
</span>
|
|
31
|
+
)}
|
|
32
|
+
<span>{d.by === null ? `"${d.title}" was deleted` : `${d.by} deleted "${d.title}"`}</span>
|
|
33
|
+
</>
|
|
34
|
+
}
|
|
35
|
+
actions={
|
|
36
|
+
<>
|
|
37
|
+
<button
|
|
38
|
+
type="button"
|
|
39
|
+
className="sync-deleted-btn"
|
|
40
|
+
disabled={d.busy || d.trashId === null}
|
|
41
|
+
onClick={() => void store.saveDeletedAsCopy()}
|
|
42
|
+
>
|
|
43
|
+
Save as copy
|
|
44
|
+
</button>
|
|
45
|
+
<button
|
|
46
|
+
type="button"
|
|
47
|
+
className="sync-deleted-btn is-primary"
|
|
48
|
+
disabled={d.busy || d.trashId === null}
|
|
49
|
+
onClick={() => void store.restoreDeleted()}
|
|
50
|
+
>
|
|
51
|
+
Restore
|
|
52
|
+
</button>
|
|
53
|
+
</>
|
|
54
|
+
}
|
|
55
|
+
>
|
|
56
|
+
<p className="sync-deleted-text">
|
|
57
|
+
It is in Trash now. You have {changes} that never reached the server. Keep them by restoring the Shot, or by
|
|
58
|
+
saving them as a new copy.
|
|
59
|
+
</p>
|
|
60
|
+
<div className="sync-deleted-pending" data-testid="sync-deleted-pending">
|
|
61
|
+
<div className="sync-deleted-label">Your {d.count} pending {d.count === 1 ? "change" : "changes"}</div>
|
|
62
|
+
<pre>{d.lines.join("\n")}</pre>
|
|
63
|
+
</div>
|
|
64
|
+
{d.error === null ? null : (
|
|
65
|
+
<p className="dialog-error" role="alert">
|
|
66
|
+
{d.error}
|
|
67
|
+
</p>
|
|
68
|
+
)}
|
|
69
|
+
</Dialog>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** "Renamed to X by Maya", beside the title until it clears itself. */
|
|
74
|
+
export function SyncRenamedNote(): ReactNode {
|
|
75
|
+
const note = useShotEditor((s) => s.syncRenamed);
|
|
76
|
+
if (note === null) return null;
|
|
77
|
+
return (
|
|
78
|
+
<span className="sync-renamed" role="status" data-testid="sync-renamed">
|
|
79
|
+
{note.by === null ? null : (
|
|
80
|
+
<span className="sync-face" style={{ background: note.color ?? undefined }} aria-hidden="true">
|
|
81
|
+
{initialsOf(note.by)}
|
|
82
|
+
</span>
|
|
83
|
+
)}
|
|
84
|
+
Renamed to {note.to}
|
|
85
|
+
{note.by === null ? "" : ` by ${note.by}`}
|
|
86
|
+
</span>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
@@ -1,13 +1,27 @@
|
|
|
1
|
+
import { IconMinus, IconPlus } from "@dailies-studio/ui";
|
|
1
2
|
import { useShotEditor, useStore } from "../../state/context.js";
|
|
3
|
+
import { layoutSpec } from "../../state/layouts.js";
|
|
4
|
+
import { shortcutHint } from "../../state/shortcuts.js";
|
|
2
5
|
|
|
3
|
-
/** Zoom readout, plus fit
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
/** Zoom readout, plus fit, at the right end of the canvas bar (CanvasBar.tsx).
|
|
7
|
+
* In the canvas bar and not the top bar because it acts on the canvas, and
|
|
8
|
+
* because `]`, `.` and Video hide columns but never that bar.
|
|
9
|
+
*
|
|
10
|
+
* Routed by the ACTIVE pane, exactly as ⌘+/⌘− and the
|
|
11
|
+
* fit key are: in a split the buttons address the pane the author last
|
|
12
|
+
* touched, and in working mode they dolly the working view instead of
|
|
13
|
+
* silently writing a camera-pane zoom nobody can see. The readout is a
|
|
14
|
+
* percentage only for the camera pane — "100%" has no meaning for an orbit
|
|
15
|
+
* camera, so the working pane shows which view the controls are pointed at
|
|
16
|
+
* instead. The stage OWNS the views (it knows the pane sizes), so "fit" asks
|
|
17
|
+
* the stage and mirrors what it answers rather than computing a second
|
|
18
|
+
* opinion here.
|
|
19
|
+
*
|
|
20
|
+
* THE HINT COMES OFF THE TABLE, NOT OFF A LITERAL. It said ⇧Z for a fit that
|
|
21
|
+
* moved to Home, which is the exact drift shortcuts.ts's header cites as the
|
|
22
|
+
* reason that table exists — and it went on saying it after the table landed
|
|
23
|
+
* because nothing here read from it. `shortcutFor` is the same lookup the menu
|
|
24
|
+
* accelerators use, so a rebind moves this tooltip too. */
|
|
11
25
|
export function ZoomControls() {
|
|
12
26
|
const store = useStore();
|
|
13
27
|
// The ZOOM alone, never the whole view object: `onViewChange` fires per
|
|
@@ -16,22 +30,39 @@ export function ZoomControls() {
|
|
|
16
30
|
const zoom = useShotEditor((s) => s.view.zoom);
|
|
17
31
|
const working = useShotEditor((s) => s.activePane === "working");
|
|
18
32
|
const open = useShotEditor((s) => s.name !== null);
|
|
33
|
+
// THE STAGE'S ZOOM ONLY. It reports `state.view.zoom`, the stage camera's,
|
|
34
|
+
// and drives `zoomActivePane`, so it lives in the stage workspace's canvas
|
|
35
|
+
// bar (CanvasBar renders it nowhere else). A mode brings its own zoom, about
|
|
36
|
+
// its own canvas: two percentages about two things would never agree.
|
|
19
37
|
if (!open) return null;
|
|
38
|
+
const fitHint = shortcutHint("view.fit");
|
|
20
39
|
return (
|
|
21
40
|
<div className="zoom-controls">
|
|
22
|
-
<button
|
|
23
|
-
|
|
41
|
+
<button
|
|
42
|
+
type="button"
|
|
43
|
+
className="btn is-icon"
|
|
44
|
+
aria-label="Zoom out"
|
|
45
|
+
title={`Zoom out${shortcutHint("view.zoomOut")}`}
|
|
46
|
+
onClick={() => store.zoomActivePane(-1)}
|
|
47
|
+
>
|
|
48
|
+
<IconMinus />
|
|
24
49
|
</button>
|
|
25
50
|
<button
|
|
26
51
|
type="button"
|
|
27
|
-
className="zoom-readout"
|
|
28
|
-
title={working ?
|
|
52
|
+
className="btn zoom-readout"
|
|
53
|
+
title={working ? `Fit the working view${fitHint}` : `Fit${fitHint}`}
|
|
29
54
|
onClick={() => store.fitActivePane()}
|
|
30
55
|
>
|
|
31
|
-
{working ? "
|
|
56
|
+
{working ? layoutSpec("working").label : `${Math.round(zoom * 100)}%`}
|
|
32
57
|
</button>
|
|
33
|
-
<button
|
|
34
|
-
|
|
58
|
+
<button
|
|
59
|
+
type="button"
|
|
60
|
+
className="btn is-icon"
|
|
61
|
+
aria-label="Zoom in"
|
|
62
|
+
title={`Zoom in${shortcutHint("view.zoomIn")}`}
|
|
63
|
+
onClick={() => store.zoomActivePane(1)}
|
|
64
|
+
>
|
|
65
|
+
<IconPlus />
|
|
35
66
|
</button>
|
|
36
67
|
</div>
|
|
37
68
|
);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// The prompt the Agent chip copies for an outside agent (Claude Code over the
|
|
2
|
+
// dailies MCP server): what "this" means right now, as ids the MCP tools take.
|
|
3
|
+
//
|
|
4
|
+
// Built from `buildShotAgentContext`, the in-app agent's own context, so the
|
|
5
|
+
// two agents are told the same thing about the same moment: the storage name,
|
|
6
|
+
// the entered definition, the Shot, the frame (def-local inside a definition),
|
|
7
|
+
// the selected layer and, on the board, the planned shot, the selected
|
|
8
|
+
// reference and the board subject (a still, a gap or a cut), and on a shot
|
|
9
|
+
// board the open sketch and its selected item. The
|
|
10
|
+
// document itself is never pasted: the agent reads it fresh through the tools.
|
|
11
|
+
import { buildShotAgentContext } from "../../state/agentActions.js";
|
|
12
|
+
import type { ShotEditorState } from "../../state/types.js";
|
|
13
|
+
|
|
14
|
+
interface ShotContext {
|
|
15
|
+
docName?: string;
|
|
16
|
+
editingDef?: string;
|
|
17
|
+
frame?: number;
|
|
18
|
+
sceneId?: string;
|
|
19
|
+
selectedLayerId?: string;
|
|
20
|
+
storyboardOpen?: boolean;
|
|
21
|
+
selectedPartId?: string;
|
|
22
|
+
selectedReferenceId?: string;
|
|
23
|
+
boardSubject?: string;
|
|
24
|
+
sketchId?: string;
|
|
25
|
+
selectedSketchItemId?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Null with no Project open: there is nothing for the agent to target. */
|
|
29
|
+
export function agentHandoffPrompt(s: ShotEditorState): string | null {
|
|
30
|
+
const { shot } = buildShotAgentContext(s) as { shot?: ShotContext };
|
|
31
|
+
if (shot?.docName === undefined) return null;
|
|
32
|
+
const title = s.doc?.title ?? shot.docName;
|
|
33
|
+
const lines = [
|
|
34
|
+
`Use the dailies MCP server to work on the Dailies Project "${title}" (storage name: ${shot.docName}).`,
|
|
35
|
+
];
|
|
36
|
+
if (shot.editingDef !== undefined) lines.push(`I am inside the component definition ${shot.editingDef}.`);
|
|
37
|
+
if (shot.sceneId !== undefined) lines.push(`Shot: ${shot.sceneId}`);
|
|
38
|
+
if (shot.frame !== undefined) {
|
|
39
|
+
lines.push(`Frame: ${shot.frame}${shot.editingDef !== undefined ? " (on the definition's own clock)" : ""}`);
|
|
40
|
+
}
|
|
41
|
+
if (shot.selectedLayerId !== undefined) lines.push(`Selected layer: ${shot.selectedLayerId}`);
|
|
42
|
+
if (shot.storyboardOpen === true) {
|
|
43
|
+
lines.push("The Storyboard is open: read get_storyboard before changing Shots.");
|
|
44
|
+
if (shot.selectedPartId !== undefined) lines.push(`Planned shot: ${shot.selectedPartId}`);
|
|
45
|
+
if (shot.selectedReferenceId !== undefined) lines.push(`Selected reference: ${shot.selectedReferenceId}`);
|
|
46
|
+
if (shot.boardSubject !== undefined) lines.push(`Board subject: ${shot.boardSubject}`);
|
|
47
|
+
if (shot.sketchId !== undefined) lines.push(`Sketch: ${shot.sketchId} (screenshot_shot sketch shows it)`);
|
|
48
|
+
if (shot.selectedSketchItemId !== undefined) lines.push(`Selected sketch item: ${shot.selectedSketchItemId}`);
|
|
49
|
+
}
|
|
50
|
+
lines.push("", "Task: ");
|
|
51
|
+
return lines.join("\n");
|
|
52
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { flushPoster } from "../../state/posterCapture.js";
|
|
2
|
+
import type { ShotEditorStore } from "../../state/store.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Leave the editor for `href`: FLUSH FIRST, AND ONLY LEAVE IF IT LANDED.
|
|
6
|
+
*
|
|
7
|
+
* Every way out (the app menu's account rows, the `Projects` crumb, the
|
|
8
|
+
* avatar) leaves the page, and the autosave debounce is measured in seconds,
|
|
9
|
+
* so the last edit is routinely still in flight when one is picked.
|
|
10
|
+
*
|
|
11
|
+
* This was `.finally` once, which navigates whether the flush RESOLVED or
|
|
12
|
+
* REJECTED, so a save that failed took the edit with it. A rejection leaves the
|
|
13
|
+
* author exactly where they were, with the document still dirty and the
|
|
14
|
+
* header's save badge still saying so, which is the whole explanation of why
|
|
15
|
+
* the click appeared to do nothing.
|
|
16
|
+
*
|
|
17
|
+
* Then the POSTER the save just made owed (hosted only; bounded, and it never
|
|
18
|
+
* rejects): its timer would otherwise die with the page, and the card for the
|
|
19
|
+
* work just done would keep an older picture.
|
|
20
|
+
*/
|
|
21
|
+
export function leaveFor(store: Pick<ShotEditorStore, "flush">, href: string): void {
|
|
22
|
+
void store.flush().then(
|
|
23
|
+
() => flushPoster().then(() => window.location.assign(href)),
|
|
24
|
+
() => {},
|
|
25
|
+
);
|
|
26
|
+
}
|