@dailies-studio/shot-editor 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.vite/manifest.json +382 -0
- package/dist/assets/AgentPanel-CQejLbiX.js +2 -0
- package/dist/assets/AgentPanel-DJEphah0.css +1 -0
- package/dist/assets/AgentQuestionDialog-BqkD0kXF.js +1 -0
- package/dist/assets/CodeStudio-Dv4ihHpz.js +23 -0
- package/dist/assets/CommentsPanel-B4yhhT1T.js +1 -0
- package/dist/assets/ComponentLibraryRail-Ch_dsAru.js +1 -0
- package/dist/assets/ComponentStage-BRhRotyH.js +1 -0
- package/dist/assets/CurvePane-BjFVabKX.css +1 -0
- package/dist/assets/CurvePane-R22WzllJ.js +1 -0
- package/dist/assets/ExportDialog-CWLQWTmG.js +2 -0
- package/dist/assets/FontPicker-DWKaYL7F.js +1 -0
- package/dist/assets/FontPicker-v0IOIArM.css +1 -0
- package/dist/assets/GenerateDrawer-Bh3SqDKt.js +2 -0
- package/dist/assets/InsertDrawer-Ce1fod1L.js +1 -0
- package/dist/assets/LibraryBrowser-D9sEGz5w.js +2 -0
- package/dist/assets/MakeItYoursSection-CMO4319_.js +2 -0
- package/dist/assets/MaterialGraphCanvas-BFzl4wWR.js +23 -0
- package/dist/assets/MaterialGraphCanvas-BIUD59ik.css +1 -0
- package/dist/assets/MaterialWorkspace-CqTWqaRA.js +14 -0
- package/dist/assets/PreferencesDialog-CBf9_OoD.js +1 -0
- package/dist/assets/PresetsSection-CUEaoCVU.js +1 -0
- package/dist/assets/SoundHistory-BeS8EW1h.js +1 -0
- package/dist/assets/SoundMode-BmcZru3i.js +3 -0
- package/dist/assets/StockSounds-DdAUGRYK.js +1 -0
- package/dist/assets/TemplateFieldSection-CaeXpLFo.js +1 -0
- package/dist/assets/VersionHistoryPanel-CxG6XhNP.js +1 -0
- package/dist/assets/board-B88EPuLO.css +1 -0
- package/dist/assets/board-CL-he7SL.js +3 -0
- package/dist/assets/components-0oOSPN0l.js +1 -0
- package/dist/assets/googleFontRanges-gp32wJMo.js +1 -0
- package/dist/assets/googleFonts-BNon6M7X.js +1 -0
- package/dist/assets/iconsAndAliases-DL7gXP5j.js +1863 -0
- package/dist/assets/index-4F5NQmI8.css +1 -0
- package/dist/assets/index-BGHQGV5z.js +117 -0
- package/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/dist/assets/libraryModel-CpIwfFha.js +1 -0
- package/dist/assets/lucide-6iK64tTB.js +9319 -0
- package/dist/assets/motionStamp-CH-Hew4E.js +1 -0
- package/dist/assets/takeMeasure-Tdg0TxEi.js +1 -0
- package/dist/assets/webmcp-BsB9gxWC.js +2 -0
- package/dist/index.html +34 -3
- package/package.json +12 -9
- package/src/addFamilies.ts +66 -0
- package/src/api.ts +1631 -55
- package/src/app/App.tsx +977 -198
- package/src/app/lazyPanel.tsx +137 -0
- package/src/app/lazySurfaces.tsx +203 -0
- package/src/audio/index.tsx +41 -10
- package/src/audio/meter.ts +485 -0
- package/src/audio/meterMath.ts +273 -0
- package/src/audio/meterStore.ts +155 -0
- package/src/audio/offlineMix.ts +289 -0
- package/src/audio/preview.ts +928 -118
- package/src/bridge.ts +15 -5
- package/src/bridgeTypes.ts +1611 -36
- package/src/channels.ts +17 -32
- package/src/fonts/googleFonts.ts +4 -0
- package/src/hostApi.ts +174 -0
- package/src/icons.tsx +770 -0
- package/src/layerWords.ts +61 -0
- package/src/main.tsx +56 -4
- package/src/panels/ResizeHandle.tsx +33 -43
- package/src/panels/agent/AgentDots.tsx +36 -0
- package/src/panels/agent/AgentPanel.tsx +119 -43
- package/src/panels/agent/AgentQuestionDialog.tsx +202 -0
- package/src/panels/agent/agent.css +113 -97
- package/src/panels/agent/agentGate.ts +35 -0
- package/src/panels/code/CodeDefaultsPanel.tsx +345 -0
- package/src/panels/code/CodeStudio.tsx +373 -0
- package/src/panels/code/FileTree.tsx +265 -0
- package/src/panels/code/codeBufferGuard.ts +52 -0
- package/src/panels/code/codeParts.tsx +321 -0
- package/src/panels/comments/CommentComposer.tsx +239 -0
- package/src/panels/comments/CommentThread.tsx +346 -0
- package/src/panels/comments/CommentsPanel.tsx +336 -0
- package/src/panels/comments/InboxButton.tsx +131 -0
- package/src/panels/comments/MentionMenu.tsx +98 -0
- package/src/panels/comments/parts.tsx +171 -0
- package/src/panels/export/ExportDialog.tsx +919 -96
- package/src/panels/export/ExportScrim.tsx +57 -0
- package/src/panels/files/AssetsTab.tsx +656 -0
- package/src/panels/files/FilePanel.tsx +1466 -216
- package/src/panels/files/LayerMarks.tsx +40 -0
- package/src/panels/files/LeftColumn.tsx +37 -0
- package/src/panels/files/McpSection.tsx +690 -47
- package/src/panels/files/MissingAssetsNotice.tsx +60 -0
- package/src/panels/files/assetDrag.ts +79 -0
- package/src/panels/files/resourceParts.tsx +255 -0
- package/src/panels/fonts/FontPicker.tsx +762 -0
- package/src/panels/fonts/faceCache.ts +207 -0
- package/src/panels/fonts/fontPicker.css +271 -0
- package/src/panels/fonts/pickerModel.ts +265 -0
- package/src/panels/fonts/pickerRequest.ts +46 -0
- package/src/panels/header/AccountAvatar.tsx +76 -0
- package/src/panels/header/AgentChip.tsx +77 -0
- package/src/panels/header/AppMenu.tsx +213 -0
- package/src/panels/header/Header.tsx +564 -84
- package/src/panels/header/HistoryControls.tsx +31 -5
- package/src/panels/header/PresenceStack.tsx +329 -0
- package/src/panels/header/ProjectTitle.tsx +232 -0
- package/src/panels/header/ShareButton.tsx +59 -0
- package/src/panels/header/StatusBadges.tsx +358 -27
- package/src/panels/header/SyncDeletedDialog.tsx +88 -0
- package/src/panels/header/ZoomControls.tsx +46 -15
- package/src/panels/header/agentHandoff.ts +52 -0
- package/src/panels/header/leave.ts +26 -0
- package/src/panels/help/HelpSection.tsx +189 -163
- package/src/panels/insert/InsertDrawer.tsx +723 -0
- package/src/panels/insert/iconSet.ts +109 -0
- package/src/panels/inspector/ExposeDot.tsx +117 -0
- package/src/panels/inspector/FramingRows.tsx +314 -44
- package/src/panels/inspector/InspectorFooter.tsx +91 -0
- package/src/panels/inspector/InspectorHead.tsx +425 -0
- package/src/panels/inspector/InspectorPanel.tsx +408 -124
- package/src/panels/inspector/KeyframeDot.tsx +373 -0
- package/src/panels/inspector/KeyframeSection.tsx +257 -0
- package/src/panels/inspector/MarkerSection.tsx +55 -11
- package/src/panels/inspector/ParamRow.tsx +84 -53
- package/src/panels/inspector/RightColumn.tsx +152 -0
- package/src/panels/inspector/TimeSection.tsx +866 -0
- package/src/panels/inspector/animate/EaseControl.tsx +244 -0
- package/src/panels/inspector/animate/PresetsSection.tsx +135 -0
- package/src/panels/inspector/animate/ShotSection.tsx +562 -48
- package/src/panels/inspector/design/AlignRow.tsx +95 -31
- package/src/panels/inspector/design/AppearanceSection.tsx +118 -9
- package/src/panels/inspector/design/ClonerSection.tsx +323 -62
- package/src/panels/inspector/design/ComponentSection.tsx +50 -43
- package/src/panels/inspector/design/CornerSection.tsx +45 -14
- package/src/panels/inspector/design/DefinitionProps.tsx +315 -0
- package/src/panels/inspector/design/DefinitionSection.tsx +136 -0
- package/src/panels/inspector/design/DeviceSection.tsx +210 -0
- package/src/panels/inspector/design/FillSection.tsx +83 -51
- package/src/panels/inspector/design/FontField.tsx +196 -0
- package/src/panels/inspector/design/HtmlSection.tsx +23 -12
- package/src/panels/inspector/design/InstanceSection.tsx +373 -154
- package/src/panels/inspector/design/MaskSection.tsx +84 -0
- package/src/panels/inspector/design/MaterialSection.tsx +177 -0
- package/src/panels/inspector/design/MediaSection.tsx +273 -120
- package/src/panels/inspector/design/MeshSection.tsx +910 -82
- package/src/panels/inspector/design/PaintRows.tsx +550 -0
- package/src/panels/inspector/design/PlateStyleSection.tsx +177 -0
- package/src/panels/inspector/design/ShadowSection.tsx +221 -48
- package/src/panels/inspector/design/SplineFollowSection.tsx +235 -0
- package/src/panels/inspector/design/StrokeSection.tsx +228 -43
- package/src/panels/inspector/design/TemplateFieldSection.tsx +57 -0
- package/src/panels/inspector/design/TransformSection.tsx +244 -50
- package/src/panels/inspector/design/TypographySection.tsx +679 -110
- package/src/panels/inspector/layout.ts +295 -0
- package/src/panels/inspector/propValue.ts +199 -0
- package/src/panels/inspector/scene/CameraSection.tsx +232 -22
- package/src/panels/inspector/scene/EffectsSection.tsx +722 -71
- package/src/panels/inspector/scene/MakeItYoursSection.tsx +180 -0
- package/src/panels/inspector/scene/SceneDocSection.tsx +191 -0
- package/src/panels/inspector/scene/SceneSection.tsx +219 -44
- package/src/panels/inspector/scene/TransitionSection.tsx +523 -72
- package/src/panels/inspector/storyboard/StoryboardReferenceSection.tsx +593 -0
- package/src/panels/inspector/storyboard/VideoStill.tsx +120 -0
- package/src/panels/inspector/timeModel.ts +190 -0
- package/src/panels/inspector/useDocWrite.ts +438 -146
- package/src/panels/library/LibraryBrowser.tsx +435 -0
- package/src/panels/library/LibraryGrid.tsx +190 -0
- package/src/panels/library/LibraryPreviewPanel.tsx +349 -0
- package/src/panels/library/LibraryRail.tsx +84 -0
- package/src/panels/library/LibrarySoundPanel.tsx +293 -0
- package/src/panels/library/SoundHistory.tsx +245 -0
- package/src/panels/library/StockSounds.tsx +390 -0
- package/src/panels/library/libraryModel.ts +147 -0
- package/src/panels/library/stockSearch.ts +77 -0
- package/src/panels/maskMarks.tsx +117 -0
- package/src/panels/material/MaterialCommentNode.tsx +54 -0
- package/src/panels/material/MaterialGraphCanvas.tsx +861 -0
- package/src/panels/material/MaterialGraphMenu.tsx +45 -0
- package/src/panels/material/MaterialGroupNode.tsx +56 -0
- package/src/panels/material/MaterialLibrary.tsx +253 -0
- package/src/panels/material/MaterialNode.tsx +453 -0
- package/src/panels/material/MaterialNodePalette.tsx +349 -0
- package/src/panels/material/MaterialParameterControls.tsx +525 -0
- package/src/panels/material/MaterialRerouteNode.tsx +42 -0
- package/src/panels/material/MaterialWorkspace.tsx +501 -0
- package/src/panels/material/NodePreview.tsx +70 -0
- package/src/panels/material/flowModel.ts +441 -0
- package/src/panels/material/parameterValues.ts +231 -0
- package/src/panels/material/previewScheduler.ts +235 -0
- package/src/panels/material/reactFlow.css +6 -0
- package/src/panels/material/socketGlyph.ts +85 -0
- package/src/panels/material/viewportMemory.ts +58 -0
- package/src/panels/preferences/PreferencesDialog.tsx +215 -0
- package/src/panels/share/ShareComponentDialog.tsx +247 -0
- package/src/panels/stage/StageMenu.tsx +35 -5
- package/src/panels/stage/StagePanel.tsx +338 -23
- package/src/panels/stage/cellSlots.tsx +70 -0
- package/src/panels/storyboard/BoardCanvas.tsx +739 -0
- package/src/panels/storyboard/BoardCursors.tsx +158 -0
- package/src/panels/storyboard/BoardDropDialog.tsx +161 -0
- package/src/panels/storyboard/BoardPlan.tsx +42 -0
- package/src/panels/storyboard/ComponentSheet.tsx +187 -0
- package/src/panels/storyboard/ComponentStage.tsx +235 -0
- package/src/panels/storyboard/MiniSketch.tsx +173 -0
- package/src/panels/storyboard/PartPicker.tsx +64 -0
- package/src/panels/storyboard/PlanPanel.tsx +617 -0
- package/src/panels/storyboard/ReferencePreview.tsx +217 -0
- package/src/panels/storyboard/ShotBrief.tsx +360 -0
- package/src/panels/storyboard/ShotCard.tsx +251 -0
- package/src/panels/storyboard/StagePreview.tsx +348 -0
- package/src/panels/storyboard/StillThumbs.tsx +162 -0
- package/src/panels/storyboard/StoryboardView.tsx +79 -0
- package/src/panels/storyboard/board.css +1065 -0
- package/src/panels/storyboard/board.ts +12 -0
- package/src/panels/storyboard/boardClipboard.ts +111 -0
- package/src/panels/storyboard/boardOrder.ts +68 -0
- package/src/panels/storyboard/boardRows.tsx +425 -0
- package/src/panels/storyboard/liveCap.ts +90 -0
- package/src/panels/storyboard/plan.css +295 -0
- package/src/panels/storyboard/posterCache.ts +133 -0
- package/src/panels/storyboard/shotboard/ComponentPicker.tsx +373 -0
- package/src/panels/storyboard/shotboard/ImagePicker.tsx +145 -0
- package/src/panels/storyboard/shotboard/ShotBoard.tsx +292 -0
- package/src/panels/storyboard/shotboard/SketchCanvas.tsx +443 -0
- package/src/panels/storyboard/shotboard/SketchInspector.tsx +471 -0
- package/src/panels/storyboard/shotboard/SketchItem.tsx +240 -0
- package/src/panels/storyboard/shotboard/SketchLayers.tsx +130 -0
- package/src/panels/storyboard/shotboard/SketchPlay.tsx +108 -0
- package/src/panels/storyboard/shotboard/SketchStrip.tsx +178 -0
- package/src/panels/storyboard/shotboard/SketchToolFooter.tsx +99 -0
- package/src/panels/storyboard/shotboard/sketch.css +1392 -0
- package/src/panels/storyboard/shotboard/sketchModel.ts +219 -0
- package/src/panels/storyboard/storyboardDrop.ts +164 -0
- package/src/panels/studio/ComponentLibraryRail.tsx +346 -0
- package/src/panels/studio/ComponentSidePanel.tsx +355 -0
- package/src/panels/studio/ComponentStudio.tsx +243 -0
- package/src/panels/studio/ComponentsHome.tsx +318 -0
- package/src/panels/studio/ComponentsPreview.tsx +179 -0
- package/src/panels/studio/EveryUse.tsx +244 -0
- package/src/panels/studio/ThisUsePanel.tsx +679 -0
- package/src/panels/studio/componentParts.tsx +341 -0
- package/src/panels/studio/components.ts +6 -0
- package/src/panels/studio/newCodeComponent.ts +66 -0
- package/src/panels/timeline/AudioFxButton.tsx +290 -0
- package/src/panels/timeline/AudioLane.tsx +674 -85
- package/src/panels/timeline/BlockRow.tsx +154 -85
- package/src/panels/timeline/ChannelRow.tsx +378 -129
- package/src/panels/timeline/CurvePane.tsx +508 -0
- package/src/panels/timeline/Filmstrip.tsx +361 -0
- package/src/panels/timeline/GenerateDrawer.tsx +407 -0
- package/src/panels/timeline/KeyStretch.tsx +115 -0
- package/src/panels/timeline/LifespanRow.tsx +730 -47
- package/src/panels/timeline/MarkerLane.tsx +46 -8
- package/src/panels/timeline/PlayButton.tsx +91 -0
- package/src/panels/timeline/PropertyPicker.tsx +254 -0
- package/src/panels/timeline/ShotRow.tsx +400 -0
- package/src/panels/timeline/SoundMode.tsx +1406 -0
- package/src/panels/timeline/TimelineDock.tsx +2338 -272
- package/src/panels/timeline/TransportVolume.tsx +163 -0
- package/src/panels/timeline/beatRuler.ts +74 -0
- package/src/panels/timeline/curveModel.ts +500 -0
- package/src/panels/timeline/duckUnderVoice.ts +68 -0
- package/src/panels/timeline/keyframeMenu.ts +138 -0
- package/src/panels/timeline/labelWidth.ts +49 -0
- package/src/panels/timeline/marquee.ts +155 -0
- package/src/panels/timeline/model.ts +1860 -199
- package/src/panels/timeline/snap.ts +125 -43
- package/src/panels/timeline/staggerPreview.tsx +92 -0
- package/src/panels/timeline/takeMeasure.ts +72 -0
- package/src/panels/timeline/useLive.ts +48 -0
- package/src/panels/timeline/viewport.ts +234 -0
- package/src/panels/toolbar/BackChip.tsx +116 -0
- package/src/panels/toolbar/CameraViewBar.tsx +118 -49
- package/src/panels/toolbar/CanvasBar.tsx +117 -0
- package/src/panels/toolbar/InsertButton.tsx +38 -0
- package/src/panels/toolbar/IsolationTrail.tsx +156 -0
- package/src/panels/toolbar/PanelToggles.tsx +86 -0
- package/src/panels/toolbar/ShadingBar.tsx +119 -26
- package/src/panels/toolbar/ToolDockBar.tsx +329 -80
- package/src/panels/toolbar/ToolFooter.tsx +102 -0
- package/src/panels/toolbar/ViewModeBar.tsx +247 -38
- package/src/panels/toolbar/WorkPlaneBar.tsx +94 -0
- package/src/panels/toolbar/useAddRows.tsx +326 -0
- package/src/panels/toolbar/useDismiss.ts +122 -0
- package/src/panels/versions/VersionHistoryPanel.tsx +274 -0
- package/src/panels/versions/versionModel.ts +103 -0
- package/src/panels/virtualWindow.ts +271 -0
- package/src/shotTokens.css +151 -60
- package/src/stage/FrustumGizmo.tsx +504 -84
- package/src/stage/GridPlane.tsx +225 -16
- package/src/stage/NodePreviews.tsx +628 -0
- package/src/stage/PerfHud.tsx +437 -47
- package/src/stage/SplitRenderer.tsx +816 -169
- package/src/stage/StageApp.tsx +4274 -486
- package/src/stage/StageRootBoundary.tsx +114 -0
- package/src/stage/StageTools.tsx +651 -151
- package/src/stage/TransformGizmo.tsx +696 -0
- package/src/stage/adaptivePresentation.ts +145 -0
- package/src/stage/bridgeClient.ts +2522 -41
- package/src/stage/exportWorker.ts +9 -0
- package/src/stage/feed.ts +305 -14
- package/src/stage/fontPreview.ts +176 -0
- package/src/stage/frustumGeometry.ts +319 -1
- package/src/stage/furnitureTokens.ts +33 -0
- package/src/stage/gizmoMath.ts +508 -16
- package/src/stage/gpuTimer.ts +276 -0
- package/src/stage/instanceModules.ts +194 -33
- package/src/stage/interactions/move.ts +24 -5
- package/src/stage/isolation.ts +291 -33
- package/src/stage/jsSelfProfiler.ts +325 -0
- package/src/stage/main.tsx +116 -16
- package/src/stage/modalState.ts +222 -23
- package/src/stage/moduleEpoch.ts +60 -0
- package/src/stage/motionPathStage.ts +279 -0
- package/src/stage/motionPreview.ts +74 -0
- package/src/stage/overlays/Overlays.tsx +1112 -159
- package/src/stage/overlays/Rulers.tsx +495 -0
- package/src/stage/panes.ts +159 -10
- package/src/stage/project.ts +68 -3
- package/src/stage/renderScaleController.ts +730 -0
- package/src/stage/rulers.ts +129 -0
- package/src/stage/sceneExtent.ts +75 -0
- package/src/stage/shading.ts +20 -0
- package/src/stage/splineStage.ts +632 -0
- package/src/stage/stagePerformanceTelemetry.ts +717 -0
- package/src/stage/stageTheme.ts +36 -0
- package/src/stage/stageWarmup.ts +11 -0
- package/src/stage/textAutoSize.ts +124 -0
- package/src/stage/tier1Export.ts +641 -0
- package/src/stage/tokenColor.ts +14 -9
- package/src/stage/view.ts +56 -9
- package/src/stage/workingView.ts +277 -33
- package/src/state/accountPrefs.ts +261 -0
- package/src/state/agentActions.ts +173 -15
- package/src/state/agentActivity.ts +198 -0
- package/src/state/agentQuestion.ts +239 -0
- package/src/state/bootOpen.ts +42 -0
- package/src/state/cameraVerbs.ts +139 -0
- package/src/state/clientId.ts +6 -1
- package/src/state/comments.ts +1099 -0
- package/src/state/context.tsx +19 -1
- package/src/state/editorPrefs.ts +220 -0
- package/src/state/errorReports.ts +121 -0
- package/src/state/fieldWrite.ts +161 -0
- package/src/state/fontRecents.ts +70 -0
- package/src/state/fonts.ts +656 -0
- package/src/state/graphClipboard.ts +414 -0
- package/src/state/imageThumbs.ts +216 -0
- package/src/state/insert.ts +444 -67
- package/src/state/isolationTrail.ts +92 -0
- package/src/state/keyMoves.ts +134 -0
- package/src/state/keyframeClipboard.ts +177 -0
- package/src/state/keymap.ts +1153 -117
- package/src/state/layoutPrefs.ts +272 -0
- package/src/state/layouts.ts +205 -0
- package/src/state/measureMedia.ts +167 -0
- package/src/state/menu.ts +2341 -28
- package/src/state/motionStamp.ts +7 -0
- package/src/state/panelSize.ts +375 -5
- package/src/state/pendingChanges.ts +119 -0
- package/src/state/playHold.ts +78 -0
- package/src/state/playbackPreparation.ts +562 -0
- package/src/state/posterCapture.ts +242 -0
- package/src/state/presence.ts +254 -0
- package/src/state/projectComponents.ts +160 -0
- package/src/state/renderEvents.ts +298 -18
- package/src/state/runtimeErrors.ts +46 -0
- package/src/state/selection.ts +12 -0
- package/src/state/shortcuts.ts +1606 -0
- package/src/state/sketchGuide.ts +51 -0
- package/src/state/splineRefs.ts +87 -0
- package/src/state/stageHealth.ts +259 -0
- package/src/state/stageWatchdog.ts +47 -0
- package/src/state/stillStage.ts +330 -0
- package/src/state/stillThumbs.ts +474 -0
- package/src/state/store.ts +23591 -2982
- package/src/state/storyboardModel.ts +528 -0
- package/src/state/sync.ts +1257 -0
- package/src/state/syncCache.ts +159 -0
- package/src/state/triangles.ts +13 -0
- package/src/state/types.ts +1698 -55
- package/src/state/unsavedGuard.ts +49 -0
- package/src/state/viewMode.ts +200 -0
- package/src/state/webmcp.ts +1128 -0
- package/src/state/workspace.ts +1147 -0
- package/src/styles/assets-tab.css +304 -0
- package/src/styles/canvas.css +545 -0
- package/src/styles/comments.css +1435 -0
- package/src/styles/controls.css +162 -0
- package/src/styles/dialogs.css +390 -0
- package/src/styles/insert-drawer.css +586 -0
- package/src/styles/inspector-board.css +207 -0
- package/src/styles/inspector-object.css +486 -0
- package/src/styles/inspector-shot.css +252 -0
- package/src/styles/inspector.css +360 -0
- package/src/styles/left-column.css +1628 -0
- package/src/styles/library-preview.css +575 -0
- package/src/styles/library.css +512 -0
- package/src/styles/modes/code.css +569 -0
- package/src/styles/modes/component.css +1561 -0
- package/src/styles/modes/material.css +1201 -0
- package/src/styles/modes/storyboard.css +518 -0
- package/src/styles/share-component.css +49 -0
- package/src/styles/shell.css +653 -0
- package/src/styles/shot-brief.css +360 -0
- package/src/styles/states.css +76 -0
- package/src/styles/sync.css +380 -0
- package/src/styles/timeline-graph.css +384 -0
- package/src/styles/timeline-meters.css +89 -0
- package/src/styles/timeline-rows.css +1959 -0
- package/src/styles/timeline-sound.css +1003 -0
- package/src/styles/timeline.css +754 -0
- package/src/styles/tool-footer.css +525 -0
- package/src/styles/topbar.css +886 -0
- package/src/styles/versions.css +262 -0
- package/src/styles.css +72 -2921
- package/src/tools.ts +108 -23
- package/dist/assets/index-B7notge7.css +0 -1
- package/dist/assets/index-BBE6Ta9L.js +0 -52
- package/src/panels/files/icons.tsx +0 -118
- package/src/panels/header/DocSwitcher.tsx +0 -85
- package/src/panels/header/TransportBar.tsx +0 -86
- package/src/panels/inspector/animate/BlockTiming.tsx +0 -341
- package/src/panels/inspector/animate/DuringBlocks.tsx +0 -85
- package/src/panels/inspector/animate/InOutPresets.tsx +0 -57
- package/src/panels/inspector/animate/ResolvedChannels.tsx +0 -150
- package/src/panels/inspector/animate/useBlocks.ts +0 -43
- package/src/panels/material/MaterialPicker.tsx +0 -172
- package/src/panels/timeline/SceneStrip.tsx +0 -145
- package/src/panels/toolbar/AddMenu.tsx +0 -230
- package/src/stage/MoveGizmo.tsx +0 -411
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
// THE COMPONENTS RIGHT PANEL'S OWN PARTS (C1, C2): the three ways out (Add to
|
|
2
|
+
// storyboard, Share, Use in a shot) and where the component lists (Made for
|
|
3
|
+
// this project, In your library) over the props, History in place of the
|
|
4
|
+
// props while it is open, and the footer that says whose it is and which
|
|
5
|
+
// version is live. InspectorPanel puts them around its definition and code
|
|
6
|
+
// branches while Components is the workspace; InspectorFooter shows the footer.
|
|
7
|
+
import { useEffect, useRef, useState } from "react";
|
|
8
|
+
import { Avatar } from "@dailies-studio/ui";
|
|
9
|
+
import { componentFilesOf, instanceModuleOf } from "@dailies-studio/shot/doc";
|
|
10
|
+
import type { Doc3D } from "@dailies-studio/shot/doc";
|
|
11
|
+
import type { WorkspaceComponentVersion, WorkspaceComponentVersionInfo } from "../../api.js";
|
|
12
|
+
import { useShotEditor, useStore } from "../../state/context.js";
|
|
13
|
+
import { shotNumberOf } from "../../state/storyboardModel.js";
|
|
14
|
+
import type { ComponentsSelection } from "../../state/types.js";
|
|
15
|
+
import { componentsSubject, libraryRuleOf, readOnlyReason } from "../../state/workspace.js";
|
|
16
|
+
import { shortAgo } from "../comments/parts.js";
|
|
17
|
+
import { useSignedIn } from "../header/AccountAvatar.js";
|
|
18
|
+
import { ShareComponentDialog } from "../share/ShareComponentDialog.js";
|
|
19
|
+
import { useSubjectRow, useSubjectVersions } from "./componentParts.js";
|
|
20
|
+
|
|
21
|
+
/** One option per Shot, numbered as the board numbers it. */
|
|
22
|
+
function shotOptions(doc: Doc3D): { id: string; label: string }[] {
|
|
23
|
+
return doc.scenes.flatMap((id, i) => {
|
|
24
|
+
const scene = doc.scenesById[id];
|
|
25
|
+
if (scene === undefined) return [];
|
|
26
|
+
const number = shotNumberOf(doc, id) ?? String(i + 1).padStart(2, "0");
|
|
27
|
+
return [{ id, label: `${number} ${scene.name}` }];
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The actions block: Add to storyboard (as Style; the board pins it to a
|
|
32
|
+
* Shot from there), Share, Use in a shot, and where it lists. */
|
|
33
|
+
export function ComponentActions() {
|
|
34
|
+
const store = useStore();
|
|
35
|
+
const doc = useShotEditor((s) => s.doc);
|
|
36
|
+
const readOnly = useShotEditor((s) => s.readOnly);
|
|
37
|
+
const codeComponent = useShotEditor((s) => s.codeComponent);
|
|
38
|
+
const editingDef = useShotEditor((s) => s.editingDef);
|
|
39
|
+
const [added, setAdded] = useState<string | null>(null);
|
|
40
|
+
if (doc === null) return null;
|
|
41
|
+
const subject = componentsSubject({ codeComponent, editingDef });
|
|
42
|
+
const shots = shotOptions(doc);
|
|
43
|
+
const add = async (): Promise<void> => {
|
|
44
|
+
if (await store.addComponentToStoryboard(null)) setAdded("On the board as Style.");
|
|
45
|
+
};
|
|
46
|
+
return (
|
|
47
|
+
<div className="components-actions" data-testid="components-actions">
|
|
48
|
+
<ShareComponentDialog />
|
|
49
|
+
<div className="components-actions-row">
|
|
50
|
+
<button
|
|
51
|
+
type="button"
|
|
52
|
+
className="components-add-board"
|
|
53
|
+
data-testid="components-add-board"
|
|
54
|
+
disabled={readOnly}
|
|
55
|
+
title="Put this component on the Storyboard"
|
|
56
|
+
onClick={() => void add()}
|
|
57
|
+
>
|
|
58
|
+
Add to storyboard
|
|
59
|
+
</button>
|
|
60
|
+
<button
|
|
61
|
+
type="button"
|
|
62
|
+
className="components-share-button"
|
|
63
|
+
data-testid="components-share"
|
|
64
|
+
title="Share this component"
|
|
65
|
+
onClick={() => store.openShareComponent()}
|
|
66
|
+
>
|
|
67
|
+
Share
|
|
68
|
+
</button>
|
|
69
|
+
</div>
|
|
70
|
+
<label className="components-field">
|
|
71
|
+
<span className="components-field-label">Use in a shot</span>
|
|
72
|
+
<select
|
|
73
|
+
className="select"
|
|
74
|
+
data-testid="components-use-in-shot"
|
|
75
|
+
value=""
|
|
76
|
+
disabled={readOnly || shots.length === 0}
|
|
77
|
+
onChange={(e) => {
|
|
78
|
+
const id = e.currentTarget.value;
|
|
79
|
+
if (id !== "") void store.placeComponentInShot(id);
|
|
80
|
+
}}
|
|
81
|
+
>
|
|
82
|
+
<option value="">{shots.length === 0 ? "No shots yet" : "Pick a shot"}</option>
|
|
83
|
+
{shots.map((s) => (
|
|
84
|
+
<option key={s.id} value={s.id}>
|
|
85
|
+
{s.label}
|
|
86
|
+
</option>
|
|
87
|
+
))}
|
|
88
|
+
</select>
|
|
89
|
+
</label>
|
|
90
|
+
{subject === null ? null : <ComponentMembership doc={doc} sel={subject} />}
|
|
91
|
+
{added === null ? null : (
|
|
92
|
+
<p className="components-actions-note" role="status" data-testid="components-added">
|
|
93
|
+
{added}
|
|
94
|
+
</p>
|
|
95
|
+
)}
|
|
96
|
+
</div>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Every Project in this workspace lists a component the library holds. */
|
|
101
|
+
const IN_LIBRARY_NOTE = "Every Project in this workspace lists it under Your library.";
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Where the component lists: "Made for this project" (code only: its file is
|
|
105
|
+
* in `componentFiles`, so it lists under This project with no shot using it)
|
|
106
|
+
* and "In your library" (its workspace row's `library` flag). Two rows and
|
|
107
|
+
* ONE note: the reason the library box is ticked by rule, else what a tick
|
|
108
|
+
* means. A box that cannot change is `aria-disabled`, still pressable, and a
|
|
109
|
+
* press says why in the error line: a viewer's refusal, the rule, or that the
|
|
110
|
+
* lists are still loading.
|
|
111
|
+
*/
|
|
112
|
+
function ComponentMembership(props: { doc: Doc3D; sel: ComponentsSelection }) {
|
|
113
|
+
const store = useStore();
|
|
114
|
+
const { doc, sel } = props;
|
|
115
|
+
const viewer = useShotEditor((s) => (s.readOnly ? readOnlyReason(s) : null));
|
|
116
|
+
const library = useShotEditor((s) => s.library);
|
|
117
|
+
const workspace = useShotEditor((s) => s.workspaceComponents);
|
|
118
|
+
const homes = useShotEditor((s) => s.componentHomes);
|
|
119
|
+
const row = useSubjectRow();
|
|
120
|
+
// A definition's Edit tab shows its layers where the rail would be, so
|
|
121
|
+
// nothing else may have read the lists yet.
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
const s = store.getState();
|
|
124
|
+
if (s.workspaceComponents === null || s.componentHomes === null) void store.refreshWorkspaceComponents();
|
|
125
|
+
}, [store]);
|
|
126
|
+
const rel = sel.kind === "code" ? instanceModuleOf(sel.module) : null;
|
|
127
|
+
const made = rel !== null && componentFilesOf(doc).includes(rel);
|
|
128
|
+
const reading = workspace === null || homes === null;
|
|
129
|
+
const rule = workspace === null || homes === null ? null : libraryRuleOf(doc, sel, library, workspace, homes);
|
|
130
|
+
const inLibrary = !reading && (rule !== null || row?.library === true);
|
|
131
|
+
const libraryRefusal = viewer ?? (reading ? "Your library is still loading." : rule);
|
|
132
|
+
const note = reading
|
|
133
|
+
? "Reading…"
|
|
134
|
+
: (rule ?? (inLibrary ? IN_LIBRARY_NOTE : rel !== null ? "Listed here even when no shot uses it." : null));
|
|
135
|
+
return (
|
|
136
|
+
<div className="components-membership" data-testid="components-membership">
|
|
137
|
+
{rel === null ? null : (
|
|
138
|
+
<label className="components-membership-row">
|
|
139
|
+
<input
|
|
140
|
+
type="checkbox"
|
|
141
|
+
className="checkbox"
|
|
142
|
+
data-testid="components-made-here"
|
|
143
|
+
checked={made}
|
|
144
|
+
aria-disabled={viewer === null ? undefined : true}
|
|
145
|
+
onChange={() => {
|
|
146
|
+
if (viewer !== null) {
|
|
147
|
+
store.reportError(viewer);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const out = store.setComponentFile(rel, !made);
|
|
151
|
+
if (!out.ok) store.reportError(out.reason);
|
|
152
|
+
}}
|
|
153
|
+
/>
|
|
154
|
+
<span>Made for this project</span>
|
|
155
|
+
</label>
|
|
156
|
+
)}
|
|
157
|
+
<label className="components-membership-row">
|
|
158
|
+
<input
|
|
159
|
+
type="checkbox"
|
|
160
|
+
className="checkbox"
|
|
161
|
+
data-testid="components-in-library"
|
|
162
|
+
checked={inLibrary}
|
|
163
|
+
aria-disabled={libraryRefusal === null ? undefined : true}
|
|
164
|
+
onChange={() => {
|
|
165
|
+
if (libraryRefusal !== null) {
|
|
166
|
+
store.reportError(libraryRefusal);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
void store.setComponentInLibrary(sel, !inLibrary);
|
|
170
|
+
}}
|
|
171
|
+
/>
|
|
172
|
+
<span>In your library</span>
|
|
173
|
+
</label>
|
|
174
|
+
{note === null ? null : (
|
|
175
|
+
<p className="insp-note" data-testid="components-membership-note">
|
|
176
|
+
{note}
|
|
177
|
+
</p>
|
|
178
|
+
)}
|
|
179
|
+
</div>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** History, in the props' place: every recorded version, newest first, with
|
|
184
|
+
* a read-only view of one and Restore. */
|
|
185
|
+
export function ComponentHistory() {
|
|
186
|
+
const store = useStore();
|
|
187
|
+
const viewing = useShotEditor((s) => s.componentHistory?.viewing ?? null);
|
|
188
|
+
const readOnly = useShotEditor((s) => s.readOnly);
|
|
189
|
+
const row = useSubjectRow();
|
|
190
|
+
const versions = useSubjectVersions();
|
|
191
|
+
// History is one component's: picking another closes it.
|
|
192
|
+
const subjectKey = useShotEditor((s) => `${s.codeComponent ?? ""}|${s.editingDef ?? ""}`);
|
|
193
|
+
const openedOn = useRef(subjectKey);
|
|
194
|
+
useEffect(() => {
|
|
195
|
+
if (subjectKey !== openedOn.current) store.closeComponentHistory();
|
|
196
|
+
}, [store, subjectKey]);
|
|
197
|
+
return (
|
|
198
|
+
<section className="components-history" data-testid="components-history" aria-label="History">
|
|
199
|
+
<div className="components-history-head">
|
|
200
|
+
{viewing === null ? (
|
|
201
|
+
<h3 className="components-history-title">History</h3>
|
|
202
|
+
) : (
|
|
203
|
+
<button
|
|
204
|
+
type="button"
|
|
205
|
+
className="components-history-back"
|
|
206
|
+
data-testid="components-history-back"
|
|
207
|
+
onClick={() => void store.viewComponentVersion(null)}
|
|
208
|
+
>
|
|
209
|
+
All versions
|
|
210
|
+
</button>
|
|
211
|
+
)}
|
|
212
|
+
<button
|
|
213
|
+
type="button"
|
|
214
|
+
className="components-history-done"
|
|
215
|
+
data-testid="components-history-close"
|
|
216
|
+
onClick={() => store.closeComponentHistory()}
|
|
217
|
+
>
|
|
218
|
+
Done
|
|
219
|
+
</button>
|
|
220
|
+
</div>
|
|
221
|
+
{viewing !== null ? (
|
|
222
|
+
<VersionView version={viewing} current={row?.version} readOnly={readOnly} />
|
|
223
|
+
) : row === undefined ? (
|
|
224
|
+
<p className="insp-empty" data-testid="components-history-empty">
|
|
225
|
+
No versions yet. A version is recorded when the component builds or when you leave Edit.
|
|
226
|
+
</p>
|
|
227
|
+
) : versions === undefined ? (
|
|
228
|
+
<p className="insp-empty">Reading the versions…</p>
|
|
229
|
+
) : (
|
|
230
|
+
<ol className="components-history-list" data-testid="components-history-list">
|
|
231
|
+
{versions.map((v) => (
|
|
232
|
+
<VersionRow key={v.version} info={v} current={v.version === row.version} readOnly={readOnly} />
|
|
233
|
+
))}
|
|
234
|
+
</ol>
|
|
235
|
+
)}
|
|
236
|
+
</section>
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function VersionRow(props: { info: WorkspaceComponentVersionInfo; current: boolean; readOnly: boolean }) {
|
|
241
|
+
const store = useStore();
|
|
242
|
+
const v = props.info;
|
|
243
|
+
return (
|
|
244
|
+
<li className={`components-history-row${props.current ? " is-current" : ""}`} data-testid="components-history-row">
|
|
245
|
+
<span className="components-history-version">v{v.version}</span>
|
|
246
|
+
<span className="components-history-when" title={new Date(v.createdAt).toLocaleString()}>
|
|
247
|
+
{props.current ? "live" : shortAgo(v.createdAt)}
|
|
248
|
+
</span>
|
|
249
|
+
<button
|
|
250
|
+
type="button"
|
|
251
|
+
className="components-history-verb"
|
|
252
|
+
data-testid="components-history-view"
|
|
253
|
+
onClick={() => void store.viewComponentVersion(v.version)}
|
|
254
|
+
>
|
|
255
|
+
View
|
|
256
|
+
</button>
|
|
257
|
+
<button
|
|
258
|
+
type="button"
|
|
259
|
+
className="components-history-verb"
|
|
260
|
+
data-testid="components-history-restore"
|
|
261
|
+
disabled={props.current || props.readOnly}
|
|
262
|
+
title={props.current ? "This is the live version" : `Make v${v.version} the live version`}
|
|
263
|
+
onClick={() => void store.restoreComponentVersion(v.version)}
|
|
264
|
+
>
|
|
265
|
+
Restore
|
|
266
|
+
</button>
|
|
267
|
+
</li>
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/** One version, read-only: a code version's files, a Studio version's props. */
|
|
272
|
+
function VersionView(props: { version: WorkspaceComponentVersion; current: number | undefined; readOnly: boolean }) {
|
|
273
|
+
const store = useStore();
|
|
274
|
+
const v = props.version;
|
|
275
|
+
const payload = v.payload;
|
|
276
|
+
return (
|
|
277
|
+
<div className="components-history-view" data-testid="components-history-viewing">
|
|
278
|
+
<div className="components-history-row is-head">
|
|
279
|
+
<span className="components-history-version">v{v.version}</span>
|
|
280
|
+
<span className="components-history-when">{v.name}</span>
|
|
281
|
+
<button
|
|
282
|
+
type="button"
|
|
283
|
+
className="components-history-verb"
|
|
284
|
+
data-testid="components-history-restore"
|
|
285
|
+
disabled={v.version === props.current || props.readOnly}
|
|
286
|
+
onClick={() => void store.restoreComponentVersion(v.version)}
|
|
287
|
+
>
|
|
288
|
+
Restore
|
|
289
|
+
</button>
|
|
290
|
+
</div>
|
|
291
|
+
{payload.kind === "code" ? (
|
|
292
|
+
payload.files.map((f) => (
|
|
293
|
+
<figure key={f.path} className="components-history-file">
|
|
294
|
+
<figcaption className="components-history-path">{f.path}</figcaption>
|
|
295
|
+
<pre className="components-history-code" data-testid="components-history-code">
|
|
296
|
+
{f.content}
|
|
297
|
+
</pre>
|
|
298
|
+
</figure>
|
|
299
|
+
))
|
|
300
|
+
) : (
|
|
301
|
+
<p className="components-history-props" data-testid="components-history-props">
|
|
302
|
+
{v.propKeys.length === 0 ? "No props." : `Props: ${v.propKeys.join(", ")}`}
|
|
303
|
+
</p>
|
|
304
|
+
)}
|
|
305
|
+
</div>
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** "Made by you · private · vN": who made it, who can see it, and which
|
|
310
|
+
* version is live. A code component adds "saved" once nothing is unsaved. */
|
|
311
|
+
export function ComponentFooter() {
|
|
312
|
+
const store = useStore();
|
|
313
|
+
const codeComponent = useShotEditor((s) => s.codeComponent);
|
|
314
|
+
const editingDef = useShotEditor((s) => s.editingDef);
|
|
315
|
+
const subject = componentsSubject({ codeComponent, editingDef });
|
|
316
|
+
const dirty = useShotEditor((s) => Object.values(s.codeFiles).some((f) => f.content !== f.savedContent));
|
|
317
|
+
const readOnly = useShotEditor((s) => s.readOnly);
|
|
318
|
+
const row = useSubjectRow();
|
|
319
|
+
const me = useSignedIn();
|
|
320
|
+
if (subject === null) return null;
|
|
321
|
+
const meta = ["private", ...(row === undefined ? [] : [`v${row.version}`]), ...(subject.kind === "code" && !dirty ? ["saved"] : [])];
|
|
322
|
+
return (
|
|
323
|
+
<footer className="inspector-footer components-footer" data-testid="components-footer">
|
|
324
|
+
<Avatar url={null} label={me?.name ?? ""} className="components-footer-avatar" />
|
|
325
|
+
<span className="components-footer-who">Made by you</span>
|
|
326
|
+
<span className="components-footer-meta" data-testid="components-footer-meta">
|
|
327
|
+
{meta.join(" · ")}
|
|
328
|
+
</span>
|
|
329
|
+
{/* A code component has Remix and History in its status footer. */}
|
|
330
|
+
{subject.kind === "studio" ? (
|
|
331
|
+
<span className="components-footer-verbs">
|
|
332
|
+
<button
|
|
333
|
+
type="button"
|
|
334
|
+
className="code-status-verb"
|
|
335
|
+
data-testid="studio-remix"
|
|
336
|
+
disabled={readOnly}
|
|
337
|
+
title="Make your own copy of this component. The original stays as it was."
|
|
338
|
+
onClick={() => void store.remixComponent()}
|
|
339
|
+
>
|
|
340
|
+
Remix
|
|
341
|
+
</button>
|
|
342
|
+
<button
|
|
343
|
+
type="button"
|
|
344
|
+
className="code-status-verb"
|
|
345
|
+
data-testid="studio-history"
|
|
346
|
+
title="Every recorded version of this component"
|
|
347
|
+
onClick={() => void store.openComponentHistory()}
|
|
348
|
+
>
|
|
349
|
+
History
|
|
350
|
+
</button>
|
|
351
|
+
</span>
|
|
352
|
+
) : null}
|
|
353
|
+
</footer>
|
|
354
|
+
);
|
|
355
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
// THE COMPONENT STUDIO: Components' Edit tab for a Studio-made definition
|
|
2
|
+
// (mock C6), the definition standalone in the docked frame.
|
|
3
|
+
//
|
|
4
|
+
// A definition is not a property of the shot it happens to sit in. It is a
|
|
5
|
+
// module: it has its own canvas, its own clock, its own named actions and its
|
|
6
|
+
// own published surface, and every one of those is a thing an author works on
|
|
7
|
+
// for a while rather than glances at. So it gets the canvas, and the frame
|
|
8
|
+
// around the canvas says what it is:
|
|
9
|
+
//
|
|
10
|
+
// the LEFT COLUMN how it is built: the Files panel's LAYERS outline,
|
|
11
|
+
// scoped to the definition, under "All components"
|
|
12
|
+
// (FilePanel.tsx; `.is-component-edit` in component.css
|
|
13
|
+
// keeps it in place of the mode slot)
|
|
14
|
+
// the CANVAS it, standalone, through a HOLE onto the stage, with the
|
|
15
|
+
// tool footer under it (`toolFooterShown`)
|
|
16
|
+
// the CANVAS BAR the way back, its name (the rename), Preview | Edit,
|
|
17
|
+
// and the zoom over its render
|
|
18
|
+
// the RIGHT COLUMN the inspector: the definition's published surface
|
|
19
|
+
// (`DefinitionSection`) with nothing selected, a selected
|
|
20
|
+
// layer's sections otherwise
|
|
21
|
+
// the BOTTOM ROW its own clock: the timeline's definition fork (def-local
|
|
22
|
+
// ruler, Actions rows) and the dock's one transport
|
|
23
|
+
//
|
|
24
|
+
// ── ONE SUBTREE, PORTALLED INTO THE CELLS ────────────────────────────────
|
|
25
|
+
//
|
|
26
|
+
// App mounts this inside its PanelBoundary as before; it renders nothing into
|
|
27
|
+
// the shell itself. Each part goes into its cell's slot through `InCell`
|
|
28
|
+
// (panels/stage/cellSlots.tsx), so the rename prompt and the zoom stay one
|
|
29
|
+
// React subtree. It portals only on its own tab, so no other mode can be
|
|
30
|
+
// filling the same cells.
|
|
31
|
+
//
|
|
32
|
+
// ── WHY THE CANVAS IS A HOLE ─────────────────────────────────────────────
|
|
33
|
+
//
|
|
34
|
+
// The stage iframe behind the shell renders the open definition standalone
|
|
35
|
+
// (no scene, no scene camera, no post), and `measureInset` hands it the hole's
|
|
36
|
+
// rect to fit into. So what is on screen is the real component under the real
|
|
37
|
+
// renderer, which is the only way "preview === export" can hold for a thing
|
|
38
|
+
// the author is about to place in fifteen shots.
|
|
39
|
+
import { useEffect } from "react";
|
|
40
|
+
import { IconMinus, IconPlus, SegmentedControl, usePrompt } from "@dailies-studio/ui";
|
|
41
|
+
import { useShotEditor, useStore } from "../../state/context.js";
|
|
42
|
+
import type { ComponentsTab } from "../../state/types.js";
|
|
43
|
+
import { componentEditOpen } from "../../state/workspace.js";
|
|
44
|
+
import { InCell } from "../stage/cellSlots.js";
|
|
45
|
+
import { ComponentCrumb, ComponentsBack } from "./componentParts.js";
|
|
46
|
+
|
|
47
|
+
/** No buffers to lose on this tab: the way back never asks. */
|
|
48
|
+
const NO_GUARD = (then: () => void): void => then();
|
|
49
|
+
|
|
50
|
+
/** One press of the zoom. Multiplicative, because the control is a scale: a
|
|
51
|
+
* fixed ±0.25 step is a quarter of the picture near 1x and a whole doubling
|
|
52
|
+
* near 0.25x. The store owns the [0.25, 4] clamp. */
|
|
53
|
+
const ZOOM_STEP = 1.25;
|
|
54
|
+
|
|
55
|
+
export function ComponentStudio() {
|
|
56
|
+
const store = useStore();
|
|
57
|
+
const doc = useShotEditor((s) => s.doc);
|
|
58
|
+
const editingDef = useShotEditor((s) => s.editingDef);
|
|
59
|
+
// THE WORKSPACE decides, not `editingDef` alone: a mode that outranks this
|
|
60
|
+
// one (`workspaceOf`'s order) owns the cells while it is open. The Edit tab
|
|
61
|
+
// of Components only: Preview is ComponentsPreview's.
|
|
62
|
+
const own = useShotEditor(componentEditOpen);
|
|
63
|
+
const { prompt, dialog } = usePrompt();
|
|
64
|
+
|
|
65
|
+
const def = editingDef === null ? undefined : doc?.components?.[editingDef];
|
|
66
|
+
if (doc === null || def === undefined || !own) return null;
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<>
|
|
70
|
+
<InCell name="centre">
|
|
71
|
+
{/* THE COMPONENT ITSELF, and it is a hole rather than a component:
|
|
72
|
+
`.stage-window` paints nothing and takes no pointer, so every press
|
|
73
|
+
over the subject reaches the stage. The stage's one pane is the
|
|
74
|
+
definition's frame there: the tools draw on it, a drag moves a
|
|
75
|
+
layer or marquees, middle-drag and Alt+left drag orbit, and the
|
|
76
|
+
wheel pans and dollies. `aria-hidden` because a screen reader has
|
|
77
|
+
nothing to read in a GL surface this document does not own. */}
|
|
78
|
+
<div
|
|
79
|
+
className="stage-window component-studio-hole"
|
|
80
|
+
aria-hidden="true"
|
|
81
|
+
data-testid="studio-preview-hole"
|
|
82
|
+
/>
|
|
83
|
+
</InCell>
|
|
84
|
+
|
|
85
|
+
<InCell name="bar">
|
|
86
|
+
<div
|
|
87
|
+
className="component-studio-bar"
|
|
88
|
+
role="group"
|
|
89
|
+
aria-label={`Component ${def.name}`}
|
|
90
|
+
data-testid="component-studio"
|
|
91
|
+
>
|
|
92
|
+
{dialog}
|
|
93
|
+
{/* THE WAY BACK: to the Shot this came from, else to All
|
|
94
|
+
components, the verb the outline's back row runs too. */}
|
|
95
|
+
<ComponentsBack testId="studio-back" guard={NO_GUARD} />
|
|
96
|
+
{/* THE NAME IS THE RENAME. One dialog for the whole chrome
|
|
97
|
+
(`usePrompt`, the app menu's), never a second inline field with its
|
|
98
|
+
own commit rules: the inspector's Name row is the other spelling of
|
|
99
|
+
this one call and both land in `renameComponent`. */}
|
|
100
|
+
<ComponentCrumb
|
|
101
|
+
sel={{ kind: "studio", def: def.id }}
|
|
102
|
+
name={
|
|
103
|
+
<button
|
|
104
|
+
type="button"
|
|
105
|
+
className="component-studio-name"
|
|
106
|
+
data-testid="studio-rename"
|
|
107
|
+
title="Rename this component"
|
|
108
|
+
onClick={() => {
|
|
109
|
+
void prompt({
|
|
110
|
+
title: "Rename component",
|
|
111
|
+
label: "Name",
|
|
112
|
+
initialValue: def.name,
|
|
113
|
+
}).then((name) => {
|
|
114
|
+
if (name !== null) store.renameComponent(def.id, name);
|
|
115
|
+
});
|
|
116
|
+
}}
|
|
117
|
+
>
|
|
118
|
+
{def.name}
|
|
119
|
+
</button>
|
|
120
|
+
}
|
|
121
|
+
/>
|
|
122
|
+
<div className="component-studio-tools">
|
|
123
|
+
<ComponentsTabSwitch />
|
|
124
|
+
<StudioZoom />
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
</InCell>
|
|
128
|
+
</>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const TAB_TEST_ID: Record<ComponentsTab, string> = {
|
|
133
|
+
preview: "components-tab-preview",
|
|
134
|
+
edit: "components-tab-edit",
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/** Set by a swap made from a focused segment, taken by the switch that mounts
|
|
138
|
+
* next: the swap unmounts this bar and mounts the other tab's, so focus has
|
|
139
|
+
* to be handed across. */
|
|
140
|
+
let refocusTab = false;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Components' Preview | Edit, in the canvas bar of both tabs: Preview is the
|
|
144
|
+
* component alone on the stage (ComponentsPreview.tsx), Edit its studio (this
|
|
145
|
+
* file for a definition, CodeStudio.tsx for code). The subject, its clock and
|
|
146
|
+
* its buffers stay across the swap (`setComponentsTab`).
|
|
147
|
+
*/
|
|
148
|
+
export function ComponentsTabSwitch() {
|
|
149
|
+
const store = useStore();
|
|
150
|
+
const tab = useShotEditor((s) => s.componentsTab);
|
|
151
|
+
useEffect(() => {
|
|
152
|
+
if (!refocusTab) return;
|
|
153
|
+
refocusTab = false;
|
|
154
|
+
document.querySelector<HTMLElement>(`[data-testid="${TAB_TEST_ID[tab]}"]`)?.focus();
|
|
155
|
+
// On mount only: this instance is the one the swap brought up.
|
|
156
|
+
}, []);
|
|
157
|
+
return (
|
|
158
|
+
<SegmentedControl
|
|
159
|
+
ariaLabel="Components view"
|
|
160
|
+
className="is-bar components-tab-switch"
|
|
161
|
+
value={tab}
|
|
162
|
+
options={[
|
|
163
|
+
{ value: "preview", label: "Preview", title: "The component alone on the stage", testId: TAB_TEST_ID.preview },
|
|
164
|
+
{ value: "edit", label: "Edit", title: "Edit the component", testId: TAB_TEST_ID.edit },
|
|
165
|
+
]}
|
|
166
|
+
onChange={(next) => {
|
|
167
|
+
if (next === tab) return;
|
|
168
|
+
const focused = document.activeElement;
|
|
169
|
+
refocusTab = focused instanceof HTMLElement && Object.values(TAB_TEST_ID).includes(focused.dataset.testid ?? "");
|
|
170
|
+
store.setComponentsTab(next === "edit" ? "edit" : "preview");
|
|
171
|
+
}}
|
|
172
|
+
/>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* How big the definition is drawn, plus the way back to the derived camera.
|
|
178
|
+
* Code Studio's canvas bar renders this same cluster for its component.
|
|
179
|
+
*
|
|
180
|
+
* The zoom is CHROME state pushed to the stage, which divides the definition's
|
|
181
|
+
* frame by it before fitting, so it scales the subject inside the hole rather
|
|
182
|
+
* than moving a camera the author may have taken. The stage bar's zoom look
|
|
183
|
+
* (`.zoom-controls`), because it is the same control over a different subject.
|
|
184
|
+
*
|
|
185
|
+
* Recenter appears exactly when it is true: once the author has taken the
|
|
186
|
+
* camera, by an orbit (middle-drag or Alt+left drag, and over a code
|
|
187
|
+
* component a plain drag too) or by a wheel pan or dolly, it is theirs and
|
|
188
|
+
* nothing re-derives it.
|
|
189
|
+
*/
|
|
190
|
+
export function StudioZoom() {
|
|
191
|
+
const store = useStore();
|
|
192
|
+
const zoom = useShotEditor((s) => s.studioZoom);
|
|
193
|
+
const orbited = useShotEditor((s) => s.isolationOrbit);
|
|
194
|
+
return (
|
|
195
|
+
<>
|
|
196
|
+
{orbited ? (
|
|
197
|
+
<button
|
|
198
|
+
type="button"
|
|
199
|
+
className="btn"
|
|
200
|
+
data-testid="studio-refit"
|
|
201
|
+
title="Frame the component head-on again (Home)"
|
|
202
|
+
onClick={() => store.fitWorkingView()}
|
|
203
|
+
>
|
|
204
|
+
Recenter
|
|
205
|
+
</button>
|
|
206
|
+
) : null}
|
|
207
|
+
<div className="zoom-controls" role="group" aria-label="Zoom">
|
|
208
|
+
<button
|
|
209
|
+
type="button"
|
|
210
|
+
className="btn is-icon"
|
|
211
|
+
data-testid="studio-zoom-out"
|
|
212
|
+
aria-label="Zoom out"
|
|
213
|
+
title="Zoom out"
|
|
214
|
+
onClick={() => store.setStudioZoom(zoom / ZOOM_STEP)}
|
|
215
|
+
>
|
|
216
|
+
<IconMinus />
|
|
217
|
+
</button>
|
|
218
|
+
{/* The readout doubles as the reset, which is where every editor puts
|
|
219
|
+
it, and it is the only way back to 1x that does not need repeated
|
|
220
|
+
presses to land exactly there. */}
|
|
221
|
+
<button
|
|
222
|
+
type="button"
|
|
223
|
+
className="btn zoom-readout"
|
|
224
|
+
data-testid="studio-zoom-level"
|
|
225
|
+
title="Reset the zoom"
|
|
226
|
+
onClick={() => store.setStudioZoom(1)}
|
|
227
|
+
>
|
|
228
|
+
{Math.round(zoom * 100)}%
|
|
229
|
+
</button>
|
|
230
|
+
<button
|
|
231
|
+
type="button"
|
|
232
|
+
className="btn is-icon"
|
|
233
|
+
data-testid="studio-zoom-in"
|
|
234
|
+
aria-label="Zoom in"
|
|
235
|
+
title="Zoom in"
|
|
236
|
+
onClick={() => store.setStudioZoom(zoom * ZOOM_STEP)}
|
|
237
|
+
>
|
|
238
|
+
<IconPlus />
|
|
239
|
+
</button>
|
|
240
|
+
</div>
|
|
241
|
+
</>
|
|
242
|
+
);
|
|
243
|
+
}
|