@hyperframes/studio 0.5.0-alpha.9 → 0.5.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/assets/hyperframes-player-CoI5h1xv.js +353 -0
- package/dist/assets/index-BKjcNNNd.css +1 -0
- package/dist/assets/index-CqiisJmo.js +93 -0
- package/dist/index.html +2 -2
- package/package.json +4 -4
- package/src/App.tsx +208 -1438
- package/src/captions/generator.test.ts +19 -0
- package/src/captions/generator.ts +9 -2
- package/src/captions/hooks/useCaptionSync.ts +6 -1
- package/src/captions/parser.test.ts +14 -0
- package/src/captions/parser.ts +1 -0
- package/src/components/LintModal.tsx +4 -3
- package/src/components/editor/PropertyPanel.tsx +206 -2466
- package/src/components/nle/NLELayout.tsx +47 -17
- package/src/components/nle/NLEPreview.tsx +5 -50
- package/src/components/sidebar/AssetsTab.tsx +4 -3
- package/src/components/sidebar/CompositionsTab.test.ts +1 -16
- package/src/components/sidebar/CompositionsTab.tsx +45 -117
- package/src/components/sidebar/LeftSidebar.tsx +55 -34
- package/src/components/ui/HyperframesLoader.tsx +104 -0
- package/src/components/ui/index.ts +2 -0
- package/src/icons/SystemIcons.tsx +2 -0
- package/src/player/components/CompositionThumbnail.tsx +10 -42
- package/src/player/components/EditModal.tsx +20 -5
- package/src/player/components/Player.tsx +129 -28
- package/src/player/components/PlayerControls.tsx +3 -44
- package/src/player/components/Timeline.test.ts +0 -12
- package/src/player/components/Timeline.tsx +25 -52
- package/src/player/components/TimelineClip.tsx +9 -21
- package/src/player/components/timelineEditing.test.ts +4 -2
- package/src/player/components/timelineEditing.ts +3 -1
- package/src/player/components/timelineTheme.test.ts +19 -0
- package/src/player/components/timelineTheme.ts +8 -4
- package/src/player/hooks/useTimelinePlayer.test.ts +160 -21
- package/src/player/hooks/useTimelinePlayer.ts +206 -93
- package/src/player/lib/time.test.ts +11 -1
- package/src/player/lib/time.ts +6 -0
- package/src/player/store/playerStore.ts +1 -0
- package/src/styles/studio.css +112 -0
- package/src/utils/frameCapture.test.ts +26 -0
- package/src/utils/frameCapture.ts +40 -0
- package/src/utils/mediaTypes.ts +1 -1
- package/src/utils/projectRouting.test.ts +87 -0
- package/src/utils/projectRouting.ts +27 -0
- package/src/utils/sourcePatcher.test.ts +1 -128
- package/src/utils/sourcePatcher.ts +18 -130
- package/src/utils/timelineAssetDrop.test.ts +11 -31
- package/src/utils/timelineAssetDrop.ts +2 -22
- package/dist/assets/hyperframes-player-vibA20NC.js +0 -198
- package/dist/assets/index-DKaNgV2Z.css +0 -1
- package/dist/assets/index-peNJzL-4.js +0 -105
- package/src/components/editor/DomEditOverlay.tsx +0 -445
- package/src/components/editor/colorValue.test.ts +0 -82
- package/src/components/editor/colorValue.ts +0 -175
- package/src/components/editor/domEditing.test.ts +0 -537
- package/src/components/editor/domEditing.ts +0 -762
- package/src/components/editor/floatingPanel.test.ts +0 -34
- package/src/components/editor/floatingPanel.ts +0 -54
- package/src/components/editor/fontAssets.ts +0 -32
- package/src/components/editor/fontCatalog.ts +0 -126
- package/src/components/editor/gradientValue.test.ts +0 -89
- package/src/components/editor/gradientValue.ts +0 -445
- package/src/player/components/CompositionThumbnail.test.ts +0 -19
- package/src/utils/clipboard.test.ts +0 -88
- package/src/utils/clipboard.ts +0 -57
|
@@ -61,26 +61,8 @@ export const TimelineClip = memo(function TimelineClip({
|
|
|
61
61
|
? theme.clipShadowHover
|
|
62
62
|
: theme.clipShadow;
|
|
63
63
|
const capabilities = getTimelineEditCapabilities(el);
|
|
64
|
+
const displayLabel = el.label || el.id || el.tag;
|
|
64
65
|
const showHandles = handleOpacity > 0.01;
|
|
65
|
-
const baseBackgroundImage = isSelected ? theme.clipBackgroundActive : theme.clipBackground;
|
|
66
|
-
const glossBackgroundImage = isSelected
|
|
67
|
-
? "linear-gradient(180deg, rgba(255,255,255,0.03), rgba(255,255,255,0))"
|
|
68
|
-
: "linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0))";
|
|
69
|
-
const accentBackgroundImage = `linear-gradient(120deg, ${trackStyle.accent}${
|
|
70
|
-
isSelected ? "22" : "1e"
|
|
71
|
-
}, transparent 28%)`;
|
|
72
|
-
const compositionStripeBackgroundImage =
|
|
73
|
-
isComposition && !hasCustomContent
|
|
74
|
-
? "repeating-linear-gradient(135deg, transparent, transparent 3px, rgba(255,255,255,0.05) 3px, rgba(255,255,255,0.05) 6px)"
|
|
75
|
-
: undefined;
|
|
76
|
-
const clipBackgroundImage = [
|
|
77
|
-
compositionStripeBackgroundImage,
|
|
78
|
-
glossBackgroundImage,
|
|
79
|
-
accentBackgroundImage,
|
|
80
|
-
baseBackgroundImage,
|
|
81
|
-
]
|
|
82
|
-
.filter(Boolean)
|
|
83
|
-
.join(", ");
|
|
84
66
|
|
|
85
67
|
return (
|
|
86
68
|
<div
|
|
@@ -94,7 +76,13 @@ export const TimelineClip = memo(function TimelineClip({
|
|
|
94
76
|
top: clipY,
|
|
95
77
|
bottom: clipY,
|
|
96
78
|
borderRadius: theme.clipRadius,
|
|
97
|
-
|
|
79
|
+
background: isSelected
|
|
80
|
+
? `linear-gradient(180deg, rgba(255,255,255,0.03), rgba(255,255,255,0)), linear-gradient(120deg, ${trackStyle.accent}22, transparent 28%), ${theme.clipBackgroundActive}`
|
|
81
|
+
: `linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0)), linear-gradient(120deg, ${trackStyle.accent}1e, transparent 28%), ${theme.clipBackground}`,
|
|
82
|
+
backgroundImage:
|
|
83
|
+
isComposition && !hasCustomContent
|
|
84
|
+
? `repeating-linear-gradient(135deg, transparent, transparent 3px, rgba(255,255,255,0.05) 3px, rgba(255,255,255,0.05) 6px)`
|
|
85
|
+
: undefined,
|
|
98
86
|
border: `1px solid ${borderColor}`,
|
|
99
87
|
boxShadow,
|
|
100
88
|
transition:
|
|
@@ -106,7 +94,7 @@ export const TimelineClip = memo(function TimelineClip({
|
|
|
106
94
|
title={
|
|
107
95
|
isComposition
|
|
108
96
|
? `${el.compositionSrc} \u2022 Double-click to open`
|
|
109
|
-
: `${
|
|
97
|
+
: `${displayLabel} \u2022 ${el.start.toFixed(1)}s \u2013 ${(el.start + el.duration).toFixed(1)}s`
|
|
110
98
|
}
|
|
111
99
|
onPointerEnter={onHoverStart}
|
|
112
100
|
onPointerLeave={onHoverEnd}
|
|
@@ -248,7 +248,7 @@ describe("getTimelineEditCapabilities", () => {
|
|
|
248
248
|
});
|
|
249
249
|
});
|
|
250
250
|
|
|
251
|
-
it("
|
|
251
|
+
it("disables move and trims for generic motion clips even when patchable", () => {
|
|
252
252
|
expect(
|
|
253
253
|
getTimelineEditCapabilities({
|
|
254
254
|
tag: "section",
|
|
@@ -256,7 +256,7 @@ describe("getTimelineEditCapabilities", () => {
|
|
|
256
256
|
selector: ".feature-card",
|
|
257
257
|
}),
|
|
258
258
|
).toEqual({
|
|
259
|
-
canMove:
|
|
259
|
+
canMove: false,
|
|
260
260
|
canTrimStart: false,
|
|
261
261
|
canTrimEnd: false,
|
|
262
262
|
});
|
|
@@ -428,6 +428,7 @@ describe("buildClipRangeSelection", () => {
|
|
|
428
428
|
});
|
|
429
429
|
});
|
|
430
430
|
});
|
|
431
|
+
|
|
431
432
|
describe("resolveTimelineAutoScroll", () => {
|
|
432
433
|
it("does not scroll when the pointer stays away from the edges", () => {
|
|
433
434
|
expect(
|
|
@@ -511,6 +512,7 @@ describe("buildTimelineElementAgentPrompt", () => {
|
|
|
511
512
|
).toContain("If this clip is animated with GSAP");
|
|
512
513
|
});
|
|
513
514
|
});
|
|
515
|
+
|
|
514
516
|
describe("resolveTimelineResize", () => {
|
|
515
517
|
it("shrinks clip duration from the right edge", () => {
|
|
516
518
|
expect(
|
|
@@ -233,7 +233,7 @@ export function getTimelineEditCapabilities(input: {
|
|
|
233
233
|
const hasFiniteDuration = Number.isFinite(input.duration) && input.duration > 0;
|
|
234
234
|
const hasDeterministicWindow = isDeterministicTimelineWindow(input);
|
|
235
235
|
return {
|
|
236
|
-
canMove: canPatch &&
|
|
236
|
+
canMove: canPatch && hasDeterministicWindow,
|
|
237
237
|
canTrimEnd: canPatch && hasFiniteDuration && hasDeterministicWindow,
|
|
238
238
|
canTrimStart: canPatch && hasFiniteDuration && canOffsetTrimClipStart(input),
|
|
239
239
|
};
|
|
@@ -273,6 +273,7 @@ export function buildClipRangeSelection(
|
|
|
273
273
|
anchorY: anchor.anchorY,
|
|
274
274
|
};
|
|
275
275
|
}
|
|
276
|
+
|
|
276
277
|
export function buildTimelineAgentPrompt({
|
|
277
278
|
rangeStart,
|
|
278
279
|
rangeEnd,
|
|
@@ -346,6 +347,7 @@ export function buildTimelineElementAgentPrompt(element: {
|
|
|
346
347
|
|
|
347
348
|
return lines.join("\n");
|
|
348
349
|
}
|
|
350
|
+
|
|
349
351
|
export function formatTimelineAttributeNumber(value: number): string {
|
|
350
352
|
return Number(roundToCentiseconds(value).toFixed(2)).toString();
|
|
351
353
|
}
|
|
@@ -53,4 +53,23 @@ describe("getRenderedTimelineElement", () => {
|
|
|
53
53
|
}),
|
|
54
54
|
).toEqual({ ...element, start: 2.4, track: 3 });
|
|
55
55
|
});
|
|
56
|
+
|
|
57
|
+
it("uses key before id when matching the dragged clip", () => {
|
|
58
|
+
const element = {
|
|
59
|
+
id: "Card",
|
|
60
|
+
key: "index.html:.card:1",
|
|
61
|
+
tag: "div",
|
|
62
|
+
start: 1,
|
|
63
|
+
duration: 2,
|
|
64
|
+
track: 0,
|
|
65
|
+
};
|
|
66
|
+
expect(
|
|
67
|
+
getRenderedTimelineElement({
|
|
68
|
+
element,
|
|
69
|
+
draggedElementId: "index.html:.card:1",
|
|
70
|
+
previewStart: 2.4,
|
|
71
|
+
previewTrack: 3,
|
|
72
|
+
}),
|
|
73
|
+
).toEqual({ ...element, start: 2.4, track: 3 });
|
|
74
|
+
});
|
|
56
75
|
});
|
|
@@ -63,12 +63,12 @@ const TRACK_STYLES: Record<string, TimelineTrackStyle> = {
|
|
|
63
63
|
const DEFAULT_TRACK_STYLE: TimelineTrackStyle = createTrackStyle();
|
|
64
64
|
|
|
65
65
|
export const defaultTimelineTheme: TimelineTheme = {
|
|
66
|
-
shellBackground: "#
|
|
66
|
+
shellBackground: "#0A0A0B",
|
|
67
67
|
shellBorder: "rgba(255,255,255,0.05)",
|
|
68
68
|
rulerBorder: "rgba(255,255,255,0.045)",
|
|
69
|
-
rowBackground: "#
|
|
69
|
+
rowBackground: "#0A0A0B",
|
|
70
70
|
rowBorder: "rgba(255,255,255,0.05)",
|
|
71
|
-
gutterBackground: "#
|
|
71
|
+
gutterBackground: "#0A0A0B",
|
|
72
72
|
gutterBorder: "rgba(255,255,255,0.05)",
|
|
73
73
|
textPrimary: "#E8EDF5",
|
|
74
74
|
textSecondary: "#8391A8",
|
|
@@ -130,7 +130,11 @@ export function getRenderedTimelineElement({
|
|
|
130
130
|
previewStart: number | null;
|
|
131
131
|
previewTrack: number | null;
|
|
132
132
|
}): TimelineElement {
|
|
133
|
-
if (
|
|
133
|
+
if (
|
|
134
|
+
(element.key ?? element.id) !== draggedElementId ||
|
|
135
|
+
previewStart === null ||
|
|
136
|
+
previewTrack === null
|
|
137
|
+
) {
|
|
134
138
|
return element;
|
|
135
139
|
}
|
|
136
140
|
return {
|
|
@@ -2,8 +2,10 @@ import { describe, expect, it } from "vitest";
|
|
|
2
2
|
import { Window } from "happy-dom";
|
|
3
3
|
import {
|
|
4
4
|
buildStandaloneRootTimelineElement,
|
|
5
|
+
createTimelineElementFromManifestClip,
|
|
5
6
|
findTimelineDomNodeForClip,
|
|
6
7
|
getTimelineElementSelector,
|
|
8
|
+
parseTimelineFromDOM,
|
|
7
9
|
type ClipManifestClip,
|
|
8
10
|
mergeTimelineElementsPreservingDowngrades,
|
|
9
11
|
resolveStandaloneRootCompositionSrc,
|
|
@@ -11,6 +13,26 @@ import {
|
|
|
11
13
|
shouldIgnorePlaybackShortcutTarget,
|
|
12
14
|
} from "./useTimelinePlayer";
|
|
13
15
|
|
|
16
|
+
function mockTargetMatching(selectorNeedle: string): EventTarget {
|
|
17
|
+
return {
|
|
18
|
+
closest: (selector: string) => (selector.includes(selectorNeedle) ? ({} as Element) : null),
|
|
19
|
+
} as unknown as EventTarget;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function mockKeyboardEvent(
|
|
23
|
+
code: string,
|
|
24
|
+
overrides: Partial<Pick<KeyboardEvent, "altKey" | "ctrlKey" | "metaKey" | "target">> = {},
|
|
25
|
+
): Pick<KeyboardEvent, "altKey" | "ctrlKey" | "metaKey" | "code" | "target"> {
|
|
26
|
+
return {
|
|
27
|
+
altKey: false,
|
|
28
|
+
ctrlKey: false,
|
|
29
|
+
metaKey: false,
|
|
30
|
+
code,
|
|
31
|
+
target: mockTargetMatching("[data-missing]"),
|
|
32
|
+
...overrides,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
14
36
|
function createDocument(markup: string): Document {
|
|
15
37
|
const window = new Window();
|
|
16
38
|
window.document.body.innerHTML = markup;
|
|
@@ -20,7 +42,7 @@ function createDocument(markup: string): Document {
|
|
|
20
42
|
function createClip(overrides: Partial<ClipManifestClip>): ClipManifestClip {
|
|
21
43
|
return {
|
|
22
44
|
id: null,
|
|
23
|
-
label: "",
|
|
45
|
+
label: "Element",
|
|
24
46
|
start: 0,
|
|
25
47
|
duration: 4,
|
|
26
48
|
track: 0,
|
|
@@ -34,26 +56,6 @@ function createClip(overrides: Partial<ClipManifestClip>): ClipManifestClip {
|
|
|
34
56
|
};
|
|
35
57
|
}
|
|
36
58
|
|
|
37
|
-
function mockTargetMatching(selectorNeedle: string): EventTarget {
|
|
38
|
-
return {
|
|
39
|
-
closest: (selector: string) => (selector.includes(selectorNeedle) ? ({} as Element) : null),
|
|
40
|
-
} as unknown as EventTarget;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function mockKeyboardEvent(
|
|
44
|
-
code: string,
|
|
45
|
-
overrides: Partial<Pick<KeyboardEvent, "altKey" | "ctrlKey" | "metaKey" | "target">> = {},
|
|
46
|
-
): Pick<KeyboardEvent, "altKey" | "ctrlKey" | "metaKey" | "code" | "target"> {
|
|
47
|
-
return {
|
|
48
|
-
altKey: false,
|
|
49
|
-
ctrlKey: false,
|
|
50
|
-
metaKey: false,
|
|
51
|
-
code,
|
|
52
|
-
target: mockTargetMatching("[data-missing]"),
|
|
53
|
-
...overrides,
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
59
|
describe("buildStandaloneRootTimelineElement", () => {
|
|
58
60
|
it("includes selector and source metadata for standalone composition fallback clips", () => {
|
|
59
61
|
expect(
|
|
@@ -66,6 +68,7 @@ describe("buildStandaloneRootTimelineElement", () => {
|
|
|
66
68
|
}),
|
|
67
69
|
).toEqual({
|
|
68
70
|
id: "hero",
|
|
71
|
+
label: "hero",
|
|
69
72
|
key: 'scenes/hero.html:[data-composition-id="hero"]:0',
|
|
70
73
|
tag: "div",
|
|
71
74
|
start: 0,
|
|
@@ -146,6 +149,83 @@ describe("findTimelineDomNodeForClip", () => {
|
|
|
146
149
|
});
|
|
147
150
|
});
|
|
148
151
|
|
|
152
|
+
describe("anonymous timeline identity", () => {
|
|
153
|
+
it("keeps fallback-parsed anonymous clips distinct when labels match", () => {
|
|
154
|
+
const doc = createDocument(`
|
|
155
|
+
<div data-composition-id="main" data-start="0" data-duration="8">
|
|
156
|
+
<div class="clip card" data-label="Card" data-start="0" data-duration="3" data-track-index="0"></div>
|
|
157
|
+
<div class="clip card" data-label="Card" data-start="3" data-duration="3" data-track-index="1"></div>
|
|
158
|
+
</div>
|
|
159
|
+
`);
|
|
160
|
+
|
|
161
|
+
const elements = parseTimelineFromDOM(doc, 8);
|
|
162
|
+
|
|
163
|
+
expect(elements).toHaveLength(2);
|
|
164
|
+
expect(elements.map((element) => element.label)).toEqual(["Card", "Card"]);
|
|
165
|
+
expect(new Set(elements.map((element) => element.id)).size).toBe(2);
|
|
166
|
+
expect(new Set(elements.map((element) => element.key)).size).toBe(2);
|
|
167
|
+
expect(elements.map((element) => element.selectorIndex)).toEqual([0, 1]);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("keeps runtime-manifest anonymous clips distinct when labels match", () => {
|
|
171
|
+
const doc = createDocument(`
|
|
172
|
+
<div data-composition-id="main" data-start="0" data-duration="8">
|
|
173
|
+
<div class="clip card" data-start="0" data-duration="3" data-track-index="0"></div>
|
|
174
|
+
<div class="clip card" data-start="3" data-duration="3" data-track-index="1"></div>
|
|
175
|
+
</div>
|
|
176
|
+
`);
|
|
177
|
+
const clips = [
|
|
178
|
+
createClip({ id: null, label: "Card", start: 0, duration: 3, track: 0 }),
|
|
179
|
+
createClip({ id: null, label: "Card", start: 3, duration: 3, track: 1 }),
|
|
180
|
+
];
|
|
181
|
+
const used = new Set<Element>();
|
|
182
|
+
const elements = clips.map((clip, index) => {
|
|
183
|
+
const hostEl = findTimelineDomNodeForClip(doc, clip, index, used);
|
|
184
|
+
if (hostEl) used.add(hostEl);
|
|
185
|
+
return createTimelineElementFromManifestClip({
|
|
186
|
+
clip,
|
|
187
|
+
fallbackIndex: index,
|
|
188
|
+
doc,
|
|
189
|
+
hostEl,
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
expect(elements.map((element) => element.label)).toEqual(["Card", "Card"]);
|
|
194
|
+
expect(new Set(elements.map((element) => element.id)).size).toBe(2);
|
|
195
|
+
expect(new Set(elements.map((element) => element.key)).size).toBe(2);
|
|
196
|
+
expect(elements.map((element) => element.selectorIndex)).toEqual([0, 1]);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("reads media metadata from owner-window media elements", () => {
|
|
200
|
+
const doc = createDocument(`
|
|
201
|
+
<div data-composition-id="main" data-start="0" data-duration="8">
|
|
202
|
+
<div class="clip video-card" data-start="0" data-duration="3" data-track-index="0">
|
|
203
|
+
<video src="/clip.mp4" data-source-duration="12"></video>
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
`);
|
|
207
|
+
const hostEl = doc.querySelector(".video-card");
|
|
208
|
+
const video = hostEl?.querySelector("video");
|
|
209
|
+
if (!hostEl || !video) throw new Error("missing video test fixture");
|
|
210
|
+
Object.defineProperty(video, "defaultPlaybackRate", {
|
|
211
|
+
value: 1.5,
|
|
212
|
+
configurable: true,
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const element = createTimelineElementFromManifestClip({
|
|
216
|
+
clip: createClip({ kind: "video", tagName: "div" }),
|
|
217
|
+
fallbackIndex: 0,
|
|
218
|
+
doc,
|
|
219
|
+
hostEl,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
expect(element.tag).toBe("video");
|
|
223
|
+
expect(element.src).toBe("/clip.mp4");
|
|
224
|
+
expect(element.sourceDuration).toBe(12);
|
|
225
|
+
expect(element.playbackRate).toBe(1.5);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
|
|
149
229
|
describe("mergeTimelineElementsPreservingDowngrades", () => {
|
|
150
230
|
it("preserves missing current elements when a shorter manifest arrives", () => {
|
|
151
231
|
expect(
|
|
@@ -174,6 +254,65 @@ describe("mergeTimelineElementsPreservingDowngrades", () => {
|
|
|
174
254
|
),
|
|
175
255
|
).toEqual([{ id: "hero", tag: "div", start: 0, duration: 4, track: 0 }]);
|
|
176
256
|
});
|
|
257
|
+
|
|
258
|
+
it("preserves distinct anonymous clips that share the same friendly id label", () => {
|
|
259
|
+
expect(
|
|
260
|
+
mergeTimelineElementsPreservingDowngrades(
|
|
261
|
+
[
|
|
262
|
+
{
|
|
263
|
+
id: "Card",
|
|
264
|
+
key: "index.html:.card:0",
|
|
265
|
+
label: "Card",
|
|
266
|
+
tag: "div",
|
|
267
|
+
start: 0,
|
|
268
|
+
duration: 3,
|
|
269
|
+
track: 0,
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
id: "Card",
|
|
273
|
+
key: "index.html:.card:1",
|
|
274
|
+
label: "Card",
|
|
275
|
+
tag: "div",
|
|
276
|
+
start: 3,
|
|
277
|
+
duration: 3,
|
|
278
|
+
track: 1,
|
|
279
|
+
},
|
|
280
|
+
],
|
|
281
|
+
[
|
|
282
|
+
{
|
|
283
|
+
id: "Card",
|
|
284
|
+
key: "index.html:.card:0",
|
|
285
|
+
label: "Card",
|
|
286
|
+
tag: "div",
|
|
287
|
+
start: 0,
|
|
288
|
+
duration: 3,
|
|
289
|
+
track: 0,
|
|
290
|
+
},
|
|
291
|
+
],
|
|
292
|
+
8,
|
|
293
|
+
8,
|
|
294
|
+
),
|
|
295
|
+
).toEqual([
|
|
296
|
+
{
|
|
297
|
+
id: "Card",
|
|
298
|
+
key: "index.html:.card:0",
|
|
299
|
+
label: "Card",
|
|
300
|
+
tag: "div",
|
|
301
|
+
start: 0,
|
|
302
|
+
duration: 3,
|
|
303
|
+
track: 0,
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
id: "Card",
|
|
307
|
+
key: "index.html:.card:1",
|
|
308
|
+
label: "Card",
|
|
309
|
+
tag: "div",
|
|
310
|
+
start: 3,
|
|
311
|
+
duration: 3,
|
|
312
|
+
track: 1,
|
|
313
|
+
},
|
|
314
|
+
]);
|
|
315
|
+
});
|
|
177
316
|
});
|
|
178
317
|
|
|
179
318
|
describe("shouldIgnorePlaybackShortcutTarget", () => {
|