@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,1959 @@
|
|
|
1
|
+
/* The timeline's ROWS mode: the sticky label column, every row kind (shots, lifespans, camera moves,
|
|
2
|
+
camera poses, channels, + property and its picker, audio, markers), the cut markers, the
|
|
3
|
+
rubber band and the group separators. Imported right after timeline.css (the dock frame), in
|
|
4
|
+
the same layer. A row rule and a frame rule never style one element at equal specificity, so
|
|
5
|
+
which of the two files comes first decides nothing; keep it that way. */
|
|
6
|
+
|
|
7
|
+
/* ── THE DOCK'S LABEL COLUMN IS STICKY ────────────────────────────────────
|
|
8
|
+
Every row in the timeline is `[label cell | time area]` inside ONE
|
|
9
|
+
horizontally scrolling box, so the names used to scroll away with the bars.
|
|
10
|
+
That was survivable while the only way to scroll was a deliberate
|
|
11
|
+
shift+wheel pan. It is not survivable now that the lane FOLLOWS THE PLAYHEAD:
|
|
12
|
+
every playback slides the whole column off the left edge and leaves the
|
|
13
|
+
author watching a wall of anonymous bars.
|
|
14
|
+
|
|
15
|
+
Six cells, one rule, because they are one column — a per-component copy is
|
|
16
|
+
six places to forget the z-index.
|
|
17
|
+
|
|
18
|
+
OPAQUE, and that is the load-bearing part: a transparent sticky cell has bars
|
|
19
|
+
sliding visibly through the layer names. `--surface-panel` is the dock's own
|
|
20
|
+
surface (`.timeline-dock`, timeline.css), so the column reads as part of
|
|
21
|
+
the dock rather than as a card on top of it.
|
|
22
|
+
|
|
23
|
+
Sticky and NOT a separate fixed column beside the scroller: the rows already
|
|
24
|
+
own their own labels, and splitting them would mean two scroll containers
|
|
25
|
+
kept in vertical sync by hand — one more thing that can drift, for a layout
|
|
26
|
+
the browser does for free. */
|
|
27
|
+
.shot-label-cell,
|
|
28
|
+
.block-row-label-cell,
|
|
29
|
+
.channel-label-cell,
|
|
30
|
+
.lifespan-label-cell,
|
|
31
|
+
.marker-label-cell,
|
|
32
|
+
.audio-label-cell {
|
|
33
|
+
position: sticky;
|
|
34
|
+
left: 0;
|
|
35
|
+
/* Above the bars in the same row (a scene cut is 3) AND above
|
|
36
|
+
`.dock-overlay` (3: the playhead, the boundaries and the snap guide). The
|
|
37
|
+
overlay is a sibling of the rows in content coordinates, so once the lanes
|
|
38
|
+
scroll left its lines slide under this column; painted over it, a playhead
|
|
39
|
+
whose frame had scrolled out of view crossed the shot names instead. In
|
|
40
|
+
view, every line sits right of `labelWidth` and nothing is covered. */
|
|
41
|
+
z-index: 4;
|
|
42
|
+
background-color: var(--surface-panel);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* THE LABEL COLUMN CONTINUES THE LEFT COLUMN'S SEAM. It is exactly the rail
|
|
46
|
+
track's width (labelWidth.ts), and the rail draws its border on its last
|
|
47
|
+
pixel, so the same 1px line runs from the top bar down through the ruler
|
|
48
|
+
corner and every row. A rule of its own because `.scene-audio-label` takes
|
|
49
|
+
the seam but not the sticky recipe above. */
|
|
50
|
+
.shot-label-cell,
|
|
51
|
+
.block-row-label-cell,
|
|
52
|
+
.channel-label-cell,
|
|
53
|
+
.lifespan-label-cell,
|
|
54
|
+
.marker-label-cell,
|
|
55
|
+
.audio-label-cell,
|
|
56
|
+
.scene-audio-label {
|
|
57
|
+
border-right: 1px solid var(--line);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* NO ROW TINTS. A selected or current row is said by its CLIP (the outline,
|
|
61
|
+
the solid live fill, `states.css`) and by its label's weight, never by an
|
|
62
|
+
`--accent-quiet` wash across the row: the wash fought the clip for the one
|
|
63
|
+
accent the lane has, and on a three-row dock it lit a third of the panel. */
|
|
64
|
+
|
|
65
|
+
/* THE CLIP, option A, the one recipe every lane clip reads from: a top-down
|
|
66
|
+
accent wash 18% to 6% in oklab under a 1.5px accent edge at 45%, radius 6,
|
|
67
|
+
20 tall at 4 from the row's top, 11/400 accent ink inside. The shot clip
|
|
68
|
+
is the same at 22%/8% with a 60% edge. Tokens so `states.css` can name the
|
|
69
|
+
rest look when it lays the selected edge over it. */
|
|
70
|
+
.timeline-dock {
|
|
71
|
+
--clip-rest: linear-gradient(
|
|
72
|
+
in oklab 180deg,
|
|
73
|
+
color-mix(in oklab, var(--accent) 18%, transparent),
|
|
74
|
+
color-mix(in oklab, var(--accent) 6%, transparent)
|
|
75
|
+
);
|
|
76
|
+
--clip-edge: color-mix(in oklab, var(--accent) 45%, transparent);
|
|
77
|
+
--clip-shot-rest: linear-gradient(
|
|
78
|
+
in oklab 180deg,
|
|
79
|
+
color-mix(in oklab, var(--accent) 22%, transparent),
|
|
80
|
+
color-mix(in oklab, var(--accent) 8%, transparent)
|
|
81
|
+
);
|
|
82
|
+
--clip-shot-edge: color-mix(in oklab, var(--accent) 60%, transparent);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* The cut marker: a small diamond ON the seam, hollow for a hard cut and
|
|
86
|
+
filled when the shot before it blends. Centred by a half-width translate so
|
|
87
|
+
it sits on the boundary rather than after it. Below the playhead (z 4) so the
|
|
88
|
+
frame you are on always wins. */
|
|
89
|
+
.scene-cut {
|
|
90
|
+
position: absolute;
|
|
91
|
+
top: 50%;
|
|
92
|
+
width: 9px;
|
|
93
|
+
height: 9px;
|
|
94
|
+
padding: 0;
|
|
95
|
+
transform: translate(-50%, -50%) rotate(45deg);
|
|
96
|
+
border: 1px solid var(--ink-3);
|
|
97
|
+
border-radius: var(--radius-2xs);
|
|
98
|
+
background: var(--surface-panel);
|
|
99
|
+
cursor: pointer;
|
|
100
|
+
z-index: 3;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.scene-cut:hover {
|
|
104
|
+
border-color: var(--ink-1);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.scene-cut.is-blend {
|
|
108
|
+
border-color: var(--accent);
|
|
109
|
+
background: var(--accent);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* The blend's length, drawn forward from the cut into the incoming shot's
|
|
113
|
+
space — which is where `transitionAt` puts the window.
|
|
114
|
+
|
|
115
|
+
Accent at low alpha rather than a fill: the ribbon overhangs the row beneath
|
|
116
|
+
it, and a solid bar there would read as a shot that is not one. It sits
|
|
117
|
+
BELOW the diamond (z-index 2 against its 3) so the seam's own control stays
|
|
118
|
+
the top target, and it is pointer-transparent so only the handle inside it
|
|
119
|
+
takes a drag. */
|
|
120
|
+
.scene-blend {
|
|
121
|
+
position: absolute;
|
|
122
|
+
top: 50%;
|
|
123
|
+
height: 9px;
|
|
124
|
+
transform: translateY(-50%);
|
|
125
|
+
border: 1px solid var(--accent);
|
|
126
|
+
border-left: none;
|
|
127
|
+
border-radius: 0 var(--radius-2xs) var(--radius-2xs) 0;
|
|
128
|
+
background: color-mix(in oklab, var(--accent) 22%, transparent);
|
|
129
|
+
pointer-events: none;
|
|
130
|
+
z-index: 2;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* The one thing in the ribbon that is a target. `.scene-trim` supplies the
|
|
134
|
+
7px column and the col-resize cursor; this only re-enables the pointer the
|
|
135
|
+
ribbon turned off. */
|
|
136
|
+
.scene-blend .scene-trim {
|
|
137
|
+
pointer-events: auto;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* THE RUBBER BAND, over the rows it is selecting.
|
|
141
|
+
|
|
142
|
+
AN EDGE AND A WASH, never a surface step. `--panel-raised` IS `--panel` in
|
|
143
|
+
the light appearance, so a band drawn as a lighter fill over the lane would
|
|
144
|
+
be invisible in exactly one of the two themes and perfect in the other. The
|
|
145
|
+
1px accent edge is what reads in both, and the 12% wash is there only to say
|
|
146
|
+
which side of the line is inside: it is the same pair the stage's own marquee
|
|
147
|
+
draws with (`Overlays.tsx`), so the two surfaces spell one gesture one way.
|
|
148
|
+
|
|
149
|
+
`--marquee` and not `--accent` directly: the token exists for this, and the
|
|
150
|
+
stage reads it from the same file (shotTokens.css).
|
|
151
|
+
|
|
152
|
+
Pointer-transparent, and level with the sticky label cells (4) but later in
|
|
153
|
+
the DOM, so it is drawn THROUGH them: a band that vanished behind the column
|
|
154
|
+
would stop halfway across its own box. Below the sticky ruler (5). */
|
|
155
|
+
.dock-marquee {
|
|
156
|
+
position: absolute;
|
|
157
|
+
border: 1px solid var(--marquee);
|
|
158
|
+
background: color-mix(in srgb, var(--marquee) 12%, transparent);
|
|
159
|
+
border-radius: var(--radius-2xs);
|
|
160
|
+
pointer-events: none;
|
|
161
|
+
z-index: 4;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/* Groups are separated by the boundary line above; this is the horizontal half
|
|
165
|
+
of the same statement, for a reader scanning the label column.
|
|
166
|
+
A CLASS AND NOT AN ADJACENT SIBLING: past the virtual window's threshold a
|
|
167
|
+
spacer can sit between two groups, so `+` lost the line at a window edge
|
|
168
|
+
while `groupHeights` still counted it. The class follows the group's index,
|
|
169
|
+
which is what the prefix sum counts. */
|
|
170
|
+
.dock-scene-group.has-separator {
|
|
171
|
+
border-top: 1px solid var(--line);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/* ---- shot rows ---------------------------------------------------------- */
|
|
175
|
+
/* One row per shot, at the head of that shot's group. Taller than a block row
|
|
176
|
+
and quieter than a lifespan bar, because it is the thing all of them sit
|
|
177
|
+
INSIDE — and its bar and the boundary lines through the track area are the
|
|
178
|
+
same prefix sum, so nothing can move one without the other. */
|
|
179
|
+
.shot-row {
|
|
180
|
+
display: flex;
|
|
181
|
+
align-items: stretch;
|
|
182
|
+
/* SHOT_ROW_H in model.ts, which the vertical follow and the group sums read.
|
|
183
|
+
A px literal so the row-metrics test can compare the two. */
|
|
184
|
+
height: 28px;
|
|
185
|
+
border-bottom: 1px solid var(--line-soft);
|
|
186
|
+
/* Same isolation boundary as .lifespan-row, same legality note. */
|
|
187
|
+
contain: layout paint;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* THE SHOT LABEL: an accent 8px swatch, the name at 12/600 ink, and at the
|
|
191
|
+
end the fold: "N layers ›", which opens the shot's layer rows under it. */
|
|
192
|
+
.shot-label-cell {
|
|
193
|
+
display: flex;
|
|
194
|
+
align-items: center;
|
|
195
|
+
gap: var(--space-4);
|
|
196
|
+
padding: 0 var(--space-7) 0 var(--space-7);
|
|
197
|
+
flex: none;
|
|
198
|
+
overflow: hidden;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.shot-swatch {
|
|
202
|
+
flex: none;
|
|
203
|
+
width: 8px;
|
|
204
|
+
height: 8px;
|
|
205
|
+
border-radius: var(--radius-2xs);
|
|
206
|
+
background: var(--accent);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.shot-disclosure {
|
|
210
|
+
flex: none;
|
|
211
|
+
display: inline-flex;
|
|
212
|
+
align-items: center;
|
|
213
|
+
gap: var(--space-2);
|
|
214
|
+
margin-left: auto;
|
|
215
|
+
font: inherit;
|
|
216
|
+
font-size: var(--type-sm);
|
|
217
|
+
line-height: var(--leading-none);
|
|
218
|
+
color: var(--ink-3);
|
|
219
|
+
background: transparent;
|
|
220
|
+
border: 0;
|
|
221
|
+
padding: var(--space-2) 0;
|
|
222
|
+
white-space: nowrap;
|
|
223
|
+
cursor: pointer;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.shot-disclosure:hover {
|
|
227
|
+
color: var(--ink-1);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.shot-fold-chevron {
|
|
231
|
+
flex: none;
|
|
232
|
+
width: 12px;
|
|
233
|
+
height: 12px;
|
|
234
|
+
transition: transform var(--dur-fast) var(--ease);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.shot-fold-chevron.is-open {
|
|
238
|
+
transform: rotate(90deg);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.shot-name {
|
|
242
|
+
font: inherit;
|
|
243
|
+
font-size: var(--type-md);
|
|
244
|
+
font-weight: var(--weight-semibold);
|
|
245
|
+
color: var(--ink-1);
|
|
246
|
+
background: transparent;
|
|
247
|
+
border: 0;
|
|
248
|
+
padding: var(--space-1) 0;
|
|
249
|
+
min-width: 0;
|
|
250
|
+
overflow: hidden;
|
|
251
|
+
text-overflow: ellipsis;
|
|
252
|
+
white-space: nowrap;
|
|
253
|
+
cursor: pointer;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.shot-rename {
|
|
257
|
+
font: inherit;
|
|
258
|
+
font-weight: 600;
|
|
259
|
+
color: var(--ink-1);
|
|
260
|
+
background: var(--fill-rest);
|
|
261
|
+
border: 1px solid var(--accent);
|
|
262
|
+
border-radius: var(--radius-xs);
|
|
263
|
+
padding: 1px var(--space-2);
|
|
264
|
+
min-width: 0;
|
|
265
|
+
flex: 1;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.shot-lane {
|
|
269
|
+
position: relative;
|
|
270
|
+
flex: 1;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/* THE SHOT CLIP, option A at its heavier step: the accent wash 22% to 8%
|
|
274
|
+
under a 1.5px accent edge at 60%, radius 6, 20 tall at 4 from the row's top
|
|
275
|
+
(the row is 28 with a 1px bottom border, so 3 stay clear under it). The name
|
|
276
|
+
at 11/600 accent ink, the length at the far end in mono 10. Solid accent
|
|
277
|
+
while the playhead is in it (`states.css`). */
|
|
278
|
+
.shot-bar {
|
|
279
|
+
position: absolute;
|
|
280
|
+
top: 4px;
|
|
281
|
+
height: 20px;
|
|
282
|
+
box-sizing: border-box;
|
|
283
|
+
display: flex;
|
|
284
|
+
align-items: center;
|
|
285
|
+
gap: var(--space-4);
|
|
286
|
+
padding: 0 var(--space-4);
|
|
287
|
+
border-radius: var(--radius-sm);
|
|
288
|
+
background: var(--clip-shot-rest);
|
|
289
|
+
border: 1.5px solid var(--clip-shot-edge);
|
|
290
|
+
color: var(--accent-ink);
|
|
291
|
+
font-size: var(--type-sm);
|
|
292
|
+
line-height: var(--leading-none);
|
|
293
|
+
cursor: pointer;
|
|
294
|
+
overflow: hidden;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.shot-bar-name {
|
|
298
|
+
min-width: 0;
|
|
299
|
+
white-space: nowrap;
|
|
300
|
+
overflow: hidden;
|
|
301
|
+
text-overflow: ellipsis;
|
|
302
|
+
font-weight: var(--weight-semibold);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/* The shot's length ("18.00s", `formatDuration`), pushed to the clip's far
|
|
306
|
+
end. Mono, so a column of lengths reads as numbers. */
|
|
307
|
+
.shot-frames {
|
|
308
|
+
flex: none;
|
|
309
|
+
margin-left: auto;
|
|
310
|
+
font-family: var(--font-mono);
|
|
311
|
+
font-size: var(--type-micro);
|
|
312
|
+
font-variant-numeric: var(--numeric);
|
|
313
|
+
white-space: nowrap;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/* Reorder feedback: the line on the side the dragged shot lands, the outline's
|
|
317
|
+
`.layer-row.is-drop-*` recipe turned on its side. */
|
|
318
|
+
.shot-bar.is-drop-before {
|
|
319
|
+
box-shadow: inset 2px 0 0 var(--ink-1);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.shot-bar.is-drop-after {
|
|
323
|
+
box-shadow: inset -2px 0 0 var(--ink-1);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/* THE WAY IN. Sits inside the clip after the length, left of the trim handle,
|
|
327
|
+
and is the only control on the row that changes what the whole chrome is
|
|
328
|
+
showing: an aimed target beside the double-click. The trim handle's width
|
|
329
|
+
stays reserved so the two never overlap. */
|
|
330
|
+
.scene-open {
|
|
331
|
+
flex: none;
|
|
332
|
+
margin-right: var(--dock-trim-w);
|
|
333
|
+
font: inherit;
|
|
334
|
+
font-size: var(--type-lg);
|
|
335
|
+
line-height: var(--leading-none);
|
|
336
|
+
color: inherit;
|
|
337
|
+
opacity: 0.7;
|
|
338
|
+
background: transparent;
|
|
339
|
+
border: 0;
|
|
340
|
+
padding: 0 var(--space-1);
|
|
341
|
+
border-radius: var(--radius-2xs);
|
|
342
|
+
cursor: pointer;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.scene-open:hover {
|
|
346
|
+
opacity: 1;
|
|
347
|
+
background: color-mix(in srgb, currentColor 18%, transparent);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.scene-trim {
|
|
351
|
+
position: absolute;
|
|
352
|
+
right: 0;
|
|
353
|
+
top: 0;
|
|
354
|
+
bottom: 0;
|
|
355
|
+
width: var(--dock-trim-w);
|
|
356
|
+
cursor: col-resize;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/* The grip, drawn only on the edge that really drags. The head of a shot is
|
|
360
|
+
not a handle (moving it is a reorder, not a trim), so it carries none: a grip
|
|
361
|
+
there would promise a drag that does nothing. A 2x12 grip in the clip's own
|
|
362
|
+
ink (accent at rest, white on the live fill): heavier than a lifespan bar's
|
|
363
|
+
grip because this edge moves every shot after it. */
|
|
364
|
+
.scene-trim::after {
|
|
365
|
+
content: "";
|
|
366
|
+
position: absolute;
|
|
367
|
+
left: 50%;
|
|
368
|
+
top: 50%;
|
|
369
|
+
width: 2px;
|
|
370
|
+
height: 12px;
|
|
371
|
+
/* The ladder's first step: on a 2px-wide grip it clamps to fully round ends. */
|
|
372
|
+
border-radius: var(--radius-2xs);
|
|
373
|
+
transform: translate(-50%, -50%);
|
|
374
|
+
background: currentColor;
|
|
375
|
+
pointer-events: none;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
/* ---- lifespan rows ------------------------------------------------------ */
|
|
380
|
+
/* The layer's own bar: taller and quieter than a block bar, because it is the
|
|
381
|
+
thing a block sits INSIDE. Solid-ish fill rather than the block's outline,
|
|
382
|
+
so a row reads as one object with intervals on it rather than as a pile of
|
|
383
|
+
equal-weight rectangles. */
|
|
384
|
+
.lifespan-row {
|
|
385
|
+
display: flex;
|
|
386
|
+
align-items: stretch;
|
|
387
|
+
height: 28px;
|
|
388
|
+
border-bottom: 1px solid var(--line-soft);
|
|
389
|
+
/* Fixed-height rows are isolation boundaries: a style change inside one
|
|
390
|
+
(a bar drag, a hover) must not invalidate layout or repaint outside it.
|
|
391
|
+
Legal here — the containment ban (styles.css:5) covers only ancestors of
|
|
392
|
+
the stage iframe, and the dock is its sibling. */
|
|
393
|
+
contain: layout paint;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/* A PICK ELSEWHERE REVEALED THIS ROW (Paper page 23, "6 · Reveal on select"):
|
|
397
|
+
one wash of the accent that settles back to the row's own ground, selected
|
|
398
|
+
or not, so the eye lands on it after the scroll. No fill mode: when it ends
|
|
399
|
+
the row is exactly what it was, and the dock takes the class back. */
|
|
400
|
+
@keyframes row-revealed {
|
|
401
|
+
from { background-color: var(--accent-soft); }
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
405
|
+
.lifespan-row.is-revealed { animation: row-revealed 900ms var(--ease); }
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.lifespan-label-cell {
|
|
409
|
+
/* `--mask-inset` makes room for the row's mask brackets (maskMarks.tsx),
|
|
410
|
+
which sit against this box like the outline's sit against its row. */
|
|
411
|
+
position: relative;
|
|
412
|
+
/* 32 in from the seam and 16 short of it, the boards' layer label, plus the
|
|
413
|
+
tree's indent and the mask brackets' room. */
|
|
414
|
+
padding-left: calc(var(--space-7) * 2 + var(--depth, 0) * var(--tree-indent) + var(--mask-inset, 0px));
|
|
415
|
+
padding-right: var(--space-7);
|
|
416
|
+
display: flex;
|
|
417
|
+
align-items: center;
|
|
418
|
+
flex: none;
|
|
419
|
+
overflow: hidden;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.lifespan-disclosure {
|
|
423
|
+
font: inherit;
|
|
424
|
+
font-size: var(--type-micro);
|
|
425
|
+
line-height: var(--leading-none);
|
|
426
|
+
color: var(--ink-3);
|
|
427
|
+
background: transparent;
|
|
428
|
+
border: 0;
|
|
429
|
+
padding: var(--space-2) var(--space-2);
|
|
430
|
+
cursor: pointer;
|
|
431
|
+
flex: none;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.lifespan-disclosure:hover {
|
|
435
|
+
color: var(--ink-1);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/* 12px secondary at rest. The row whose clip the playhead is in lifts its
|
|
439
|
+
name to 600 ink (`states.css`); selection does not, the clip says it. */
|
|
440
|
+
.lifespan-name {
|
|
441
|
+
font: inherit;
|
|
442
|
+
font-size: var(--type-md);
|
|
443
|
+
color: var(--ink-2);
|
|
444
|
+
background: transparent;
|
|
445
|
+
border: 0;
|
|
446
|
+
text-align: left;
|
|
447
|
+
padding: 0 var(--space-3);
|
|
448
|
+
cursor: pointer;
|
|
449
|
+
flex: 1;
|
|
450
|
+
min-width: 0;
|
|
451
|
+
white-space: nowrap;
|
|
452
|
+
overflow: hidden;
|
|
453
|
+
text-overflow: ellipsis;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.lifespan-lane {
|
|
457
|
+
position: relative;
|
|
458
|
+
flex: 1;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/* CLIP OPTION A AT REST: the accent wash under its 45% edge, radius 6, 20
|
|
462
|
+
tall at 4 under the row's top (3 clear of its 1px bottom border), 8 in, the
|
|
463
|
+
name at 11/400 accent ink. The tokens are on `.timeline-dock` above. */
|
|
464
|
+
.lifespan-bar {
|
|
465
|
+
position: absolute;
|
|
466
|
+
top: 4px;
|
|
467
|
+
bottom: 3px;
|
|
468
|
+
display: flex;
|
|
469
|
+
align-items: center;
|
|
470
|
+
padding: 0 var(--space-4);
|
|
471
|
+
border-radius: var(--radius-sm);
|
|
472
|
+
background: var(--clip-rest);
|
|
473
|
+
border: 1.5px solid var(--clip-edge);
|
|
474
|
+
color: var(--accent-ink);
|
|
475
|
+
font-size: var(--type-sm);
|
|
476
|
+
font-weight: var(--weight-regular);
|
|
477
|
+
cursor: grab;
|
|
478
|
+
overflow: hidden;
|
|
479
|
+
white-space: nowrap;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.lifespan-bar:hover {
|
|
483
|
+
border-color: color-mix(in oklab, var(--accent) 70%, transparent);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/* A VIDEO CLIP keeps the hueless ground: its picture and its waveform fill the
|
|
487
|
+
box (the inline style drops the border and the padding), and an accent wash
|
|
488
|
+
under footage would tint the frames. */
|
|
489
|
+
.lifespan-bar.is-video {
|
|
490
|
+
background: var(--bar-ground);
|
|
491
|
+
color: var(--ink-1);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/* SELECTED IS AN EDGE, never a fill: the fill says whether the clip is playing
|
|
495
|
+
now (states.css), and an author routinely has one clip selected and another
|
|
496
|
+
under the playhead. The full accent edge at 1.5px over the rest wash, the
|
|
497
|
+
name at 600, and white trim handles 3x12 at each end. Selected AND live is
|
|
498
|
+
the solid fill with the same handles. The video clip has no border of its
|
|
499
|
+
own, so its selection stays an outline. */
|
|
500
|
+
.lifespan-bar.is-selected {
|
|
501
|
+
border-color: var(--accent);
|
|
502
|
+
padding-inline: calc(var(--space-3) / 2);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.lifespan-bar.is-selected .lifespan-bar-name {
|
|
506
|
+
font-weight: var(--weight-semibold);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
.lifespan-bar.is-video.is-selected {
|
|
510
|
+
outline: 1.5px solid var(--accent);
|
|
511
|
+
outline-offset: -1.5px;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/* THE STAGGER (Paper page 23, "8 · Stagger"). A dashed ghost where each moving
|
|
515
|
+
bar started, an accent rule above it as long as the bar's shift, and the step
|
|
516
|
+
readout beside the grabbed bar. All three are drawn only while ⌥ drags a
|
|
517
|
+
selection, and none takes a pointer. The ghost is the bar's own box. */
|
|
518
|
+
.stagger-ghost {
|
|
519
|
+
position: absolute;
|
|
520
|
+
top: 4px;
|
|
521
|
+
bottom: 5px;
|
|
522
|
+
border: 1px dashed var(--ink-3);
|
|
523
|
+
border-radius: var(--radius-2xs);
|
|
524
|
+
background: color-mix(in srgb, var(--wash-ink) 3%, transparent);
|
|
525
|
+
pointer-events: none;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
.stagger-shift {
|
|
529
|
+
position: absolute;
|
|
530
|
+
top: calc(-1 * var(--space-3));
|
|
531
|
+
height: 1px;
|
|
532
|
+
background: var(--accent-ink);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
.stagger-chip {
|
|
536
|
+
position: absolute;
|
|
537
|
+
top: 50%;
|
|
538
|
+
transform: translateY(-50%);
|
|
539
|
+
margin-left: var(--space-4);
|
|
540
|
+
display: inline-flex;
|
|
541
|
+
align-items: center;
|
|
542
|
+
gap: var(--space-3);
|
|
543
|
+
padding: var(--space-1) var(--space-4);
|
|
544
|
+
border: 1px solid var(--line-strong);
|
|
545
|
+
border-radius: var(--radius-sm);
|
|
546
|
+
background: var(--surface-panel);
|
|
547
|
+
box-shadow: var(--shadow-control);
|
|
548
|
+
font-size: var(--type-sm);
|
|
549
|
+
white-space: nowrap;
|
|
550
|
+
pointer-events: none;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
.stagger-chip-step {
|
|
554
|
+
color: var(--ink-1);
|
|
555
|
+
font-weight: var(--weight-semibold);
|
|
556
|
+
font-variant-numeric: var(--numeric);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
.stagger-chip-key {
|
|
560
|
+
color: var(--ink-3);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/* ---- the stretch bracket (KeyStretch.tsx) --------------------------------
|
|
564
|
+
The selected keys' span: a guide through the rows at each end (in the
|
|
565
|
+
pointer-transparent overlay), in the key colour (never the accent), and in
|
|
566
|
+
the sticky ruler a band over the span with a grab tab just inside each end,
|
|
567
|
+
so it never scrolls away and survives the ruler's clip at frame 0. The
|
|
568
|
+
readout sits in the ruler beside the tab being dragged. */
|
|
569
|
+
.dock-key-stretch-guide {
|
|
570
|
+
position: absolute;
|
|
571
|
+
top: 0;
|
|
572
|
+
bottom: 0;
|
|
573
|
+
width: 1px;
|
|
574
|
+
background: var(--kf-soft);
|
|
575
|
+
pointer-events: none;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
.dock-key-stretch-handle {
|
|
579
|
+
position: absolute;
|
|
580
|
+
top: var(--space-1);
|
|
581
|
+
bottom: var(--space-1);
|
|
582
|
+
width: var(--space-4);
|
|
583
|
+
border: 1px solid var(--kf);
|
|
584
|
+
background: var(--surface-panel);
|
|
585
|
+
border-radius: var(--radius-2xs);
|
|
586
|
+
cursor: ew-resize;
|
|
587
|
+
pointer-events: auto;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
.dock-key-stretch-band {
|
|
591
|
+
position: absolute;
|
|
592
|
+
top: var(--space-1);
|
|
593
|
+
bottom: var(--space-1);
|
|
594
|
+
background: var(--kf-soft);
|
|
595
|
+
border-radius: var(--radius-2xs);
|
|
596
|
+
pointer-events: none;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
.dock-key-stretch-handle.is-right {
|
|
600
|
+
transform: translateX(-100%);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
.dock-key-stretch-handle:hover {
|
|
604
|
+
background: var(--kf-soft);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
.dock-key-stretch-chip {
|
|
608
|
+
z-index: 1;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
.lifespan-bar-name {
|
|
612
|
+
min-width: 0;
|
|
613
|
+
overflow: hidden;
|
|
614
|
+
text-overflow: ellipsis;
|
|
615
|
+
pointer-events: none;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/* THE FULL NAME, OUTSIDE. A name that does not fit its clip is never cut: it
|
|
619
|
+
sits in the lane after the clip, or before it when the clip ends at the
|
|
620
|
+
track's end (`clipNamePlace`), at the inside name's size and ink. The 6px
|
|
621
|
+
gap and the side come from the row; this is only the type. */
|
|
622
|
+
.lifespan-bar-name.is-outside {
|
|
623
|
+
position: absolute;
|
|
624
|
+
top: 7px;
|
|
625
|
+
width: max-content;
|
|
626
|
+
overflow: visible;
|
|
627
|
+
white-space: pre;
|
|
628
|
+
font-size: var(--type-sm);
|
|
629
|
+
line-height: var(--leading-tight);
|
|
630
|
+
color: var(--accent-ink);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/* A MASK'S BAR (gap 7, Paper page 25). Dashed in the accent ink, a pill, with
|
|
634
|
+
the mode beside the name: a mask draws nothing of its own, so its bar must
|
|
635
|
+
not read as a layer that paints. Its edge survives the live fill, see
|
|
636
|
+
states.css. */
|
|
637
|
+
.lifespan-bar.is-mask {
|
|
638
|
+
gap: var(--space-3);
|
|
639
|
+
padding: 0 var(--space-4);
|
|
640
|
+
border: 1px dashed var(--accent-ink);
|
|
641
|
+
border-radius: var(--radius-full);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
.lifespan-bar.is-mask .lifespan-bar-name {
|
|
645
|
+
color: var(--accent-ink);
|
|
646
|
+
font-size: var(--type-sm);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
.lifespan-bar-mask {
|
|
650
|
+
flex: none;
|
|
651
|
+
font-family: var(--font-mono);
|
|
652
|
+
font-size: var(--type-micro);
|
|
653
|
+
color: var(--ink-2);
|
|
654
|
+
pointer-events: none;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
.lifespan-handle {
|
|
658
|
+
position: absolute;
|
|
659
|
+
top: 0;
|
|
660
|
+
bottom: 0;
|
|
661
|
+
width: 8px;
|
|
662
|
+
cursor: col-resize;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/* THE TRIM HANDLES, shown on a selected clip only: white 3x12 tabs, radius
|
|
666
|
+
2, centred in each 8px hit target. A hairline accent ring keeps the white
|
|
667
|
+
readable on the light wash; on the solid live fill it disappears into it.
|
|
668
|
+
The hit target is there at rest too, unmarked. Not on a video clip: its edges
|
|
669
|
+
are trims with their own picture under them, and a tab over the frames is
|
|
670
|
+
noise. */
|
|
671
|
+
.lifespan-bar.is-selected:not(.is-video) .lifespan-handle::after {
|
|
672
|
+
content: "";
|
|
673
|
+
position: absolute;
|
|
674
|
+
left: 50%;
|
|
675
|
+
top: 50%;
|
|
676
|
+
width: 3px;
|
|
677
|
+
height: 12px;
|
|
678
|
+
border-radius: var(--radius-2xs);
|
|
679
|
+
transform: translate(-50%, -50%);
|
|
680
|
+
background: var(--on-accent);
|
|
681
|
+
box-shadow: 0 0 0 1px var(--accent);
|
|
682
|
+
pointer-events: none;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
.lifespan-handle-in {
|
|
686
|
+
left: 0;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
.lifespan-handle-out {
|
|
690
|
+
right: 0;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/* ---- the camera row ----------------------------------------------------- */
|
|
694
|
+
/* 28, the row ruling, and `BLOCK_ROW_H` in model.ts: the camera row is counted
|
|
695
|
+
by the dock's height sums at that number. */
|
|
696
|
+
.block-row {
|
|
697
|
+
display: flex;
|
|
698
|
+
align-items: stretch;
|
|
699
|
+
height: 28px;
|
|
700
|
+
border-bottom: 1px solid var(--line-soft);
|
|
701
|
+
/* Same isolation boundary as .lifespan-row, same legality note. */
|
|
702
|
+
contain: layout paint;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/* The label column: the name, and on the camera row a create button beside it.
|
|
706
|
+
Fixed width from the dock's label width, so the lanes of every row still
|
|
707
|
+
start on the same x as the ruler. */
|
|
708
|
+
.block-row-label-cell {
|
|
709
|
+
display: flex;
|
|
710
|
+
align-items: stretch;
|
|
711
|
+
flex: none;
|
|
712
|
+
overflow: hidden;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/* 12px secondary, the lifespan label's recipe, so the camera row reads as part
|
|
716
|
+
of the same column. */
|
|
717
|
+
.block-row-label {
|
|
718
|
+
align-self: stretch;
|
|
719
|
+
font: inherit;
|
|
720
|
+
font-size: var(--type-md);
|
|
721
|
+
color: var(--ink-2);
|
|
722
|
+
background: transparent;
|
|
723
|
+
border: 0;
|
|
724
|
+
text-align: left;
|
|
725
|
+
padding: 0;
|
|
726
|
+
cursor: pointer;
|
|
727
|
+
flex: 1;
|
|
728
|
+
min-width: 0;
|
|
729
|
+
white-space: nowrap;
|
|
730
|
+
overflow: hidden;
|
|
731
|
+
text-overflow: ellipsis;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/* THE CAMERA ROW'S LABEL: a hollow 8px square (the shot's own eye, not one of
|
|
735
|
+
its contents), "Camera" in the secondary ink, then at the end how many marks
|
|
736
|
+
it holds in mono accent and the add icon. 16 in from the seam, like the shot
|
|
737
|
+
and the sound rows it sits between. */
|
|
738
|
+
.block-row.is-camera .block-row-label-cell {
|
|
739
|
+
align-items: center;
|
|
740
|
+
gap: var(--space-4);
|
|
741
|
+
padding: 0 var(--space-5) 0 var(--space-7);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
.camera-swatch {
|
|
745
|
+
flex: none;
|
|
746
|
+
width: 8px;
|
|
747
|
+
height: 8px;
|
|
748
|
+
border: 1.5px solid var(--ink-2);
|
|
749
|
+
border-radius: var(--radius-2xs);
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
.camera-keyed {
|
|
753
|
+
flex: none;
|
|
754
|
+
font-family: var(--font-mono);
|
|
755
|
+
font-size: var(--type-micro);
|
|
756
|
+
font-variant-numeric: var(--numeric);
|
|
757
|
+
color: var(--accent-ink);
|
|
758
|
+
white-space: nowrap;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/* VISIBLE AT REST: the one place a camera move is created, and every dimmed
|
|
762
|
+
version of it read as a capability the editor does not have ("quiet is not
|
|
763
|
+
hidden"). A compact house icon button, 20 square with a 12px glyph. */
|
|
764
|
+
.block-row-add {
|
|
765
|
+
flex: none;
|
|
766
|
+
width: 20px;
|
|
767
|
+
height: 20px;
|
|
768
|
+
padding: 0;
|
|
769
|
+
color: var(--ink-2);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
.block-row-add svg {
|
|
773
|
+
width: 12px;
|
|
774
|
+
height: 12px;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
.block-row-lane {
|
|
778
|
+
position: relative;
|
|
779
|
+
flex: 1;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/* THE CAMERA LANE IS A LINE WITH MARKS ON IT: a 1px hairline 13 from the top,
|
|
783
|
+
from 8 in, and the poses as diamonds on it. The moves are still bars (they
|
|
784
|
+
take the drag, the trim and the menu) but they draw nothing at rest, so the
|
|
785
|
+
lane reads as the boards' keyframe line; hovering or selecting a move shows
|
|
786
|
+
its extent as a clip edge, and its name. */
|
|
787
|
+
.block-row.is-camera .block-row-lane::before {
|
|
788
|
+
content: "";
|
|
789
|
+
position: absolute;
|
|
790
|
+
top: 13px;
|
|
791
|
+
left: 8px;
|
|
792
|
+
right: 0;
|
|
793
|
+
height: 1px;
|
|
794
|
+
background: var(--line);
|
|
795
|
+
pointer-events: none;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
.block-row.is-camera .block-bar {
|
|
799
|
+
background: transparent;
|
|
800
|
+
border-color: transparent;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
.block-row.is-camera .block-bar .block-label,
|
|
804
|
+
.block-row.is-camera .block-handle::after {
|
|
805
|
+
visibility: hidden;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
.block-row.is-camera .block-bar:hover,
|
|
809
|
+
.block-row.is-camera .block-bar.is-selected {
|
|
810
|
+
border-color: var(--clip-edge);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
.block-row.is-camera .block-bar.is-selected {
|
|
814
|
+
border-color: var(--accent);
|
|
815
|
+
outline: 0;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
.block-row.is-camera .block-bar:hover .block-label,
|
|
819
|
+
.block-row.is-camera .block-bar.is-selected .block-label {
|
|
820
|
+
visibility: visible;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
/* Dark fill, bright border, Raylight's clip. A solid bar per move turns a
|
|
824
|
+
busy row into a colour field where nothing is readable; an outline keeps
|
|
825
|
+
forty of them distinct and lets overlaps show through. */
|
|
826
|
+
/* ── THE INSET LIVES ON THE LABEL, NOT ON THE BAR ─────────────────────────
|
|
827
|
+
|
|
828
|
+
`padding: 0 8px` here, under the global `box-sizing: border-box`, put a hard
|
|
829
|
+
FLOOR of 16px plus the border under every bar: a box cannot render narrower
|
|
830
|
+
than its own padding. So a bar the code sized at 9.9px for a one-frame move
|
|
831
|
+
drew 18px, nearly twice its length and most of it over the top of the next
|
|
832
|
+
block. The camera row this was found on has twenty-two moves, eight of them
|
|
833
|
+
one frame long, so eight bars overdrew their neighbour: both a lie about the
|
|
834
|
+
move's length and a theft of the neighbour's trim handle and click target.
|
|
835
|
+
|
|
836
|
+
Moving the same 8px onto `.block-label` keeps every wide bar looking exactly
|
|
837
|
+
as it did, and lets a narrow one be as narrow as the block really is. The
|
|
838
|
+
`Math.max(4, ...)` in BlockBarView stays the only floor, which is what it
|
|
839
|
+
was always meant to be. */
|
|
840
|
+
.block-bar {
|
|
841
|
+
position: absolute;
|
|
842
|
+
top: 4px;
|
|
843
|
+
bottom: 4px;
|
|
844
|
+
display: flex;
|
|
845
|
+
align-items: center;
|
|
846
|
+
border-radius: var(--radius-sm);
|
|
847
|
+
/* THE FILL SAYS WHEN, THE BORDER SAYS WHAT. At rest the fill is the house
|
|
848
|
+
control ground, the same as a lifetime bar's, and the live rule at the
|
|
849
|
+
overrides layer (states.css) paints it while the playhead is inside the bar.
|
|
850
|
+
The border and the label keep the move hue at all times: that hue is the one
|
|
851
|
+
fact a move has that a lifetime bar does not, so it takes the place of the
|
|
852
|
+
control edge here. */
|
|
853
|
+
background: var(--bar-ground);
|
|
854
|
+
border: 1px solid color-mix(in srgb, var(--block-during) 60%, transparent);
|
|
855
|
+
color: color-mix(in srgb, var(--block-during) 40%, var(--timeline-ink));
|
|
856
|
+
cursor: grab;
|
|
857
|
+
overflow: visible;
|
|
858
|
+
white-space: nowrap;
|
|
859
|
+
transition: background var(--dur-fast) var(--ease);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
.block-bar:hover {
|
|
863
|
+
background: var(--bar-ground-hover);
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
/* Selected is the accent edge and the accent label, the lifetime bar's own
|
|
867
|
+
selection. An inset outline, so it sits on the border instead of a pixel
|
|
868
|
+
outside it, and survives the live rule repainting that border. */
|
|
869
|
+
.block-bar.is-selected {
|
|
870
|
+
outline: 1px solid var(--accent);
|
|
871
|
+
outline-offset: -1px;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
.block-bar.is-selected .block-label {
|
|
875
|
+
color: var(--accent-ink);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
.block-label {
|
|
879
|
+
padding: 0 var(--space-4);
|
|
880
|
+
overflow: hidden;
|
|
881
|
+
/* A BACKSTOP, not the mechanism. `fitsBlockLabel` decides whether the label
|
|
882
|
+
is rendered at all, and it estimates the text's width rather than
|
|
883
|
+
measuring it, so this catches the case where the estimate runs a pixel
|
|
884
|
+
short instead of letting a word spill out of its bar. */
|
|
885
|
+
text-overflow: ellipsis;
|
|
886
|
+
pointer-events: none;
|
|
887
|
+
font-weight: 500;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/* ── A SHARE OF THE BAR, NOT A FIXED STRIP ───────────────────────────────
|
|
891
|
+
|
|
892
|
+
7px flat was safe only while `.block-bar` carried padding, because that put
|
|
893
|
+
an 18px floor under every bar and two 7px handles could never meet. With the
|
|
894
|
+
padding moved to the label a bar is as narrow as its block is short, and on a
|
|
895
|
+
one-frame move the two handles covered it end to end: the out handle is later
|
|
896
|
+
in DOM order, so it won every hit test and the body drag became unreachable.
|
|
897
|
+
On a narrow SPRING block it was worse, because that handle swallows the press
|
|
898
|
+
and does nothing, which left the bar completely inert.
|
|
899
|
+
|
|
900
|
+
`min(7px, 35%)` keeps the 7px target on any bar wide enough for it (20px and
|
|
901
|
+
up) and leaves at least 30% of a narrow bar for the body. Below about 8px the
|
|
902
|
+
handles are smaller than the pointer anyway; the bar can still be selected,
|
|
903
|
+
and trimming a block that thin is a zoom-in job either way. */
|
|
904
|
+
.block-handle {
|
|
905
|
+
position: absolute;
|
|
906
|
+
top: 0;
|
|
907
|
+
bottom: 0;
|
|
908
|
+
width: min(7px, 35%);
|
|
909
|
+
cursor: col-resize;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
.block-handle-in {
|
|
913
|
+
left: -1px;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
.block-handle-out {
|
|
917
|
+
right: -1px;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
/* A 1x8 grip centred in each live handle, the lifetime bar's grip. */
|
|
921
|
+
.block-handle:not(.is-disabled)::after {
|
|
922
|
+
content: "";
|
|
923
|
+
position: absolute;
|
|
924
|
+
left: 50%;
|
|
925
|
+
top: 50%;
|
|
926
|
+
width: 1px;
|
|
927
|
+
height: 8px;
|
|
928
|
+
transform: translate(-50%, -50%);
|
|
929
|
+
background: var(--bar-grip);
|
|
930
|
+
pointer-events: none;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
/* The dead variant has to stop promising a resize on an edge that cannot
|
|
934
|
+
resize. That promise IS the defect, so it loses both the cursor and the
|
|
935
|
+
grip above. */
|
|
936
|
+
.block-handle.is-disabled {
|
|
937
|
+
cursor: default;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
/* A spring keeps moving past its own window. The tail says so, and the OUT
|
|
941
|
+
handle is drawn dead, with the reason in its title. */
|
|
942
|
+
.block-spring-tail {
|
|
943
|
+
position: absolute;
|
|
944
|
+
right: -18px;
|
|
945
|
+
top: 50%;
|
|
946
|
+
width: 18px;
|
|
947
|
+
border-top: 1px dashed currentColor;
|
|
948
|
+
opacity: 0.6;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
/* ---- camera poses on the camera row (P4) --------------------------------
|
|
952
|
+
THE CAMERA IS ALREADY A KEYFRAME SYSTEM, and this is its picture. What it
|
|
953
|
+
is NOT is a property lane: a move carries ONE ease for its whole window and
|
|
954
|
+
the gap after it is a hold, so these marks get their own glyph and their own
|
|
955
|
+
state rather than borrowing `.channel-key`'s. Drawing them alike would teach
|
|
956
|
+
an author that every diamond behaves alike, and the first right-click
|
|
957
|
+
expecting an ease menu would prove it wrong.
|
|
958
|
+
|
|
959
|
+
HOLLOW is the distinction, and it carries the meaning: a camera pose is
|
|
960
|
+
read-only here (it lives in `shot.from`/`shot.to`, which the inspector owns)
|
|
961
|
+
while a property keyframe is filled and draggable. */
|
|
962
|
+
.camera-key {
|
|
963
|
+
position: absolute;
|
|
964
|
+
/* A 9x9 square turned 45deg, centred on its frame horizontally and on the
|
|
965
|
+
hairline (13.5) vertically. */
|
|
966
|
+
top: 9px;
|
|
967
|
+
width: 9px;
|
|
968
|
+
height: 9px;
|
|
969
|
+
box-sizing: border-box;
|
|
970
|
+
transform: translateX(-50%) rotate(45deg);
|
|
971
|
+
background: var(--accent);
|
|
972
|
+
border-radius: 1px;
|
|
973
|
+
/* A READING, not a control. Without this the mark would eat the press on a
|
|
974
|
+
bar's own trim handle, and every pose sits exactly on one. */
|
|
975
|
+
pointer-events: none;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
/* CHAINED: the next move opens on the pose this one closed on, so the camera
|
|
979
|
+
passes through it. The other kind of mark: hollow, the dock's ground inside
|
|
980
|
+
a 1.5px accent edge. A filled diamond is a pose the camera stops on. */
|
|
981
|
+
.camera-key.is-chained {
|
|
982
|
+
background: var(--surface-panel);
|
|
983
|
+
border: 1.5px solid var(--accent);
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
/* A PATH's interior waypoint: smaller, and quieter, because it is the one mark
|
|
987
|
+
here that is not an authorable endpoint at all. Still on the hairline. */
|
|
988
|
+
.camera-key.is-waypoint {
|
|
989
|
+
top: 11px;
|
|
990
|
+
width: 5px;
|
|
991
|
+
height: 5px;
|
|
992
|
+
background: var(--surface-panel);
|
|
993
|
+
border: 1px solid var(--accent-ink);
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
/* THE RIG BREAK. The end of a zoom move that changes the zoom: `cameraRigAt`
|
|
997
|
+
pins the distance only INSIDE that window, so one frame later the distance
|
|
998
|
+
and the fov both jump and every layer at a non-zero depth pops.
|
|
999
|
+
Drawn SPLIT, a warm half-mark with a gap where the other half would be,
|
|
1000
|
+
because an unbroken diamond would promise a continuity that is not there.
|
|
1001
|
+
The warm hue is `--guide`'s family, this lane's "look at this" colour, and
|
|
1002
|
+
deliberately not `--danger`: the document is legal and the render is
|
|
1003
|
+
correct, it is the MOTION that has a step in it. */
|
|
1004
|
+
.camera-key.is-break {
|
|
1005
|
+
background: linear-gradient(to right, var(--accent-warm) 0 45%, transparent 45%);
|
|
1006
|
+
border: 1.5px dashed var(--accent-warm);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
/* ---- channel rows (one animated property per row — the Lottie read) ------ */
|
|
1010
|
+
/* 28, the row ruling: `CHANNEL_ROW_H` in model.ts, the unit the dock's height
|
|
1011
|
+
sums count channel rows in. */
|
|
1012
|
+
.channel-row {
|
|
1013
|
+
display: flex;
|
|
1014
|
+
align-items: stretch;
|
|
1015
|
+
height: 28px;
|
|
1016
|
+
border-bottom: 1px solid var(--line-soft);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
.channel-label-cell {
|
|
1020
|
+
padding-left: calc(var(--space-4) + var(--depth, 0) * var(--tree-indent));
|
|
1021
|
+
display: flex;
|
|
1022
|
+
align-items: center;
|
|
1023
|
+
gap: var(--space-3);
|
|
1024
|
+
flex: none;
|
|
1025
|
+
overflow: hidden;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
/* THE NAME TAKES THE SLACK, rather than the value claiming it with
|
|
1029
|
+
`margin-left: auto`. A row can now have a value and a clear button, a clear
|
|
1030
|
+
button alone (a tracked path with no block channel), or neither — and one
|
|
1031
|
+
auto margin cannot right-pack all three cases, while a growing first child
|
|
1032
|
+
can. */
|
|
1033
|
+
.channel-name {
|
|
1034
|
+
flex: 1;
|
|
1035
|
+
min-width: 0;
|
|
1036
|
+
color: var(--ink-3);
|
|
1037
|
+
font-size: var(--type-micro);
|
|
1038
|
+
white-space: nowrap;
|
|
1039
|
+
overflow: hidden;
|
|
1040
|
+
text-overflow: ellipsis;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
/* The resolved value at the playhead. Tabular, so playback reads as a counter
|
|
1044
|
+
rather than a word that keeps changing width. */
|
|
1045
|
+
.channel-value {
|
|
1046
|
+
padding-right: var(--space-4);
|
|
1047
|
+
color: var(--ink-2);
|
|
1048
|
+
font-size: var(--type-micro);
|
|
1049
|
+
font-variant-numeric: var(--numeric);
|
|
1050
|
+
white-space: nowrap;
|
|
1051
|
+
overflow: hidden;
|
|
1052
|
+
text-overflow: ellipsis;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
.channel-lane {
|
|
1056
|
+
position: relative;
|
|
1057
|
+
flex: 1;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
/* SOLID diamonds, the Lottie/Figma mark, and this shape means a TRACK
|
|
1061
|
+
KEYFRAME and nothing else.
|
|
1062
|
+
`--kf` is the house token for exactly this and is not the accent: a keyframe
|
|
1063
|
+
is a fact about the document, while the accent means selection and playhead.
|
|
1064
|
+
An 8px diamond with a 1px corner, the boards' key. The radius is a literal
|
|
1065
|
+
because the house radius scale starts at 2px, which rounds an 8px diamond
|
|
1066
|
+
into a dot.
|
|
1067
|
+
|
|
1068
|
+
SELECTED KEEPS ITS HUE and grows: 12px with a 1px ink-1 edge. Whitening the
|
|
1069
|
+
fill threw away the one thing the mark says (this is a keyframe) at the
|
|
1070
|
+
moment the author is looking hardest at it; size and an edge say "this one"
|
|
1071
|
+
without spending it. 12px turned 45deg is 17px across, inside the 20px row. */
|
|
1072
|
+
.channel-key {
|
|
1073
|
+
position: absolute;
|
|
1074
|
+
top: 50%;
|
|
1075
|
+
width: 8px;
|
|
1076
|
+
height: 8px;
|
|
1077
|
+
transform: translate(-50%, -50%) rotate(45deg);
|
|
1078
|
+
background: var(--kf);
|
|
1079
|
+
border: 1px solid var(--surface-app);
|
|
1080
|
+
border-radius: 1px;
|
|
1081
|
+
cursor: col-resize;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
.channel-key:hover {
|
|
1085
|
+
background: color-mix(in srgb, var(--kf) 70%, var(--timeline-ink));
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
.channel-key.is-selected {
|
|
1089
|
+
width: 12px;
|
|
1090
|
+
height: 12px;
|
|
1091
|
+
border-color: var(--ink-1);
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
/* A HOLD does not interpolate, so it is drawn hollow: the segment leaving it is
|
|
1095
|
+
a step, and a filled mark would promise a ramp. A SPRING carries physics past
|
|
1096
|
+
its own segment, and the ring says the motion does not stop at the next key.
|
|
1097
|
+
Both are shape statements on the one mark, never a second hue. */
|
|
1098
|
+
.channel-key.ease-hold {
|
|
1099
|
+
background: transparent;
|
|
1100
|
+
border-color: var(--kf);
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
.channel-key.ease-hold:hover {
|
|
1104
|
+
background: color-mix(in srgb, var(--kf) 30%, transparent);
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
.channel-key.ease-spring {
|
|
1108
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--spring) 45%, transparent);
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
/* Remove-animation, on the row that has a track. Visible at rest (a control
|
|
1112
|
+
hidden until hover is one a touch user never finds), but quiet: --ink-3, and
|
|
1113
|
+
red only under the pointer. */
|
|
1114
|
+
.channel-clear {
|
|
1115
|
+
flex: none;
|
|
1116
|
+
margin-right: var(--space-2);
|
|
1117
|
+
padding: 0 var(--space-2);
|
|
1118
|
+
border: 0;
|
|
1119
|
+
background: none;
|
|
1120
|
+
color: var(--ink-3);
|
|
1121
|
+
font: inherit;
|
|
1122
|
+
line-height: var(--leading-none);
|
|
1123
|
+
cursor: pointer;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
.channel-clear:hover {
|
|
1127
|
+
color: var(--danger-ink);
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
/* The expanded-but-empty layer: say where the first keyframe comes from. The
|
|
1131
|
+
same quiet lane text carries the selected-key status beside "+ property",
|
|
1132
|
+
in mono there, because it is a readout of numbers. */
|
|
1133
|
+
.channel-empty-hint,
|
|
1134
|
+
.channel-add-status {
|
|
1135
|
+
display: flex;
|
|
1136
|
+
align-items: center;
|
|
1137
|
+
padding-left: var(--space-5);
|
|
1138
|
+
color: var(--ink-3);
|
|
1139
|
+
font-size: var(--type-micro);
|
|
1140
|
+
white-space: nowrap;
|
|
1141
|
+
overflow: hidden;
|
|
1142
|
+
text-overflow: ellipsis;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
/* ---- + property --------------------------------------------------------- */
|
|
1146
|
+
/* The last row of a layer's expansion, and the timeline's own way into
|
|
1147
|
+
keyframing. A BORDERED BUTTON, the boards' recipe: the strong edge, the
|
|
1148
|
+
small radius, 12/18 500 in ink-1. Docked, so flat (no control shadow).
|
|
1149
|
+
|
|
1150
|
+
THE CLIP'S PLACE IN THE ROW: 20 tall at 4 under the row's top, 3 clear of
|
|
1151
|
+
its 1px bottom border, where `.lifespan-bar` sits in the lane beside it.
|
|
1152
|
+
At the row's own 28 (the 28 includes the border) its edge lay on both row
|
|
1153
|
+
lines. 12/18 and the 1px edge make the 20 exactly. The ROW stays
|
|
1154
|
+
`CHANNEL_ROW_H`, the unit `expandedLayerHeight` and `groupHeights` reserve
|
|
1155
|
+
for it. Never a row override of its own: an 18px one once put every row
|
|
1156
|
+
below an expanded layer 2px off its spacer. */
|
|
1157
|
+
.channel-add {
|
|
1158
|
+
display: inline-flex;
|
|
1159
|
+
align-items: center;
|
|
1160
|
+
flex: none;
|
|
1161
|
+
align-self: flex-start;
|
|
1162
|
+
height: 20px;
|
|
1163
|
+
margin-top: var(--space-2);
|
|
1164
|
+
font: inherit;
|
|
1165
|
+
font-size: var(--type-md);
|
|
1166
|
+
line-height: var(--leading-sm);
|
|
1167
|
+
font-weight: 500;
|
|
1168
|
+
color: var(--ink-1);
|
|
1169
|
+
background: transparent;
|
|
1170
|
+
border: 1px solid var(--line-strong);
|
|
1171
|
+
padding: 0 var(--space-4);
|
|
1172
|
+
border-radius: var(--radius-sm);
|
|
1173
|
+
cursor: pointer;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
.channel-add:hover {
|
|
1177
|
+
background: var(--fill-rest);
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
/* OPEN, while its menu is up: the accent-quiet ground and the accent edge, so
|
|
1181
|
+
the control the menu belongs to is the one that says so. */
|
|
1182
|
+
.channel-add[aria-expanded="true"] {
|
|
1183
|
+
background: var(--accent-quiet);
|
|
1184
|
+
border-color: var(--accent);
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
/* The status beside it is a readout, not a label. */
|
|
1188
|
+
.channel-add-status {
|
|
1189
|
+
font-family: var(--font-mono);
|
|
1190
|
+
font-variant-numeric: var(--numeric);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
/* THE HOUSE MENU, restated rather than borrowed. `Popover` already draws the
|
|
1194
|
+
panel ground, the strong edge, the radius and the popover shadow; the rows
|
|
1195
|
+
below take `.menu-item`'s metrics (30 tall, 12/20, the small radius, the
|
|
1196
|
+
accent-quiet hover) on their own class, because styling one element from two
|
|
1197
|
+
sheets at equal specificity is the thing this codebase forbids. 320 wide, so
|
|
1198
|
+
a name and its note sit on one line. */
|
|
1199
|
+
.property-pop {
|
|
1200
|
+
width: 320px;
|
|
1201
|
+
max-height: 340px;
|
|
1202
|
+
overflow-y: auto;
|
|
1203
|
+
padding: var(--space-3);
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
/* Sentence case, the house's group label: the caps-and-tracking version read as
|
|
1207
|
+
a second heading level the menu does not have. */
|
|
1208
|
+
.property-group-label {
|
|
1209
|
+
display: flex;
|
|
1210
|
+
align-items: center;
|
|
1211
|
+
min-height: 24px;
|
|
1212
|
+
padding: 0 var(--space-4);
|
|
1213
|
+
font-size: var(--type-md);
|
|
1214
|
+
line-height: var(--leading-md);
|
|
1215
|
+
font-weight: 500;
|
|
1216
|
+
color: var(--ink-3);
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
.property-item {
|
|
1220
|
+
display: flex;
|
|
1221
|
+
align-items: center;
|
|
1222
|
+
gap: var(--space-4);
|
|
1223
|
+
width: 100%;
|
|
1224
|
+
min-height: 30px;
|
|
1225
|
+
font: inherit;
|
|
1226
|
+
font-size: var(--type-md);
|
|
1227
|
+
line-height: var(--leading-md);
|
|
1228
|
+
text-align: left;
|
|
1229
|
+
color: var(--ink-1);
|
|
1230
|
+
background: transparent;
|
|
1231
|
+
border: 0;
|
|
1232
|
+
padding: 0 var(--space-4);
|
|
1233
|
+
border-radius: var(--radius-sm);
|
|
1234
|
+
cursor: pointer;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
.property-item:hover:not(:disabled) {
|
|
1238
|
+
background: var(--accent-quiet);
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
/* REFUSED, NOT HIDDEN. The reason rides beside the name — "why can I not
|
|
1242
|
+
animate the stroke width" is answered where it is asked. */
|
|
1243
|
+
.property-item:disabled {
|
|
1244
|
+
color: var(--ink-disabled);
|
|
1245
|
+
cursor: default;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
.property-item-note {
|
|
1249
|
+
margin-left: auto;
|
|
1250
|
+
/* The house menu's detail size (`.menu-detail`, `.menu-shortcut`). */
|
|
1251
|
+
font-size: var(--type-sm);
|
|
1252
|
+
color: var(--ink-3);
|
|
1253
|
+
white-space: nowrap;
|
|
1254
|
+
overflow: hidden;
|
|
1255
|
+
text-overflow: ellipsis;
|
|
1256
|
+
max-width: 55%;
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
/* A tracked property's name stays ink-1: the "animated" note beside it is the
|
|
1260
|
+
mark. Tinting the name too said one fact twice, and spent the accent ink in a
|
|
1261
|
+
menu where the accent means "you are on this row". */
|
|
1262
|
+
|
|
1263
|
+
/* ---- audio lane --------------------------------------------------------- */
|
|
1264
|
+
/* A DISTINCT COLOURED BAND under the video rows. Taller than a block row,
|
|
1265
|
+
because the waveform inside it is the thing the author is reading. 32 with
|
|
1266
|
+
its seam, the boards' sound row: AUDIO_ROW_H in model.ts (AUDIO_LANE_H in
|
|
1267
|
+
AudioLane.tsx is the same number), which the fitted dock height counts. */
|
|
1268
|
+
.audio-lane {
|
|
1269
|
+
display: flex;
|
|
1270
|
+
align-items: stretch;
|
|
1271
|
+
position: relative;
|
|
1272
|
+
height: 32px;
|
|
1273
|
+
border-top: 1px solid var(--line);
|
|
1274
|
+
background: color-mix(in srgb, var(--audio-clip) 4%, transparent);
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
/* A file dragged over the lane. The whole lane lights, not the row under the
|
|
1278
|
+
pointer: the clip lands at the PLAYHEAD (see AudioLane), so highlighting a
|
|
1279
|
+
position would promise a drop point the import does not honour. */
|
|
1280
|
+
.audio-lane.is-over {
|
|
1281
|
+
background: color-mix(in srgb, var(--audio-clip) 14%, transparent);
|
|
1282
|
+
outline: 1px dashed var(--audio-clip);
|
|
1283
|
+
outline-offset: -1px;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
/* Inset 16, the shot and camera rows' inset, so the three swatches stand in
|
|
1287
|
+
one column. */
|
|
1288
|
+
.audio-label-cell {
|
|
1289
|
+
display: flex;
|
|
1290
|
+
align-items: center;
|
|
1291
|
+
gap: var(--space-4);
|
|
1292
|
+
flex: none;
|
|
1293
|
+
padding-left: var(--space-7);
|
|
1294
|
+
overflow: hidden;
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
/* The sound row's mark, in both audio rows: a teal 8px square, the lane's own
|
|
1298
|
+
hue, where the shot row wears the accent one. */
|
|
1299
|
+
.audio-swatch {
|
|
1300
|
+
flex: none;
|
|
1301
|
+
width: 8px;
|
|
1302
|
+
height: 8px;
|
|
1303
|
+
border-radius: var(--radius-2xs);
|
|
1304
|
+
background: var(--audio-clip);
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
/* A SHOT'S OWN AUDIO, inside that shot's group. The master lane's height and
|
|
1308
|
+
tint (`SHOT_AUDIO_ROW_H` is `AUDIO_LANE_H`), so the two read as one medium in
|
|
1309
|
+
two places rather than two features. Inside a shot it is the only audio row,
|
|
1310
|
+
32 tall. Its seam is an inset line rather than a border, so the row's box is
|
|
1311
|
+
the whole 32 the group sums count. Its empty state is the same drop box as
|
|
1312
|
+
the master lane's, below. */
|
|
1313
|
+
.scene-audio-row {
|
|
1314
|
+
display: flex;
|
|
1315
|
+
align-items: stretch;
|
|
1316
|
+
position: relative;
|
|
1317
|
+
height: 32px;
|
|
1318
|
+
box-shadow: inset 0 1px 0 var(--line);
|
|
1319
|
+
background: color-mix(in srgb, var(--audio-clip) 4%, transparent);
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
.scene-audio-row.is-over {
|
|
1323
|
+
background: color-mix(in srgb, var(--audio-clip) 14%, transparent);
|
|
1324
|
+
outline: 1px dashed var(--audio-clip);
|
|
1325
|
+
outline-offset: -1px;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
.scene-audio-label {
|
|
1329
|
+
position: sticky;
|
|
1330
|
+
left: 0;
|
|
1331
|
+
z-index: 4; /* the sticky label column's layer (see the shared rule) */
|
|
1332
|
+
display: flex;
|
|
1333
|
+
align-items: center;
|
|
1334
|
+
gap: var(--space-4);
|
|
1335
|
+
flex: none;
|
|
1336
|
+
/* 16 in, the swatch column the shot and camera rows share. */
|
|
1337
|
+
padding-left: var(--space-7);
|
|
1338
|
+
overflow: hidden;
|
|
1339
|
+
background-color: var(--surface-panel);
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
/* "Sound", the row's name: 12/600 ink, a heading row like the shot's. */
|
|
1343
|
+
.scene-audio-name,
|
|
1344
|
+
.audio-lane-name {
|
|
1345
|
+
font-size: var(--type-md);
|
|
1346
|
+
font-weight: var(--weight-semibold);
|
|
1347
|
+
color: var(--ink-1);
|
|
1348
|
+
flex: 1;
|
|
1349
|
+
min-width: 0;
|
|
1350
|
+
white-space: nowrap;
|
|
1351
|
+
overflow: hidden;
|
|
1352
|
+
text-overflow: ellipsis;
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
.audio-add {
|
|
1356
|
+
font: inherit;
|
|
1357
|
+
color: var(--ink-2);
|
|
1358
|
+
background: var(--fill-rest);
|
|
1359
|
+
border: 0;
|
|
1360
|
+
border-radius: var(--radius-xs);
|
|
1361
|
+
padding: 1px var(--space-3);
|
|
1362
|
+
margin-right: var(--space-3);
|
|
1363
|
+
cursor: pointer;
|
|
1364
|
+
flex: none;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
.audio-add:hover:not(:disabled) {
|
|
1368
|
+
color: var(--ink-1);
|
|
1369
|
+
background: var(--fill-hover);
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
.audio-track {
|
|
1373
|
+
position: relative;
|
|
1374
|
+
flex: 1;
|
|
1375
|
+
/* THE SHOT CONTEXT'S LEFT EDGE. This lane is master-timed and the shot view
|
|
1376
|
+
re-zeros it on the shot, so a bed that started earlier is drawn from a
|
|
1377
|
+
NEGATIVE x — which without this lands on top of the label cell. `clip` and
|
|
1378
|
+
not `hidden`: hidden on one axis forces `auto` on the other, and the fade
|
|
1379
|
+
corners and the gain band overflow vertically by design. The margin keeps
|
|
1380
|
+
a bar that starts exactly at the shot's zero from losing its rounded end. */
|
|
1381
|
+
overflow-x: clip;
|
|
1382
|
+
overflow-clip-margin: 8px;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
/* THE DROP BOX, the boards' empty audio row in both the master lane and a
|
|
1386
|
+
shot's own row: a dashed line edge on the sunken ground, so it reads as a
|
|
1387
|
+
place a file goes rather than as one more clip. The copy says the two ways
|
|
1388
|
+
in, and the mono hint after it lists the formats the import accepts, built
|
|
1389
|
+
from the same list the file input's `accept` is (AudioLane.tsx), so the box
|
|
1390
|
+
cannot advertise a format the import refuses. Inert to the pointer: the
|
|
1391
|
+
drop target is the whole row, and the box is only its label. */
|
|
1392
|
+
.audio-empty {
|
|
1393
|
+
position: absolute;
|
|
1394
|
+
left: var(--space-2);
|
|
1395
|
+
right: var(--space-2);
|
|
1396
|
+
max-width: 420px;
|
|
1397
|
+
top: var(--space-2);
|
|
1398
|
+
bottom: var(--space-2);
|
|
1399
|
+
display: flex;
|
|
1400
|
+
align-items: center;
|
|
1401
|
+
gap: var(--space-4);
|
|
1402
|
+
padding: 0 var(--space-5);
|
|
1403
|
+
border: 1px dashed var(--line);
|
|
1404
|
+
border-radius: var(--radius-xs);
|
|
1405
|
+
background: var(--surface-sunken);
|
|
1406
|
+
color: var(--ink-3);
|
|
1407
|
+
font-size: var(--type-sm);
|
|
1408
|
+
pointer-events: none;
|
|
1409
|
+
white-space: nowrap;
|
|
1410
|
+
overflow: hidden;
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
.audio-empty-copy {
|
|
1414
|
+
min-width: 0;
|
|
1415
|
+
overflow: hidden;
|
|
1416
|
+
text-overflow: ellipsis;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
.audio-empty-formats {
|
|
1420
|
+
flex: none;
|
|
1421
|
+
font-family: var(--font-mono);
|
|
1422
|
+
font-size: var(--type-sm);
|
|
1423
|
+
color: var(--ink-3);
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
/* Same quiet-fill/bright-border grammar as a block bar, in the lane's own hue.
|
|
1427
|
+
The 4px inset is AUDIO_CLIP_INSET in AudioLane.tsx, which sizes the wave. */
|
|
1428
|
+
.audio-clip {
|
|
1429
|
+
position: absolute;
|
|
1430
|
+
top: 4px;
|
|
1431
|
+
bottom: 4px;
|
|
1432
|
+
border-radius: var(--radius-sm);
|
|
1433
|
+
background: color-mix(in srgb, var(--audio-clip) 16%, transparent);
|
|
1434
|
+
border: 1px solid color-mix(in srgb, var(--audio-clip) 65%, transparent);
|
|
1435
|
+
color: color-mix(in srgb, var(--audio-clip) 35%, var(--timeline-ink));
|
|
1436
|
+
cursor: grab;
|
|
1437
|
+
overflow: hidden;
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
.audio-clip:hover {
|
|
1441
|
+
background: color-mix(in srgb, var(--audio-clip) 26%, transparent);
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
/* A muted clip is KEPT and dimmed, never hidden: mute is a mix decision the
|
|
1445
|
+
author has to be able to see in order to undo. */
|
|
1446
|
+
.audio-clip.is-muted {
|
|
1447
|
+
opacity: 0.45;
|
|
1448
|
+
border-style: dashed;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
.audio-wave {
|
|
1452
|
+
position: absolute;
|
|
1453
|
+
inset: 0;
|
|
1454
|
+
width: 100%;
|
|
1455
|
+
height: 100%;
|
|
1456
|
+
pointer-events: none;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
.audio-fade-curve {
|
|
1460
|
+
position: absolute;
|
|
1461
|
+
inset: 0;
|
|
1462
|
+
width: 100%;
|
|
1463
|
+
height: 100%;
|
|
1464
|
+
pointer-events: none;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
.audio-fade-curve polyline {
|
|
1468
|
+
stroke: var(--audio-line);
|
|
1469
|
+
stroke-width: 1;
|
|
1470
|
+
vector-effect: non-scaling-stroke;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
.audio-clip-name {
|
|
1474
|
+
position: absolute;
|
|
1475
|
+
left: 8px;
|
|
1476
|
+
top: 1px;
|
|
1477
|
+
pointer-events: none;
|
|
1478
|
+
font-weight: 500;
|
|
1479
|
+
max-width: calc(100% - 46px);
|
|
1480
|
+
overflow: hidden;
|
|
1481
|
+
text-overflow: ellipsis;
|
|
1482
|
+
white-space: nowrap;
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
/* THE SOUND ROW'S CLIP, in the dock's two audio rows (Sound mode draws its
|
|
1486
|
+
own, timeline-sound.css): 24 tall at 4 from the top (the base rule's inset),
|
|
1487
|
+
the teal wash 18% to 6% under a 55% teal edge, radius 6. The waveform is
|
|
1488
|
+
drawn in the same teal (`--audio-waveform` is `--audio-clip` in shotTokens.css;
|
|
1489
|
+
the canvas reads it off itself) at 0.75. */
|
|
1490
|
+
.audio-lane .audio-clip,
|
|
1491
|
+
.scene-audio-row .audio-clip {
|
|
1492
|
+
border-radius: var(--radius-sm);
|
|
1493
|
+
background: linear-gradient(
|
|
1494
|
+
in oklab 180deg,
|
|
1495
|
+
color-mix(in oklab, var(--audio-clip) 18%, transparent),
|
|
1496
|
+
color-mix(in oklab, var(--audio-clip) 6%, transparent)
|
|
1497
|
+
);
|
|
1498
|
+
border: 1px solid color-mix(in oklab, var(--audio-clip) 55%, transparent);
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
.audio-lane .audio-clip:hover,
|
|
1502
|
+
.scene-audio-row .audio-clip:hover {
|
|
1503
|
+
border-color: color-mix(in oklab, var(--audio-clip) 80%, transparent);
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
.audio-lane .audio-wave,
|
|
1507
|
+
.scene-audio-row .audio-wave {
|
|
1508
|
+
opacity: 0.75;
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
/* The clip's name as a chip on the wave: a dark teal tile with light teal
|
|
1512
|
+
type, 11/600, radius 4, 16 tall, so the name reads over any waveform. */
|
|
1513
|
+
.audio-lane .audio-clip-name,
|
|
1514
|
+
.scene-audio-row .audio-clip-name {
|
|
1515
|
+
top: 50%;
|
|
1516
|
+
left: 4px;
|
|
1517
|
+
transform: translateY(-50%);
|
|
1518
|
+
/* Block, so the base rule's ellipsis works; the 16px line is the chip. */
|
|
1519
|
+
display: block;
|
|
1520
|
+
padding: 0 calc(var(--space-5) / 2);
|
|
1521
|
+
border-radius: var(--radius-xs);
|
|
1522
|
+
background: var(--audio-chip-bg);
|
|
1523
|
+
color: var(--audio-chip-ink);
|
|
1524
|
+
font-size: var(--type-sm);
|
|
1525
|
+
font-weight: var(--weight-semibold);
|
|
1526
|
+
line-height: 16px;
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
/* The rubber band. A hairline across the whole clip with a fat invisible grab
|
|
1530
|
+
area, because a 1px drag target is a control nobody can hit. */
|
|
1531
|
+
.audio-gain-band {
|
|
1532
|
+
position: absolute;
|
|
1533
|
+
left: 0;
|
|
1534
|
+
right: 0;
|
|
1535
|
+
height: 9px;
|
|
1536
|
+
margin-bottom: calc(-1 * var(--space-2));
|
|
1537
|
+
cursor: ns-resize;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
.audio-gain-band::after {
|
|
1541
|
+
content: "";
|
|
1542
|
+
position: absolute;
|
|
1543
|
+
left: 0;
|
|
1544
|
+
right: 0;
|
|
1545
|
+
top: 4px;
|
|
1546
|
+
height: 1px;
|
|
1547
|
+
background: var(--audio-line);
|
|
1548
|
+
opacity: 0.8;
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
/* AUTOMATED: the flat line is gone (the envelope svg draws the level now) and
|
|
1552
|
+
the band becomes an invisible full-height grab area for the key under the
|
|
1553
|
+
playhead. Bottom 0 / top 0 rather than a level, because there is no single
|
|
1554
|
+
level left to sit at. */
|
|
1555
|
+
.audio-gain-band.is-automated {
|
|
1556
|
+
top: 0;
|
|
1557
|
+
bottom: 0;
|
|
1558
|
+
height: auto;
|
|
1559
|
+
margin-bottom: 0;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
.audio-gain-band.is-automated::after {
|
|
1563
|
+
content: none;
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
/* The sampled envelope, through the SAME engine function the mixer uses. */
|
|
1567
|
+
.audio-gain-curve {
|
|
1568
|
+
position: absolute;
|
|
1569
|
+
inset: 0;
|
|
1570
|
+
width: 100%;
|
|
1571
|
+
height: 100%;
|
|
1572
|
+
pointer-events: none;
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
.audio-gain-curve polyline {
|
|
1576
|
+
stroke: var(--audio-line);
|
|
1577
|
+
stroke-width: 1;
|
|
1578
|
+
vector-effect: non-scaling-stroke;
|
|
1579
|
+
opacity: 0.9;
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
/* The tail of an owned clip that runs past its shot: kept in the document,
|
|
1583
|
+
absent from the mix. Hatched so it reads as "not this" rather than as a
|
|
1584
|
+
different clip, and inert so every gesture still belongs to the bar. */
|
|
1585
|
+
.audio-overrun {
|
|
1586
|
+
position: absolute;
|
|
1587
|
+
top: 0;
|
|
1588
|
+
bottom: 0;
|
|
1589
|
+
right: 0;
|
|
1590
|
+
pointer-events: none;
|
|
1591
|
+
background: repeating-linear-gradient(
|
|
1592
|
+
-45deg,
|
|
1593
|
+
color-mix(in srgb, var(--surface-app) 70%, transparent) 0 4px,
|
|
1594
|
+
transparent 4px 8px
|
|
1595
|
+
);
|
|
1596
|
+
border-left: 1px solid var(--audio-line);
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
/* Centred on its point by `translate`, which composes with the rotation
|
|
1600
|
+
rather than replacing it: `left` and `bottom` name the key's frame and gain
|
|
1601
|
+
exactly. */
|
|
1602
|
+
.audio-gain-key {
|
|
1603
|
+
position: absolute;
|
|
1604
|
+
width: 5px;
|
|
1605
|
+
height: 5px;
|
|
1606
|
+
translate: -50% 50%;
|
|
1607
|
+
background: var(--audio-line);
|
|
1608
|
+
transform: rotate(45deg);
|
|
1609
|
+
pointer-events: none;
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
.audio-handle {
|
|
1613
|
+
position: absolute;
|
|
1614
|
+
top: 0;
|
|
1615
|
+
bottom: 0;
|
|
1616
|
+
width: 7px;
|
|
1617
|
+
cursor: col-resize;
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
.audio-handle-in {
|
|
1621
|
+
left: -1px;
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
.audio-handle-out {
|
|
1625
|
+
right: -1px;
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
/* The fade corners ride ON the ramp's top end, so the handle is where the
|
|
1629
|
+
author sees the fade finish rather than at the edge of the clip. Centred on
|
|
1630
|
+
that x by `translate`. */
|
|
1631
|
+
.audio-fade {
|
|
1632
|
+
position: absolute;
|
|
1633
|
+
top: -1px;
|
|
1634
|
+
width: 11px;
|
|
1635
|
+
height: 11px;
|
|
1636
|
+
translate: -50% 0;
|
|
1637
|
+
border-radius: var(--radius-full);
|
|
1638
|
+
cursor: ew-resize;
|
|
1639
|
+
background: color-mix(in srgb, var(--audio-line) 70%, transparent);
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
.audio-clip-buttons {
|
|
1643
|
+
position: absolute;
|
|
1644
|
+
right: 2px;
|
|
1645
|
+
top: 1px;
|
|
1646
|
+
display: flex;
|
|
1647
|
+
gap: var(--space-1);
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
.audio-clip-btn {
|
|
1651
|
+
font: inherit;
|
|
1652
|
+
font-size: var(--type-micro);
|
|
1653
|
+
line-height: var(--leading-none);
|
|
1654
|
+
color: var(--ink-2);
|
|
1655
|
+
background: transparent;
|
|
1656
|
+
border: 0;
|
|
1657
|
+
padding: var(--space-1);
|
|
1658
|
+
border-radius: var(--radius-xs);
|
|
1659
|
+
cursor: pointer;
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
.audio-clip-btn:hover {
|
|
1663
|
+
color: var(--ink-1);
|
|
1664
|
+
background: var(--fill-rest);
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
/* DUCKING. The button is present whether or not the clip ducks (a
|
|
1668
|
+
capability's only affordance stays visible at rest); armed is what changes,
|
|
1669
|
+
and it uses the accent the rest of the chrome uses for an active tool. */
|
|
1670
|
+
.audio-duck {
|
|
1671
|
+
position: relative;
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
.audio-clip-btn[aria-pressed="true"] {
|
|
1675
|
+
color: var(--accent-ink);
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
.audio-duck-pop {
|
|
1679
|
+
position: absolute;
|
|
1680
|
+
right: 0;
|
|
1681
|
+
top: 100%;
|
|
1682
|
+
/* Local to the dock (a stacking context): above the ruler (5) and the
|
|
1683
|
+
resize edge (6). */
|
|
1684
|
+
z-index: 7;
|
|
1685
|
+
min-width: 190px;
|
|
1686
|
+
margin-top: var(--space-2);
|
|
1687
|
+
padding: var(--space-4);
|
|
1688
|
+
display: flex;
|
|
1689
|
+
flex-direction: column;
|
|
1690
|
+
gap: var(--space-2);
|
|
1691
|
+
background: var(--surface-raised);
|
|
1692
|
+
border: 1px solid var(--line);
|
|
1693
|
+
border-radius: var(--radius-lg);
|
|
1694
|
+
box-shadow: 0 8px 24px rgb(0 0 0 / 0.35);
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
.audio-duck-title,
|
|
1698
|
+
.audio-duck-empty {
|
|
1699
|
+
margin: 0 0 var(--space-1);
|
|
1700
|
+
font-size: var(--type-sm);
|
|
1701
|
+
color: var(--ink-2);
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
.audio-duck-row {
|
|
1705
|
+
display: flex;
|
|
1706
|
+
align-items: center;
|
|
1707
|
+
gap: var(--space-3);
|
|
1708
|
+
font-size: var(--type-sm);
|
|
1709
|
+
color: var(--ink-1);
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
.audio-duck-strength {
|
|
1713
|
+
margin-top: var(--space-2);
|
|
1714
|
+
padding-top: var(--space-3);
|
|
1715
|
+
border-top: 1px solid var(--line);
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
.audio-duck-strength input[type="range"] {
|
|
1719
|
+
flex: 1;
|
|
1720
|
+
min-width: 0;
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
.audio-duck-db {
|
|
1724
|
+
min-width: 40px;
|
|
1725
|
+
text-align: right;
|
|
1726
|
+
font-variant-numeric: var(--numeric);
|
|
1727
|
+
color: var(--ink-2);
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
/* CLIP EFFECTS: the duck's popover, a stage per block. */
|
|
1731
|
+
.audio-fx-btn {
|
|
1732
|
+
font: var(--weight-medium) var(--type-micro)/1 var(--font-sans);
|
|
1733
|
+
letter-spacing: var(--tracking-caps);
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
.audio-fx-pop {
|
|
1737
|
+
min-width: 260px;
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
.audio-fx-stage {
|
|
1741
|
+
margin-top: var(--space-2);
|
|
1742
|
+
padding-top: var(--space-3);
|
|
1743
|
+
border-top: 1px solid var(--line);
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
.audio-fx-label {
|
|
1747
|
+
min-width: 64px;
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
.audio-fx-slider input[type="range"] {
|
|
1751
|
+
flex: 1;
|
|
1752
|
+
min-width: 0;
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
.audio-fx-room {
|
|
1756
|
+
margin-left: auto;
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
.audio-fx-foot {
|
|
1760
|
+
margin-top: var(--space-2);
|
|
1761
|
+
display: flex;
|
|
1762
|
+
align-items: center;
|
|
1763
|
+
justify-content: space-between;
|
|
1764
|
+
gap: var(--space-3);
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
.audio-error {
|
|
1768
|
+
position: absolute;
|
|
1769
|
+
left: 8px;
|
|
1770
|
+
bottom: 2px;
|
|
1771
|
+
margin: 0;
|
|
1772
|
+
color: var(--danger-ink);
|
|
1773
|
+
pointer-events: none;
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
/* ---- marker lane -------------------------------------------------------- */
|
|
1777
|
+
/* A THIN ORANGE STRIP over the audio. Short on purpose: a marker carries no
|
|
1778
|
+
duration, so there is nothing here that gets more readable with height —
|
|
1779
|
+
every pixel it takes is a pixel off the tracks, which do. */
|
|
1780
|
+
.marker-lane {
|
|
1781
|
+
display: flex;
|
|
1782
|
+
align-items: stretch;
|
|
1783
|
+
position: relative;
|
|
1784
|
+
height: 20px;
|
|
1785
|
+
border-top: 1px solid var(--line);
|
|
1786
|
+
background: color-mix(in srgb, var(--marker) 10%, transparent);
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
.marker-label-cell {
|
|
1790
|
+
display: flex;
|
|
1791
|
+
align-items: center;
|
|
1792
|
+
gap: var(--space-3);
|
|
1793
|
+
flex: none;
|
|
1794
|
+
padding-left: var(--space-4);
|
|
1795
|
+
overflow: hidden;
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
.marker-lane-name {
|
|
1799
|
+
color: var(--ink-2);
|
|
1800
|
+
flex: 1;
|
|
1801
|
+
min-width: 0;
|
|
1802
|
+
white-space: nowrap;
|
|
1803
|
+
overflow: hidden;
|
|
1804
|
+
text-overflow: ellipsis;
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
.marker-add {
|
|
1808
|
+
font: inherit;
|
|
1809
|
+
line-height: var(--leading-none);
|
|
1810
|
+
color: var(--ink-2);
|
|
1811
|
+
background: var(--fill-rest);
|
|
1812
|
+
border: 0;
|
|
1813
|
+
border-radius: var(--radius-xs);
|
|
1814
|
+
padding: 1px var(--space-3);
|
|
1815
|
+
margin-right: var(--space-3);
|
|
1816
|
+
cursor: pointer;
|
|
1817
|
+
flex: none;
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
.marker-add:hover {
|
|
1821
|
+
color: var(--ink-1);
|
|
1822
|
+
background: var(--fill-hover);
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
.marker-track {
|
|
1826
|
+
position: relative;
|
|
1827
|
+
flex: 1;
|
|
1828
|
+
/* The audio track's rule, for the same reason: this lane is master-timed and
|
|
1829
|
+
the shot view re-zeroes it, so a mark before the shot's start is drawn at a
|
|
1830
|
+
NEGATIVE x and would land on the label cell. Clipping here rather than
|
|
1831
|
+
filtering the list is what keeps a pin MOUNTED while it is being dragged
|
|
1832
|
+
across that edge — the drag holds the pointer capture on the node. `clip`
|
|
1833
|
+
and not `hidden`, so the hover label may still overflow vertically. */
|
|
1834
|
+
overflow-x: clip;
|
|
1835
|
+
overflow-clip-margin: 8px;
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
.marker-empty {
|
|
1839
|
+
position: absolute;
|
|
1840
|
+
left: 10px;
|
|
1841
|
+
top: 50%;
|
|
1842
|
+
transform: translateY(-50%);
|
|
1843
|
+
color: var(--ink-3);
|
|
1844
|
+
pointer-events: none;
|
|
1845
|
+
white-space: nowrap;
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
/* CENTRED ON ITS FRAME, which is why the pin is a zero-width anchor with the
|
|
1849
|
+
diamond translated out of it: `left` is the frame's exact x, so a diamond
|
|
1850
|
+
given a width would sit a few px late and drift further as the author zoomed
|
|
1851
|
+
in — the one thing a marker must never do. */
|
|
1852
|
+
.marker-pin {
|
|
1853
|
+
position: absolute;
|
|
1854
|
+
top: 0;
|
|
1855
|
+
bottom: 0;
|
|
1856
|
+
width: 0;
|
|
1857
|
+
display: flex;
|
|
1858
|
+
align-items: center;
|
|
1859
|
+
cursor: grab;
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
.marker-diamond {
|
|
1863
|
+
width: 8px;
|
|
1864
|
+
height: 8px;
|
|
1865
|
+
margin-left: calc(-1 * var(--space-2));
|
|
1866
|
+
background: var(--marker-pin);
|
|
1867
|
+
border: 1px solid color-mix(in srgb, var(--marker) 70%, #000000);
|
|
1868
|
+
transform: rotate(45deg);
|
|
1869
|
+
flex: none;
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
.marker-pin.is-selected .marker-diamond {
|
|
1873
|
+
background: var(--marker);
|
|
1874
|
+
border-color: var(--marker-pin);
|
|
1875
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--marker) 45%, transparent);
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
.marker-pin.is-at-playhead .marker-diamond {
|
|
1879
|
+
border-color: var(--playhead);
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
/* HOVER (and selection) only. Always-on labels turn a strip of beats into a
|
|
1883
|
+
wall of overlapping text at any zoom the author actually works at. */
|
|
1884
|
+
.marker-pin-label {
|
|
1885
|
+
display: none;
|
|
1886
|
+
position: absolute;
|
|
1887
|
+
left: 8px;
|
|
1888
|
+
white-space: nowrap;
|
|
1889
|
+
padding: 0 var(--space-2);
|
|
1890
|
+
border-radius: var(--radius-2xs);
|
|
1891
|
+
color: var(--ink-1);
|
|
1892
|
+
background: var(--surface-panel);
|
|
1893
|
+
border: 1px solid var(--line);
|
|
1894
|
+
pointer-events: none;
|
|
1895
|
+
z-index: 1;
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
.marker-pin:hover .marker-pin-label,
|
|
1899
|
+
.marker-pin.is-selected .marker-pin-label {
|
|
1900
|
+
display: block;
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
/* ---- three kinds of row, told apart ------------------------------------- */
|
|
1904
|
+
/* Every row carried the same hairline, the same label ink and (for layers) the
|
|
1905
|
+
same hueless bar, so a shot header, its camera and its elements read as one
|
|
1906
|
+
undifferentiated stack. The hierarchy is real; it just was not drawn.
|
|
1907
|
+
|
|
1908
|
+
Heights are UNTOUCHED. `SHOT_ROW_H`, `expandedLayerHeight` and
|
|
1909
|
+
`groupHeights` mirror them in JS for virtualisation, and `dockFollow`'s
|
|
1910
|
+
scroll maths asserts them — so this is colour and weight only. */
|
|
1911
|
+
/* NO GROUND STEP ON THE SHOT ROW. Every row sits on the dock's one ground,
|
|
1912
|
+
split by the house divider `--line-soft`; a shot header is told from its
|
|
1913
|
+
element rows by its accent swatch, its 12/600 name and its own clip, never
|
|
1914
|
+
by a tint. */
|
|
1915
|
+
.shot-row {
|
|
1916
|
+
position: relative;
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
/* THE LAST ROW HAS NO LINE. The last shot's last row sits on the marker or
|
|
1920
|
+
Sound lane's own `--line` top edge, and two rules stacked read as a
|
|
1921
|
+
double seam. The border goes TRANSPARENT rather than away, so the row keeps
|
|
1922
|
+
its 1px and `groupHeights` still adds up. */
|
|
1923
|
+
.dock-scene-group:not(:has(~ .dock-scene-group))
|
|
1924
|
+
> :is(.shot-row, .lifespan-row, .block-row, .channel-row):last-child {
|
|
1925
|
+
border-bottom-color: transparent;
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
/* The element rows get the outliner's own kind glyph, so a row means the same
|
|
1929
|
+
thing in both panels. `.layer-kind` is reused deliberately and NOT
|
|
1930
|
+
re-coloured at rest: its neutral-with-an-accent-for-components rule is the
|
|
1931
|
+
taxonomy, and any dock-scoped rest colour would outrank
|
|
1932
|
+
`.layer-kind.is-component` on specificity and flatten it. */
|
|
1933
|
+
.lifespan-label-cell .layer-kind {
|
|
1934
|
+
width: 16px;
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
.lifespan-label-cell .layer-kind svg {
|
|
1938
|
+
width: 14px;
|
|
1939
|
+
height: 14px;
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
/* SELECTED, the glyph alone takes the accent ink, and the name stays ink: the
|
|
1943
|
+
boards mark the selected layer with its kind icon and its bar label, not by
|
|
1944
|
+
tinting a column of names. This flattens nothing, because a component's
|
|
1945
|
+
glyph is already the accent ink. */
|
|
1946
|
+
.lifespan-row.is-selected .layer-kind {
|
|
1947
|
+
color: var(--accent-ink);
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
/* The two row disclosures hold an inline <svg> where a text glyph used to
|
|
1951
|
+
sit, so their line box still reserved the font's descender under it and the
|
|
1952
|
+
icon rode ~1.5px high. `inline-flex` drops the baseline and centres the box
|
|
1953
|
+
instead. (The head's chips are `.btn`, which is already this.) */
|
|
1954
|
+
.shot-disclosure,
|
|
1955
|
+
.lifespan-disclosure {
|
|
1956
|
+
display: inline-flex;
|
|
1957
|
+
align-items: center;
|
|
1958
|
+
justify-content: center;
|
|
1959
|
+
}
|