@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,12 @@
|
|
|
1
|
+
// THE STORYBOARD'S CHUNK: the board, the shot board, the drop dialog, and the
|
|
2
|
+
// panels the board puts in the right column, the inspector's card section
|
|
3
|
+
// among them. Loaded on demand through app/lazySurfaces.tsx and nowhere else,
|
|
4
|
+
// so all of it, its stylesheet included, leaves the /scene entry. Studio's
|
|
5
|
+
// Brief (ShotBrief) is not here: it is a Studio panel.
|
|
6
|
+
import "./board.css";
|
|
7
|
+
|
|
8
|
+
export { StoryboardView } from "./StoryboardView.js";
|
|
9
|
+
export { ComponentSheet } from "./ComponentSheet.js";
|
|
10
|
+
export { PlanOccupant } from "./BoardPlan.js";
|
|
11
|
+
export { SketchInspector } from "./shotboard/SketchInspector.js";
|
|
12
|
+
export { StoryboardReferenceSection } from "../inspector/storyboard/StoryboardReferenceSection.js";
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// THE BOARD'S CLIPBOARD: copy, cut and paste of cards and sketch items.
|
|
2
|
+
//
|
|
3
|
+
// Its own file because TWO documents bind it, the chrome's (StoryboardView)
|
|
4
|
+
// and the stage iframe's (StagePanel), and the stage panel loads with the
|
|
5
|
+
// shell while the board is a chunk of its own.
|
|
6
|
+
import type { ShotEditorStore } from "../../state/store.js";
|
|
7
|
+
import { isReferenceId } from "../../state/storyboardModel.js";
|
|
8
|
+
import { shotBoardOf, workspaceOf } from "../../state/workspace.js";
|
|
9
|
+
import { CLIP_MARKER, STORYBOARD_MIME, urlFromTransfer } from "./storyboardDrop.js";
|
|
10
|
+
|
|
11
|
+
/** What a copy of sketch items marks the clipboard with. Its own type, not
|
|
12
|
+
* `STORYBOARD_MIME`, so a card paste never takes it, nor it a card clip. */
|
|
13
|
+
export const SKETCH_MIME = "application/x-dailies-sketch";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The board's copy, cut and paste, bound on one document until `signal`
|
|
17
|
+
* aborts. TWO DOCUMENTS bind it: the chrome's (StoryboardView) and the stage
|
|
18
|
+
* iframe's (StagePanel). The pointer on the frame focuses the stage, and the native
|
|
19
|
+
* event then fires in ITS document, so a chrome-only binding left ⌘C ⌘V on a
|
|
20
|
+
* layer dead. Each event checks the board is the workspace, because the stage
|
|
21
|
+
* document outlives the board.
|
|
22
|
+
*/
|
|
23
|
+
export function bindBoardClipboard(doc: Document, store: ShotEditorStore, signal: AbortSignal): void {
|
|
24
|
+
const on = (): boolean => workspaceOf(store.getState()) === "storyboard";
|
|
25
|
+
/** Is this event aimed at something an author is typing into? A note card's
|
|
26
|
+
* own text, or the stage's text editor, must still copy and paste as text.
|
|
27
|
+
* Duck-typed: a stage element is not this realm's `HTMLElement`. */
|
|
28
|
+
const isField = (target: EventTarget | null): boolean => {
|
|
29
|
+
const el = target as Partial<HTMLElement> | null;
|
|
30
|
+
return el?.tagName === "INPUT" || el?.tagName === "TEXTAREA" || el?.isContentEditable === true;
|
|
31
|
+
};
|
|
32
|
+
/** A RANGE OF TEXT BEATS THE BOARD. Without this, selecting a timecode or a
|
|
33
|
+
* label anywhere in the chrome and pressing ⌘C would copy the board's cards
|
|
34
|
+
* instead of the words under the cursor. */
|
|
35
|
+
const hasTextSelection = (): boolean => doc.getSelection()?.isCollapsed === false;
|
|
36
|
+
|
|
37
|
+
const onPaste = (event: ClipboardEvent): void => {
|
|
38
|
+
if (!on() || isField(event.target)) return;
|
|
39
|
+
const sketching = shotBoardOf(store.getState()) !== null;
|
|
40
|
+
const all = Array.from(event.clipboardData?.files ?? []);
|
|
41
|
+
if (all.length > 0) {
|
|
42
|
+
// FILES open the drop dialog (S2), which asks where they count. Never a
|
|
43
|
+
// fall-through to the board clip, which would paste a copy of whatever
|
|
44
|
+
// was copied last. On a shot board they are the sketch's: each lands as
|
|
45
|
+
// a pinned card and its item, with no dialog.
|
|
46
|
+
event.preventDefault();
|
|
47
|
+
if (sketching) void store.addSketchItemsFromFiles(all);
|
|
48
|
+
else store.openBoardDrop(all);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
// THE BOARD'S OWN CLIPS go back as they were copied, and a stale clip is
|
|
52
|
+
// never pasted: only under its marker.
|
|
53
|
+
const data = event.clipboardData;
|
|
54
|
+
const types = Array.from(data?.types ?? []);
|
|
55
|
+
if (sketching && types.includes(SKETCH_MIME)) {
|
|
56
|
+
event.preventDefault();
|
|
57
|
+
store.pasteSketchItems();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (types.includes(STORYBOARD_MIME)) {
|
|
61
|
+
event.preventDefault();
|
|
62
|
+
store.pasteStoryboardItems();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
// A LINK OR WORDS from anywhere else land on the Style wall: a link card,
|
|
66
|
+
// else a note.
|
|
67
|
+
if (data === null) return;
|
|
68
|
+
const url = urlFromTransfer(data);
|
|
69
|
+
if (url !== null) {
|
|
70
|
+
event.preventDefault();
|
|
71
|
+
store.addLinkReference(url, null, { select: false });
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const text = data.getData("text/plain").trim();
|
|
75
|
+
if (text === "") return;
|
|
76
|
+
event.preventDefault();
|
|
77
|
+
store.addStoryboardNote(null, { select: false, text });
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/** A copy, or a cut that also deletes. Both write the clip's marker, so a
|
|
81
|
+
* paste, which takes the clip only under it, takes cards copied anywhere
|
|
82
|
+
* on the board. */
|
|
83
|
+
const clip = (event: ClipboardEvent, cut: boolean): void => {
|
|
84
|
+
if (!on() || isField(event.target) || hasTextSelection()) return;
|
|
85
|
+
// ON A SHOT BOARD, the sketch items selected: onto the shot board's own
|
|
86
|
+
// clip, which a paste on any sketch takes back.
|
|
87
|
+
const state = store.getState();
|
|
88
|
+
if (shotBoardOf(state) !== null) {
|
|
89
|
+
const items = state.sketchSelection;
|
|
90
|
+
if (items.length === 0) return;
|
|
91
|
+
event.preventDefault();
|
|
92
|
+
event.clipboardData?.setData(SKETCH_MIME, CLIP_MARKER);
|
|
93
|
+
store.copySketchItems(items);
|
|
94
|
+
if (cut) store.removeSketchItems(items);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
// The CARDS in the selection. A press on a lane head selects the planned
|
|
98
|
+
// shot, and a cut of that would copy nothing and then delete the lane, its
|
|
99
|
+
// cards thrown to Global. Nothing to copy leaves the event alone.
|
|
100
|
+
const ids = store.getState().storyboardSelection.filter(isReferenceId);
|
|
101
|
+
if (ids.length === 0) return;
|
|
102
|
+
event.preventDefault();
|
|
103
|
+
event.clipboardData?.setData(STORYBOARD_MIME, CLIP_MARKER);
|
|
104
|
+
store.copyStoryboardItems(ids);
|
|
105
|
+
if (cut) store.deleteStoryboardItems(ids);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
doc.addEventListener("copy", (event) => clip(event, false), { signal });
|
|
109
|
+
doc.addEventListener("cut", (event) => clip(event, true), { signal });
|
|
110
|
+
doc.addEventListener("paste", onPaste, { signal });
|
|
111
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// THE BOARD'S ORDER, MOVED BY A DRAG (Paper SB1): a shot card dropped between
|
|
2
|
+
// two others. Pure, so the drop and the tests read one answer.
|
|
3
|
+
//
|
|
4
|
+
// TWO ARRAYS SAY THE ORDER. A planned shot's place is its index in
|
|
5
|
+
// `storyboard.parts` (`movePart`); a Shot's place is its index in the playing
|
|
6
|
+
// order `doc.scenes` (`reorderScene`), and `storyEntries` places the planned
|
|
7
|
+
// shots with no Shot around the Shots by part order. So a move writes both:
|
|
8
|
+
// the parts in the new reading order, and the Shots in it too. Moving a built
|
|
9
|
+
// shot on the board moves it in the film, which is the point: one order.
|
|
10
|
+
//
|
|
11
|
+
// A SHOT NO PLAN LINKS has no index in the parts, so a planned shot with no
|
|
12
|
+
// Shot could not be placed beside it: `storyEntries` would put it by the
|
|
13
|
+
// nearest linked Shot instead, and the drop would land somewhere else. Only
|
|
14
|
+
// then does the move give each such Shot its planned shot first
|
|
15
|
+
// (`pinPartFor`, as a drop on its card does), in the same edit; a move the
|
|
16
|
+
// two arrays can say as they are adds no part.
|
|
17
|
+
import { movePart, pinPartFor, reorderScene } from "@dailies-studio/shot/doc";
|
|
18
|
+
import type { Doc3D } from "@dailies-studio/shot/doc";
|
|
19
|
+
import { storyEntries } from "../../state/storyboardModel.js";
|
|
20
|
+
import type { StoryEntry } from "../../state/storyboardModel.js";
|
|
21
|
+
|
|
22
|
+
/** The id a card selects and drags under: its planned shot, else its Shot. */
|
|
23
|
+
export function entryIdOf(entry: StoryEntry): string {
|
|
24
|
+
return entry.kind === "shot" ? (entry.shot.part?.id ?? entry.shot.sceneId) : entry.part.id;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* `doc` with the card `id` moved to `slot`: the gap before the card now at
|
|
29
|
+
* `slot` in `entries` (`entries.length` is the end). The same document when
|
|
30
|
+
* nothing moves.
|
|
31
|
+
*/
|
|
32
|
+
export function moveBoardEntry(doc: Doc3D, entries: readonly StoryEntry[], id: string, slot: number): Doc3D {
|
|
33
|
+
const from = entries.findIndex((entry) => entryIdOf(entry) === id);
|
|
34
|
+
if (from < 0) return doc;
|
|
35
|
+
const at = Math.max(0, Math.min(entries.length, Math.trunc(slot)));
|
|
36
|
+
const to = at > from ? at - 1 : at;
|
|
37
|
+
if (to === from) return doc;
|
|
38
|
+
const next = [...entries];
|
|
39
|
+
const [moved] = next.splice(from, 1);
|
|
40
|
+
next.splice(to, 0, moved!);
|
|
41
|
+
const plain = placeInOrder(doc, next, false);
|
|
42
|
+
const want = next.map(entryIdOf).join(" ");
|
|
43
|
+
return storyEntries(plain).map(entryIdOf).join(" ") === want ? plain : placeInOrder(doc, next, true);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Both arrays in `order`, each placed at its index in turn: everything
|
|
47
|
+
* before it is already in place, so they end in exactly that order. With
|
|
48
|
+
* `pin`, a Shot no plan links gets its planned shot first. */
|
|
49
|
+
function placeInOrder(doc: Doc3D, order: readonly StoryEntry[], pin: boolean): Doc3D {
|
|
50
|
+
let out = doc;
|
|
51
|
+
const partIds = order.flatMap((entry): string[] => {
|
|
52
|
+
if (entry.kind === "placeholder") return [entry.part.id];
|
|
53
|
+
if (entry.shot.part !== null) return [entry.shot.part.id];
|
|
54
|
+
const pinned = pin ? pinPartFor(out, entry.shot.sceneId) : null;
|
|
55
|
+
if (pinned === null) return [];
|
|
56
|
+
out = pinned.doc;
|
|
57
|
+
return [pinned.part];
|
|
58
|
+
});
|
|
59
|
+
partIds.forEach((partId, index) => {
|
|
60
|
+
out = movePart(out, partId, index);
|
|
61
|
+
});
|
|
62
|
+
order
|
|
63
|
+
.flatMap((entry) => (entry.kind === "shot" ? [entry.shot.sceneId] : []))
|
|
64
|
+
.forEach((sceneId, index) => {
|
|
65
|
+
out = reorderScene(out, sceneId, index);
|
|
66
|
+
});
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
// THE BOARD'S SHARED PARTS, one home for what the board and its panels all
|
|
2
|
+
// draw: the press policy, the requirement's rows and action slot, the card-use
|
|
3
|
+
// block and the read-only row.
|
|
4
|
+
import { useRef, useState } from "react";
|
|
5
|
+
import type { ReactNode } from "react";
|
|
6
|
+
import {
|
|
7
|
+
REFERENCE_IMPORTANCES,
|
|
8
|
+
REFERENCE_IMPORTANCE_SPECS,
|
|
9
|
+
REFERENCE_USES,
|
|
10
|
+
REFERENCE_USE_SPECS,
|
|
11
|
+
REQUIREMENT_STATUS_SPECS,
|
|
12
|
+
referenceImportanceOf,
|
|
13
|
+
referenceUseOf,
|
|
14
|
+
requirementStatus,
|
|
15
|
+
updateReference,
|
|
16
|
+
} from "@dailies-studio/shot/doc";
|
|
17
|
+
import type { Doc3D, ReferenceItem3D } from "@dailies-studio/shot/doc";
|
|
18
|
+
import { IconUndo, InspectorRow, NumberField, SegmentedControl, TextField, useContextMenu, useLocked } from "@dailies-studio/ui";
|
|
19
|
+
import type { MenuEntry } from "@dailies-studio/ui";
|
|
20
|
+
import { useShotEditor, useStore } from "../../state/context.js";
|
|
21
|
+
import { instanceHostRefusal } from "../../state/storyboardModel.js";
|
|
22
|
+
import { useScrub } from "../inspector/useDocWrite.js";
|
|
23
|
+
import { AUDIO_ACCEPT_ATTRIBUTE } from "./storyboardDrop.js";
|
|
24
|
+
|
|
25
|
+
/** A press's modifiers, all a selection policy reads, and its click count
|
|
26
|
+
* (`detail`: 2 on a double press's second press, 0 from a key). */
|
|
27
|
+
export type Press = { shiftKey: boolean; ctrlKey: boolean; metaKey: boolean; detail?: number };
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* THE SELECTION A PRESS LEAVES, over a list in its visible `order`: Shift
|
|
31
|
+
* takes the range from the anchor (which stays), Ctrl or Cmd toggles the one
|
|
32
|
+
* pressed, a plain press selects it alone. Both move the anchor to it. Pure,
|
|
33
|
+
* so every board list presses one way.
|
|
34
|
+
*/
|
|
35
|
+
export function pressSelection(
|
|
36
|
+
current: readonly string[],
|
|
37
|
+
order: readonly string[],
|
|
38
|
+
anchor: string | null,
|
|
39
|
+
id: string,
|
|
40
|
+
event: Press,
|
|
41
|
+
): { selection: string[]; anchor: string | null } {
|
|
42
|
+
if (event.shiftKey && anchor !== null) {
|
|
43
|
+
const a = order.indexOf(anchor);
|
|
44
|
+
const b = order.indexOf(id);
|
|
45
|
+
if (a !== -1 && b !== -1) return { selection: order.slice(Math.min(a, b), Math.max(a, b) + 1), anchor };
|
|
46
|
+
}
|
|
47
|
+
if (event.ctrlKey || event.metaKey) {
|
|
48
|
+
return { selection: current.includes(id) ? current.filter((x) => x !== id) : [...current, id], anchor: id };
|
|
49
|
+
}
|
|
50
|
+
return { selection: [id], anchor: id };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* WHAT A REQUIREMENT'S ACTION SLOT OFFERS, and the reason when it offers
|
|
55
|
+
* nothing. Read straight off `requirementStatus` and the card's planned shot:
|
|
56
|
+
*
|
|
57
|
+
* place the requirement is missing and Place can write it here
|
|
58
|
+
* place-in a Style card that makes a layer: Place asks which Shot
|
|
59
|
+
* in satisfied: names where, and opens it
|
|
60
|
+
* choose-file a sound with no file yet
|
|
61
|
+
* none nothing to do from the row (done, unchecked library, or a
|
|
62
|
+
* refusal Place would make anyway), with `reason` as the title
|
|
63
|
+
*/
|
|
64
|
+
export type PlaceActionKind =
|
|
65
|
+
| { kind: "place"; title: string }
|
|
66
|
+
| { kind: "place-in"; title: string }
|
|
67
|
+
| { kind: "in"; sceneId: string; layerId: string | null; label: string }
|
|
68
|
+
| { kind: "choose-file" }
|
|
69
|
+
| { kind: "none"; reason: string };
|
|
70
|
+
|
|
71
|
+
export function placeActionOf(doc: Doc3D, item: ReferenceItem3D, componentHost: (id: string) => string | null): PlaceActionKind {
|
|
72
|
+
const report = requirementStatus(doc, item);
|
|
73
|
+
if (report === null) return { kind: "none", reason: "" };
|
|
74
|
+
const part = item.part === undefined ? undefined : doc.storyboard?.parts.find((p) => p.id === item.part);
|
|
75
|
+
const linked = part?.scene === undefined ? undefined : doc.scenesById[part.scene];
|
|
76
|
+
if (report.state === "satisfied") {
|
|
77
|
+
const sceneId = report.evidence?.scene ?? linked?.id ?? null;
|
|
78
|
+
const scene = sceneId === null ? undefined : doc.scenesById[sceneId];
|
|
79
|
+
if (scene !== undefined && sceneId !== null) {
|
|
80
|
+
return { kind: "in", sceneId, layerId: report.evidence?.layer ?? null, label: `In ${scene.name}` };
|
|
81
|
+
}
|
|
82
|
+
return { kind: "none", reason: report.reason };
|
|
83
|
+
}
|
|
84
|
+
if (report.state === "done") return { kind: "none", reason: report.reason };
|
|
85
|
+
if (item.type === "sound" && item.asset === undefined) return { kind: "choose-file" };
|
|
86
|
+
if (item.type === "library" || item.type === "note") return { kind: "none", reason: report.reason };
|
|
87
|
+
if (part !== undefined && part.scene === undefined) return { kind: "none", reason: `Create a Shot for ${part.label} first.` };
|
|
88
|
+
if (part !== undefined && linked === undefined) return { kind: "none", reason: `The Shot ${part.label} was linked to is gone. Link another.` };
|
|
89
|
+
if (item.type === "component") {
|
|
90
|
+
const host = item.component === undefined ? null : componentHost(item.component);
|
|
91
|
+
if (host === null) return { kind: "none", reason: `${item.component ?? item.label} is not in the registry.` };
|
|
92
|
+
if (host !== "three") return { kind: "none", reason: instanceHostRefusal(host) };
|
|
93
|
+
}
|
|
94
|
+
if (part === undefined && item.type !== "transition" && item.type !== "sound") {
|
|
95
|
+
return { kind: "place-in", title: "Choose the Shot to place it in" };
|
|
96
|
+
}
|
|
97
|
+
return { kind: "place", title: linked === undefined ? "Place it in the video" : `Place it in ${linked.name}` };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* THE ACTION SLOT, from `placeActionOf`: Place, Place in…
|
|
102
|
+
* (a menu of Shots), In <Shot> (opens it on the evidence), Choose file… (a
|
|
103
|
+
* sound that names a need), or a dimmed Place whose title is the reason.
|
|
104
|
+
* The row and the inspector both draw this, so the two cannot disagree.
|
|
105
|
+
*
|
|
106
|
+
* WITH A SHOT (`sceneId`), the slot is that Shot's: a card that would ask
|
|
107
|
+
* "Place in…" places straight into it, because the author is already in it.
|
|
108
|
+
*/
|
|
109
|
+
export function PlaceAction({ doc, item, sceneId }: { doc: Doc3D; item: ReferenceItem3D; sceneId?: string }) {
|
|
110
|
+
const store = useStore();
|
|
111
|
+
const library = useShotEditor((s) => s.library);
|
|
112
|
+
const report = requirementStatus(doc, item);
|
|
113
|
+
const action = placeActionOf(doc, item, (id) => library.find((row) => row.id === id)?.host ?? null);
|
|
114
|
+
const fileRef = useRef<HTMLInputElement | null>(null);
|
|
115
|
+
const placeIn = useContextMenu();
|
|
116
|
+
const [busy, setBusy] = useState(false);
|
|
117
|
+
// A viewer's panel locks the verbs that write (Place, Place in, Choose
|
|
118
|
+
// file); "In <Shot>" only navigates, so it stays live.
|
|
119
|
+
const locked = useLocked();
|
|
120
|
+
const inert = busy || locked;
|
|
121
|
+
const shot = sceneId === undefined ? undefined : doc.scenesById[sceneId];
|
|
122
|
+
|
|
123
|
+
const run = (work: Promise<void>): void => {
|
|
124
|
+
setBusy(true);
|
|
125
|
+
void work
|
|
126
|
+
.catch((e: unknown) => store.reportError(e instanceof Error ? e.message : String(e)))
|
|
127
|
+
.finally(() => setBusy(false));
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const openPlaceIn = (at: { x: number; y: number }): void => {
|
|
131
|
+
const entries: MenuEntry[] = doc.scenes.map((id) => ({
|
|
132
|
+
kind: "action",
|
|
133
|
+
id: `ledger-place-in-${id}`,
|
|
134
|
+
label: doc.scenesById[id]?.name ?? id,
|
|
135
|
+
onSelect: () => run(store.placeStoryboardItem(item.id, id)),
|
|
136
|
+
}));
|
|
137
|
+
if (entries.length === 0) {
|
|
138
|
+
store.reportError("This Project has no Shot yet. Create one from a planned shot first.");
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
placeIn.open(at, entries);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<>
|
|
146
|
+
{action.kind === "place" ? (
|
|
147
|
+
<button type="button" className="btn" title={action.title} disabled={inert} onClick={() => run(store.placeStoryboardItem(item.id))}>
|
|
148
|
+
Place
|
|
149
|
+
</button>
|
|
150
|
+
) : action.kind === "place-in" && sceneId !== undefined && shot !== undefined ? (
|
|
151
|
+
<button
|
|
152
|
+
type="button"
|
|
153
|
+
className="btn"
|
|
154
|
+
title={`Place it in ${shot.name}`}
|
|
155
|
+
disabled={inert}
|
|
156
|
+
onClick={() => run(store.placeStoryboardItem(item.id, sceneId))}
|
|
157
|
+
>
|
|
158
|
+
Place
|
|
159
|
+
</button>
|
|
160
|
+
) : action.kind === "place-in" ? (
|
|
161
|
+
<button
|
|
162
|
+
type="button"
|
|
163
|
+
className="btn"
|
|
164
|
+
title={action.title}
|
|
165
|
+
aria-haspopup="menu"
|
|
166
|
+
disabled={inert}
|
|
167
|
+
onClick={(event) => {
|
|
168
|
+
const r = event.currentTarget.getBoundingClientRect();
|
|
169
|
+
openPlaceIn({ x: r.left, y: r.bottom + 6 });
|
|
170
|
+
}}
|
|
171
|
+
>
|
|
172
|
+
Place in…
|
|
173
|
+
</button>
|
|
174
|
+
) : action.kind === "in" ? (
|
|
175
|
+
<button
|
|
176
|
+
type="button"
|
|
177
|
+
className="btn ledger-in"
|
|
178
|
+
title="Open the Shot on what satisfies this"
|
|
179
|
+
onClick={() => store.openStoryboardShot(action.sceneId, action.layerId)}
|
|
180
|
+
>
|
|
181
|
+
{action.label}
|
|
182
|
+
</button>
|
|
183
|
+
) : action.kind === "choose-file" ? (
|
|
184
|
+
<>
|
|
185
|
+
<button type="button" className="btn" disabled={inert} onClick={() => fileRef.current?.click()}>
|
|
186
|
+
Choose file…
|
|
187
|
+
</button>
|
|
188
|
+
<input
|
|
189
|
+
ref={fileRef}
|
|
190
|
+
type="file"
|
|
191
|
+
hidden
|
|
192
|
+
accept={AUDIO_ACCEPT_ATTRIBUTE}
|
|
193
|
+
data-testid="ledger-sound-file"
|
|
194
|
+
onChange={(event) => {
|
|
195
|
+
const file = event.currentTarget.files?.[0];
|
|
196
|
+
event.currentTarget.value = "";
|
|
197
|
+
if (file !== undefined) run(store.setSoundFile(item.id, file));
|
|
198
|
+
}}
|
|
199
|
+
/>
|
|
200
|
+
</>
|
|
201
|
+
) : report?.state === "missing" ? (
|
|
202
|
+
// A REFUSAL SAYS WHY: dimmed, with Place's own sentence as the title.
|
|
203
|
+
<button
|
|
204
|
+
type="button"
|
|
205
|
+
className="btn"
|
|
206
|
+
aria-disabled="true"
|
|
207
|
+
title={action.reason}
|
|
208
|
+
onClick={() => store.reportError(action.reason)}
|
|
209
|
+
>
|
|
210
|
+
Place
|
|
211
|
+
</button>
|
|
212
|
+
) : (
|
|
213
|
+
<span className="ledger-note" title={action.reason}>
|
|
214
|
+
{report?.state === "done" ? "Ticked by hand" : action.reason}
|
|
215
|
+
</span>
|
|
216
|
+
)}
|
|
217
|
+
{placeIn.menu}
|
|
218
|
+
</>
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* A READ-ONLY ROW, the house readout (`insp-readout`, widgets.css), the one
|
|
224
|
+
* the Studio's Time section wears: the field's box on the control ground with
|
|
225
|
+
* no hover, its value in the numeric face and the second ink. `prose` is a
|
|
226
|
+
* sentence rather than a measurement (what an item is for, where a still came
|
|
227
|
+
* from): the body face, wrapping, the row aligned to its first line. It never
|
|
228
|
+
* wears `inert`, which dimmed the row to 0.55 and set its numbers in the 10px
|
|
229
|
+
* sans.
|
|
230
|
+
*/
|
|
231
|
+
export function ReadoutRow(props: { label: string; prose?: boolean; title?: string; children: ReactNode }) {
|
|
232
|
+
return (
|
|
233
|
+
<InspectorRow label={props.label} className="insp-readout" {...(props.prose === true ? { align: "start" as const } : {})}>
|
|
234
|
+
<span className={`insp-dim${props.prose === true ? " is-text" : ""}`} {...(props.title === undefined ? {} : { title: props.title })}>
|
|
235
|
+
{props.children}
|
|
236
|
+
</span>
|
|
237
|
+
</InspectorRow>
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** A requirement's standing as a badge tone (G1 `.badge`): met reads as
|
|
242
|
+
* success, Missing in the accent because it asks for work, Unchecked quiet. */
|
|
243
|
+
const REQUIREMENT_TONE: Record<keyof typeof REQUIREMENT_STATUS_SPECS, string> = {
|
|
244
|
+
satisfied: " is-success",
|
|
245
|
+
done: " is-success",
|
|
246
|
+
missing: " is-accent",
|
|
247
|
+
unchecked: "",
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* A REQUIREMENT'S STANDING: whether the video has it (derived on this render,
|
|
252
|
+
* never stored), why, and the two things to do about it: Place and the Done
|
|
253
|
+
* tick. `PlaceAction` above is the one slot for those verbs, so no two
|
|
254
|
+
* surfaces offer different verbs for one card. The board inspector and the
|
|
255
|
+
* component sheet both draw these rows.
|
|
256
|
+
*/
|
|
257
|
+
export function RequirementRows({ doc, item }: { doc: Doc3D; item: ReferenceItem3D }) {
|
|
258
|
+
const store = useStore();
|
|
259
|
+
const locked = useLocked();
|
|
260
|
+
const report = requirementStatus(doc, item);
|
|
261
|
+
if (report === null) return null;
|
|
262
|
+
const spec = REQUIREMENT_STATUS_SPECS[report.state];
|
|
263
|
+
const done = item.done === true;
|
|
264
|
+
const where = [report.evidence?.scene, report.evidence?.layer, report.evidence?.clip].filter((x) => x !== undefined);
|
|
265
|
+
return (
|
|
266
|
+
<>
|
|
267
|
+
{/* A readout row holding the house badge and the reason, which wraps:
|
|
268
|
+
the house readout stands the badge 2px short, so a one-line Status
|
|
269
|
+
row is still 28px tall. */}
|
|
270
|
+
<InspectorRow label="Status" align="start" className="insp-readout">
|
|
271
|
+
<span className={`badge${REQUIREMENT_TONE[report.state]}`} data-testid="requirement-status">
|
|
272
|
+
{spec.label}
|
|
273
|
+
</span>
|
|
274
|
+
<span className="insp-dim is-text">
|
|
275
|
+
{report.reason || spec.title}
|
|
276
|
+
{where.length === 0 ? "" : ` (${where.map((x) => `#${x}`).join(" ")})`}
|
|
277
|
+
</span>
|
|
278
|
+
</InspectorRow>
|
|
279
|
+
<InspectorRow>
|
|
280
|
+
<PlaceAction doc={doc} item={item} />
|
|
281
|
+
<button
|
|
282
|
+
type="button"
|
|
283
|
+
className={`btn${done ? " is-active" : ""}`}
|
|
284
|
+
aria-pressed={done}
|
|
285
|
+
disabled={locked}
|
|
286
|
+
title={done ? "Clear the tick: the status goes back to what the video holds" : "Tick it by hand, whatever the video holds"}
|
|
287
|
+
onClick={() => store.setStoryboardDone(item.id, !done)}
|
|
288
|
+
>
|
|
289
|
+
{done ? "Clear done" : "Mark done"}
|
|
290
|
+
</button>
|
|
291
|
+
</InspectorRow>
|
|
292
|
+
</>
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Both option lists come off the schema's own spec tables, so the control's
|
|
297
|
+
// words, the card's hover text and the agent brief's legend cannot drift.
|
|
298
|
+
const USES = REFERENCE_USES.map((value) => ({
|
|
299
|
+
value,
|
|
300
|
+
label: REFERENCE_USE_SPECS[value].label,
|
|
301
|
+
title: REFERENCE_USE_SPECS[value].title,
|
|
302
|
+
}));
|
|
303
|
+
const IMPORTANCES = REFERENCE_IMPORTANCES.map((value) => ({
|
|
304
|
+
value,
|
|
305
|
+
label: REFERENCE_IMPORTANCE_SPECS[value].label,
|
|
306
|
+
title: REFERENCE_IMPORTANCE_SPECS[value].title,
|
|
307
|
+
}));
|
|
308
|
+
|
|
309
|
+
/** A typed or dragged frame as the card stores it: a whole, non-negative
|
|
310
|
+
* frame, or null for a value that is no number at all. */
|
|
311
|
+
function cardFrameOf(raw: unknown): number | null {
|
|
312
|
+
return typeof raw === "number" && Number.isFinite(raw) ? Math.max(0, Math.round(raw)) : null;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* THE CARD-USE BLOCK, one for every panel a card is picked in: the board
|
|
317
|
+
* inspector, the component sheet and the shot board's item inspector. It was
|
|
318
|
+
* three copies in three vocabularies ("Asset | Inspiration" in one, "In the
|
|
319
|
+
* shot | Look like this" in another, and nothing at all for a component card
|
|
320
|
+
* on the board, whose one panel is the sheet).
|
|
321
|
+
*
|
|
322
|
+
* Frame a component card's moment (`frame`): the preview holds it
|
|
323
|
+
* instead of looping. Unset reads the dash; the accessory in its
|
|
324
|
+
* box clears it, and the preview loops again.
|
|
325
|
+
* Use Asset (a requirement the video must hold) or Inspiration.
|
|
326
|
+
* Importance Must or Optional.
|
|
327
|
+
*
|
|
328
|
+
* Each writes null for its own default, so a card nobody has judged carries
|
|
329
|
+
* no keys, and a press on the lit chip writes nothing (`updateReference`
|
|
330
|
+
* returns the same document).
|
|
331
|
+
*/
|
|
332
|
+
export function CardUseRows({ card }: { card: ReferenceItem3D }) {
|
|
333
|
+
const store = useStore();
|
|
334
|
+
const locked = useLocked();
|
|
335
|
+
const frameScrub = useScrub((doc, raw) => {
|
|
336
|
+
const frame = cardFrameOf(raw);
|
|
337
|
+
return frame === null ? doc : updateReference(doc, card.id, { frame });
|
|
338
|
+
});
|
|
339
|
+
const held = card.type === "component" ? card.frame : undefined;
|
|
340
|
+
return (
|
|
341
|
+
<>
|
|
342
|
+
{card.type === "component" ? (
|
|
343
|
+
<InspectorRow>
|
|
344
|
+
<NumberField
|
|
345
|
+
// No frame is the loop, not frame 0: the field says so with its dash.
|
|
346
|
+
value={held ?? Number.NaN}
|
|
347
|
+
label="Frame"
|
|
348
|
+
ariaLabel="Frame, component"
|
|
349
|
+
min={0}
|
|
350
|
+
step={1}
|
|
351
|
+
title="The moment of the component this card means. The preview holds it."
|
|
352
|
+
onCommit={(next) => {
|
|
353
|
+
const frame = cardFrameOf(next);
|
|
354
|
+
if (frame !== null) store.updateReference(card.id, { frame });
|
|
355
|
+
}}
|
|
356
|
+
onScrubStart={frameScrub.start}
|
|
357
|
+
onScrub={frameScrub.move}
|
|
358
|
+
onScrubEnd={frameScrub.end}
|
|
359
|
+
trailing={
|
|
360
|
+
held === undefined ? null : (
|
|
361
|
+
<button
|
|
362
|
+
type="button"
|
|
363
|
+
className="insp-key"
|
|
364
|
+
aria-label="Clear the component frame"
|
|
365
|
+
title="Loop the preview again"
|
|
366
|
+
disabled={locked}
|
|
367
|
+
onClick={(e) => {
|
|
368
|
+
// The clear goes with the frame it clears, and the focus
|
|
369
|
+
// fell to <body> with it: it goes to the Frame field the
|
|
370
|
+
// clear sat in, which stays.
|
|
371
|
+
const field = e.currentTarget.closest(".numfield")?.querySelector<HTMLElement>(".numfield-input");
|
|
372
|
+
store.updateReference(card.id, { frame: null });
|
|
373
|
+
field?.focus();
|
|
374
|
+
}}
|
|
375
|
+
>
|
|
376
|
+
<IconUndo />
|
|
377
|
+
</button>
|
|
378
|
+
)
|
|
379
|
+
}
|
|
380
|
+
/>
|
|
381
|
+
</InspectorRow>
|
|
382
|
+
) : null}
|
|
383
|
+
<InspectorRow label="Use">
|
|
384
|
+
<SegmentedControl
|
|
385
|
+
options={USES}
|
|
386
|
+
value={referenceUseOf(card)}
|
|
387
|
+
ariaLabel="Use"
|
|
388
|
+
onChange={(value) => store.updateReference(card.id, { use: value === "asset" ? "asset" : null })}
|
|
389
|
+
/>
|
|
390
|
+
</InspectorRow>
|
|
391
|
+
<InspectorRow label="Importance">
|
|
392
|
+
<SegmentedControl
|
|
393
|
+
options={IMPORTANCES}
|
|
394
|
+
value={referenceImportanceOf(card)}
|
|
395
|
+
ariaLabel="Importance"
|
|
396
|
+
onChange={(value) => store.updateReference(card.id, { importance: value === "optional" ? "optional" : null })}
|
|
397
|
+
/>
|
|
398
|
+
</InspectorRow>
|
|
399
|
+
</>
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* THE CARD'S NOTE TO THE AGENT, the same row wherever the card is picked (the
|
|
405
|
+
* board inspector, the component sheet, the item inspector): multiline,
|
|
406
|
+
* trimmed, and dropped when emptied, so a card nobody annotated carries no
|
|
407
|
+
* key. A note card's own words are its `text`, edited where it is drawn.
|
|
408
|
+
*/
|
|
409
|
+
export function CardNoteRow({ card }: { card: Exclude<ReferenceItem3D, { type: "note" }> }) {
|
|
410
|
+
const store = useStore();
|
|
411
|
+
return (
|
|
412
|
+
<InspectorRow label="Note" align="start">
|
|
413
|
+
<TextField
|
|
414
|
+
multiline
|
|
415
|
+
value={card.note ?? ""}
|
|
416
|
+
placeholder="What should the agent notice?"
|
|
417
|
+
ariaLabel="Note"
|
|
418
|
+
onCommit={(text) => {
|
|
419
|
+
const note = text.trim();
|
|
420
|
+
if (note !== (card.note ?? "")) store.updateReference(card.id, { note: note === "" ? null : note });
|
|
421
|
+
}}
|
|
422
|
+
/>
|
|
423
|
+
</InspectorRow>
|
|
424
|
+
);
|
|
425
|
+
}
|