@hyperframes/studio 0.8.50 → 0.8.51
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/assets/{hyperframes-player-CxxDxHTW.js → hyperframes-player-Cn_PpHqP.js} +3 -3
- package/dist/assets/index-C05SNrvY.js +360 -0
- package/dist/assets/{index-DUAIWxkI.js → index-CPS3Cvj8.js} +1 -1
- package/dist/assets/{index-Dt3qBI-9.js → index-Clrb11lj.js} +1 -1
- package/dist/assets/index-DUR9qzQM.css +1 -0
- package/dist/index.d.ts +18 -13
- package/dist/index.html +2 -2
- package/dist/index.js +1867 -3335
- package/dist/index.js.map +1 -1
- package/dist/styles/tailwind-preset.js.map +1 -1
- package/package.json +11 -12
- package/src/App.tsx +128 -141
- package/src/captions/components/CaptionAnimationPanel.tsx +4 -4
- package/src/captions/components/CaptionOverlayUtils.ts +2 -3
- package/src/captions/components/CaptionPropertyPanel.tsx +3 -3
- package/src/captions/components/CaptionTimeline.tsx +1 -1
- package/src/captions/components/shared.tsx +2 -2
- package/src/components/AskAgentModal.tsx +5 -5
- package/src/components/EditorShell.tsx +4 -7
- package/src/components/ExternalFileConflictBanner.tsx +16 -16
- package/src/components/LintModal.tsx +6 -16
- package/src/components/MediaPreview.tsx +2 -2
- package/src/components/SaveQueuePausedBanner.tsx +2 -2
- package/src/components/StudioHeader.tsx +2 -62
- package/src/components/StudioLeftSidebar.tsx +7 -4
- package/src/components/StudioOverlays.tsx +1 -1
- package/src/components/StudioRightPanel.tsx +4 -4
- package/src/components/StudioToast.tsx +2 -2
- package/src/components/TimelineToolbar.tsx +7 -7
- package/src/components/dock/Dock.test.tsx +192 -0
- package/src/components/dock/Dock.tsx +264 -0
- package/src/components/dock/dock.css +23 -0
- package/src/components/dock/dockLayout.ts +58 -0
- package/src/components/dock/dockLayoutSchema.ts +66 -0
- package/src/components/dock/dockLayoutStore.ts +91 -0
- package/src/components/dock/panelRegistry.ts +67 -0
- package/src/components/editor/AnimationCard.tsx +4 -4
- package/src/components/editor/AnimationCardParts.tsx +6 -6
- package/src/components/editor/BlockParamsPanel.tsx +4 -4
- package/src/components/editor/BorderRadiusEditor.tsx +1 -1
- package/src/components/editor/CanvasContextMenu.tsx +1 -1
- package/src/components/editor/ComputedTweenNotice.tsx +1 -1
- package/src/components/editor/DomEditOverlay.tsx +1 -1
- package/src/components/editor/EaseCurveSection.tsx +1 -1
- package/src/components/editor/EaseParamFields.tsx +5 -5
- package/src/components/editor/FileTree.tsx +4 -4
- package/src/components/editor/FileTreeIcons.tsx +1 -1
- package/src/components/editor/FileTreeNodes.tsx +13 -13
- package/src/components/editor/InlineTextToolbar.tsx +1 -1
- package/src/components/editor/InspectorHeaderActions.tsx +3 -3
- package/src/components/editor/KeyframeDiamond.tsx +1 -1
- package/src/components/editor/KeyframeEaseList.tsx +1 -1
- package/src/components/editor/LayersPanel.tsx +3 -3
- package/src/components/editor/MarqueeOverlay.tsx +1 -1
- package/src/components/editor/OffCanvasIndicators.tsx +1 -1
- package/src/components/editor/PromotableControl.tsx +2 -2
- package/src/components/editor/PropertyPanelEmptyState.tsx +7 -7
- package/src/components/editor/PropertyPanelFlatHeader.tsx +2 -2
- package/src/components/editor/SnapToolbar.tsx +2 -2
- package/src/components/editor/TimelineFxPopover.tsx +1 -1
- package/src/components/editor/TopologyLens.tsx +4 -4
- package/src/components/editor/Transform3DCube.tsx +2 -2
- package/src/components/editor/audioFxRevealTarget.ts +1 -0
- package/src/components/editor/domEditing.test.ts +1 -1
- package/src/components/editor/domEditingAgentPrompt.test.ts +8 -6
- package/src/components/editor/domEditingAgentPrompt.ts +4 -3
- package/src/components/editor/propertyPanelColor.tsx +11 -11
- package/src/components/editor/propertyPanelColorCurveGraph.tsx +1 -1
- package/src/components/editor/propertyPanelColorGradingControls.tsx +7 -9
- package/src/components/editor/propertyPanelColorGradingSection.tsx +6 -6
- package/src/components/editor/propertyPanelColorGradingSlider.tsx +5 -5
- package/src/components/editor/propertyPanelColorWheels.tsx +1 -1
- package/src/components/editor/propertyPanelCommitField.tsx +1 -1
- package/src/components/editor/propertyPanelFill.tsx +4 -4
- package/src/components/editor/propertyPanelFlatColorGradingAccessory.tsx +3 -3
- package/src/components/editor/propertyPanelFlatColorGradingSection.tsx +7 -7
- package/src/components/editor/propertyPanelFlatEffectsSection.tsx +4 -4
- package/src/components/editor/propertyPanelFlatMediaSection.tsx +3 -3
- package/src/components/editor/propertyPanelFlatOverlaysSection.tsx +1 -1
- package/src/components/editor/propertyPanelFlatPrimitives.tsx +10 -10
- package/src/components/editor/propertyPanelFlatSelectRow.tsx +3 -3
- package/src/components/editor/propertyPanelFlatTextSection.test.tsx +8 -9
- package/src/components/editor/propertyPanelFlatTextSection.tsx +5 -5
- package/src/components/editor/propertyPanelFlatToggle.tsx +1 -1
- package/src/components/editor/propertyPanelFont.tsx +5 -5
- package/src/components/editor/propertyPanelFxCarveModule.tsx +1 -1
- package/src/components/editor/propertyPanelFxControls.test.tsx +1 -1
- package/src/components/editor/propertyPanelFxControls.tsx +6 -6
- package/src/components/editor/propertyPanelFxEqModule.tsx +2 -2
- package/src/components/editor/propertyPanelFxPresetStyle.ts +2 -2
- package/src/components/editor/propertyPanelGradingNumberField.tsx +1 -1
- package/src/components/editor/propertyPanelMediaSection.tsx +1 -1
- package/src/components/editor/propertyPanelPrimitives.tsx +7 -7
- package/src/components/editor/propertyPanelSections.tsx +8 -8
- package/src/components/feedback/StudioFeedbackCard.tsx +7 -7
- package/src/components/feedback/feedbackTrigger.ts +0 -1
- package/src/components/nle/AssetPreviewOverlay.tsx +6 -2
- package/src/components/nle/CompositionBreadcrumb.tsx +3 -3
- package/src/components/nle/NLEPreview.tsx +3 -3
- package/src/components/nle/PreviewOverlays.tsx +6 -6
- package/src/components/nle/PreviewPane.tsx +2 -2
- package/src/components/nle/TimelinePane.tsx +3 -3
- package/src/components/nle/TimelineResizeDivider.tsx +2 -2
- package/src/components/panels/SlideshowPanel.tsx +2 -2
- package/src/components/panels/SlideshowSubPanels.tsx +20 -17
- package/src/components/panels/VariablesBindElement.tsx +2 -2
- package/src/components/panels/VariablesDeclarationForm.tsx +2 -2
- package/src/components/panels/VariablesOtherCompositions.tsx +1 -1
- package/src/components/panels/VariablesPanel.tsx +3 -3
- package/src/components/panels/VariablesValueControls.tsx +2 -2
- package/src/components/renders/FfmpegRequiredNotice.tsx +5 -5
- package/src/components/renders/RenderQueue.test.tsx +7 -0
- package/src/components/renders/RenderQueue.tsx +6 -6
- package/src/components/renders/RenderQueueItem.tsx +6 -6
- package/src/components/sidebar/AssetCard.tsx +8 -8
- package/src/components/sidebar/AssetContextMenu.tsx +8 -8
- package/src/components/sidebar/AssetsTab.tsx +2 -2
- package/src/components/sidebar/AudioRow.tsx +6 -6
- package/src/components/sidebar/BlocksTab.tsx +6 -6
- package/src/components/sidebar/CompositionsTab.tsx +5 -5
- package/src/components/sidebar/GlobalAssetsView.tsx +1 -1
- package/src/components/sidebar/LeftSidebar.lintBadge.test.tsx +58 -0
- package/src/components/sidebar/LeftSidebar.tsx +19 -5
- package/src/components/sidebar/PromptPreviewModal.tsx +3 -3
- package/src/components/ui/Button.test.tsx +60 -0
- package/src/components/ui/Button.tsx +4 -4
- package/src/components/ui/SearchInput.tsx +2 -2
- package/src/components/ui/Tooltip.tsx +1 -1
- package/src/hooks/useAppHotkeys.ts +1 -1
- package/src/hooks/useLintModal.test.tsx +65 -0
- package/src/hooks/useLintModal.ts +3 -0
- package/src/hooks/useSdkSession.lifecycle.test.tsx +49 -4
- package/src/hooks/useSdkSession.ts +43 -10
- package/src/hooks/useTimelineAssetDropOps.test.tsx +41 -1
- package/src/hooks/useTimelineAssetDropOps.ts +52 -11
- package/src/main.tsx +9 -3
- package/src/player/components/AudioWaveform.tsx +1 -1
- package/src/player/components/AutomationSelectionMenu.tsx +2 -2
- package/src/player/components/ClipContextMenu.tsx +3 -3
- package/src/player/components/CompositionThumbnail.test.ts +0 -3
- package/src/player/components/CompositionThumbnail.tsx +3 -4
- package/src/player/components/EditModal.tsx +1 -1
- package/src/player/components/ImageThumbnail.tsx +1 -1
- package/src/player/components/KeyframeDiamondContextMenu.tsx +4 -4
- package/src/player/components/LayerDisclosureRow.tsx +2 -2
- package/src/player/components/PlayerControls.tsx +1 -1
- package/src/player/components/ShortcutsPanel.tsx +9 -9
- package/src/player/components/SpeedMenu.tsx +3 -5
- package/src/player/components/Timeline.tsx +2 -1
- package/src/player/components/TimelineCanvas.tsx +17 -10
- package/src/player/components/TimelineDiamondConnectors.tsx +1 -1
- package/src/player/components/TimelineEmptyState.tsx +5 -5
- package/src/player/components/TimelineFxButton.tsx +6 -6
- package/src/player/components/TimelineGroupHeader.tsx +5 -5
- package/src/player/components/TimelineGroupLaneLabels.tsx +1 -1
- package/src/player/components/TimelineGroupRow.tsx +1 -1
- package/src/player/components/TimelineLanes.test.tsx +32 -0
- package/src/player/components/TimelineLanes.tsx +2 -5
- package/src/player/components/TimelineLayerGroupHeader.tsx +2 -2
- package/src/player/components/TimelineRuler.tsx +1 -1
- package/src/player/components/TimelineShortcutHint.tsx +1 -1
- package/src/player/components/TimelineTrackHeader.tsx +1 -1
- package/src/player/components/TimelineTrackPlainHeader.tsx +3 -3
- package/src/player/components/TimelineTypes.ts +2 -1
- package/src/player/components/TrackGapContextMenu.tsx +1 -1
- package/src/player/components/VideoThumbnail.tsx +4 -2
- package/src/player/components/VolumeControl.tsx +2 -2
- package/src/player/components/timelineCallbacks.ts +8 -8
- package/src/player/components/timelineClipDragPreview.ts +51 -27
- package/src/player/components/timelineClipDragTypes.ts +3 -0
- package/src/player/components/timelineDragDrop.test.tsx +61 -1
- package/src/player/components/timelineDragDrop.ts +57 -15
- package/src/player/components/timelineLaneProps.ts +3 -0
- package/src/player/components/timelineSnapping.test.ts +27 -0
- package/src/player/components/timelineSnapping.ts +14 -1
- package/src/player/components/trackHeaderLabelRows.tsx +4 -4
- package/src/player/components/useTimelineClipDrag.resize.test.tsx +136 -2
- package/src/player/components/useTimelineClipDrag.ts +30 -3
- package/src/player/lib/describeClips.ts +3 -54
- package/src/styles/iconTokens.ts +20 -0
- package/src/styles/palette-reset.css +6 -0
- package/src/styles/studio.css +24 -21
- package/src/styles/tailwind-preset.shared.js +3 -0
- package/src/styles/theme.css +213 -0
- package/src/styles/theme.test.ts +196 -0
- package/src/utils/studioUiPreferences.ts +7 -0
- package/src/utils/timelineDropTrackInsert.test.ts +134 -0
- package/src/utils/timelineDropTrackInsert.ts +83 -0
- package/dist/assets/index-n2ml4tUn.js +0 -442
- package/dist/assets/index-pO6p0H27.css +0 -1
- package/src/components/storyboard/AgentChatMessageButton.test.tsx +0 -47
- package/src/components/storyboard/AgentChatMessageButton.tsx +0 -45
- package/src/components/storyboard/FramePoster.test.tsx +0 -61
- package/src/components/storyboard/FramePoster.tsx +0 -86
- package/src/components/storyboard/StoryboardDirection.tsx +0 -45
- package/src/components/storyboard/StoryboardFrameFocus.tsx +0 -412
- package/src/components/storyboard/StoryboardFrameTile.tsx +0 -117
- package/src/components/storyboard/StoryboardGrid.tsx +0 -55
- package/src/components/storyboard/StoryboardLoaded.tsx +0 -307
- package/src/components/storyboard/StoryboardReviewGuide.tsx +0 -300
- package/src/components/storyboard/StoryboardScriptPanel.tsx +0 -26
- package/src/components/storyboard/StoryboardSourceEditor.tsx +0 -231
- package/src/components/storyboard/StoryboardStatusLegend.tsx +0 -21
- package/src/components/storyboard/StoryboardView.tsx +0 -169
- package/src/components/storyboard/StoryboardViewModeGuard.test.tsx +0 -187
- package/src/components/storyboard/frameComments.test.ts +0 -95
- package/src/components/storyboard/frameComments.ts +0 -115
- package/src/components/storyboard/frameStatus.ts +0 -36
- package/src/components/storyboard/storyboardReviewStage.test.ts +0 -40
- package/src/components/storyboard/storyboardReviewStage.ts +0 -45
- package/src/components/storyboard/useFrameComments.ts +0 -98
- package/src/contexts/ViewModeContext.tsx +0 -142
- package/src/hooks/useProjectSignaturePoll.test.tsx +0 -135
- package/src/hooks/useProjectSignaturePoll.ts +0 -68
- package/src/hooks/useStoryboard.ts +0 -99
|
@@ -31,10 +31,14 @@ afterEach(() => {
|
|
|
31
31
|
function renderResizeHarness(
|
|
32
32
|
elements: TimelineElement[],
|
|
33
33
|
selected: string[],
|
|
34
|
-
options: {
|
|
34
|
+
options: {
|
|
35
|
+
wireGroupResize?: boolean;
|
|
36
|
+
snap?: boolean;
|
|
37
|
+
onSeek?: (time: number, seekOptions?: { keepPlaying?: boolean }) => void;
|
|
38
|
+
} = {},
|
|
35
39
|
) {
|
|
36
40
|
usePlayerStore.getState().setElements(elements);
|
|
37
|
-
usePlayerStore.setState({ timelineSnapEnabled:
|
|
41
|
+
usePlayerStore.setState({ timelineSnapEnabled: options.snap === true });
|
|
38
42
|
usePlayerStore.getState().setSelectedElementIds(new Set(selected));
|
|
39
43
|
|
|
40
44
|
const scroll = document.createElement("div");
|
|
@@ -61,6 +65,7 @@ function renderResizeHarness(
|
|
|
61
65
|
onMoveElement,
|
|
62
66
|
onBlockedEditAttempt,
|
|
63
67
|
onResizeElements: options.wireGroupResize === false ? undefined : onResizeElements,
|
|
68
|
+
onSeek: options.onSeek,
|
|
64
69
|
setShowPopover: vi.fn(),
|
|
65
70
|
setRangeSelectionRef: { current: vi.fn() },
|
|
66
71
|
sessionEpoch,
|
|
@@ -92,6 +97,9 @@ function renderResizeHarness(
|
|
|
92
97
|
getResizeProjection() {
|
|
93
98
|
return resizingClip?.groupPreview ?? [];
|
|
94
99
|
},
|
|
100
|
+
getResizingClip() {
|
|
101
|
+
return resizingClip;
|
|
102
|
+
},
|
|
95
103
|
getBlockedClip() {
|
|
96
104
|
return blockedClipRef?.current ?? null;
|
|
97
105
|
},
|
|
@@ -441,3 +449,129 @@ describe("useTimelineClipDrag — multi-select group resize (restored)", () => {
|
|
|
441
449
|
h.unmount();
|
|
442
450
|
});
|
|
443
451
|
});
|
|
452
|
+
|
|
453
|
+
describe("useTimelineClipDrag — trim guide and preview frame", () => {
|
|
454
|
+
/** Playhead at 1.25s that follows preview seeks; clip a's end edge grabbed and dragged once. */
|
|
455
|
+
function trimEndWithSeekingPlayhead(state: { isPlaying?: boolean } = {}) {
|
|
456
|
+
usePlayerStore.setState({ currentTime: 1.25, ...state });
|
|
457
|
+
const onSeek = vi.fn((t: number) => usePlayerStore.setState({ currentTime: t }));
|
|
458
|
+
const a = el("a", { start: 1, duration: 2 });
|
|
459
|
+
const h = renderResizeHarness([a], [], { onSeek });
|
|
460
|
+
h.startResize(a, "end");
|
|
461
|
+
h.movePointer(50);
|
|
462
|
+
return { onSeek, h };
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/** Clip a (0-2s) with neighbour b starting at 5s, snapping on, a's end edge grabbed. */
|
|
466
|
+
function trimAEndBesideB() {
|
|
467
|
+
const a = el("a", { start: 0, duration: 2 });
|
|
468
|
+
const b = el("b", { start: 5, duration: 2 });
|
|
469
|
+
const h = renderResizeHarness([a, b], [], { snap: true });
|
|
470
|
+
h.startResize(a, "end");
|
|
471
|
+
return h;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
it("publishes the snap target while a trimmed edge is on a neighbour edge", () => {
|
|
475
|
+
const h = trimAEndBesideB();
|
|
476
|
+
h.movePointer(296); // a's end lands at 4.96s, 4px from b's start
|
|
477
|
+
expect(h.getResizingClip()).toMatchObject({ snapTime: 5, snapType: "clip-edge" });
|
|
478
|
+
h.unmount();
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
it("keeps the guide when the trimmed edge sits exactly on the neighbour edge", () => {
|
|
482
|
+
const h = trimAEndBesideB();
|
|
483
|
+
h.movePointer(300);
|
|
484
|
+
expect(h.getResizingClip()).toMatchObject({ snapTime: 5, previewDuration: 5 });
|
|
485
|
+
h.unmount();
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
it("publishes no snap target when the trimmed edge is free", () => {
|
|
489
|
+
const h = trimAEndBesideB();
|
|
490
|
+
h.movePointer(150);
|
|
491
|
+
expect(h.getResizingClip()).toMatchObject({ snapTime: null, snapType: null });
|
|
492
|
+
h.unmount();
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
it("never snaps a trimmed edge to the playhead — the edge drives its own seek", () => {
|
|
496
|
+
usePlayerStore.setState({ currentTime: 5 }); // no clip edge or beat nearby, only the playhead
|
|
497
|
+
const a = el("a", { start: 0, duration: 2 });
|
|
498
|
+
const h = renderResizeHarness([a], [], { snap: true });
|
|
499
|
+
h.startResize(a, "end");
|
|
500
|
+
h.movePointer(296); // a's end lands at 4.96s, 4px from the playhead
|
|
501
|
+
expect(h.getResizingClip()).toMatchObject({
|
|
502
|
+
snapTime: null,
|
|
503
|
+
snapType: null,
|
|
504
|
+
previewDuration: 4.96,
|
|
505
|
+
});
|
|
506
|
+
h.unmount();
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
it("shows the frame at the dragged edge, then puts the playhead back on release", async () => {
|
|
510
|
+
usePlayerStore.setState({ currentTime: 1.25 });
|
|
511
|
+
const onSeek = vi.fn();
|
|
512
|
+
const a = el("a", { start: 1, duration: 2 });
|
|
513
|
+
const h = renderResizeHarness([a], [], { onSeek });
|
|
514
|
+
h.startResize(a, "end");
|
|
515
|
+
h.movePointer(50);
|
|
516
|
+
expect(onSeek).toHaveBeenLastCalledWith(3.5 - 1 / 30, { keepPlaying: true });
|
|
517
|
+
await h.dropPointer();
|
|
518
|
+
expect(onSeek).toHaveBeenLastCalledWith(1.25, { keepPlaying: true });
|
|
519
|
+
h.unmount();
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
it("previews the new in-point when the start edge is trimmed", () => {
|
|
523
|
+
const onSeek = vi.fn();
|
|
524
|
+
const a = el("a", { start: 1, duration: 2 });
|
|
525
|
+
const h = renderResizeHarness([a], [], { onSeek });
|
|
526
|
+
h.startResize(a, "start");
|
|
527
|
+
h.movePointer(50);
|
|
528
|
+
expect(onSeek).toHaveBeenLastCalledWith(1.5, { keepPlaying: true });
|
|
529
|
+
h.unmount();
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
it("publishes the guide when the start edge snaps to a neighbour's end", () => {
|
|
533
|
+
// playbackStart leaves source to reveal when the in-point moves left.
|
|
534
|
+
const a = el("a", { start: 3, duration: 2, playbackStart: 5 });
|
|
535
|
+
const b = el("b", { start: 0, duration: 2 });
|
|
536
|
+
const h = renderResizeHarness([a, b], [], { snap: true });
|
|
537
|
+
h.startResize(a, "start");
|
|
538
|
+
h.movePointer(-96); // a's start lands at 2.04s, 4px from b's end
|
|
539
|
+
expect(h.getResizingClip()).toMatchObject({ snapTime: 2, previewStart: 2 });
|
|
540
|
+
h.unmount();
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
it("restores the playhead it had before the first preview seek, not a seeked time", async () => {
|
|
544
|
+
const { onSeek, h } = trimEndWithSeekingPlayhead();
|
|
545
|
+
h.movePointer(80);
|
|
546
|
+
h.movePointer(120);
|
|
547
|
+
await h.dropPointer();
|
|
548
|
+
expect(onSeek).toHaveBeenLastCalledWith(1.25, { keepPlaying: true });
|
|
549
|
+
h.unmount();
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
it("leaves the playhead where playback is when a trim is released while playing", async () => {
|
|
553
|
+
const { onSeek, h } = trimEndWithSeekingPlayhead({ isPlaying: true });
|
|
554
|
+
h.movePointer(120);
|
|
555
|
+
const callsBeforeRelease = onSeek.mock.calls.length;
|
|
556
|
+
await h.dropPointer();
|
|
557
|
+
expect(onSeek).toHaveBeenCalledTimes(callsBeforeRelease);
|
|
558
|
+
expect(onSeek).not.toHaveBeenCalledWith(1.25, expect.anything());
|
|
559
|
+
usePlayerStore.setState({ isPlaying: false });
|
|
560
|
+
h.unmount();
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
it("draws no guide and seeks to the rendered edge when a group member clamps the trim", () => {
|
|
564
|
+
const onSeek = vi.fn();
|
|
565
|
+
const a = el("a", { start: 0, duration: 4 });
|
|
566
|
+
const b = el("b", { start: 0, duration: 1 });
|
|
567
|
+
const c = el("c", { start: 2, duration: 1 });
|
|
568
|
+
const h = renderResizeHarness([a, b, c], ["a", "b"], { snap: true, onSeek });
|
|
569
|
+
h.startResize(a, "end");
|
|
570
|
+
h.movePointer(-198); // a's raw end 2.02s snaps to c at 2s; b clamps the shared delta
|
|
571
|
+
const clip = h.getResizingClip()!;
|
|
572
|
+
expect(clip.previewStart + clip.previewDuration).toBeCloseTo(3.1, 3);
|
|
573
|
+
expect(clip).toMatchObject({ snapTime: null, snapType: null });
|
|
574
|
+
expect(onSeek).toHaveBeenLastCalledWith(expect.closeTo(3.1 - 1 / 30, 3), { keepPlaying: true });
|
|
575
|
+
h.unmount();
|
|
576
|
+
});
|
|
577
|
+
});
|
|
@@ -17,6 +17,7 @@ import type { TimelineEditCallbacks } from "./timelineCallbacks";
|
|
|
17
17
|
import {
|
|
18
18
|
computeDragPreview,
|
|
19
19
|
computeResizePreview,
|
|
20
|
+
trimPreviewTime,
|
|
20
21
|
previewGroupResize,
|
|
21
22
|
type ResizePreviewResult,
|
|
22
23
|
} from "./timelineClipDragPreview";
|
|
@@ -64,6 +65,8 @@ interface UseTimelineClipDragInput {
|
|
|
64
65
|
) => Promise<void> | void;
|
|
65
66
|
onResizeElements?: NonNullable<TimelineEditCallbacks["onResizeElements"]>;
|
|
66
67
|
onBlockedEditAttempt?: (element: TimelineElement, intent: BlockedClipState["intent"]) => void;
|
|
68
|
+
/** Seeks the preview; a trim shows the frame at its dragged edge. */
|
|
69
|
+
onSeek?: (time: number, options?: { keepPlaying?: boolean }) => void;
|
|
67
70
|
setShowPopover: (show: boolean) => void;
|
|
68
71
|
/** Stable ref to the range selection setter — wired after mount to break circular dependency. */
|
|
69
72
|
setRangeSelectionRef: React.RefObject<((sel: null) => void) | null>;
|
|
@@ -92,6 +95,7 @@ export function useTimelineClipDrag({
|
|
|
92
95
|
onResizeElement,
|
|
93
96
|
onResizeElements,
|
|
94
97
|
onBlockedEditAttempt,
|
|
98
|
+
onSeek,
|
|
95
99
|
setShowPopover,
|
|
96
100
|
setRangeSelectionRef,
|
|
97
101
|
readZIndex,
|
|
@@ -149,11 +153,15 @@ export function useTimelineClipDrag({
|
|
|
149
153
|
const dragAudioTracksRef = useRef<ReadonlySet<number> | null>(null);
|
|
150
154
|
|
|
151
155
|
const buildSnapTargets = useCallback(
|
|
152
|
-
(
|
|
156
|
+
(
|
|
157
|
+
excludeElementKey: string | null,
|
|
158
|
+
includeBeats: boolean,
|
|
159
|
+
includePlayhead = true,
|
|
160
|
+
): TimelineSnapTarget[] => {
|
|
153
161
|
// Magnet off ⇒ no targets and no scan; do NOT cache so a mid-gesture toggle
|
|
154
162
|
// back on starts scanning immediately (preserves the existing skip).
|
|
155
163
|
if (!snapContextRef.current.enabled) return [];
|
|
156
|
-
const cacheKey = `${excludeElementKey ?? ""}|${includeBeats ? 1 : 0}`;
|
|
164
|
+
const cacheKey = `${excludeElementKey ?? ""}|${includeBeats ? 1 : 0}|${includePlayhead ? 1 : 0}`;
|
|
157
165
|
const cached = snapTargetsCacheRef.current.get(cacheKey);
|
|
158
166
|
if (cached) return cached;
|
|
159
167
|
const targets = collectTimelineSnapTargets({
|
|
@@ -161,6 +169,7 @@ export function useTimelineClipDrag({
|
|
|
161
169
|
playheadTime: usePlayerStore.getState().currentTime,
|
|
162
170
|
beatTimes: includeBeats ? snapContextRef.current.beatTimes : [],
|
|
163
171
|
excludeElementKey,
|
|
172
|
+
includePlayhead,
|
|
164
173
|
});
|
|
165
174
|
snapTargetsCacheRef.current.set(cacheKey, targets);
|
|
166
175
|
return targets;
|
|
@@ -244,6 +253,10 @@ export function useTimelineClipDrag({
|
|
|
244
253
|
onResizeElementRef.current = onResizeElement;
|
|
245
254
|
const onResizeElementsRef = useRef(onResizeElements);
|
|
246
255
|
onResizeElementsRef.current = onResizeElements;
|
|
256
|
+
const onSeekRef = useRef(onSeek);
|
|
257
|
+
onSeekRef.current = onSeek;
|
|
258
|
+
// Playhead time before the first trim preview seek; restored when the gesture ends.
|
|
259
|
+
const trimSeekOriginRef = useRef<number | null>(null);
|
|
247
260
|
const readZIndexRef = useRef(readZIndex);
|
|
248
261
|
readZIndexRef.current = readZIndex;
|
|
249
262
|
const onStackingPatchesRef = useRef(onStackingPatches);
|
|
@@ -292,10 +305,17 @@ export function useTimelineClipDrag({
|
|
|
292
305
|
pps: ppsRef.current,
|
|
293
306
|
buildSnapTargets,
|
|
294
307
|
});
|
|
295
|
-
|
|
308
|
+
trimSeekOriginRef.current ??= usePlayerStore.getState().currentTime;
|
|
309
|
+
const setResizeState = (v: ResizePreviewResult) => {
|
|
310
|
+
// A trim never changes the play state: keepPlaying lets seek() decide,
|
|
311
|
+
// and it only resumes playback if it was already playing.
|
|
312
|
+
onSeekRef.current?.(trimPreviewTime(resize.edge, v.previewStart, v.previewDuration), {
|
|
313
|
+
keepPlaying: true,
|
|
314
|
+
});
|
|
296
315
|
publishResizingClip(
|
|
297
316
|
resizingClipRef.current ? { ...resizingClipRef.current, started: true, ...v } : null,
|
|
298
317
|
);
|
|
318
|
+
};
|
|
299
319
|
|
|
300
320
|
// Group resize: a capability-clean multi-selection resizes rigidly by one
|
|
301
321
|
// shared, member-clamped delta (legacy main 36413da7f). The grabbed clip
|
|
@@ -338,6 +358,13 @@ export function useTimelineClipDrag({
|
|
|
338
358
|
cancelAnimationFrame(clipDragScrollRaf.current);
|
|
339
359
|
clipDragScrollRaf.current = 0;
|
|
340
360
|
}
|
|
361
|
+
if (trimSeekOriginRef.current != null) {
|
|
362
|
+
// Paused: put the playhead back. Playing: leave it, a backward jump would rewind live playback.
|
|
363
|
+
if (!usePlayerStore.getState().isPlaying) {
|
|
364
|
+
onSeekRef.current?.(trimSeekOriginRef.current, { keepPlaying: true });
|
|
365
|
+
}
|
|
366
|
+
trimSeekOriginRef.current = null;
|
|
367
|
+
}
|
|
341
368
|
// Gesture teardown: drop frozen caches so the next gesture reads fresh state.
|
|
342
369
|
snapTargetsCacheRef.current.clear();
|
|
343
370
|
dragAudioTracksRef.current = null;
|
|
@@ -1,34 +1,9 @@
|
|
|
1
1
|
/** Plain-field clip facts from the player store; feeds the Ask-agent prompt and `studio_look`. */
|
|
2
2
|
import type { TimelineElement } from "../store/playerStore";
|
|
3
3
|
import { elementAutomationLanes } from "../components/automationLaneData";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
export
|
|
7
|
-
/** `volume`, or `fx.<nodeId>.<param>`. */
|
|
8
|
-
target: string;
|
|
9
|
-
/** `t` is seconds from the start of the clip, not the composition. */
|
|
10
|
-
points: { t: number; v: number }[];
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface ClipFact {
|
|
14
|
-
id: string;
|
|
15
|
-
label: string | null;
|
|
16
|
-
kind: string;
|
|
17
|
-
start: number;
|
|
18
|
-
duration: number;
|
|
19
|
-
end: number;
|
|
20
|
-
/** The `data-track-index` as written in the source file. */
|
|
21
|
-
trackIndex: number;
|
|
22
|
-
src: string | null;
|
|
23
|
-
sourceFile: string | null;
|
|
24
|
-
/** `null` when `data-volume` is not authored (the clip plays at 1). */
|
|
25
|
-
volume: number | null;
|
|
26
|
-
lanes: ClipLane[];
|
|
27
|
-
/** `null` at normal speed: not authored, or 1 (the manifest defaults it to 1). */
|
|
28
|
-
playbackRate: number | null;
|
|
29
|
-
audioGroup: string | null;
|
|
30
|
-
role: string | null;
|
|
31
|
-
}
|
|
4
|
+
import { byStart, formatClipLine, type ClipFact } from "@hyperframes/core/clip-facts";
|
|
5
|
+
export { byStart } from "@hyperframes/core/clip-facts";
|
|
6
|
+
export type { ClipFact } from "@hyperframes/core/clip-facts";
|
|
32
7
|
|
|
33
8
|
/** The store holds preview URLs; the agent edits project files, so drop the origin and preview prefix. */
|
|
34
9
|
function projectRelativeSrc(src: string): string {
|
|
@@ -57,36 +32,10 @@ export function describeClip(element: TimelineElement): ClipFact {
|
|
|
57
32
|
};
|
|
58
33
|
}
|
|
59
34
|
|
|
60
|
-
export const byStart = (a: ClipFact, b: ClipFact) =>
|
|
61
|
-
a.start - b.start || a.trackIndex - b.trackIndex;
|
|
62
|
-
|
|
63
35
|
export function describeClips(elements: readonly TimelineElement[]): ClipFact[] {
|
|
64
36
|
return elements.map(describeClip).sort(byStart);
|
|
65
37
|
}
|
|
66
38
|
|
|
67
|
-
const num = (n: number) => String(roundTo3(n));
|
|
68
|
-
|
|
69
|
-
function formatClipLine(clip: ClipFact): string {
|
|
70
|
-
const parts = [
|
|
71
|
-
`${clip.kind} "${clip.id}"`,
|
|
72
|
-
clip.src && `src=${clip.src}`,
|
|
73
|
-
`start=${num(clip.start)}`,
|
|
74
|
-
`duration=${num(clip.duration)}`,
|
|
75
|
-
`end=${num(clip.end)}`,
|
|
76
|
-
`track=${clip.trackIndex}`,
|
|
77
|
-
clip.volume !== null && `volume=${num(clip.volume)}`,
|
|
78
|
-
clip.playbackRate !== null && `rate=${num(clip.playbackRate)}`,
|
|
79
|
-
clip.audioGroup && `group=${clip.audioGroup}`,
|
|
80
|
-
clip.role && `role=${clip.role}`,
|
|
81
|
-
clip.sourceFile && `file=${clip.sourceFile}`,
|
|
82
|
-
...clip.lanes.map(
|
|
83
|
-
(lane) =>
|
|
84
|
-
`${lane.target}-lane=[${lane.points.map((p) => `${num(p.t)}:${num(p.v)}`).join(", ")}]`,
|
|
85
|
-
),
|
|
86
|
-
];
|
|
87
|
-
return `- ${parts.filter(Boolean).join(" ")}`;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
39
|
const PROMPT_CLIP_CAP = 200;
|
|
91
40
|
|
|
92
41
|
/** The Ask-agent prompt's Timeline block; empty when the timeline has no clips. */
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { IconWeight } from "@phosphor-icons/react";
|
|
2
|
+
|
|
3
|
+
const WEIGHTS = ["thin", "light", "regular", "bold", "fill", "duotone"] as const;
|
|
4
|
+
|
|
5
|
+
function asWeight(value: string): IconWeight | null {
|
|
6
|
+
return (WEIGHTS as readonly string[]).includes(value) ? (value as IconWeight) : null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Studio's Phosphor defaults, read off `theme.css` so size and weight have one
|
|
10
|
+
* owner instead of a CSS variable and a JS constant that drift. A missing or
|
|
11
|
+
* unrecognized value falls back to Phosphor's own default. */
|
|
12
|
+
export function readIconTokens(root: Element = document.documentElement): {
|
|
13
|
+
size: string;
|
|
14
|
+
weight: IconWeight;
|
|
15
|
+
} {
|
|
16
|
+
const styles = getComputedStyle(root);
|
|
17
|
+
const size = styles.getPropertyValue("--icon-size").trim();
|
|
18
|
+
const weight = asWeight(styles.getPropertyValue("--icon-weight").trim());
|
|
19
|
+
return { size: size || "1em", weight: weight ?? "regular" };
|
|
20
|
+
}
|
package/src/styles/studio.css
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
|
-
@
|
|
2
|
-
@
|
|
3
|
-
@
|
|
1
|
+
@import "tailwindcss" source(none);
|
|
2
|
+
@import "./palette-reset.css";
|
|
3
|
+
@import "./theme.css";
|
|
4
|
+
|
|
5
|
+
/* Explicit sources: Studio's own TypeScript and its shell HTML. Automatic
|
|
6
|
+
* detection would walk up to the repo root and scan `data/projects`, gitignored user content. */
|
|
7
|
+
@source "../**/*.{ts,tsx}";
|
|
8
|
+
@source "../../index.html";
|
|
9
|
+
|
|
10
|
+
/* Tailwind v4 preflight no longer gives enabled buttons a pointer cursor. */
|
|
11
|
+
@layer base {
|
|
12
|
+
button:not(:disabled),
|
|
13
|
+
[role="button"]:not(:disabled) {
|
|
14
|
+
cursor: pointer;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
4
17
|
|
|
5
18
|
/*
|
|
6
19
|
* Studio is a dark-only UI — pin the user-agent color scheme to dark so that
|
|
@@ -14,9 +27,9 @@
|
|
|
14
27
|
body {
|
|
15
28
|
margin: 0;
|
|
16
29
|
padding: 0;
|
|
17
|
-
background:
|
|
18
|
-
color:
|
|
19
|
-
font-family: -
|
|
30
|
+
background: var(--color-bg-0);
|
|
31
|
+
color: var(--color-text-body);
|
|
32
|
+
font-family: var(--font-sans);
|
|
20
33
|
overflow: hidden;
|
|
21
34
|
}
|
|
22
35
|
|
|
@@ -290,7 +303,7 @@ body {
|
|
|
290
303
|
left: 12px;
|
|
291
304
|
overflow: hidden;
|
|
292
305
|
color: rgba(255, 255, 255, 0.34);
|
|
293
|
-
font-family:
|
|
306
|
+
font-family: var(--font-mono);
|
|
294
307
|
font-size: 9px;
|
|
295
308
|
font-variant-numeric: tabular-nums;
|
|
296
309
|
line-height: 1;
|
|
@@ -328,7 +341,7 @@ body {
|
|
|
328
341
|
}
|
|
329
342
|
|
|
330
343
|
.cm-editor .cm-scroller {
|
|
331
|
-
font-family:
|
|
344
|
+
font-family: var(--font-mono);
|
|
332
345
|
}
|
|
333
346
|
|
|
334
347
|
.cm-editor.cm-focused {
|
|
@@ -385,12 +398,7 @@ body {
|
|
|
385
398
|
}
|
|
386
399
|
|
|
387
400
|
.hf-loader-title {
|
|
388
|
-
font-family:
|
|
389
|
-
Inter,
|
|
390
|
-
-apple-system,
|
|
391
|
-
BlinkMacSystemFont,
|
|
392
|
-
"Segoe UI",
|
|
393
|
-
sans-serif;
|
|
401
|
+
font-family: var(--font-sans);
|
|
394
402
|
font-size: 1rem;
|
|
395
403
|
font-weight: 600;
|
|
396
404
|
letter-spacing: 0;
|
|
@@ -406,12 +414,7 @@ body {
|
|
|
406
414
|
min-height: 2.5rem;
|
|
407
415
|
overflow: hidden;
|
|
408
416
|
color: var(--hf-text-secondary, rgba(244, 244, 245, 0.68));
|
|
409
|
-
font-family:
|
|
410
|
-
Inter,
|
|
411
|
-
-apple-system,
|
|
412
|
-
BlinkMacSystemFont,
|
|
413
|
-
"Segoe UI",
|
|
414
|
-
sans-serif;
|
|
417
|
+
font-family: var(--font-sans);
|
|
415
418
|
font-size: 0.82rem;
|
|
416
419
|
line-height: 1.6;
|
|
417
420
|
}
|
|
@@ -423,7 +426,7 @@ body {
|
|
|
423
426
|
text-overflow: ellipsis;
|
|
424
427
|
white-space: nowrap;
|
|
425
428
|
color: var(--hf-text-tertiary, rgba(244, 244, 245, 0.46));
|
|
426
|
-
font-family:
|
|
429
|
+
font-family: var(--font-mono);
|
|
427
430
|
font-size: 0.75rem;
|
|
428
431
|
letter-spacing: 0;
|
|
429
432
|
font-variant-numeric: tabular-nums;
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/* Studio's design tokens, the one place a design value is decided. Tailwind emits
|
|
2
|
+
* each as a utility and a CSS custom property; `@theme static` keeps unused
|
|
3
|
+
* tokens in `:root` so plain CSS and canvas code can still read them. */
|
|
4
|
+
|
|
5
|
+
@theme static {
|
|
6
|
+
/* ---------------------------------------------------------------- colour */
|
|
7
|
+
|
|
8
|
+
/* Background levels, darkest (app shell) to lightest (inset panel body). */
|
|
9
|
+
--color-bg-0: #0a0a0a;
|
|
10
|
+
--color-bg-1: #0c0c0e;
|
|
11
|
+
--color-bg-2: #121214;
|
|
12
|
+
|
|
13
|
+
/* Raised surfaces. */
|
|
14
|
+
--color-surface: #18181b;
|
|
15
|
+
--color-surface-alt: #141414;
|
|
16
|
+
--color-input: #161618;
|
|
17
|
+
--color-hover: #27272a;
|
|
18
|
+
|
|
19
|
+
/* Separators, weakest to strongest. */
|
|
20
|
+
--color-hairline: #1a1a1c;
|
|
21
|
+
--color-border: #1e1e1e;
|
|
22
|
+
--color-border-input: #27272a;
|
|
23
|
+
--color-border-strong: #262626;
|
|
24
|
+
|
|
25
|
+
/* Text, brightest (0) to dimmest (5). */
|
|
26
|
+
--color-text-0: #fafafa;
|
|
27
|
+
--color-text-1: #e4e4e7;
|
|
28
|
+
--color-text-2: #a1a1aa;
|
|
29
|
+
--color-text-3: #71717a;
|
|
30
|
+
--color-text-4: #52525b;
|
|
31
|
+
--color-text-5: #3f3f46;
|
|
32
|
+
--color-text-body: #e5e5e5;
|
|
33
|
+
--color-text-muted: #737373;
|
|
34
|
+
|
|
35
|
+
/* Roles. Selection and playhead are reserved and differ from the accent so a
|
|
36
|
+
* selected clip, the playhead and an accented control stay tellable apart. */
|
|
37
|
+
--color-accent: #3ce6ac;
|
|
38
|
+
--color-selection: #4ba3d2;
|
|
39
|
+
--color-playhead: #ffffff;
|
|
40
|
+
--color-danger: #ef4444;
|
|
41
|
+
--color-media: #00e3ff;
|
|
42
|
+
--color-container: #f5a623;
|
|
43
|
+
|
|
44
|
+
/* -------------------------------------------------------------- geometry */
|
|
45
|
+
|
|
46
|
+
--radius-sm: 0.25rem;
|
|
47
|
+
--radius-md: 0.375rem;
|
|
48
|
+
--radius-lg: 0.5rem;
|
|
49
|
+
|
|
50
|
+
/* Control heights: 24, 28 and 32 px. `--spacing-*` gives `h-ctl`, `min-h-ctl`
|
|
51
|
+
* and friends. */
|
|
52
|
+
--spacing-ctl-sm: 24px;
|
|
53
|
+
--spacing-ctl: 28px;
|
|
54
|
+
--spacing-ctl-lg: 32px;
|
|
55
|
+
|
|
56
|
+
/* ------------------------------------------------------------------ type */
|
|
57
|
+
|
|
58
|
+
/* Dense UI type sizes named by pixel value. `step` avoids a clash with
|
|
59
|
+
* Tailwind's own `--text-*` scale, whose `xs`, `sm` and `lg` are unchanged. */
|
|
60
|
+
--text-step-7: 7px;
|
|
61
|
+
--text-step-8: 8px;
|
|
62
|
+
--text-step-9: 9px;
|
|
63
|
+
--text-step-10: 10px;
|
|
64
|
+
--text-step-11: 11px;
|
|
65
|
+
--text-step-12: 12px;
|
|
66
|
+
--text-step-13: 13px;
|
|
67
|
+
--text-step-14: 14px;
|
|
68
|
+
--text-step-15: 15px;
|
|
69
|
+
--text-step-16: 16px;
|
|
70
|
+
--text-step-17: 17px;
|
|
71
|
+
--text-step-18: 18px;
|
|
72
|
+
|
|
73
|
+
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
74
|
+
--font-mono: "JetBrains Mono", "IBM Plex Mono", "SF Mono", "Fira Code", monospace;
|
|
75
|
+
|
|
76
|
+
/* ---------------------------------------------------------------- shadow */
|
|
77
|
+
|
|
78
|
+
/* Floating chrome over a near-black canvas: the hairline ring does the edge
|
|
79
|
+
* definition that a light-mode drop shadow cannot. */
|
|
80
|
+
--shadow-menu: 0 8px 24px rgb(0 0 0 / 0.45), 0 0 0 1px rgb(0 0 0 / 0.4);
|
|
81
|
+
--shadow-popover: 0 16px 40px rgb(0 0 0 / 0.55), 0 0 0 1px rgb(0 0 0 / 0.45);
|
|
82
|
+
|
|
83
|
+
/* ---------------------------------------------------------------- motion */
|
|
84
|
+
|
|
85
|
+
--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
|
|
86
|
+
--ease-out-expo: cubic-bezier(0.23, 1, 0.32, 1);
|
|
87
|
+
--ease-in-out-circ: cubic-bezier(0.77, 0, 0.175, 1);
|
|
88
|
+
|
|
89
|
+
/* Durations have no Tailwind namespace, so these are plain variables paired
|
|
90
|
+
* with the `@utility` rules at the bottom of this file. All are at or under
|
|
91
|
+
* 200 ms. */
|
|
92
|
+
--duration-focus: 100ms;
|
|
93
|
+
--duration-press: 120ms;
|
|
94
|
+
--duration-hover: 150ms;
|
|
95
|
+
--duration-expand: 160ms;
|
|
96
|
+
--duration-open: 180ms;
|
|
97
|
+
|
|
98
|
+
/* ------------------------------------------------------------------ icon */
|
|
99
|
+
|
|
100
|
+
/* Read by `readIconTokens` and handed to Phosphor's `IconContext`. */
|
|
101
|
+
--icon-size: 16px;
|
|
102
|
+
--icon-weight: regular;
|
|
103
|
+
|
|
104
|
+
/* ------------------------------------------------- deprecated: old names */
|
|
105
|
+
|
|
106
|
+
/* The `studio.*` and `panel.*` scales, aliases of the tokens above so existing
|
|
107
|
+
* markup keeps working during the sweep. U12 removes them from Studio's
|
|
108
|
+
* source; the published JS preset keeps them one more major. */
|
|
109
|
+
--color-studio-bg: var(--color-bg-0);
|
|
110
|
+
--color-studio-surface: var(--color-surface-alt);
|
|
111
|
+
--color-studio-border: var(--color-border-strong);
|
|
112
|
+
--color-studio-text: var(--color-text-body);
|
|
113
|
+
--color-studio-muted: var(--color-text-muted);
|
|
114
|
+
--color-studio-accent: var(--color-accent);
|
|
115
|
+
--color-panel-bg: var(--color-bg-1);
|
|
116
|
+
--color-panel-bg-inset: var(--color-bg-2);
|
|
117
|
+
--color-panel-input: var(--color-input);
|
|
118
|
+
--color-panel-surface: var(--color-surface);
|
|
119
|
+
--color-panel-hover: var(--color-hover);
|
|
120
|
+
--color-panel-border: var(--color-border);
|
|
121
|
+
--color-panel-border-input: var(--color-border-input);
|
|
122
|
+
--color-panel-hairline: var(--color-hairline);
|
|
123
|
+
--color-panel-text-0: var(--color-text-0);
|
|
124
|
+
--color-panel-text-1: var(--color-text-1);
|
|
125
|
+
--color-panel-text-2: var(--color-text-2);
|
|
126
|
+
--color-panel-text-3: var(--color-text-3);
|
|
127
|
+
--color-panel-text-4: var(--color-text-4);
|
|
128
|
+
--color-panel-text-5: var(--color-text-5);
|
|
129
|
+
--color-panel-accent: var(--color-accent);
|
|
130
|
+
--color-panel-danger: var(--color-danger);
|
|
131
|
+
--color-panel-media: var(--color-media);
|
|
132
|
+
--color-panel-container: var(--color-container);
|
|
133
|
+
|
|
134
|
+
/* ------------------------------------------- deprecated: Tailwind palette */
|
|
135
|
+
|
|
136
|
+
/* Verbatim Tailwind 4.3.3 defaults for stock colours markup still names, so
|
|
137
|
+
* `--color-*: initial` does not blank them. U12 deletes this block;
|
|
138
|
+
* `theme.test.ts` fails if a value drifts from upstream. */
|
|
139
|
+
--color-amber-50: oklch(98.7% 0.022 95.277);
|
|
140
|
+
--color-amber-100: oklch(96.2% 0.059 95.617);
|
|
141
|
+
--color-amber-200: oklch(92.4% 0.12 95.746);
|
|
142
|
+
--color-amber-300: oklch(87.9% 0.169 91.605);
|
|
143
|
+
--color-amber-400: oklch(82.8% 0.189 84.429);
|
|
144
|
+
--color-amber-500: oklch(76.9% 0.188 70.08);
|
|
145
|
+
--color-amber-900: oklch(41.4% 0.112 45.904);
|
|
146
|
+
--color-amber-950: oklch(27.9% 0.077 45.635);
|
|
147
|
+
--color-black: #000;
|
|
148
|
+
--color-blue-400: oklch(70.7% 0.165 254.624);
|
|
149
|
+
--color-blue-500: oklch(62.3% 0.214 259.815);
|
|
150
|
+
--color-cyan-400: oklch(78.9% 0.154 211.53);
|
|
151
|
+
--color-cyan-500: oklch(71.5% 0.143 215.221);
|
|
152
|
+
--color-emerald-300: oklch(84.5% 0.143 164.978);
|
|
153
|
+
--color-emerald-400: oklch(76.5% 0.177 163.223);
|
|
154
|
+
--color-emerald-500: oklch(69.6% 0.17 162.48);
|
|
155
|
+
--color-emerald-600: oklch(59.6% 0.145 163.225);
|
|
156
|
+
--color-emerald-700: oklch(50.8% 0.118 165.612);
|
|
157
|
+
--color-green-400: oklch(79.2% 0.209 151.711);
|
|
158
|
+
--color-green-500: oklch(72.3% 0.219 149.579);
|
|
159
|
+
--color-green-600: oklch(62.7% 0.194 149.214);
|
|
160
|
+
--color-neutral-50: oklch(98.5% 0 none);
|
|
161
|
+
--color-neutral-100: oklch(97% 0 none);
|
|
162
|
+
--color-neutral-200: oklch(92.2% 0 none);
|
|
163
|
+
--color-neutral-300: oklch(87% 0 none);
|
|
164
|
+
--color-neutral-400: oklch(70.8% 0 none);
|
|
165
|
+
--color-neutral-500: oklch(55.6% 0 none);
|
|
166
|
+
--color-neutral-600: oklch(43.9% 0 none);
|
|
167
|
+
--color-neutral-700: oklch(37.1% 0 none);
|
|
168
|
+
--color-neutral-800: oklch(26.9% 0 none);
|
|
169
|
+
--color-neutral-900: oklch(20.5% 0 none);
|
|
170
|
+
--color-neutral-950: oklch(14.5% 0 none);
|
|
171
|
+
--color-orange-300: oklch(83.7% 0.128 66.29);
|
|
172
|
+
--color-orange-400: oklch(75% 0.183 55.934);
|
|
173
|
+
--color-pink-400: oklch(71.8% 0.202 349.761);
|
|
174
|
+
--color-pink-500: oklch(65.6% 0.241 354.308);
|
|
175
|
+
--color-purple-200: oklch(90.2% 0.063 306.703);
|
|
176
|
+
--color-purple-300: oklch(82.7% 0.119 306.383);
|
|
177
|
+
--color-purple-400: oklch(71.4% 0.203 305.504);
|
|
178
|
+
--color-purple-500: oklch(62.7% 0.265 303.9);
|
|
179
|
+
--color-purple-900: oklch(38.1% 0.176 304.987);
|
|
180
|
+
--color-red-100: oklch(93.6% 0.032 17.717);
|
|
181
|
+
--color-red-200: oklch(88.5% 0.062 18.334);
|
|
182
|
+
--color-red-300: oklch(80.8% 0.114 19.571);
|
|
183
|
+
--color-red-400: oklch(70.4% 0.191 22.216);
|
|
184
|
+
--color-red-500: oklch(63.7% 0.237 25.331);
|
|
185
|
+
--color-red-600: oklch(57.7% 0.245 27.325);
|
|
186
|
+
--color-red-700: oklch(50.5% 0.213 27.518);
|
|
187
|
+
--color-red-800: oklch(44.4% 0.177 26.899);
|
|
188
|
+
--color-red-900: oklch(39.6% 0.141 25.723);
|
|
189
|
+
--color-red-950: oklch(25.8% 0.092 26.042);
|
|
190
|
+
--color-rose-400: oklch(71.2% 0.194 13.428);
|
|
191
|
+
--color-rose-500: oklch(64.5% 0.246 16.439);
|
|
192
|
+
--color-sky-200: oklch(90.1% 0.058 230.902);
|
|
193
|
+
--color-sky-300: oklch(82.8% 0.111 230.318);
|
|
194
|
+
--color-sky-400: oklch(74.6% 0.16 232.661);
|
|
195
|
+
--color-sky-500: oklch(68.5% 0.169 237.323);
|
|
196
|
+
--color-sky-700: oklch(50% 0.134 242.749);
|
|
197
|
+
--color-sky-900: oklch(39.1% 0.09 240.876);
|
|
198
|
+
--color-sky-950: oklch(29.3% 0.066 243.157);
|
|
199
|
+
--color-violet-400: oklch(70.2% 0.183 293.541);
|
|
200
|
+
--color-violet-500: oklch(60.6% 0.25 292.717);
|
|
201
|
+
--color-white: #fff;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/* Motion durations as utilities, since Tailwind has no `--duration-*`
|
|
205
|
+
* namespace. Each one zeroes itself under `prefers-reduced-motion`, so a
|
|
206
|
+
* caller cannot forget the reduced-motion case. */
|
|
207
|
+
|
|
208
|
+
@utility duration-* {
|
|
209
|
+
transition-duration: --value(--duration-*);
|
|
210
|
+
@media (prefers-reduced-motion: reduce) {
|
|
211
|
+
transition-duration: 0ms;
|
|
212
|
+
}
|
|
213
|
+
}
|