@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
package/src/state/insert.ts
CHANGED
|
@@ -16,17 +16,36 @@
|
|
|
16
16
|
// +y DOWN (see the header of @dailies-studio/shot's types.ts). The stage did the
|
|
17
17
|
// conversion because it is the half that knows the pane's pixel size. Nothing
|
|
18
18
|
// here divides by a zoom, and nothing here knows what a screen is.
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
DEFAULT_ARROW_HEAD,
|
|
21
|
+
DEFAULT_HTML_CSS,
|
|
22
|
+
DEFAULT_HTML_MARKUP,
|
|
23
|
+
DEFAULT_PRIMITIVE_COLOR,
|
|
24
|
+
DEFAULT_TEXT_CONTENT,
|
|
25
|
+
DEFAULT_TEXT_STYLE,
|
|
26
|
+
SPLINE_POINT_ID_PREFIX,
|
|
27
|
+
LAYER_KINDS,
|
|
28
|
+
clampSize,
|
|
29
|
+
iconNodesToPathD,
|
|
30
|
+
pathBounds,
|
|
31
|
+
pathToD,
|
|
32
|
+
translatePath,
|
|
33
|
+
} from "@dailies-studio/shot/doc";
|
|
20
34
|
import type {
|
|
35
|
+
IconNode,
|
|
21
36
|
InstanceContractRef,
|
|
22
37
|
Layer3D,
|
|
23
38
|
Layer3DKind,
|
|
39
|
+
MeshPrimitiveKind,
|
|
40
|
+
MeshPrimitive,
|
|
24
41
|
Paint,
|
|
25
42
|
PathData,
|
|
26
43
|
Stroke,
|
|
27
|
-
|
|
44
|
+
SplinePoint3D,
|
|
28
45
|
} from "@dailies-studio/shot/doc";
|
|
29
|
-
import type {
|
|
46
|
+
import type { SplineCreateDraft } from "../bridgeTypes.js";
|
|
47
|
+
import { parseHex } from "@dailies-studio/engine";
|
|
48
|
+
import type { Value, Vec2, Vec3 } from "@dailies-studio/engine";
|
|
30
49
|
import type { InsertTool } from "../tools.js";
|
|
31
50
|
|
|
32
51
|
/**
|
|
@@ -70,35 +89,35 @@ export const DEFAULT_DEVICE_SIZE: Vec2 = [400, 800];
|
|
|
70
89
|
* resized to something a card actually wants. */
|
|
71
90
|
export const DEFAULT_HTML_SIZE: Vec2 = [720, 360];
|
|
72
91
|
|
|
73
|
-
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
export const
|
|
92
|
+
/** The colour a fresh paint opens on, for BOTH doors into one: the draw tools
|
|
93
|
+
* here and the inspector's own "+ Fill" (`FillSection`). White on the
|
|
94
|
+
* near-black a fresh scene opens on, because a default that draws nothing looks
|
|
95
|
+
* exactly like a tool that did nothing. */
|
|
96
|
+
export const DEFAULT_PAINT_COLOR = "#ffffff";
|
|
78
97
|
|
|
79
|
-
/** What a drawn shape is filled with.
|
|
80
|
-
|
|
81
|
-
* nothing. */
|
|
82
|
-
export const DRAW_FILL: Paint = { type: "solid", color: "#ffffff" };
|
|
98
|
+
/** What a drawn shape is filled with. */
|
|
99
|
+
export const DRAW_FILL: Paint = { type: "solid", color: DEFAULT_PAINT_COLOR };
|
|
83
100
|
|
|
84
101
|
/** …and what a drawn line, arrow or path is stroked with. A line layer with no
|
|
85
102
|
* stroke is not drawn at all (`drawStroke` is the only thing that draws one),
|
|
86
|
-
* so this is not a nicety.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
sizePx: 64,
|
|
93
|
-
lineHeight: 1.2,
|
|
94
|
-
letterSpacingPx: 0,
|
|
95
|
-
align: "left",
|
|
103
|
+
* so this is not a nicety.
|
|
104
|
+
*
|
|
105
|
+
* 4px, and deliberately NOT `StrokeSection`'s `DEFAULT_STROKE` (1px): this
|
|
106
|
+
* stroke IS the drawn mark, while that one is a hairline added around a shape
|
|
107
|
+
* that already draws. Two gestures, two widths. Do not merge them. */
|
|
108
|
+
export const DRAW_STROKE: Stroke = {
|
|
96
109
|
color: "#ffffff",
|
|
110
|
+
widthPx: 4,
|
|
111
|
+
align: "center",
|
|
97
112
|
};
|
|
98
113
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
114
|
+
// The text style, the text content, the html snippet and the arrowhead ratio
|
|
115
|
+
// USED to be declared here too, and the agent's `add_layer` had a byte-identical
|
|
116
|
+
// copy of each. They now ship from `@dailies-studio/shot/doc` (`layerOps.ts`),
|
|
117
|
+
// which is the only place either door should read them from. The fill and the
|
|
118
|
+
// stroke above stay LOCAL on purpose: a drawn mark takes the author's own drag
|
|
119
|
+
// and the agent has no drag to take, so those two genuinely differ per lane.
|
|
120
|
+
// See layerOps.ts for which defaults were hoisted and which were not.
|
|
102
121
|
|
|
103
122
|
/** Where the playhead is and how long its scene is: everything a new layer
|
|
104
123
|
* needs to land in TIME. */
|
|
@@ -107,6 +126,13 @@ export interface InsertContext {
|
|
|
107
126
|
sceneFrame: number;
|
|
108
127
|
/** The scene's own length, which is where a lifetime that starts late ends. */
|
|
109
128
|
durationFrames: number;
|
|
129
|
+
/**
|
|
130
|
+
* The design point (scene px) a default-placed layer centres on: the centre
|
|
131
|
+
* of what the frame shows, so an Add lands in view under a panned or pushed
|
|
132
|
+
* camera (F54). Absent = the design origin, which is also the answer inside
|
|
133
|
+
* a definition, whose interior has no camera.
|
|
134
|
+
*/
|
|
135
|
+
origin?: DrawPoint;
|
|
110
136
|
/** For the kinds that name one. Absent for everything else. */
|
|
111
137
|
asset?: string;
|
|
112
138
|
/**
|
|
@@ -122,6 +148,27 @@ export interface InsertContext {
|
|
|
122
148
|
* Absent = an image, which is what every caller before video existed meant.
|
|
123
149
|
*/
|
|
124
150
|
assetKind?: "image" | "video";
|
|
151
|
+
/**
|
|
152
|
+
* width / height of the source the browser decoded, for the media arm.
|
|
153
|
+
*
|
|
154
|
+
* ── WHY THE GEOMETRY NEEDS IT ─────────────────────────────────────────
|
|
155
|
+
*
|
|
156
|
+
* Without it every dropped recording landed in the literal `DEFAULT_DRAW_SIZE`
|
|
157
|
+
* box at `fit: "fill"`, so a 16:9 screen capture arrived cropped to 3:2 with
|
|
158
|
+
* no affordance for getting its aspect back but typing numbers. A mockup tool
|
|
159
|
+
* whose first act is to crop the thing you gave it is not one.
|
|
160
|
+
*
|
|
161
|
+
* A NUMBER AND NOT THE PIXELS, because the box is design px and the source's
|
|
162
|
+
* own resolution is not: a 3840x2160 capture and a 1280x720 one want the same
|
|
163
|
+
* plate. Absent (or non-finite, or non-positive) reproduces the old geometry
|
|
164
|
+
* exactly, which is what a host with nothing to measure has to fall back to —
|
|
165
|
+
* jsdom decodes no media, and a real browser can still fail to.
|
|
166
|
+
*
|
|
167
|
+
* MEASURED IN THE STORE, never here: this file's contract is no document, no
|
|
168
|
+
* history, no asynchrony, no DOM, and that is what keeps `boxOf` provable by
|
|
169
|
+
* calling it.
|
|
170
|
+
*/
|
|
171
|
+
sourceAspect?: number;
|
|
125
172
|
}
|
|
126
173
|
|
|
127
174
|
/**
|
|
@@ -158,7 +205,10 @@ interface DrawBox {
|
|
|
158
205
|
* saved. The ceiling comes along for free, which is what a drag across a
|
|
159
206
|
* zoomed-way-out pane needs.
|
|
160
207
|
*/
|
|
161
|
-
function boxOf(
|
|
208
|
+
function boxOf(
|
|
209
|
+
points: readonly DrawPoint[],
|
|
210
|
+
sourceAspect?: number,
|
|
211
|
+
): DrawBox | null {
|
|
162
212
|
const first = points[0];
|
|
163
213
|
const last = points[points.length - 1];
|
|
164
214
|
if (first === undefined || last === undefined) return null;
|
|
@@ -178,7 +228,10 @@ function boxOf(points: readonly DrawPoint[]): DrawBox | null {
|
|
|
178
228
|
const rawW = maxX - minX;
|
|
179
229
|
const rawH = maxY - minY;
|
|
180
230
|
if (rawW <= 0 && rawH <= 0) {
|
|
181
|
-
|
|
231
|
+
// A CLICK. The default box, re-shaped to the source's aspect at the SAME
|
|
232
|
+
// AREA, so a portrait capture and a landscape one land equally prominent
|
|
233
|
+
// rather than one of them dwarfing the other.
|
|
234
|
+
const [w, h] = defaultBoxFor(sourceAspect);
|
|
182
235
|
return boxOf([
|
|
183
236
|
[minX - w / 2, minY - h / 2],
|
|
184
237
|
[minX + w / 2, minY + h / 2],
|
|
@@ -193,15 +246,64 @@ function boxOf(points: readonly DrawPoint[]): DrawBox | null {
|
|
|
193
246
|
rawH > 0 ? (p[1] - minY) / rawH : 0.5,
|
|
194
247
|
];
|
|
195
248
|
|
|
249
|
+
// A REAL DRAG. The aspect is fitted INSIDE what the author drew and never
|
|
250
|
+
// grows past it: they expressed an extent, and honouring the source's shape
|
|
251
|
+
// must not be a licence to spill outside the rectangle they were looking at.
|
|
252
|
+
const [fitW, fitH] = fitAspect(rawW, rawH, sourceAspect);
|
|
253
|
+
|
|
196
254
|
return {
|
|
197
255
|
min: [minX, minY],
|
|
198
256
|
position: [(minX + maxX) / 2, (minY + maxY) / 2, 0],
|
|
199
|
-
size: [clampSize(
|
|
257
|
+
size: [clampSize(fitW), clampSize(fitH)],
|
|
200
258
|
from: frac(first),
|
|
201
259
|
to: frac(last),
|
|
202
260
|
};
|
|
203
261
|
}
|
|
204
262
|
|
|
263
|
+
/** A usable width/height ratio, or null. One gate for every caller, so "no
|
|
264
|
+
* aspect", "NaN because the decode failed" and "0 because the element reported
|
|
265
|
+
* nothing yet" all take the identical fallback path. */
|
|
266
|
+
function usableAspect(sourceAspect: number | undefined): number | null {
|
|
267
|
+
return sourceAspect !== undefined &&
|
|
268
|
+
Number.isFinite(sourceAspect) &&
|
|
269
|
+
sourceAspect > 0
|
|
270
|
+
? sourceAspect
|
|
271
|
+
: null;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** The click box, at `DEFAULT_DRAW_SIZE`'s AREA and the source's shape. Without
|
|
275
|
+
* an aspect it is `DEFAULT_DRAW_SIZE` itself, to the byte. */
|
|
276
|
+
function defaultBoxFor(sourceAspect: number | undefined): Vec2 {
|
|
277
|
+
const aspect = usableAspect(sourceAspect);
|
|
278
|
+
const [w, h] = DEFAULT_DRAW_SIZE;
|
|
279
|
+
if (aspect === null) return [w, h];
|
|
280
|
+
const area = w * h;
|
|
281
|
+
const height = Math.sqrt(area / aspect);
|
|
282
|
+
return [height * aspect, height];
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** The largest box of `sourceAspect` that fits inside `w` x `h`. Without an
|
|
286
|
+
* aspect the drawn box passes through untouched. */
|
|
287
|
+
function fitAspect(
|
|
288
|
+
w: number,
|
|
289
|
+
h: number,
|
|
290
|
+
sourceAspect: number | undefined,
|
|
291
|
+
): Vec2 {
|
|
292
|
+
const aspect = usableAspect(sourceAspect);
|
|
293
|
+
// A degenerate axis has no shape to honour: a horizontal line drag is floored
|
|
294
|
+
// by `clampSize` into a box the author never framed, and fitting an aspect
|
|
295
|
+
// into it would invent a height out of the floor.
|
|
296
|
+
if (aspect === null || w <= 0 || h <= 0) return [w, h];
|
|
297
|
+
return w / h > aspect ? [h * aspect, h] : [w, w / aspect];
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/** Only the media arm has a source to honour. Every other tool draws exactly
|
|
301
|
+
* the box it was given, and a stray `sourceAspect` on the context must not
|
|
302
|
+
* quietly re-shape a rectangle or a device. */
|
|
303
|
+
function aspectFor(tool: InsertTool, ctx: InsertContext): number | undefined {
|
|
304
|
+
return tool === "image" ? ctx.sourceAspect : undefined;
|
|
305
|
+
}
|
|
306
|
+
|
|
205
307
|
/**
|
|
206
308
|
* THE INSERT MAP. Keyed on `InsertTool`, so a tool added to the table without
|
|
207
309
|
* an answer here is a compile error rather than a button that does nothing.
|
|
@@ -211,17 +313,42 @@ function boxOf(points: readonly DrawPoint[]): DrawBox | null {
|
|
|
211
313
|
*/
|
|
212
314
|
const INSERT: Record<
|
|
213
315
|
InsertTool,
|
|
214
|
-
(
|
|
316
|
+
(
|
|
317
|
+
box: DrawBox,
|
|
318
|
+
points: readonly DrawPoint[],
|
|
319
|
+
ctx: InsertContext,
|
|
320
|
+
) => NewLayer3D | null
|
|
215
321
|
> = {
|
|
216
|
-
rect: (box) => ({
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
322
|
+
rect: (box) => ({
|
|
323
|
+
kind: "rect",
|
|
324
|
+
label: "Rect",
|
|
325
|
+
...common(box),
|
|
326
|
+
paint: { fill: DRAW_FILL },
|
|
327
|
+
}),
|
|
328
|
+
ellipse: (box) => ({
|
|
329
|
+
kind: "ellipse",
|
|
330
|
+
label: "Ellipse",
|
|
221
331
|
...common(box),
|
|
222
|
-
paint: {},
|
|
223
|
-
text: { content: "Text", style: DRAW_TEXT_STYLE },
|
|
332
|
+
paint: { fill: DRAW_FILL },
|
|
224
333
|
}),
|
|
334
|
+
// A CLICK makes an auto-width box (`TextBox`), as in Figma: its left edge on
|
|
335
|
+
// the click, centred on it vertically, at a size estimated here and measured
|
|
336
|
+
// exactly by the stage once it draws. A DRAG makes a fixed box that wraps
|
|
337
|
+
// inside the dragged width.
|
|
338
|
+
text: (box, points) => {
|
|
339
|
+
const [x, y] = points[0]!;
|
|
340
|
+
const click = points.every((p) => p[0] === x && p[1] === y);
|
|
341
|
+
const style = DEFAULT_TEXT_STYLE;
|
|
342
|
+
const w = Math.ceil(DEFAULT_TEXT_CONTENT.length * style.sizePx * 0.6);
|
|
343
|
+
const h = style.sizePx * style.lineHeight;
|
|
344
|
+
return {
|
|
345
|
+
kind: "text",
|
|
346
|
+
label: "Text",
|
|
347
|
+
...common(click ? { ...box, position: [x + w / 2, y, 0], size: [w, h] } : box),
|
|
348
|
+
paint: {},
|
|
349
|
+
text: { content: DEFAULT_TEXT_CONTENT, style, ...(click ? { box: "auto" as const } : {}) },
|
|
350
|
+
};
|
|
351
|
+
},
|
|
225
352
|
// ONE ARM, TWO KINDS. `image` and `video` are one pipeline everywhere it
|
|
226
353
|
// matters — the runtime collapses both to {asset, fit, crop} and draws them
|
|
227
354
|
// through one plane — so the Frame tool draws one box and the file the author
|
|
@@ -232,7 +359,13 @@ const INSERT: Record<
|
|
|
232
359
|
// "fill" and not "fit", for both: the author drew the box they want
|
|
233
360
|
// covered, and letterboxing it would leave the drawn rectangle half empty.
|
|
234
361
|
if (ctx.assetKind !== "video") {
|
|
235
|
-
return {
|
|
362
|
+
return {
|
|
363
|
+
kind: "image",
|
|
364
|
+
label: "Image",
|
|
365
|
+
...common(box),
|
|
366
|
+
paint: {},
|
|
367
|
+
image: { asset: ctx.asset, fit: "fill" },
|
|
368
|
+
};
|
|
236
369
|
}
|
|
237
370
|
// THE SOURCE STARTS WHERE THE LAYER DOES. `withLifetime` puts the layer's
|
|
238
371
|
// `in` at the playhead, and a video whose source clock still began at scene
|
|
@@ -247,7 +380,11 @@ const INSERT: Record<
|
|
|
247
380
|
label: "Video",
|
|
248
381
|
...common(box),
|
|
249
382
|
paint: {},
|
|
250
|
-
video: {
|
|
383
|
+
video: {
|
|
384
|
+
asset: ctx.asset,
|
|
385
|
+
fit: "fill",
|
|
386
|
+
...(start > 0 ? { offsetFrames: start } : {}),
|
|
387
|
+
},
|
|
251
388
|
};
|
|
252
389
|
},
|
|
253
390
|
line: (box) => ({
|
|
@@ -262,7 +399,7 @@ const INSERT: Record<
|
|
|
262
399
|
label: "Arrow",
|
|
263
400
|
...common(box),
|
|
264
401
|
paint: { stroke: DRAW_STROKE },
|
|
265
|
-
arrow: { from: box.from, to: box.to, head:
|
|
402
|
+
arrow: { from: box.from, to: box.to, head: DEFAULT_ARROW_HEAD },
|
|
266
403
|
}),
|
|
267
404
|
path: (box, points) => ({
|
|
268
405
|
kind: "path",
|
|
@@ -278,7 +415,7 @@ const INSERT: Record<
|
|
|
278
415
|
};
|
|
279
416
|
|
|
280
417
|
/** The fields every drawn layer fills in the same way. */
|
|
281
|
-
function common(box: DrawBox): { transform: Layer3D["transform"]
|
|
418
|
+
function common(box: DrawBox): { transform: Layer3D["transform"] } {
|
|
282
419
|
return {
|
|
283
420
|
transform: {
|
|
284
421
|
position: [box.position[0], box.position[1], box.position[2]],
|
|
@@ -287,7 +424,6 @@ function common(box: DrawBox): { transform: Layer3D["transform"]; blocks: [] } {
|
|
|
287
424
|
rollDeg: 0,
|
|
288
425
|
opacity: 1,
|
|
289
426
|
},
|
|
290
|
-
blocks: [],
|
|
291
427
|
};
|
|
292
428
|
}
|
|
293
429
|
|
|
@@ -295,7 +431,12 @@ function common(box: DrawBox): { transform: Layer3D["transform"]; blocks: [] } {
|
|
|
295
431
|
* a document written twice from the same drag is byte-identical. */
|
|
296
432
|
function polyline(points: readonly DrawPoint[], min: Vec2): string {
|
|
297
433
|
const n = (v: number): string => String(Math.round(v * 100) / 100);
|
|
298
|
-
return points
|
|
434
|
+
return points
|
|
435
|
+
.map(
|
|
436
|
+
(p, i) =>
|
|
437
|
+
`${i === 0 ? "M" : "L"} ${n(p[0] - min[0])} ${n(p[1] - min[1])}`,
|
|
438
|
+
)
|
|
439
|
+
.join(" ");
|
|
299
440
|
}
|
|
300
441
|
|
|
301
442
|
/**
|
|
@@ -315,7 +456,10 @@ export function layerFromDraw(
|
|
|
315
456
|
// already two identical ones, so this is the only arity worth stating.
|
|
316
457
|
if (tool === "path" && dedupe(points).length < 2) return null;
|
|
317
458
|
const cleaned = tool === "path" ? dedupe(points) : points;
|
|
318
|
-
|
|
459
|
+
// The aspect reaches `boxOf` and nothing else: it is a GEOMETRY input, so it
|
|
460
|
+
// belongs where the box is decided rather than in the media arm, which would
|
|
461
|
+
// then be re-deriving a position and a size the box already computed.
|
|
462
|
+
const box = boxOf(cleaned, aspectFor(tool, ctx));
|
|
319
463
|
if (box === null) return null;
|
|
320
464
|
const layer = INSERT[tool](box, cleaned, ctx);
|
|
321
465
|
return layer === null ? null : withLifetime(layer, ctx);
|
|
@@ -337,7 +481,10 @@ export function layerFromDraw(
|
|
|
337
481
|
* origin into `d` and break `pathToD(points) === d` the moment the document was
|
|
338
482
|
* opened.
|
|
339
483
|
*/
|
|
340
|
-
export function penLayer(
|
|
484
|
+
export function penLayer(
|
|
485
|
+
data: PathData,
|
|
486
|
+
ctx: InsertContext,
|
|
487
|
+
): NewLayer3D | null {
|
|
341
488
|
if (data.points.length < 2) return null;
|
|
342
489
|
const bounds = pathBounds(data);
|
|
343
490
|
const box = boxOf([
|
|
@@ -355,7 +502,11 @@ export function penLayer(data: PathData, ctx: InsertContext): NewLayer3D | null
|
|
|
355
502
|
// author was drawing, and a filled shape appearing on close would be a
|
|
356
503
|
// second edit they did not make. The inspector adds one in a click.
|
|
357
504
|
paint: { stroke: DRAW_STROKE },
|
|
358
|
-
path: {
|
|
505
|
+
path: {
|
|
506
|
+
d: pathToD(local),
|
|
507
|
+
viewBox: [box.size[0], box.size[1]],
|
|
508
|
+
points: local,
|
|
509
|
+
},
|
|
359
510
|
},
|
|
360
511
|
ctx,
|
|
361
512
|
);
|
|
@@ -367,25 +518,153 @@ export function meshLayer(asset: string, ctx: InsertContext): NewLayer3D {
|
|
|
367
518
|
{
|
|
368
519
|
kind: "mesh",
|
|
369
520
|
label: "Model",
|
|
370
|
-
transform: centred(DEFAULT_MESH_SIZE),
|
|
521
|
+
transform: centred(DEFAULT_MESH_SIZE, ctx),
|
|
371
522
|
// Inert on a mesh — the look is `mesh.shading`, not a Canvas2D fill.
|
|
372
523
|
paint: {},
|
|
373
|
-
blocks: [],
|
|
374
524
|
mesh: { asset },
|
|
375
525
|
},
|
|
376
526
|
ctx,
|
|
377
527
|
);
|
|
378
528
|
}
|
|
379
529
|
|
|
530
|
+
/** A shape's box. Square like a model's: every primitive derives its radii
|
|
531
|
+
* from the box, and a square makes no claim about which way a shape is long. */
|
|
532
|
+
export const DEFAULT_SHAPE_SIZE: Vec2 = [300, 300];
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* A parametric shape (schema v11), from the ADD menu.
|
|
536
|
+
*
|
|
537
|
+
* TOON BY DEFAULT, which is this factory's one real decision: an unlit
|
|
538
|
+
* flat-colour solid has no visible form — the silhouette is all there is — so
|
|
539
|
+
* a fresh box would read as a rounded rectangle until the author found the
|
|
540
|
+
* shading row. The default colour is authored INTO the document rather than
|
|
541
|
+
* left to the runtime's fallback, so the inspector's colour well shows the
|
|
542
|
+
* value the stage is actually drawing.
|
|
543
|
+
*/
|
|
544
|
+
export function shapeLayer(
|
|
545
|
+
kind: MeshPrimitiveKind,
|
|
546
|
+
ctx: InsertContext,
|
|
547
|
+
): NewLayer3D {
|
|
548
|
+
return withLifetime(
|
|
549
|
+
{
|
|
550
|
+
kind: "mesh",
|
|
551
|
+
label: kind.charAt(0).toUpperCase() + kind.slice(1),
|
|
552
|
+
transform: centred(DEFAULT_SHAPE_SIZE, ctx),
|
|
553
|
+
// Inert on a mesh — the look is colour + shading, not a Canvas2D fill.
|
|
554
|
+
paint: {},
|
|
555
|
+
mesh: {
|
|
556
|
+
primitive: primitiveForKind(kind),
|
|
557
|
+
color: DEFAULT_PRIMITIVE_COLOR,
|
|
558
|
+
shading: { mode: "toon" },
|
|
559
|
+
},
|
|
560
|
+
},
|
|
561
|
+
ctx,
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/** A complete valid primitive for an Add-menu insert or an inspector kind
|
|
566
|
+
* switch. Structural spline defaults cannot be represented by `{kind}`. */
|
|
567
|
+
export function primitiveForKind(kind: MeshPrimitiveKind): MeshPrimitive {
|
|
568
|
+
if (kind === "spline") {
|
|
569
|
+
return {
|
|
570
|
+
kind,
|
|
571
|
+
frameSize: [300, 300],
|
|
572
|
+
points: [
|
|
573
|
+
{
|
|
574
|
+
id: `${SPLINE_POINT_ID_PREFIX}0`,
|
|
575
|
+
p: [-150, 60, 0],
|
|
576
|
+
out: [-50, -60, 0],
|
|
577
|
+
},
|
|
578
|
+
{ id: `${SPLINE_POINT_ID_PREFIX}1`, p: [150, -60, 0], in: [50, 60, 0] },
|
|
579
|
+
],
|
|
580
|
+
basis: "bezier",
|
|
581
|
+
profile: { kind: "tube", radiusPx: 8 },
|
|
582
|
+
caps: "round",
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
return { kind } as MeshPrimitive;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/** Normalize a stage-authored world-space draft into one local spline layer. */
|
|
589
|
+
export function splineLayer(
|
|
590
|
+
draft: SplineCreateDraft,
|
|
591
|
+
ctx: InsertContext,
|
|
592
|
+
): NewLayer3D | null {
|
|
593
|
+
if (draft.points.length < (draft.closed ? 3 : 2)) return null;
|
|
594
|
+
const values = draft.points.flatMap((point) =>
|
|
595
|
+
[point.p, point.in, point.out].filter(isVec3),
|
|
596
|
+
);
|
|
597
|
+
if (values.length === 0) return null;
|
|
598
|
+
const min: Vec3 = [Infinity, Infinity, Infinity];
|
|
599
|
+
const max: Vec3 = [-Infinity, -Infinity, -Infinity];
|
|
600
|
+
for (const value of values) {
|
|
601
|
+
for (let axis = 0; axis < 3; axis += 1) {
|
|
602
|
+
min[axis] = Math.min(min[axis]!, value[axis]!);
|
|
603
|
+
max[axis] = Math.max(max[axis]!, value[axis]!);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
if (![...min, ...max].every(Number.isFinite)) return null;
|
|
607
|
+
const centre: Vec3 = [
|
|
608
|
+
(min[0] + max[0]) / 2,
|
|
609
|
+
(min[1] + max[1]) / 2,
|
|
610
|
+
(min[2] + max[2]) / 2,
|
|
611
|
+
];
|
|
612
|
+
const size: Vec2 = [clampSize(max[0] - min[0]), clampSize(max[1] - min[1])];
|
|
613
|
+
const local = (value: readonly [number, number, number]): Vec3 => [
|
|
614
|
+
value[0] - centre[0],
|
|
615
|
+
value[1] - centre[1],
|
|
616
|
+
value[2] - centre[2],
|
|
617
|
+
];
|
|
618
|
+
const points: SplinePoint3D[] = draft.points.map((point, index) => ({
|
|
619
|
+
id: `${SPLINE_POINT_ID_PREFIX}${index}`,
|
|
620
|
+
p: local(point.p),
|
|
621
|
+
...(point.in === undefined ? {} : { in: local(point.in) }),
|
|
622
|
+
...(point.out === undefined ? {} : { out: local(point.out) }),
|
|
623
|
+
}));
|
|
624
|
+
return withLifetime(
|
|
625
|
+
{
|
|
626
|
+
kind: "mesh",
|
|
627
|
+
label: "Spline",
|
|
628
|
+
transform: {
|
|
629
|
+
position: centre,
|
|
630
|
+
size,
|
|
631
|
+
tilt: [0, 0],
|
|
632
|
+
rollDeg: 0,
|
|
633
|
+
opacity: 1,
|
|
634
|
+
},
|
|
635
|
+
paint: {},
|
|
636
|
+
mesh: {
|
|
637
|
+
primitive: {
|
|
638
|
+
kind: "spline",
|
|
639
|
+
frameSize: [size[0], size[1]],
|
|
640
|
+
points,
|
|
641
|
+
basis: draft.basis,
|
|
642
|
+
...(draft.closed ? { closed: true } : {}),
|
|
643
|
+
profile: { kind: "tube", radiusPx: 8 },
|
|
644
|
+
caps: "round",
|
|
645
|
+
},
|
|
646
|
+
color: DEFAULT_PRIMITIVE_COLOR,
|
|
647
|
+
shading: { mode: "toon" },
|
|
648
|
+
},
|
|
649
|
+
},
|
|
650
|
+
ctx,
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
function isVec3(value: Vec3 | undefined): value is Vec3 {
|
|
655
|
+
return (
|
|
656
|
+
value !== undefined && value.length === 3 && value.every(Number.isFinite)
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
|
|
380
660
|
/** A device slab, from the ADD menu. */
|
|
381
661
|
export function deviceLayer(preset: string, ctx: InsertContext): NewLayer3D {
|
|
382
662
|
return withLifetime(
|
|
383
663
|
{
|
|
384
664
|
kind: "device",
|
|
385
665
|
label: preset.charAt(0).toUpperCase() + preset.slice(1),
|
|
386
|
-
transform: centred(DEFAULT_DEVICE_SIZE),
|
|
666
|
+
transform: centred(DEFAULT_DEVICE_SIZE, ctx),
|
|
387
667
|
paint: {},
|
|
388
|
-
blocks: [],
|
|
389
668
|
device: { preset, children: [] },
|
|
390
669
|
},
|
|
391
670
|
ctx,
|
|
@@ -408,15 +687,9 @@ export function htmlLayer(ctx: InsertContext): NewLayer3D {
|
|
|
408
687
|
{
|
|
409
688
|
kind: "html",
|
|
410
689
|
label: "Html",
|
|
411
|
-
transform: centred(DEFAULT_HTML_SIZE),
|
|
690
|
+
transform: centred(DEFAULT_HTML_SIZE, ctx),
|
|
412
691
|
paint: {},
|
|
413
|
-
|
|
414
|
-
html: {
|
|
415
|
-
markup: `<div class="root"><span>Html</span></div>`,
|
|
416
|
-
css:
|
|
417
|
-
".root{width:100%;height:100%;display:flex;align-items:center;justify-content:center;" +
|
|
418
|
-
"font:600 64px Inter,system-ui,sans-serif;color:#fff}",
|
|
419
|
-
},
|
|
692
|
+
html: { markup: DEFAULT_HTML_MARKUP, css: DEFAULT_HTML_CSS },
|
|
420
693
|
},
|
|
421
694
|
ctx,
|
|
422
695
|
);
|
|
@@ -429,32 +702,113 @@ export function htmlLayer(ctx: InsertContext): NewLayer3D {
|
|
|
429
702
|
* hash it was bound at, and the contract itself when the registry says a
|
|
430
703
|
* generated file could not import it. That is what makes a later source edit
|
|
431
704
|
* show up as DRIFT the author resolves, instead of silently re-typing props
|
|
432
|
-
* authored against the old shape. `props`
|
|
433
|
-
* the contract's
|
|
434
|
-
* insert time would freeze them against later edits to
|
|
705
|
+
* authored against the old shape. `props` seeds the instance's own values;
|
|
706
|
+
* every unset key takes the contract's default through `buildProps`, and
|
|
707
|
+
* baking defaults in at insert time would freeze them against later edits to
|
|
708
|
+
* the component itself.
|
|
709
|
+
*
|
|
710
|
+
* ONE SIZE, `frame`: the whole frame. A shipped component composes against its
|
|
711
|
+
* box and its card thumbnail shows it on a whole frame, so what is clicked is
|
|
712
|
+
* what lands, from every door (F51, F58).
|
|
435
713
|
*/
|
|
436
714
|
export function instanceLayer(
|
|
437
715
|
component: string,
|
|
438
716
|
label: string,
|
|
439
717
|
binding: { contractHash?: string; contract?: InstanceContractRef },
|
|
718
|
+
props: Readonly<Record<string, Value>>,
|
|
719
|
+
frame: Vec2,
|
|
440
720
|
ctx: InsertContext,
|
|
441
721
|
): NewLayer3D {
|
|
442
722
|
return withLifetime(
|
|
443
723
|
{
|
|
444
724
|
kind: "instance",
|
|
445
725
|
label,
|
|
446
|
-
transform: centred(
|
|
726
|
+
transform: centred(frame, ctx),
|
|
447
727
|
// Inert on an instance, as on a mesh: the component paints itself.
|
|
448
728
|
paint: {},
|
|
449
|
-
|
|
450
|
-
|
|
729
|
+
instance: { component, props: { ...props }, ...binding },
|
|
730
|
+
},
|
|
731
|
+
ctx,
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/** An icon's box. Square, like the 24×24 grid it is drawn on. */
|
|
736
|
+
export const ICON_SIZE = 96;
|
|
737
|
+
|
|
738
|
+
/** The two inks an icon is drawn in: light on a dark Shot, dark on a light
|
|
739
|
+
* one. */
|
|
740
|
+
export const ICON_INK = { light: "#f5f5f5", dark: "#111111" } as const;
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
* The ink that reads on `background`: its mean relative luminance (sRGB
|
|
744
|
+
* weights, linearised) against 0.18, where black and white ink contrast
|
|
745
|
+
* equally. A gradient averages its stops.
|
|
746
|
+
* A colour that does not parse counts as dark, the scene's own default.
|
|
747
|
+
*/
|
|
748
|
+
export function inkFor(background: Paint): string {
|
|
749
|
+
const colors = background.type === "solid" ? [background.color] : background.stops.map((s) => s.color);
|
|
750
|
+
const lin = (c: number): number => (c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4);
|
|
751
|
+
let sum = 0;
|
|
752
|
+
for (const color of colors) {
|
|
753
|
+
try {
|
|
754
|
+
const { r, g, b } = parseHex(color);
|
|
755
|
+
sum += 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b);
|
|
756
|
+
} catch {
|
|
757
|
+
/* not a hex: counted as black */
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
return colors.length > 0 && sum / colors.length > 0.18 ? ICON_INK.dark : ICON_INK.light;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
/** The name "Use as background" gives the ground it places. */
|
|
764
|
+
export const BACKGROUND_LABEL = "Background";
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* The ground "Use as background" placed in this Shot, or null: the back root
|
|
768
|
+
* layer, locked and named Background. Locked layers skip stage picks, so the
|
|
769
|
+
* Shot inspector's door selects it by this.
|
|
770
|
+
*/
|
|
771
|
+
export function backgroundLayerOf(scene: { layers: readonly string[]; layersById: Readonly<Record<string, Layer3D>> } | undefined): string | null {
|
|
772
|
+
const id = scene?.layers[0];
|
|
773
|
+
const layer = id === undefined ? undefined : scene?.layersById[id];
|
|
774
|
+
return layer !== undefined && layer.locked === true && layer.label === BACKGROUND_LABEL ? layer.id : null;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/**
|
|
778
|
+
* A Lucide icon as a PATH layer: editable, recolourable and keyframable, never
|
|
779
|
+
* an image. Lucide draws a 2px stroke on a 24px grid with round ends and no
|
|
780
|
+
* fill, and the stroke width here is that 2px scaled to the box (8px on 96),
|
|
781
|
+
* so the layer looks like the tile the author clicked.
|
|
782
|
+
*/
|
|
783
|
+
export function iconLayer(
|
|
784
|
+
name: string,
|
|
785
|
+
nodes: readonly IconNode[],
|
|
786
|
+
ink: string,
|
|
787
|
+
ctx: InsertContext,
|
|
788
|
+
): NewLayer3D {
|
|
789
|
+
return withLifetime(
|
|
790
|
+
{
|
|
791
|
+
kind: "path",
|
|
792
|
+
label: name,
|
|
793
|
+
transform: centred([ICON_SIZE, ICON_SIZE], ctx),
|
|
794
|
+
paint: {
|
|
795
|
+
stroke: { color: ink, widthPx: (2 * ICON_SIZE) / 24, align: "center", cap: "round", join: "round" },
|
|
796
|
+
},
|
|
797
|
+
path: { d: iconNodesToPathD(nodes), viewBox: [24, 24] },
|
|
451
798
|
},
|
|
452
799
|
ctx,
|
|
453
800
|
);
|
|
454
801
|
}
|
|
455
802
|
|
|
456
|
-
function centred(size: Vec2): Layer3D["transform"] {
|
|
457
|
-
|
|
803
|
+
function centred(size: Vec2, ctx: InsertContext): Layer3D["transform"] {
|
|
804
|
+
const [x, y] = ctx.origin ?? [0, 0];
|
|
805
|
+
return {
|
|
806
|
+
position: [x, y, 0],
|
|
807
|
+
size: [size[0], size[1]],
|
|
808
|
+
tilt: [0, 0],
|
|
809
|
+
rollDeg: 0,
|
|
810
|
+
opacity: 1,
|
|
811
|
+
};
|
|
458
812
|
}
|
|
459
813
|
|
|
460
814
|
/**
|
|
@@ -470,10 +824,33 @@ function centred(size: Vec2): Layer3D["transform"] {
|
|
|
470
824
|
* Same placement, one fewer thing to maintain.
|
|
471
825
|
*/
|
|
472
826
|
function withLifetime(layer: NewLayer3D, ctx: InsertContext): NewLayer3D {
|
|
827
|
+
const life = lifetimeFor(ctx);
|
|
828
|
+
return life === undefined
|
|
829
|
+
? layer
|
|
830
|
+
: ({ ...layer, lifetime: life } as NewLayer3D);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
* The rule above, as a value, for the one create that does not build its
|
|
835
|
+
* layer here.
|
|
836
|
+
*
|
|
837
|
+
* `placeComponent` goes through `componentOps.addComponentInstance`, which
|
|
838
|
+
* assembles its own instance, so it could not wrap itself in `withLifetime`
|
|
839
|
+
* and for a long time did not land at the playhead at all: the rail's `+` on a
|
|
840
|
+
* definition made a whole-scene instance while the `+` one list below it, on a
|
|
841
|
+
* code component, landed under the clock. Two adjacent buttons, two answers to
|
|
842
|
+
* "when does this start". Exported so there is one.
|
|
843
|
+
*
|
|
844
|
+
* `undefined` at frame 0 for the reason above: absent already means the whole
|
|
845
|
+
* scene, and it keeps meaning that when the scene is later trimmed.
|
|
846
|
+
*/
|
|
847
|
+
export function lifetimeFor(
|
|
848
|
+
ctx: InsertContext,
|
|
849
|
+
): { in: number; out: number } | undefined {
|
|
473
850
|
const start = startFrameOf(ctx);
|
|
474
851
|
const end = Math.trunc(ctx.durationFrames);
|
|
475
|
-
if (start <= 0 || end <= start) return
|
|
476
|
-
return {
|
|
852
|
+
if (start <= 0 || end <= start) return undefined;
|
|
853
|
+
return { in: start, out: end };
|
|
477
854
|
}
|
|
478
855
|
|
|
479
856
|
/** The scene frame a new layer lands on. ONE function because TWO things read
|