@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,235 @@
|
|
|
1
|
+
// A layer riding a spline: which spline, how far along, and how it turns.
|
|
2
|
+
//
|
|
3
|
+
// EVERY ROW READS ITS OWN SCALAR across the selection, one `useSelectionValue`
|
|
4
|
+
// per field (ClonerSection's shape), and shows Mixed where the layers
|
|
5
|
+
// disagree. The rows used to read the FIRST layer's follow and show its
|
|
6
|
+
// progress, offset and bank as if the selection shared them, while every
|
|
7
|
+
// write fanned out to all of them. A layer in the selection that follows
|
|
8
|
+
// nothing reads `undefined`, which disagrees with any value: honest, because
|
|
9
|
+
// a write finds nothing on that layer to land on either.
|
|
10
|
+
import {
|
|
11
|
+
Combobox,
|
|
12
|
+
InspectorRow,
|
|
13
|
+
InspectorSection,
|
|
14
|
+
NumberField,
|
|
15
|
+
SegmentedControl,
|
|
16
|
+
useLocked,
|
|
17
|
+
wordOptions,
|
|
18
|
+
} from "@dailies-studio/ui";
|
|
19
|
+
import {
|
|
20
|
+
SPLINE_DOMAINS,
|
|
21
|
+
SPLINE_FORWARD_AXES,
|
|
22
|
+
SPLINE_ORIENTATIONS,
|
|
23
|
+
meshPrimitiveOf,
|
|
24
|
+
// The path table's bounds, read by `FollowNumber` below instead of restated
|
|
25
|
+
// here: `numberRange` is what the keyframe gate clamps against, so a second
|
|
26
|
+
// copy in the chrome is a pill that offers a number the diamond then refuses.
|
|
27
|
+
trackablePath,
|
|
28
|
+
} from "@dailies-studio/shot/doc";
|
|
29
|
+
import type {
|
|
30
|
+
Layer3D,
|
|
31
|
+
SplineDomain,
|
|
32
|
+
SplineFollow,
|
|
33
|
+
SplineForwardAxis,
|
|
34
|
+
SplineOrientation,
|
|
35
|
+
} from "@dailies-studio/shot/doc";
|
|
36
|
+
import { useShotEditor } from "../../../state/context.js";
|
|
37
|
+
import { useSelectedLayers, useSelectionScrub, useSelectionValue, useSelectionWrite } from "../useDocWrite.js";
|
|
38
|
+
import { KeyframeDot } from "../KeyframeDot.js";
|
|
39
|
+
import { splineSourceIsSafe } from "../../../state/splineRefs.js";
|
|
40
|
+
|
|
41
|
+
/** How a widget with no `mixed` of its own says Mixed: an empty value selects
|
|
42
|
+
* no segment, and a combobox shows its placeholder over it. */
|
|
43
|
+
const NO_VALUE = "";
|
|
44
|
+
const MIXED = "Mixed";
|
|
45
|
+
|
|
46
|
+
export function SplineFollowSection() {
|
|
47
|
+
const write = useSelectionWrite();
|
|
48
|
+
const layers = useSelectedLayers();
|
|
49
|
+
const doc = useShotEditor((state) => state.doc);
|
|
50
|
+
const sceneId = useShotEditor((state) => state.sceneId);
|
|
51
|
+
const editingDef = useShotEditor((state) => state.editingDef);
|
|
52
|
+
// Add follow and Remove follow are plain buttons, so they read a viewer's
|
|
53
|
+
// lock here; every row is a widget that reads it itself.
|
|
54
|
+
const locked = useLocked();
|
|
55
|
+
// One scalar per row, read before any early return (they are hooks).
|
|
56
|
+
const spline = useSelectionValue((layer) => layer.splineFollow?.spline);
|
|
57
|
+
const domain = useSelectionValue((layer) => followOf(layer, (f) => f.domain ?? "full"));
|
|
58
|
+
const orientation = useSelectionValue((layer) => followOf(layer, (f) => f.orientation ?? "path"));
|
|
59
|
+
const forwardAxis = useSelectionValue((layer) => followOf(layer, (f) => f.forwardAxis ?? "+x"));
|
|
60
|
+
const progress = useSelectionValue((layer) => followOf(layer, (f) => f.progress ?? 0));
|
|
61
|
+
const progressOffset = useSelectionValue((layer) => followOf(layer, (f) => f.progressOffset ?? 0));
|
|
62
|
+
const bank = useSelectionValue((layer) => followOf(layer, (f) => f.bankDeg ?? 0));
|
|
63
|
+
if (layers.length === 0 || doc === null) return null;
|
|
64
|
+
const home = editingDef === null
|
|
65
|
+
? (sceneId === null ? undefined : doc.scenesById[sceneId])
|
|
66
|
+
: doc.components?.[editingDef];
|
|
67
|
+
const selected = new Set(layers.map((layer) => layer.id));
|
|
68
|
+
const splines = Object.values(home?.layersById ?? {})
|
|
69
|
+
.filter((layer) =>
|
|
70
|
+
layer.kind === "mesh" &&
|
|
71
|
+
meshPrimitiveOf(layer.mesh)?.kind === "spline" &&
|
|
72
|
+
!selected.has(layer.id) &&
|
|
73
|
+
splineSourceIsSafe(home?.layersById ?? {}, [...selected], layer.id, "follow"),
|
|
74
|
+
)
|
|
75
|
+
.map((layer) => ({ value: layer.id, label: layer.label }));
|
|
76
|
+
// The LEAD's follow decides whether there is a follow to edit at all; each
|
|
77
|
+
// row then reads its own field across the whole selection.
|
|
78
|
+
const follow = layers[0]?.splineFollow;
|
|
79
|
+
const clonerDistributionOwnsPath = layers.some(
|
|
80
|
+
(layer) => layer.kind === "cloner" && layer.cloner.mode.kind === "spline",
|
|
81
|
+
);
|
|
82
|
+
const patch = (build: (current: SplineFollow | undefined) => SplineFollow | undefined): void =>
|
|
83
|
+
write((layer) => withFollow(layer, build));
|
|
84
|
+
const set = <K extends keyof SplineFollow>(key: K, value: SplineFollow[K]): void =>
|
|
85
|
+
patch((current) => current === undefined ? undefined : { ...current, [key]: value });
|
|
86
|
+
|
|
87
|
+
// Nothing to follow and nothing following: the section would be one
|
|
88
|
+
// disabled button, so it withholds itself (see `sectionsFor`).
|
|
89
|
+
if (follow === undefined && splines.length === 0 && !clonerDistributionOwnsPath) return null;
|
|
90
|
+
if (follow === undefined) {
|
|
91
|
+
return (
|
|
92
|
+
<InspectorSection className="insp-object" title="Spline follow" persistKey="shot:spline-follow">
|
|
93
|
+
<InspectorRow label="Path">
|
|
94
|
+
<button
|
|
95
|
+
type="button"
|
|
96
|
+
className="btn"
|
|
97
|
+
disabled={locked || splines[0] === undefined || clonerDistributionOwnsPath}
|
|
98
|
+
title={
|
|
99
|
+
clonerDistributionOwnsPath
|
|
100
|
+
? "A cloner cannot use spline follow and spline distribution together"
|
|
101
|
+
: undefined
|
|
102
|
+
}
|
|
103
|
+
onClick={() => {
|
|
104
|
+
const first = splines[0]?.value;
|
|
105
|
+
// A layer that already follows keeps its own follow.
|
|
106
|
+
if (first !== undefined) patch((current) => current ?? { spline: first, progress: 0, orientation: "path" });
|
|
107
|
+
}}
|
|
108
|
+
>
|
|
109
|
+
{clonerDistributionOwnsPath
|
|
110
|
+
? "Spline distribution active"
|
|
111
|
+
: splines.length === 0
|
|
112
|
+
? "No spline available"
|
|
113
|
+
: "Add follow"}
|
|
114
|
+
</button>
|
|
115
|
+
</InspectorRow>
|
|
116
|
+
</InspectorSection>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const targetOptions = splines.some((option) => option.value === follow.spline)
|
|
121
|
+
? splines
|
|
122
|
+
: [{ value: follow.spline, label: home?.layersById[follow.spline]?.label ?? follow.spline }, ...splines];
|
|
123
|
+
return (
|
|
124
|
+
<InspectorSection className="insp-object" title="Spline follow" persistKey="shot:spline-follow">
|
|
125
|
+
<InspectorRow label="Path">
|
|
126
|
+
<Combobox
|
|
127
|
+
options={targetOptions}
|
|
128
|
+
value={spline.mixed ? NO_VALUE : follow.spline}
|
|
129
|
+
ariaLabel="Follow spline"
|
|
130
|
+
placeholder={spline.mixed ? MIXED : "Choose spline"}
|
|
131
|
+
onChange={(next) => set("spline", next)}
|
|
132
|
+
/>
|
|
133
|
+
</InspectorRow>
|
|
134
|
+
<InspectorRow label="Domain">
|
|
135
|
+
<SegmentedControl
|
|
136
|
+
ariaLabel="Follow domain"
|
|
137
|
+
value={domain.mixed ? NO_VALUE : (follow.domain ?? "full")}
|
|
138
|
+
options={wordOptions(SPLINE_DOMAINS)}
|
|
139
|
+
onChange={(next) => set("domain", next as SplineDomain)}
|
|
140
|
+
/>
|
|
141
|
+
</InspectorRow>
|
|
142
|
+
<FollowNumber path="splineFollow.progress" label="Progress" value={follow.progress ?? 0} mixed={progress.mixed} step={0.01} edit={(f, value) => ({ ...f, progress: value })} />
|
|
143
|
+
<FollowNumber path="splineFollow.progressOffset" label="Offset" value={follow.progressOffset ?? 0} mixed={progressOffset.mixed} step={0.01} edit={(f, value) => ({ ...f, progressOffset: value })} />
|
|
144
|
+
<InspectorRow label="Orient">
|
|
145
|
+
<SegmentedControl
|
|
146
|
+
ariaLabel="Follow orientation"
|
|
147
|
+
value={orientation.mixed ? NO_VALUE : (follow.orientation ?? "path")}
|
|
148
|
+
options={wordOptions(SPLINE_ORIENTATIONS)}
|
|
149
|
+
onChange={(next) => set("orientation", next as SplineOrientation)}
|
|
150
|
+
/>
|
|
151
|
+
</InspectorRow>
|
|
152
|
+
{(follow.orientation ?? "path") === "path" ? (
|
|
153
|
+
<>
|
|
154
|
+
<InspectorRow label="Forward">
|
|
155
|
+
<Combobox
|
|
156
|
+
options={SPLINE_FORWARD_AXES.map((value) => ({ value, label: value.toUpperCase() }))}
|
|
157
|
+
value={forwardAxis.mixed ? NO_VALUE : (follow.forwardAxis ?? "+x")}
|
|
158
|
+
ariaLabel="Follow forward axis"
|
|
159
|
+
placeholder={forwardAxis.mixed ? MIXED : "Axis"}
|
|
160
|
+
onChange={(next) => set("forwardAxis", next as SplineForwardAxis)}
|
|
161
|
+
/>
|
|
162
|
+
</InspectorRow>
|
|
163
|
+
<FollowNumber path="splineFollow.bankDeg" label="Bank" value={follow.bankDeg ?? 0} mixed={bank.mixed} step={1} unit="°" edit={(f, value) => ({ ...f, bankDeg: value })} />
|
|
164
|
+
</>
|
|
165
|
+
) : null}
|
|
166
|
+
<InspectorRow>
|
|
167
|
+
<button type="button" className="btn" disabled={locked} onClick={() => patch(() => undefined)}>
|
|
168
|
+
Remove follow
|
|
169
|
+
</button>
|
|
170
|
+
</InspectorRow>
|
|
171
|
+
</InspectorSection>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** One follow field as a scalar, or undefined for a layer that follows
|
|
176
|
+
* nothing, so the selection read stays a primitive. */
|
|
177
|
+
function followOf<T>(layer: Layer3D, read: (follow: SplineFollow) => T): T | undefined {
|
|
178
|
+
return layer.splineFollow === undefined ? undefined : read(layer.splineFollow);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** The ONE follow edit, so a drag's move and its release land the same
|
|
182
|
+
* document. Clearing deletes the key rather than writing `undefined`. */
|
|
183
|
+
function withFollow(layer: Layer3D, build: (current: SplineFollow | undefined) => SplineFollow | undefined): Layer3D {
|
|
184
|
+
const next = build(layer.splineFollow);
|
|
185
|
+
if (next === undefined) {
|
|
186
|
+
if (layer.splineFollow === undefined) return layer;
|
|
187
|
+
const { splineFollow: _drop, ...rest } = layer;
|
|
188
|
+
return rest as Layer3D;
|
|
189
|
+
}
|
|
190
|
+
return { ...layer, splineFollow: next } as Layer3D;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** A row whose HARD limits default to the path table's own `numberRange`, the
|
|
194
|
+
* rule `MeshNumber` (MeshSection) states in full. An explicit `min`/`max`
|
|
195
|
+
* still wins, for a chrome limit that is deliberately tighter. Its typed
|
|
196
|
+
* value and its drag are one builder (`edit`) over each layer's own follow,
|
|
197
|
+
* and a layer that follows nothing is left alone by both. */
|
|
198
|
+
function FollowNumber({ label, value, mixed, unit, min, max, step, path, edit }: {
|
|
199
|
+
label: string;
|
|
200
|
+
value: number;
|
|
201
|
+
mixed: boolean;
|
|
202
|
+
path: string;
|
|
203
|
+
unit?: string;
|
|
204
|
+
min?: number;
|
|
205
|
+
max?: number;
|
|
206
|
+
step: number;
|
|
207
|
+
edit: (follow: SplineFollow, value: number) => SplineFollow;
|
|
208
|
+
}) {
|
|
209
|
+
const write = useSelectionWrite();
|
|
210
|
+
const build = (layer: Layer3D, next: number): Layer3D =>
|
|
211
|
+
withFollow(layer, (current) => (current === undefined ? undefined : edit(current, next)));
|
|
212
|
+
const scrub = useSelectionScrub((layer, raw) => build(layer, Number(raw)));
|
|
213
|
+
const tabled = trackablePath(path)?.numberRange;
|
|
214
|
+
const lo = min ?? tabled?.min;
|
|
215
|
+
const hi = max ?? tabled?.max;
|
|
216
|
+
return (
|
|
217
|
+
<InspectorRow>
|
|
218
|
+
<NumberField
|
|
219
|
+
value={value}
|
|
220
|
+
mixed={mixed}
|
|
221
|
+
label={label}
|
|
222
|
+
ariaLabel={label}
|
|
223
|
+
{...(unit === undefined ? {} : { unit })}
|
|
224
|
+
{...(lo === undefined ? {} : { min: lo })}
|
|
225
|
+
{...(hi === undefined ? {} : { max: hi })}
|
|
226
|
+
step={step}
|
|
227
|
+
trailing={<KeyframeDot path={path} label={label} />}
|
|
228
|
+
onCommit={(next) => write((layer) => build(layer, next))}
|
|
229
|
+
onScrubStart={scrub.start}
|
|
230
|
+
onScrub={scrub.move}
|
|
231
|
+
onScrubEnd={scrub.end}
|
|
232
|
+
/>
|
|
233
|
+
</InspectorRow>
|
|
234
|
+
);
|
|
235
|
+
}
|
|
@@ -1,70 +1,175 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
// The stroke: a header switch, Width and Alpha as a pair, the colour row, and
|
|
2
|
+
// Align. Paper's Stroke card puts the pair first: Width is what an outline is
|
|
3
|
+
// tuned by, and the colour row under it reads as Fill's does.
|
|
4
|
+
//
|
|
5
|
+
// ── THE SWITCH IS `Stroke.hidden`, NOT ADD/REMOVE ──────────────────────
|
|
6
|
+
//
|
|
7
|
+
// The section used to carry a `+`/`-` action that added a default stroke or
|
|
8
|
+
// deleted the key. Deleting threw away the width, colour, align and dash, so
|
|
9
|
+
// "turn the outline off for a moment" cost the author the outline. Schema v36
|
|
10
|
+
// gave the stroke a `hidden` flag every renderer reads through `drawnStroke`,
|
|
11
|
+
// and the switch writes only that flag:
|
|
12
|
+
//
|
|
13
|
+
// - Off writes `hidden: true` on every selected layer that HAS a stroke. A
|
|
14
|
+
// layer with none is left alone: hiding a stroke nobody drew would add a
|
|
15
|
+
// document key with no picture behind it.
|
|
16
|
+
// - On deletes the `hidden` key rather than writing `false`, so off then on
|
|
17
|
+
// is the same bytes as before, and a layer with no stroke gets the 1px
|
|
18
|
+
// default (what the old `+` did).
|
|
19
|
+
//
|
|
20
|
+
// A selection whose strokes disagree reads Mixed, and the click turns every
|
|
21
|
+
// layer on: "on for all" is the action a half-lit set asks for, and the one
|
|
22
|
+
// policy every inspector switch keeps.
|
|
23
|
+
//
|
|
24
|
+
// ── ALPHA IS THE COLOUR'S ALPHA BYTE ───────────────────────────────────
|
|
25
|
+
//
|
|
26
|
+
// There is no `Stroke.alpha`. A second alpha would multiply with the colour's,
|
|
27
|
+
// so the picker's alpha bar and this field would each show a number that is not
|
|
28
|
+
// what is drawn. The field edits `#rrggbbaa` in place, per layer, keeping each
|
|
29
|
+
// layer's own RGB, and is keyed through the `paint.stroke.color` track, so its
|
|
30
|
+
// box carries THAT diamond, the colour row's, and Width's box its own: one
|
|
31
|
+
// diamond for the pair keyed the width alone, beside an Alpha it did not key.
|
|
32
|
+
// The colour row's box keeps the track's one expose pin. Over a colour the
|
|
33
|
+
// engine cannot read it is disabled: there is no byte to set.
|
|
34
|
+
//
|
|
35
|
+
// ── CAP AND JOIN (schema v39) ──────────────────────────────────────────
|
|
36
|
+
//
|
|
37
|
+
// Two segmented rows after Align, in Align's row style. An absent field shows
|
|
38
|
+
// what the raster DRAWS for it (`capOf`/`joinOf`): a path layer's butt, every
|
|
39
|
+
// other stroked kind's round. Hidden while any text layer is selected: a text
|
|
40
|
+
// layer's stroke outlines its plate, a closed box with no ends to cap.
|
|
41
|
+
import {
|
|
42
|
+
ColorField,
|
|
43
|
+
InspectorRow,
|
|
44
|
+
InspectorSection,
|
|
45
|
+
NumberField,
|
|
46
|
+
SegmentedControl,
|
|
47
|
+
Toggle,
|
|
48
|
+
tryParseHex,
|
|
49
|
+
} from "@dailies-studio/ui";
|
|
50
|
+
import { formatHex } from "@dailies-studio/engine";
|
|
51
|
+
import { KeyframeDot } from "../KeyframeDot.js";
|
|
52
|
+
import type { Layer3D, Stroke } from "@dailies-studio/shot/doc";
|
|
53
|
+
import { STROKE_CAPS, STROKE_JOINS } from "@dailies-studio/shot/doc";
|
|
54
|
+
import { useSelectionScrub, useSelectionValue, useSelectionWrite } from "../useDocWrite.js";
|
|
55
|
+
import { colorOverMixedAlpha } from "./PaintRows.js";
|
|
4
56
|
|
|
57
|
+
/** 1px, and deliberately NOT insert.ts's `DRAW_STROKE` (4px): this is the
|
|
58
|
+
* hairline outline added around a shape that already draws, while that one IS
|
|
59
|
+
* the drawn mark of the line, arrow and path tools. Do not merge them. */
|
|
5
60
|
const DEFAULT_STROKE: Stroke = { color: "#ffffff", widthPx: 1, align: "center" };
|
|
6
61
|
|
|
7
62
|
export function StrokeSection() {
|
|
8
63
|
const write = useSelectionWrite();
|
|
9
|
-
const
|
|
64
|
+
const drawn = useSelectionValue((layer) => layer.paint.stroke !== undefined && layer.paint.stroke.hidden !== true);
|
|
10
65
|
const color = useSelectionValue((layer) => layer.paint.stroke?.color ?? DEFAULT_STROKE.color);
|
|
66
|
+
// The colour row shows six digits, the RGB: Mixed is asked of THAT, and the
|
|
67
|
+
// alpha byte has its own field (and its own Mixed) beside the width.
|
|
68
|
+
const rgb = useSelectionValue((layer) => rgbKey(layer.paint.stroke?.color ?? DEFAULT_STROKE.color));
|
|
11
69
|
const width = useSelectionValue((layer) => layer.paint.stroke?.widthPx ?? DEFAULT_STROKE.widthPx);
|
|
70
|
+
const alpha = useSelectionValue((layer) => alphaPct(layer.paint.stroke?.color ?? DEFAULT_STROKE.color));
|
|
12
71
|
const align = useSelectionValue((layer) => layer.paint.stroke?.align ?? DEFAULT_STROKE.align);
|
|
72
|
+
const cap = useSelectionValue(capOf);
|
|
73
|
+
const join = useSelectionValue(joinOf);
|
|
74
|
+
const text = useSelectionValue((layer) => layer.kind === "text");
|
|
75
|
+
const on = drawn.value === true && !drawn.mixed;
|
|
76
|
+
// A colour pick keeps each layer's OWN alpha where the alphas differ, as the
|
|
77
|
+
// fill's does: a six-digit hex typed over a selection whose opacities
|
|
78
|
+
// disagree would otherwise copy the lead's alpha onto every stroke. A pick
|
|
79
|
+
// that moved the alpha the field shows (eight digits, the alpha bar) sets it
|
|
80
|
+
// on all of them (`colorOverMixedAlpha`).
|
|
81
|
+
const withColor = (layer: Layer3D, hex: string): Layer3D =>
|
|
82
|
+
withStroke(layer, {
|
|
83
|
+
color: alpha.mixed
|
|
84
|
+
? colorOverMixedAlpha(hex, color.value ?? DEFAULT_STROKE.color, layer.paint.stroke?.color ?? DEFAULT_STROKE.color)
|
|
85
|
+
: hex,
|
|
86
|
+
});
|
|
13
87
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
88
|
+
// THE DRAG HALF, for the controls that HAVE a drag. The picker's three bars
|
|
89
|
+
// and the two fields' labels report through `scrub.move` and never call
|
|
90
|
+
// `commit` while the pointer is down: with the commit half alone the thumb
|
|
91
|
+
// moved and the document did not.
|
|
92
|
+
const colorScrub = useSelectionScrub((layer, raw) => withColor(layer, String(raw)));
|
|
93
|
+
const widthScrub = useSelectionScrub((layer, raw) => withStroke(layer, { widthPx: Number(raw) }));
|
|
94
|
+
const alphaScrub = useSelectionScrub((layer, raw) => withAlpha(layer, Number(raw)));
|
|
95
|
+
|
|
96
|
+
const patch = (next: Partial<Stroke>): void => write((layer) => withStroke(layer, next));
|
|
19
97
|
|
|
20
98
|
return (
|
|
21
99
|
<InspectorSection
|
|
22
100
|
title="Stroke"
|
|
101
|
+
className="insp-object"
|
|
23
102
|
persistKey="shot:stroke"
|
|
24
103
|
action={
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
: patch({})
|
|
33
|
-
}
|
|
34
|
-
>
|
|
35
|
-
{present.value === true ? "−" : "+"}
|
|
36
|
-
</button>
|
|
104
|
+
<Toggle
|
|
105
|
+
className="insp-stroke-switch"
|
|
106
|
+
checked={on}
|
|
107
|
+
mixed={drawn.mixed}
|
|
108
|
+
ariaLabel="Stroke"
|
|
109
|
+
onChange={(next) => write((layer) => (next ? shown(layer) : hidden(layer)))}
|
|
110
|
+
/>
|
|
37
111
|
}
|
|
38
112
|
>
|
|
39
|
-
{
|
|
113
|
+
{/* Rows while ANY selected stroke draws, so a half-lit selection can
|
|
114
|
+
still be edited; an all-off selection shows the switch alone. */}
|
|
115
|
+
{drawn.value === true || drawn.mixed ? (
|
|
40
116
|
<>
|
|
41
|
-
<InspectorRow
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
117
|
+
<InspectorRow>
|
|
118
|
+
<div className="insp-vec">
|
|
119
|
+
<NumberField
|
|
120
|
+
value={width.value ?? 1}
|
|
121
|
+
mixed={width.mixed}
|
|
122
|
+
label="Width"
|
|
123
|
+
ariaLabel="Stroke width"
|
|
124
|
+
unit="px"
|
|
125
|
+
min={0}
|
|
126
|
+
// A negative width is not a stroke; there is no upper limit.
|
|
127
|
+
step={0.5}
|
|
128
|
+
trailing={<KeyframeDot path="paint.stroke.widthPx" />}
|
|
129
|
+
onCommit={(next) => patch({ widthPx: next })}
|
|
130
|
+
onScrubStart={widthScrub.start}
|
|
131
|
+
onScrub={widthScrub.move}
|
|
132
|
+
onScrubEnd={widthScrub.end}
|
|
133
|
+
/>
|
|
134
|
+
<NumberField
|
|
135
|
+
value={alpha.value ?? 100}
|
|
136
|
+
mixed={alpha.mixed}
|
|
137
|
+
disabled={alpha.value === null}
|
|
138
|
+
label="Alpha"
|
|
139
|
+
ariaLabel="Stroke alpha"
|
|
140
|
+
unit="%"
|
|
141
|
+
min={0}
|
|
142
|
+
max={100}
|
|
143
|
+
step={1}
|
|
144
|
+
trailing={<KeyframeDot path="paint.stroke.color" pin={false} />}
|
|
145
|
+
onCommit={(pct) => write((layer) => withAlpha(layer, pct))}
|
|
146
|
+
onScrubStart={alphaScrub.start}
|
|
147
|
+
onScrub={alphaScrub.move}
|
|
148
|
+
onScrubEnd={alphaScrub.end}
|
|
149
|
+
/>
|
|
150
|
+
</div>
|
|
48
151
|
</InspectorRow>
|
|
49
152
|
<InspectorRow>
|
|
50
|
-
<
|
|
51
|
-
value={
|
|
52
|
-
mixed={
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
step={0.5}
|
|
58
|
-
onCommit={(next) => patch({ widthPx: next })}
|
|
153
|
+
<ColorField
|
|
154
|
+
value={color.value ?? DEFAULT_STROKE.color}
|
|
155
|
+
mixed={rgb.mixed}
|
|
156
|
+
ariaLabel="Stroke colour"
|
|
157
|
+
scrub={colorScrub}
|
|
158
|
+
commit={(hex) => write((layer) => withColor(layer, hex))}
|
|
159
|
+
trailing={<KeyframeDot path="paint.stroke.color" />}
|
|
59
160
|
/>
|
|
60
161
|
</InspectorRow>
|
|
61
162
|
<InspectorRow label="Align">
|
|
62
163
|
{/* Canvas2D only CENTRES a stroke, so inside and outside are a
|
|
63
|
-
half-width inset either way
|
|
164
|
+
half-width inset either way; raster.ts does that, and the three
|
|
64
165
|
options are honest because the raster honours all three. */}
|
|
65
166
|
<SegmentedControl
|
|
66
167
|
ariaLabel="Stroke align"
|
|
67
|
-
|
|
168
|
+
/* A disagreeing selection selects NOTHING rather than showing the
|
|
169
|
+
first layer's choice as if the others shared it: the control
|
|
170
|
+
has no `mixed` prop, and an unmatched value is exactly how it
|
|
171
|
+
renders "none of these". */
|
|
172
|
+
value={align.mixed ? "" : (align.value ?? "center")}
|
|
68
173
|
options={[
|
|
69
174
|
{ value: "inside", label: "In" },
|
|
70
175
|
{ value: "center", label: "Mid" },
|
|
@@ -73,13 +178,93 @@ export function StrokeSection() {
|
|
|
73
178
|
onChange={(next) => patch({ align: next as Stroke["align"] })}
|
|
74
179
|
/>
|
|
75
180
|
</InspectorRow>
|
|
181
|
+
{text.value === false && !text.mixed ? (
|
|
182
|
+
<>
|
|
183
|
+
<InspectorRow label="Cap">
|
|
184
|
+
<SegmentedControl
|
|
185
|
+
ariaLabel="Stroke cap"
|
|
186
|
+
value={cap.mixed ? "" : (cap.value ?? "")}
|
|
187
|
+
options={STROKE_CAPS.map((value) => ({ value, label: titleCase(value) }))}
|
|
188
|
+
onChange={(next) => patch({ cap: next as NonNullable<Stroke["cap"]> })}
|
|
189
|
+
/>
|
|
190
|
+
</InspectorRow>
|
|
191
|
+
<InspectorRow label="Join">
|
|
192
|
+
<SegmentedControl
|
|
193
|
+
ariaLabel="Stroke join"
|
|
194
|
+
value={join.mixed ? "" : (join.value ?? "")}
|
|
195
|
+
options={STROKE_JOINS.map((value) => ({ value, label: titleCase(value) }))}
|
|
196
|
+
onChange={(next) => patch({ join: next as NonNullable<Stroke["join"]> })}
|
|
197
|
+
/>
|
|
198
|
+
</InspectorRow>
|
|
199
|
+
</>
|
|
200
|
+
) : null}
|
|
76
201
|
</>
|
|
77
202
|
) : null}
|
|
78
203
|
</InspectorSection>
|
|
79
204
|
);
|
|
80
205
|
}
|
|
81
206
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
207
|
+
/** The cap the raster draws: an absent field is butt on a path layer and
|
|
208
|
+
* round on every other stroked kind (raster.ts `drawStroke`). */
|
|
209
|
+
export function capOf(layer: Layer3D): NonNullable<Stroke["cap"]> {
|
|
210
|
+
return layer.paint.stroke?.cap ?? (layer.kind === "path" ? "butt" : "round");
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** The join the raster draws: absent is Canvas2D's miter on every kind. The
|
|
214
|
+
* stage's analytic rect draws the same corner (shapePlane.ts `JOIN_CODE`). */
|
|
215
|
+
export function joinOf(layer: Layer3D): NonNullable<Stroke["join"]> {
|
|
216
|
+
return layer.paint.stroke?.join ?? "miter";
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function titleCase(word: string): string {
|
|
220
|
+
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** The colour's alpha as a whole percent, or null when the engine cannot read
|
|
224
|
+
* the colour. A number, so the selection read stays a primitive. */
|
|
225
|
+
function alphaPct(color: string): number | null {
|
|
226
|
+
const rgb = tryParseHex(color);
|
|
227
|
+
return rgb === null ? null : Math.round(rgb.alpha * 100);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** The colour's RGB as one string, its alpha left out: what the colour row
|
|
231
|
+
* shows and compares. A colour the engine cannot read is its own text. */
|
|
232
|
+
function rgbKey(color: string): string {
|
|
233
|
+
const rgb = tryParseHex(color);
|
|
234
|
+
return rgb === null ? color : `${rgb.r},${rgb.g},${rgb.b}`;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** The ONE stroke edit, so a drag's move and its release land the same
|
|
238
|
+
* document: two spellings could disagree, and the release would then appear to
|
|
239
|
+
* undo the drag. Spread from the default because the stroke may be absent. */
|
|
240
|
+
function withStroke(layer: Layer3D, next: Partial<Stroke>): Layer3D {
|
|
241
|
+
return {
|
|
242
|
+
...layer,
|
|
243
|
+
paint: { ...layer.paint, stroke: { ...DEFAULT_STROKE, ...layer.paint.stroke, ...next } },
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** Set this layer's alpha byte, keeping its own RGB. An unreadable colour is
|
|
248
|
+
* left as it is rather than replaced with a guess. */
|
|
249
|
+
function withAlpha(layer: Layer3D, pct: number): Layer3D {
|
|
250
|
+
const rgb = tryParseHex(layer.paint.stroke?.color ?? DEFAULT_STROKE.color);
|
|
251
|
+
if (rgb === null) return layer;
|
|
252
|
+
return withStroke(layer, { color: formatHex({ ...rgb, alpha: Math.min(1, Math.max(0, pct / 100)) }) });
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** On: the key is DELETED, not set false, and the stroke's own keys are spread
|
|
256
|
+
* without the default in front, so off then on is byte-identical whatever key
|
|
257
|
+
* order the document had. No stroke at all gets the default. */
|
|
258
|
+
function shown(layer: Layer3D): Layer3D {
|
|
259
|
+
const current = layer.paint.stroke;
|
|
260
|
+
if (current === undefined) return withStroke(layer, {});
|
|
261
|
+
const { hidden: _drop, ...stroke } = current;
|
|
262
|
+
return { ...layer, paint: { ...layer.paint, stroke } };
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/** Off: kept and flagged, nothing else touched. A layer with no stroke has
|
|
266
|
+
* nothing to hide. */
|
|
267
|
+
function hidden(layer: Layer3D): Layer3D {
|
|
268
|
+
const current = layer.paint.stroke;
|
|
269
|
+
return current === undefined ? layer : { ...layer, paint: { ...layer.paint, stroke: { ...current, hidden: true } } };
|
|
85
270
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// TEMPLATE FIELD (schema v50): whether a person who starts from this Project
|
|
2
|
+
// swaps this layer for their own, and what the field asks for ("Screenshot",
|
|
3
|
+
// "Headline", "Logo"). Only text, image and video list it (layout.ts), and it
|
|
4
|
+
// describes ONE layer, so a selection of two withholds it.
|
|
5
|
+
//
|
|
6
|
+
// Both rows write through the store's `setLayerSwap`: one undo step each, a
|
|
7
|
+
// viewer's `VIEW_ONLY`, a locked layer's refusal and the op's own (a name past
|
|
8
|
+
// `SWAP_LABEL_MAX`) in the error line. The name row stays on screen while the
|
|
9
|
+
// field is off, so its hint says what a name looks like, and naming the field
|
|
10
|
+
// is also how it turns on.
|
|
11
|
+
import { InspectorRow, InspectorSection, TextField, Toggle } from "@dailies-studio/ui";
|
|
12
|
+
import { SWAP_LABEL_MAX } from "@dailies-studio/shot/doc";
|
|
13
|
+
import type { Layer3D, SwapKind } from "@dailies-studio/shot/doc";
|
|
14
|
+
import { useStore } from "../../../state/context.js";
|
|
15
|
+
import { useSelectedLayers } from "../useDocWrite.js";
|
|
16
|
+
|
|
17
|
+
/** What a field of each kind usually asks for: the empty name's hint. */
|
|
18
|
+
export const SWAP_PLACEHOLDER: Record<SwapKind, string> = {
|
|
19
|
+
text: "Headline",
|
|
20
|
+
image: "Screenshot",
|
|
21
|
+
video: "Product video",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/** The name a field takes when it is switched on: the layer's own label on
|
|
25
|
+
* one line, clipped to `SWAP_LABEL_MAX`, else its kind's hint. */
|
|
26
|
+
export function defaultSwapLabel(layer: Layer3D): string {
|
|
27
|
+
const own = layer.label.replace(/\p{Cc}+/gu, " ").trim().slice(0, SWAP_LABEL_MAX).trim();
|
|
28
|
+
return own === "" ? SWAP_PLACEHOLDER[layer.kind as SwapKind] : own;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function TemplateFieldSection() {
|
|
32
|
+
const store = useStore();
|
|
33
|
+
const layers = useSelectedLayers();
|
|
34
|
+
const layer = layers.length === 1 ? layers[0] : undefined;
|
|
35
|
+
if (layer === undefined) return null;
|
|
36
|
+
const label = layer.swap?.label;
|
|
37
|
+
return (
|
|
38
|
+
<InspectorSection className="insp-object" title="Template field" persistKey="shot:template-field">
|
|
39
|
+
<InspectorRow label="Swappable" labelTitle="People who start from this template swap this layer for their own">
|
|
40
|
+
<Toggle
|
|
41
|
+
checked={label !== undefined}
|
|
42
|
+
ariaLabel="Swappable"
|
|
43
|
+
onChange={(on) => store.setLayerSwap(layer.id, on ? defaultSwapLabel(layer) : null)}
|
|
44
|
+
/>
|
|
45
|
+
</InspectorRow>
|
|
46
|
+
<InspectorRow label="Name">
|
|
47
|
+
<TextField
|
|
48
|
+
value={label ?? ""}
|
|
49
|
+
placeholder={SWAP_PLACEHOLDER[layer.kind as SwapKind]}
|
|
50
|
+
ariaLabel="Name, what the template field asks for"
|
|
51
|
+
// Blank clears the field, as switching it off does.
|
|
52
|
+
onCommit={(next) => store.setLayerSwap(layer.id, next)}
|
|
53
|
+
/>
|
|
54
|
+
</InspectorRow>
|
|
55
|
+
</InspectorSection>
|
|
56
|
+
);
|
|
57
|
+
}
|