@hyperframes/studio 0.8.55 → 0.8.56
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-BjAGc2ID.js → hyperframes-player-k8LzMtXX.js} +1 -1
- package/dist/assets/{index-TcBZyzNA.js → index-BlkJ_Axe.js} +191 -191
- package/dist/assets/{index-DaNpNFJC.js → index-CxR7F2YQ.js} +1 -1
- package/dist/assets/{index-BEIRytvV.js → index-DFlILHJe.js} +1 -1
- package/dist/assets/{index-DrHQAuGO.css → index-ZmBtVrEm.css} +1 -1
- package/dist/index.d.ts +16 -8
- package/dist/index.html +2 -2
- package/dist/index.js +3111 -2998
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +7 -7
- package/src/App.tsx +14 -1
- package/src/components/EditorShell.readOnlyHostMount.test.tsx +116 -0
- package/src/components/EditorShell.selectionSync.test.tsx +57 -67
- package/src/components/EditorShell.tsx +59 -50
- package/src/components/EditorShellTestMocks.ts +46 -0
- package/src/components/editor/DomEditOverlay.tsx +19 -40
- package/src/components/editor/DomEditOverlayHover.test.tsx +1 -15
- package/src/components/editor/DomEditSelectionChrome.tsx +11 -9
- package/src/components/editor/PropertyPanel.deselect.test.tsx +71 -0
- package/src/components/editor/PropertyPanel.tsx +4 -3
- package/src/components/editor/SnapToolbar.test.tsx +1 -0
- package/src/components/editor/domEditOverlayTestMocks.ts +16 -0
- package/src/components/editor/previewReadOnly.overlay.test.tsx +260 -0
- package/src/components/editor/previewReadOnlyContext.tsx +59 -0
- package/src/components/editor/propertyPanelFlatLayoutSection.tsx +9 -0
- package/src/components/editor/propertyPanelFlatPrimitives.tsx +4 -1
- package/src/components/editor/propertyPanelInputCoverage.test.tsx +3 -13
- package/src/components/editor/useDomEditNudge.ts +9 -0
- package/src/components/editor/useDomEditOverlayGestures.ts +4 -0
- package/src/components/editor/useOffCanvasIndicators.ts +50 -0
- package/src/components/nle/PreviewOverlays.topologyLens.test.tsx +4 -35
- package/src/components/nle/PreviewOverlays.tsx +15 -8
- package/src/components/nle/PreviewOverlaysTestMocks.ts +40 -0
- package/src/components/nle/TimelinePane.tsx +5 -1
- package/src/components/nle/previewReadOnly.test.tsx +128 -0
- package/src/components/nle/useCanvasZOrderTimelineMirror.test.tsx +1 -1
- package/src/components/nle/useCanvasZOrderTimelineMirror.ts +3 -6
- package/src/components/nle/usePreviewBlockDrop.ts +3 -1
- package/src/hooks/appHotkeysDispatch.ts +16 -1
- package/src/hooks/newTweenTargetHooks.test.tsx +1 -0
- package/src/hooks/timelineAudioGroupCreate.ts +3 -4
- package/src/hooks/timelineTrackVisibility.ts +7 -8
- package/src/hooks/useAppHotkeys.test.ts +71 -3
- package/src/hooks/useAppHotkeys.textEditing.test.tsx +1 -0
- package/src/hooks/useAppHotkeys.ts +4 -0
- package/src/hooks/useDomEditCommits.test.tsx +1 -0
- package/src/hooks/useDomEditCommits.ts +3 -0
- package/src/hooks/useDomEditSession.test.tsx +1 -0
- package/src/hooks/useDomEditSession.ts +7 -4
- package/src/hooks/useDomEditWiring.ts +40 -40
- package/src/hooks/useDomGeometryCommits.test.tsx +85 -2
- package/src/hooks/useDomGeometryCommits.ts +26 -20
- package/src/hooks/useGestureCommit.test.tsx +29 -0
- package/src/hooks/useGestureCommit.ts +23 -0
- package/src/hooks/useInlineTextEdit.test.tsx +23 -2
- package/src/hooks/useInlineTextEdit.ts +11 -2
- package/src/hooks/useTimelineAssetDropOps.test.tsx +3 -4
- package/src/hooks/useTimelineAssetDropOps.ts +1 -6
- package/src/hooks/useTimelineEditing.test.tsx +10 -7
- package/src/hooks/useTimelineEditing.ts +2 -7
- package/src/player/components/Timeline.tsx +22 -23
- package/src/player/components/TimelineClip.test.tsx +52 -0
- package/src/player/components/TimelineClip.tsx +13 -2
- package/src/player/components/TimelineTrackHeader.test.tsx +26 -5
- package/src/player/components/TimelineTrackHeader.tsx +1 -1
- package/src/player/components/timelineMotionStyles.test.ts +3 -3
- package/src/player/components/timelineTheme.test.ts +4 -4
- package/src/player/components/timelineTheme.ts +34 -30
- package/src/player/components/timelineTrackDisplay.ts +1 -4
- package/src/player/components/useTimelineTrackDerivations.ts +2 -2
- package/src/player/hooks/timelineSyncHydration.test.ts +0 -18
- package/src/player/hooks/timelineSyncHydration.ts +0 -23
- package/src/player/hooks/useTimelineSyncCallbacks.ts +0 -3
- package/src/player/store/playerStore.ts +0 -4
- package/src/player/store/timelineElement.ts +2 -4
- package/src/player/store/timelineResetState.ts +0 -1
- package/src/styles/hex-baseline.json +3 -4
- package/src/styles/studio.css +7 -7
- package/src/styles/theme.css +29 -0
- package/src/player/hooks/useTimelineRowElements.test.ts +0 -28
- package/src/player/hooks/useTimelineRowElements.ts +0 -17
|
@@ -22,6 +22,8 @@ export interface TimelineTheme {
|
|
|
22
22
|
tickMinor: string;
|
|
23
23
|
clipBackground: string;
|
|
24
24
|
clipBackgroundActive: string;
|
|
25
|
+
clipBackgroundHover: string;
|
|
26
|
+
clipBackgroundDragging: string;
|
|
25
27
|
clipBorder: string;
|
|
26
28
|
clipBorderHover: string;
|
|
27
29
|
clipBorderActive: string;
|
|
@@ -36,43 +38,45 @@ export interface TimelineTheme {
|
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
const TRACK_STYLE: TimelineTrackStyle = {
|
|
39
|
-
clip: "
|
|
40
|
-
clipActive: "
|
|
41
|
-
accent: "
|
|
42
|
-
label: "
|
|
41
|
+
clip: "var(--timeline-track-clip-fill)",
|
|
42
|
+
clipActive: "var(--timeline-track-clip-active)",
|
|
43
|
+
accent: "var(--color-accent)",
|
|
44
|
+
label: "var(--timeline-track-label)",
|
|
43
45
|
};
|
|
44
46
|
|
|
47
|
+
// Every field reads a CSS custom property (declared in styles/theme.css)
|
|
48
|
+
// rather than a literal, so a host themes the timeline the same way it
|
|
49
|
+
// themes the rest of Studio: by overriding the token, not this object.
|
|
45
50
|
export const defaultTimelineTheme: TimelineTheme = {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
clipBorderActive: "rgba(255,255,255,0.24)",
|
|
51
|
+
shellBackground: "var(--timeline-shell-bg)",
|
|
52
|
+
shellBorder: "var(--timeline-shell-border)",
|
|
53
|
+
rulerBorder: "var(--timeline-ruler-border)",
|
|
54
|
+
rowBackground: "var(--timeline-row-bg)",
|
|
55
|
+
rowBorder: "var(--timeline-row-border)",
|
|
56
|
+
gutterBackground: "var(--timeline-gutter-bg)",
|
|
57
|
+
gutterBorder: "var(--timeline-gutter-border)",
|
|
58
|
+
textPrimary: "var(--timeline-text-primary)",
|
|
59
|
+
textSecondary: "var(--timeline-text-secondary)",
|
|
60
|
+
tickText: "var(--timeline-tick-text)",
|
|
61
|
+
tickMajor: "var(--timeline-tick-major)",
|
|
62
|
+
tickMinor: "var(--timeline-tick-minor)",
|
|
63
|
+
clipBackground: "var(--timeline-clip-bg)",
|
|
64
|
+
clipBackgroundActive: "var(--timeline-clip-bg-active)",
|
|
65
|
+
clipBackgroundHover: "var(--timeline-clip-bg-hover)",
|
|
66
|
+
clipBackgroundDragging: "var(--timeline-clip-bg-dragging)",
|
|
67
|
+
clipBorder: "var(--timeline-clip-border)",
|
|
68
|
+
clipBorderHover: "var(--timeline-clip-border-hover)",
|
|
69
|
+
clipBorderActive: "var(--timeline-clip-border-active)",
|
|
70
|
+
// Shadows stay literal: a light host still wants a dark contact shadow, so
|
|
71
|
+
// these are not part of the surface/hairline/text/clip-colour theming ask.
|
|
68
72
|
clipShadow: "none",
|
|
69
73
|
clipShadowHover: "0 2px 8px rgba(0,0,0,0.2)",
|
|
70
74
|
clipShadowActive: "0 2px 8px rgba(0,0,0,0.2), 0 0 0 1px rgba(255,255,255,0.04)",
|
|
71
75
|
clipShadowDragging: "0 8px 24px rgba(0,0,0,0.4), 0 0 0 1px rgba(255,255,255,0.06)",
|
|
72
|
-
handleColor: "
|
|
73
|
-
panelResizeSeam: "
|
|
74
|
-
panelResizeActive: "
|
|
75
|
-
clipRadius: "
|
|
76
|
+
handleColor: "var(--timeline-handle)",
|
|
77
|
+
panelResizeSeam: "var(--timeline-resize-seam)",
|
|
78
|
+
panelResizeActive: "var(--timeline-resize-active)",
|
|
79
|
+
clipRadius: "var(--radius-lg)",
|
|
76
80
|
};
|
|
77
81
|
|
|
78
82
|
export function getTimelineTrackStyle(_tag: string): TimelineTrackStyle {
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The one owner of "what track number does the user see".
|
|
3
3
|
*
|
|
4
|
-
* `TimelineElement.track` is a z-order SORT key, not a row number
|
|
5
|
-
* sub-composition child gets a synthesized fractional key (`host.track + n /
|
|
6
|
-
* (siblings + 2)`, see `useTimelineRowElements`), so putting it in a string
|
|
7
|
-
* announces "Hide track 0.16666666666666666". Every user-visible track number,
|
|
4
|
+
* `TimelineElement.track` is a z-order SORT key, not a row number. Every
|
|
8
5
|
* whether it is rendered by a component or baked into an undo-history label,
|
|
9
6
|
* routes through here; the raw key stays in callbacks and lookups only.
|
|
10
7
|
*/
|
|
@@ -98,8 +98,8 @@ function buildGroupInfo(
|
|
|
98
98
|
id: groupId,
|
|
99
99
|
label: membership.labelByGroup.get(groupId) ?? groupId,
|
|
100
100
|
// Exactly x.5, which sub-composition child rows are now kept strictly below
|
|
101
|
-
//
|
|
102
|
-
//
|
|
101
|
+
// the timeline row source — they used to be able to land here and collide,
|
|
102
|
+
// duplicating the group header.
|
|
103
103
|
anchorKey: (memberTracks[0] ?? fallbackTrackNum) - 0.5,
|
|
104
104
|
memberTracks,
|
|
105
105
|
memberElements: memberTracks.flatMap((track) => rawByTrack.get(track) ?? []),
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import { describe, expect, it } from "vitest";
|
|
4
4
|
import {
|
|
5
5
|
collectSubCompositionDomChildren,
|
|
6
|
-
collectTopLevelElementIds,
|
|
7
6
|
collectSubCompositionHostState,
|
|
8
7
|
} from "./timelineSyncHydration";
|
|
9
8
|
import type { ClipManifestClip } from "../lib/playbackTypes";
|
|
@@ -95,20 +94,3 @@ describe("collectSubCompositionHostState", () => {
|
|
|
95
94
|
expect(state.size).toBe(0);
|
|
96
95
|
});
|
|
97
96
|
});
|
|
98
|
-
|
|
99
|
-
describe("collectTopLevelElementIds", () => {
|
|
100
|
-
const docOf = (html: string) => new DOMParser().parseFromString(html, "text/html");
|
|
101
|
-
|
|
102
|
-
it("names the sub-composition host and a timed element, not what is nested inside them", () => {
|
|
103
|
-
const doc = docOf(`<div data-composition-id="root">
|
|
104
|
-
<div id="wrap"><div id="a" data-start="0" data-duration="2"><div id="inner" data-start="0"></div></div></div>
|
|
105
|
-
<div id="scene" data-composition-id="scene" data-composition-src="scene.html"><div id="box" data-start="0"></div></div>
|
|
106
|
-
</div>`);
|
|
107
|
-
expect([...collectTopLevelElementIds(doc)!].sort()).toEqual(["a", "scene"]);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it("is null when the document has no composition root", () => {
|
|
111
|
-
expect(collectTopLevelElementIds(docOf("<p>hi</p>"))).toBeNull();
|
|
112
|
-
expect(collectTopLevelElementIds(null)).toBeNull();
|
|
113
|
-
});
|
|
114
|
-
});
|
|
@@ -14,7 +14,6 @@ import { usePlayerStore } from "../store/playerStore";
|
|
|
14
14
|
import type { TimelineElement, DomClipChild, SubCompositionHostState } from "../store/playerStore";
|
|
15
15
|
import { resolveCssStackingContextId } from "@hyperframes/core/runtime/stacking-context";
|
|
16
16
|
import type { ClipTree } from "@hyperframes/core/runtime/clipTree";
|
|
17
|
-
import { topLevelElements, type StructureNode } from "@hyperframes/parsers/top-level-elements";
|
|
18
17
|
import { HF_AUDIO_GROUP_ATTR } from "@hyperframes/core/audio-groups";
|
|
19
18
|
import { groupInfoFor } from "../lib/timelineGroupInfo";
|
|
20
19
|
import type { PlaybackAdapter, ClipManifestClip, IframeWindow } from "../lib/playbackTypes";
|
|
@@ -137,28 +136,6 @@ export function collectSubCompositionDomChildren(
|
|
|
137
136
|
return out;
|
|
138
137
|
}
|
|
139
138
|
|
|
140
|
-
interface DomStructureNode extends StructureNode<DomStructureNode> {
|
|
141
|
-
id: string;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function toStructureNode(el: Element): DomStructureNode {
|
|
145
|
-
const attrs: Record<string, string> = {};
|
|
146
|
-
for (const attr of Array.from(el.attributes)) attrs[attr.name] = attr.value;
|
|
147
|
-
return {
|
|
148
|
-
tag: el.tagName,
|
|
149
|
-
attrs,
|
|
150
|
-
id: el.id,
|
|
151
|
-
children: Array.from(el.children).map(toStructureNode),
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/** DOM ids of the timeline's rows, by the definition the structure lint shares; null when there is no readable root. */
|
|
156
|
-
export function collectTopLevelElementIds(doc: Document | null): Set<string> | null {
|
|
157
|
-
const root = doc?.querySelector("[data-composition-id]");
|
|
158
|
-
if (!root) return null;
|
|
159
|
-
return new Set(topLevelElements(toStructureNode(root)).flatMap((n) => (n.id ? [n.id] : [])));
|
|
160
|
-
}
|
|
161
|
-
|
|
162
139
|
/** The host-element `data-*` state one element carries, or null when it has none. */
|
|
163
140
|
function readSubCompositionHostState(el: Element): SubCompositionHostState | null {
|
|
164
141
|
const state: SubCompositionHostState = {};
|
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
buildTimelineElementsFromClips,
|
|
20
20
|
syncManifestTimeline,
|
|
21
21
|
clipTreeParentMap,
|
|
22
|
-
collectTopLevelElementIds,
|
|
23
22
|
collectSubCompositionDomChildren,
|
|
24
23
|
collectSubCompositionHostState,
|
|
25
24
|
hydrateTimelineFromPreview,
|
|
@@ -130,13 +129,11 @@ export function useTimelineSyncCallbacks({
|
|
|
130
129
|
const parentMap = clipTreeParentMap(iframeRef.current?.contentWindow ?? null);
|
|
131
130
|
const domClipChildren = collectSubCompositionDomChildren(iframeDoc, data.clips, parentMap);
|
|
132
131
|
usePlayerStore.getState().setClipParentMap(parentMap);
|
|
133
|
-
usePlayerStore.getState().setTopLevelIds(collectTopLevelElementIds(iframeDoc));
|
|
134
132
|
usePlayerStore.getState().setDomClipChildren(domClipChildren);
|
|
135
133
|
usePlayerStore
|
|
136
134
|
.getState()
|
|
137
135
|
.setSubCompositionHostState(collectSubCompositionHostState(iframeDoc, data.clips));
|
|
138
136
|
} catch {
|
|
139
|
-
usePlayerStore.getState().setTopLevelIds(null);
|
|
140
137
|
// cross-origin or __clipTree not available — maps stay empty
|
|
141
138
|
}
|
|
142
139
|
|
|
@@ -202,8 +202,6 @@ interface PlayerState extends PlayerStoreSlices {
|
|
|
202
202
|
clipManifest: ClipManifestClip[] | null;
|
|
203
203
|
setClipManifest: (clips: ClipManifestClip[] | null) => void;
|
|
204
204
|
clipParentMap: Map<string, string>;
|
|
205
|
-
topLevelIds: ReadonlySet<string> | null;
|
|
206
|
-
setTopLevelIds: (ids: ReadonlySet<string> | null) => void;
|
|
207
205
|
setClipParentMap: (map: Map<string, string>) => void;
|
|
208
206
|
/**
|
|
209
207
|
* Sub-composition DOM descendants (groups + their children) that have no
|
|
@@ -394,8 +392,6 @@ export const usePlayerStore = create<PlayerState>((set, get) => ({
|
|
|
394
392
|
setClipManifest: (clips) => set({ clipManifest: clips }),
|
|
395
393
|
clipParentMap: new Map(),
|
|
396
394
|
setClipParentMap: (map) => set({ clipParentMap: map }),
|
|
397
|
-
topLevelIds: null,
|
|
398
|
-
setTopLevelIds: (ids) => set({ topLevelIds: ids }),
|
|
399
395
|
domClipChildren: [],
|
|
400
396
|
setDomClipChildren: (children) => set({ domClipChildren: children }),
|
|
401
397
|
subCompositionHostState: new Map(),
|
|
@@ -77,10 +77,8 @@ export interface TimelineElement {
|
|
|
77
77
|
audioGroupFxChain?: string;
|
|
78
78
|
audioGroupAutomation?: string;
|
|
79
79
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* lives in. Presence marks the element as expanded; edits subtract it to get
|
|
83
|
-
* the child's local (sourceFile-relative) time. Works at any nesting depth.
|
|
80
|
+
* Legacy source-coordinate marker for an inline sub-composition child. The
|
|
81
|
+
* current timeline keeps sub-compositions as single rows.
|
|
84
82
|
*/
|
|
85
83
|
expandedParentStart?: number;
|
|
86
84
|
expandedHostKey?: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"total":
|
|
2
|
+
"total": 385,
|
|
3
3
|
"files": {
|
|
4
4
|
"packages/studio/src/captions/components/CaptionOverlay.tsx": 5,
|
|
5
5
|
"packages/studio/src/captions/components/CaptionTimeline.tsx": 11,
|
|
@@ -61,7 +61,6 @@
|
|
|
61
61
|
"packages/studio/src/player/components/SpeedMenu.tsx": 2,
|
|
62
62
|
"packages/studio/src/player/components/TimelineAutomationLane.tsx": 4,
|
|
63
63
|
"packages/studio/src/player/components/TimelineCanvas.tsx": 13,
|
|
64
|
-
"packages/studio/src/player/components/TimelineClip.tsx": 2,
|
|
65
64
|
"packages/studio/src/player/components/TimelineClipDiamonds.tsx": 2,
|
|
66
65
|
"packages/studio/src/player/components/TimelineDiamondConnectors.tsx": 2,
|
|
67
66
|
"packages/studio/src/player/components/TimelineEmptyState.tsx": 1,
|
|
@@ -75,9 +74,9 @@
|
|
|
75
74
|
"packages/studio/src/player/components/TimelineTrackHeader.tsx": 2,
|
|
76
75
|
"packages/studio/src/player/components/TimelineTrackPlainHeader.tsx": 2,
|
|
77
76
|
"packages/studio/src/player/components/VideoThumbnail.tsx": 6,
|
|
78
|
-
"packages/studio/src/player/components/timelineTheme.ts":
|
|
77
|
+
"packages/studio/src/player/components/timelineTheme.ts": 5,
|
|
79
78
|
"packages/studio/src/player/components/trackHeaderLabelRows.tsx": 6,
|
|
80
|
-
"packages/studio/src/styles/studio.css":
|
|
79
|
+
"packages/studio/src/styles/studio.css": 63,
|
|
81
80
|
"packages/studio/src/telemetry/client.ts": 1,
|
|
82
81
|
"packages/studio/src/webmcp/webmcpTestUtils.ts": 1
|
|
83
82
|
}
|
package/src/styles/studio.css
CHANGED
|
@@ -200,8 +200,8 @@ body {
|
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
.timeline-clip {
|
|
203
|
-
background-color:
|
|
204
|
-
border: 1px solid
|
|
203
|
+
background-color: var(--clip-bg);
|
|
204
|
+
border: 1px solid var(--clip-border);
|
|
205
205
|
border-radius: 8px;
|
|
206
206
|
}
|
|
207
207
|
|
|
@@ -210,13 +210,13 @@ body {
|
|
|
210
210
|
position: absolute;
|
|
211
211
|
inset: 0;
|
|
212
212
|
border-radius: inherit;
|
|
213
|
-
background:
|
|
213
|
+
background: var(--clip-bg-active);
|
|
214
214
|
opacity: 0;
|
|
215
215
|
pointer-events: none;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
.timeline-clip[data-active] {
|
|
219
|
-
border-color:
|
|
219
|
+
border-color: var(--clip-border-active);
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
.timeline-clip[data-active]::before {
|
|
@@ -240,11 +240,11 @@ body {
|
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
.timeline-clip.is-hovered {
|
|
243
|
-
background-color:
|
|
243
|
+
background-color: var(--clip-bg-hover);
|
|
244
244
|
}
|
|
245
245
|
|
|
246
246
|
.timeline-clip[data-active].is-hovered {
|
|
247
|
-
border-color:
|
|
247
|
+
border-color: var(--clip-border-hover);
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
.timeline-clip.is-selected {
|
|
@@ -258,7 +258,7 @@ body {
|
|
|
258
258
|
.timeline-clip.is-dragging {
|
|
259
259
|
/* Solid background so the picked-up clip reads clearly while dragging — audio
|
|
260
260
|
clips (waveform drawn on a separate layer) would otherwise look transparent. */
|
|
261
|
-
background-color:
|
|
261
|
+
background-color: var(--clip-bg-dragging);
|
|
262
262
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
|
|
263
263
|
}
|
|
264
264
|
|
package/src/styles/theme.css
CHANGED
|
@@ -46,6 +46,35 @@
|
|
|
46
46
|
--color-brand-gradient-from: #06e3fa;
|
|
47
47
|
--color-brand-gradient-to: #4fdb5e;
|
|
48
48
|
|
|
49
|
+
/* The timeline's own surfaces: darker and bluer than the shared scale, so they
|
|
50
|
+
* get their own names instead of snapping to the nearest --color-bg/-surface
|
|
51
|
+
* step (a host themes the timeline by overriding these). */
|
|
52
|
+
--timeline-shell-bg: #0a0a0b;
|
|
53
|
+
--timeline-shell-border: rgba(255, 255, 255, 0.05);
|
|
54
|
+
--timeline-ruler-border: rgba(255, 255, 255, 0.16);
|
|
55
|
+
--timeline-row-bg: #101014;
|
|
56
|
+
--timeline-row-border: rgba(255, 255, 255, 0.06);
|
|
57
|
+
--timeline-gutter-bg: #0e0f12;
|
|
58
|
+
--timeline-gutter-border: rgba(255, 255, 255, 0.1);
|
|
59
|
+
--timeline-text-primary: rgba(255, 255, 255, 0.92);
|
|
60
|
+
--timeline-text-secondary: rgba(255, 255, 255, 0.62);
|
|
61
|
+
--timeline-tick-text: rgba(255, 255, 255, 0.34);
|
|
62
|
+
--timeline-tick-major: rgba(255, 255, 255, 0.1);
|
|
63
|
+
--timeline-tick-minor: rgba(255, 255, 255, 0.06);
|
|
64
|
+
--timeline-clip-bg: rgba(255, 255, 255, 0.12);
|
|
65
|
+
--timeline-clip-bg-active: rgba(60, 230, 172, 0.2);
|
|
66
|
+
--timeline-clip-bg-hover: rgba(255, 255, 255, 0.16);
|
|
67
|
+
--timeline-clip-bg-dragging: rgba(38, 42, 52, 0.96);
|
|
68
|
+
--timeline-clip-border: rgba(255, 255, 255, 0.22);
|
|
69
|
+
--timeline-clip-border-hover: rgba(60, 230, 172, 0.75);
|
|
70
|
+
--timeline-clip-border-active: rgba(60, 230, 172, 0.55);
|
|
71
|
+
--timeline-handle: rgba(255, 255, 255, 0.55);
|
|
72
|
+
--timeline-resize-seam: rgba(255, 255, 255, 0.12);
|
|
73
|
+
--timeline-resize-active: rgba(255, 255, 255, 0.24);
|
|
74
|
+
--timeline-track-clip-fill: rgba(255, 255, 255, 0.055);
|
|
75
|
+
--timeline-track-clip-active: rgba(60, 230, 172, 0.16);
|
|
76
|
+
--timeline-track-label: rgba(255, 255, 255, 0.5);
|
|
77
|
+
|
|
49
78
|
/* -------------------------------------------------------------- geometry */
|
|
50
79
|
|
|
51
80
|
--radius-sm: 0.25rem;
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import type { TimelineElement } from "../store/playerStore";
|
|
3
|
-
import { selectTimelineRowElements } from "./useTimelineRowElements";
|
|
4
|
-
|
|
5
|
-
const el = (id: string, domId?: string): TimelineElement => ({
|
|
6
|
-
id,
|
|
7
|
-
domId,
|
|
8
|
-
start: 0,
|
|
9
|
-
duration: 1,
|
|
10
|
-
track: 0,
|
|
11
|
-
tag: "div",
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
describe("selectTimelineRowElements", () => {
|
|
15
|
-
it("drops an element the shared definition does not call top-level", () => {
|
|
16
|
-
const rows = selectTimelineRowElements([el("a", "a"), el("b", "b")], new Set(["a"]));
|
|
17
|
-
expect(rows.map((r) => r.id)).toEqual(["a"]);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it("keeps everything until the document has been read", () => {
|
|
21
|
-
const all = [el("a", "a"), el("b", "b")];
|
|
22
|
-
expect(selectTimelineRowElements(all, null)).toBe(all);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("keeps an element with no DOM id", () => {
|
|
26
|
-
expect(selectTimelineRowElements([el("k")], new Set())).toHaveLength(1);
|
|
27
|
-
});
|
|
28
|
-
});
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { useMemo } from "react";
|
|
2
|
-
import { usePlayerStore, type TimelineElement } from "../store/playerStore";
|
|
3
|
-
|
|
4
|
-
/** Rows are the ids `topLevelElements` names, as the lint does; id-less ones are kept. */
|
|
5
|
-
export function selectTimelineRowElements(
|
|
6
|
-
elements: TimelineElement[],
|
|
7
|
-
topLevelIds: ReadonlySet<string> | null,
|
|
8
|
-
): TimelineElement[] {
|
|
9
|
-
if (!topLevelIds) return elements;
|
|
10
|
-
return elements.filter((el) => el.domId === undefined || topLevelIds.has(el.domId));
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function useTimelineRowElements(): TimelineElement[] {
|
|
14
|
-
const elements = usePlayerStore((s) => s.elements);
|
|
15
|
-
const topLevelIds = usePlayerStore((s) => s.topLevelIds);
|
|
16
|
-
return useMemo(() => selectTimelineRowElements(elements, topLevelIds), [elements, topLevelIds]);
|
|
17
|
-
}
|