@hyperframes/studio 0.8.101 → 0.8.103
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-CFJLrHRF.js → hyperframes-player-DG795Hwu.js} +1 -1
- package/dist/assets/{index-BBNVnnyH.js → index-BPJRf2X9.js} +119 -119
- package/dist/assets/{index-CJOzbslX.js → index-BiEtDkae.js} +1 -1
- package/dist/assets/{index-xB5O0hPw.css → index-CQN3M56N.css} +1 -1
- package/dist/assets/{index-C33EDBL2.js → index-DuCZ3pQw.js} +1 -1
- package/dist/{chunk-MTAVNAGW.js → chunk-46XP7J3B.js} +2 -2
- package/dist/{chunk-S65AQH6M.js → chunk-DI4LCJHL.js} +3 -2
- package/dist/chunk-DI4LCJHL.js.map +1 -0
- package/dist/{chunk-7X4XRP3L.js → chunk-HIGHVWYD.js} +2 -2
- package/dist/{domEditingLayers-NU6Z5QWA.js → domEditingLayers-AQBDFNUC.js} +3 -3
- package/dist/index.d.ts +13 -3
- package/dist/index.html +2 -2
- package/dist/index.js +932 -850
- package/dist/index.js.map +1 -1
- package/dist/player.js +2 -2
- package/dist/styles.css +1 -1
- package/package.json +8 -8
- package/src/components/editor/CanvasContextMenu.test.tsx +23 -2
- package/src/components/editor/CanvasContextMenu.tsx +7 -10
- package/src/components/editor/ConnectedDomEditOverlay.tsx +3 -128
- package/src/components/editor/DomEditOverlay.tsx +2 -1
- package/src/components/editor/PropertyPanel.tsx +2 -2
- package/src/components/editor/canvasContextMenuZOrder.ts +13 -0
- package/src/components/editor/domEditOverlayGestures.ts +1 -1
- package/src/components/editor/domEditOverlayStartGesture.ts +2 -2
- package/src/components/editor/gsapAnimatesProperty.ts +0 -29
- package/src/components/editor/gsapLivePreview.test.ts +46 -1
- package/src/components/editor/manualOffsetDrag.test.ts +16 -0
- package/src/components/editor/manualOffsetDrag.ts +5 -5
- package/src/components/editor/plainTranslate.test.ts +38 -22
- package/src/components/editor/plainTranslate.ts +66 -36
- package/src/components/editor/previewReadOnly.overlay.test.tsx +30 -0
- package/src/components/editor/previewReadOnlyContext.tsx +3 -1
- package/src/components/editor/propertyPanelHelpers.ts +3 -0
- package/src/components/editor/propertyPanelTransformCommit.test.ts +51 -0
- package/src/components/editor/propertyPanelTransformCommit.ts +3 -1
- package/src/components/editor/useColorGradingController.test.ts +36 -1
- package/src/components/editor/useColorGradingController.ts +2 -2
- package/src/components/editor/useDomEditNudge.test.tsx +1 -0
- package/src/components/editor/useDomEditNudge.ts +6 -3
- package/src/components/editor/useDomEditZOrder.ts +185 -0
- package/src/components/nle/PreviewOverlaysTestMocks.ts +4 -0
- package/src/domEditExports.test.tsx +92 -0
- package/src/hooks/gsapRuntimeKeyframes.ts +6 -0
- package/src/hooks/useDomEditCommits.test.tsx +94 -90
- package/src/hooks/usePreviewInteraction.test.ts +32 -0
- package/src/hooks/usePreviewInteraction.ts +3 -2
- package/src/index.ts +3 -0
- package/src/player/components/TimelineClipFades.test.tsx +103 -3
- package/src/player/components/TimelineClipFades.tsx +53 -50
- package/src/styles/theme.css +1 -1
- package/src/utils/projectRouting.test.ts +9 -0
- package/src/utils/projectRouting.ts +3 -1
- package/dist/chunk-S65AQH6M.js.map +0 -1
- /package/dist/{chunk-MTAVNAGW.js.map → chunk-46XP7J3B.js.map} +0 -0
- /package/dist/{chunk-7X4XRP3L.js.map → chunk-HIGHVWYD.js.map} +0 -0
- /package/dist/{domEditingLayers-NU6Z5QWA.js.map → domEditingLayers-AQBDFNUC.js.map} +0 -0
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
useEffect,
|
|
3
|
-
useRef,
|
|
4
|
-
useState,
|
|
5
|
-
type CSSProperties,
|
|
6
|
-
type KeyboardEvent,
|
|
7
|
-
type PointerEvent,
|
|
8
|
-
} from "react";
|
|
1
|
+
import { useEffect, useRef, useState, type CSSProperties, type PointerEvent } from "react";
|
|
9
2
|
import {
|
|
10
3
|
HF_AUDIO_FADE_IN_ATTR,
|
|
11
4
|
HF_AUDIO_FADE_OUT_ATTR,
|
|
@@ -13,13 +6,16 @@ import {
|
|
|
13
6
|
formatFadeSeconds,
|
|
14
7
|
} from "@hyperframes/core/audio-fade";
|
|
15
8
|
import type { TimelineElement } from "../store/playerStore";
|
|
9
|
+
import { Tooltip } from "../../components/ui";
|
|
16
10
|
import { useTimelineEditContextOptional } from "../../contexts/TimelineEditContext";
|
|
17
11
|
import { releasedOutsideWindow } from "./timelinePointerRelease";
|
|
18
12
|
|
|
19
13
|
type FadeEdge = "in" | "out";
|
|
20
14
|
|
|
21
15
|
const HANDLE_SIZE = 10;
|
|
22
|
-
const HANDLE_HIT =
|
|
16
|
+
const HANDLE_HIT = 24;
|
|
17
|
+
const HANDLE_Z_ABOVE_CLIP_CONTENT = 30;
|
|
18
|
+
const SUPPRESS_CLIP_NATIVE_TITLE = "";
|
|
23
19
|
/** Pixels of pointer travel before a press on the handle counts as a drag. */
|
|
24
20
|
const DRAG_THRESHOLD_PX = 2;
|
|
25
21
|
|
|
@@ -147,31 +143,38 @@ export function TimelineClipFades({ el, pps, widthPx, showHandles }: TimelineCli
|
|
|
147
143
|
);
|
|
148
144
|
};
|
|
149
145
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
146
|
+
useEffect(() => {
|
|
147
|
+
if (dragging === null) return;
|
|
148
|
+
const cancelOnWindowEscape = (e: KeyboardEvent) => {
|
|
149
|
+
const g = gesture.current;
|
|
150
|
+
if (e.key !== "Escape" || !g) return;
|
|
151
|
+
e.preventDefault();
|
|
152
|
+
e.stopPropagation();
|
|
153
|
+
gesture.current = null;
|
|
154
|
+
setDragging(null);
|
|
155
|
+
revertGesture(g);
|
|
156
|
+
};
|
|
157
|
+
window.addEventListener("keydown", cancelOnWindowEscape, { capture: true });
|
|
158
|
+
return () => window.removeEventListener("keydown", cancelOnWindowEscape, { capture: true });
|
|
159
|
+
});
|
|
158
160
|
|
|
159
161
|
const showIn = fades.fadeIn > 0;
|
|
160
162
|
const showOut = fades.fadeOut > 0;
|
|
161
163
|
const handlesVisible = showHandles || dragging !== null;
|
|
162
164
|
if (!showIn && !showOut && !handlesVisible) return null;
|
|
163
165
|
|
|
164
|
-
const
|
|
166
|
+
const hitWidth = Math.min(HANDLE_HIT, widthPx / 2);
|
|
167
|
+
const handleStyle = (edge: FadeEdge): CSSProperties => ({
|
|
165
168
|
position: "absolute",
|
|
166
169
|
top: -(HANDLE_HIT - HANDLE_SIZE) / 2 + 1,
|
|
167
|
-
left:
|
|
168
|
-
width:
|
|
170
|
+
left: edge === "in" ? 0 : widthPx - hitWidth,
|
|
171
|
+
width: hitWidth,
|
|
169
172
|
height: HANDLE_HIT,
|
|
170
173
|
display: "flex",
|
|
171
174
|
alignItems: "center",
|
|
172
175
|
justifyContent: "center",
|
|
173
176
|
cursor: "ew-resize",
|
|
174
|
-
zIndex:
|
|
177
|
+
zIndex: HANDLE_Z_ABOVE_CLIP_CONTENT,
|
|
175
178
|
opacity: handlesVisible ? 1 : 0,
|
|
176
179
|
pointerEvents: handlesVisible && canEdit ? "auto" : "none",
|
|
177
180
|
touchAction: "none",
|
|
@@ -191,7 +194,8 @@ export function TimelineClipFades({ el, pps, widthPx, showHandles }: TimelineCli
|
|
|
191
194
|
height: "100%",
|
|
192
195
|
pointerEvents: "none",
|
|
193
196
|
zIndex: 5,
|
|
194
|
-
overflow: "
|
|
197
|
+
overflow: "hidden",
|
|
198
|
+
borderRadius: "inherit",
|
|
195
199
|
}}
|
|
196
200
|
viewBox={`0 0 ${Math.max(widthPx, 1)} 100`}
|
|
197
201
|
preserveAspectRatio="none"
|
|
@@ -245,25 +249,23 @@ export function TimelineClipFades({ el, pps, widthPx, showHandles }: TimelineCli
|
|
|
245
249
|
direction="in"
|
|
246
250
|
value={fades.fadeIn}
|
|
247
251
|
max={Math.max(0, el.duration - fades.fadeOut)}
|
|
248
|
-
style={handleStyle(
|
|
252
|
+
style={handleStyle("in")}
|
|
249
253
|
dragging={dragging === "in"}
|
|
250
254
|
onPointerDown={onHandlePointerDown("in")}
|
|
251
255
|
onPointerMove={onHandlePointerMove}
|
|
252
256
|
onPointerUp={(e) => finish(e, false)}
|
|
253
257
|
onPointerCancel={(e) => finish(e, true)}
|
|
254
|
-
onKeyDown={onHandleKeyDown}
|
|
255
258
|
/>
|
|
256
259
|
<FadeHandle
|
|
257
260
|
direction="out"
|
|
258
261
|
value={fades.fadeOut}
|
|
259
262
|
max={Math.max(0, el.duration - fades.fadeIn)}
|
|
260
|
-
style={handleStyle(
|
|
263
|
+
style={handleStyle("out")}
|
|
261
264
|
dragging={dragging === "out"}
|
|
262
265
|
onPointerDown={onHandlePointerDown("out")}
|
|
263
266
|
onPointerMove={onHandlePointerMove}
|
|
264
267
|
onPointerUp={(e) => finish(e, false)}
|
|
265
268
|
onPointerCancel={(e) => finish(e, true)}
|
|
266
|
-
onKeyDown={onHandleKeyDown}
|
|
267
269
|
/>
|
|
268
270
|
</>
|
|
269
271
|
)}
|
|
@@ -281,7 +283,6 @@ function FadeHandle({
|
|
|
281
283
|
onPointerMove,
|
|
282
284
|
onPointerUp,
|
|
283
285
|
onPointerCancel,
|
|
284
|
-
onKeyDown,
|
|
285
286
|
}: {
|
|
286
287
|
direction: "in" | "out";
|
|
287
288
|
value: number;
|
|
@@ -292,31 +293,31 @@ function FadeHandle({
|
|
|
292
293
|
onPointerMove: (event: PointerEvent<HTMLDivElement>) => void;
|
|
293
294
|
onPointerUp: (event: PointerEvent<HTMLDivElement>) => void;
|
|
294
295
|
onPointerCancel: (event: PointerEvent<HTMLDivElement>) => void;
|
|
295
|
-
onKeyDown: (event: KeyboardEvent<HTMLDivElement>) => void;
|
|
296
296
|
}) {
|
|
297
297
|
const label = direction === "in" ? "Fade in" : "Fade out";
|
|
298
298
|
return (
|
|
299
|
-
<
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
299
|
+
<Tooltip label={`${label} ${formatFadeSeconds(value)} s`}>
|
|
300
|
+
<div
|
|
301
|
+
role="slider"
|
|
302
|
+
tabIndex={-1}
|
|
303
|
+
aria-label={label}
|
|
304
|
+
aria-valuemin={0}
|
|
305
|
+
aria-valuemax={max}
|
|
306
|
+
aria-valuenow={value}
|
|
307
|
+
aria-valuetext={`${formatFadeSeconds(value)}s`}
|
|
308
|
+
data-testid={`clip-fade-handle-${direction}`}
|
|
309
|
+
title={SUPPRESS_CLIP_NATIVE_TITLE}
|
|
310
|
+
style={style}
|
|
311
|
+
onPointerDown={onPointerDown}
|
|
312
|
+
onPointerMove={onPointerMove}
|
|
313
|
+
onPointerUp={onPointerUp}
|
|
314
|
+
onPointerCancel={onPointerCancel}
|
|
315
|
+
onClick={(e) => e.stopPropagation()}
|
|
316
|
+
onDoubleClick={(e) => e.stopPropagation()}
|
|
317
|
+
>
|
|
318
|
+
<FadeDot active={dragging} />
|
|
319
|
+
</div>
|
|
320
|
+
</Tooltip>
|
|
320
321
|
);
|
|
321
322
|
}
|
|
322
323
|
|
|
@@ -326,10 +327,12 @@ function FadeDot({ active }: { active: boolean }) {
|
|
|
326
327
|
aria-hidden="true"
|
|
327
328
|
style={{
|
|
328
329
|
display: "block",
|
|
330
|
+
flexShrink: 0,
|
|
331
|
+
pointerEvents: "none",
|
|
329
332
|
width: HANDLE_SIZE,
|
|
330
333
|
height: HANDLE_SIZE,
|
|
331
334
|
borderRadius: "50%",
|
|
332
|
-
background:
|
|
335
|
+
background: "var(--timeline-fade-dot)",
|
|
333
336
|
boxShadow: "0 0 0 1.5px var(--timeline-fade-dot-ring)",
|
|
334
337
|
transform: active ? "scale(1.15)" : undefined,
|
|
335
338
|
transition: "transform 80ms ease-out",
|
package/src/styles/theme.css
CHANGED
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
--timeline-handle: rgba(255, 255, 255, 0.55);
|
|
115
115
|
/* Audio clip fades: the shaded wedge over the faded gain, and the corner dots. */
|
|
116
116
|
--timeline-fade-shade: rgba(0, 0, 0, 0.38);
|
|
117
|
-
--timeline-fade-dot
|
|
117
|
+
--timeline-fade-dot: #fff;
|
|
118
118
|
--timeline-fade-dot-ring: rgba(0, 0, 0, 0.55);
|
|
119
119
|
--timeline-resize-seam: rgba(255, 255, 255, 0.12);
|
|
120
120
|
--timeline-resize-active: rgba(255, 255, 255, 0.24);
|
|
@@ -18,6 +18,15 @@ describe("project routing utilities", () => {
|
|
|
18
18
|
},
|
|
19
19
|
);
|
|
20
20
|
|
|
21
|
+
it("opens a folder whose name has a colon, encoded once both ways", () => {
|
|
22
|
+
const id = "Customer story: Northwind";
|
|
23
|
+
expect(encodeProjectId(id)).toBe("Customer%20story%3A%20Northwind");
|
|
24
|
+
expect(buildProjectApiPath(id, "/preview")).toBe(
|
|
25
|
+
"/api/projects/Customer%20story%3A%20Northwind/preview",
|
|
26
|
+
);
|
|
27
|
+
expect(parseProjectIdFromHash(buildProjectHash(id))).toBe(id);
|
|
28
|
+
});
|
|
29
|
+
|
|
21
30
|
it("decodes project ids from hash routes before building capture URLs", () => {
|
|
22
31
|
vi.useFakeTimers();
|
|
23
32
|
vi.setSystemTime(new Date("2026-05-01T12:00:00Z"));
|
|
@@ -5,13 +5,15 @@ export interface ProjectHashRoute {
|
|
|
5
5
|
params: URLSearchParams;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
const WINDOWS_DRIVE_PREFIX = /^[a-z]:/i;
|
|
9
|
+
|
|
8
10
|
/** Project names are single path segments, including when received from a hash or server. */
|
|
9
11
|
export function isValidProjectId(value: string): boolean {
|
|
10
12
|
return (
|
|
11
13
|
value.length > 0 &&
|
|
12
14
|
value !== "." &&
|
|
13
15
|
value !== ".." &&
|
|
14
|
-
!
|
|
16
|
+
!WINDOWS_DRIVE_PREFIX.test(value) &&
|
|
15
17
|
!value.includes("/") &&
|
|
16
18
|
!value.includes("\\") &&
|
|
17
19
|
!Array.from(value).some((char) => char.charCodeAt(0) < 32)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/sourceScopedSelectorIndex.ts","../src/utils/projectRouting.ts"],"sourcesContent":["/**\n * Occurrence index for a selector within its source document.\n *\n * The preview flattens multiple composition files into one DOM, so a raw\n * `querySelectorAll` index is not a stable source-file identity. Callers supply\n * their existing source resolver; this helper alone owns occurrence scoping.\n */\n\n/** Every element matching one selector, mapped to the source file it belongs to\n * and its occurrence index within that file. One document query fills it. */\ntype SelectorOccurrences = Map<Element, { scope: string; index: number }>;\n\ninterface SelectorIndexPass {\n doc: Document;\n bySelector: Map<string, SelectorOccurrences>;\n}\n\nlet activePass: SelectorIndexPass | null = null;\n\n/**\n * Share one occurrence index per selector across every\n * `getSourceScopedSelectorIndex` call made synchronously inside `run`.\n *\n * Unshared, each call runs its own whole-document `querySelectorAll(selector)`\n * and resolves the source file of every match. A rebuild that walks n elements\n * sharing a selector therefore does n whole-document queries and n² resolver\n * calls — a composition of repeated cards or tiles is exactly that shape. One\n * index per selector makes the same pass linear.\n *\n * Correctness rests on the pass being ONE synchronous walk of a document that\n * does not change under it: nothing invalidates the index, so `run` must not\n * mutate `doc`, and every call inside it must resolve source files the same way\n * (the resolver may be a fresh closure per call, as long as it answers the\n * same). Outside a pass the index is per-call, exactly as before.\n */\nexport function withSelectorIndexPass<T>(doc: Document, run: () => T): T {\n const previous = activePass;\n activePass = { doc, bySelector: new Map() };\n try {\n return run();\n } finally {\n activePass = previous;\n }\n}\n\nfunction buildOccurrences(\n doc: Document,\n selector: string,\n resolveSourceFile: (candidate: Element) => string | undefined,\n): SelectorOccurrences {\n const occurrences: SelectorOccurrences = new Map();\n const counts = new Map<string, number>();\n for (const candidate of doc.querySelectorAll(selector)) {\n const scope = resolveSourceFile(candidate) ?? \"index.html\";\n const index = counts.get(scope) ?? 0;\n counts.set(scope, index + 1);\n occurrences.set(candidate, { scope, index });\n }\n return occurrences;\n}\n\nexport function getSourceScopedSelectorIndex(\n doc: Document,\n el: Element,\n selector: string | undefined,\n sourceFile: string | undefined,\n resolveSourceFile: (candidate: Element) => string | undefined,\n): number | undefined {\n if (!selector || selector.startsWith(\"#\") || selector.startsWith(\"[data-composition-id=\")) {\n return undefined;\n }\n\n try {\n const pass = activePass?.doc === doc ? activePass : null;\n let occurrences = pass?.bySelector.get(selector);\n if (!occurrences) {\n occurrences = buildOccurrences(doc, selector, resolveSourceFile);\n pass?.bySelector.set(selector, occurrences);\n }\n // An element outside the requested scope is not in that scope's occurrence\n // run at all, which is the `indexOf` miss the filtered form returned before.\n const hit = occurrences.get(el);\n return hit && hit.scope === (sourceFile ?? \"index.html\") ? hit.index : undefined;\n } catch {\n return undefined;\n }\n}\n","const PROJECT_HASH_PREFIX = \"#project/\";\n\nexport interface ProjectHashRoute {\n projectId: string;\n params: URLSearchParams;\n}\n\n/** Project names are single path segments, including when received from a hash or server. */\nexport function isValidProjectId(value: string): boolean {\n return (\n value.length > 0 &&\n value !== \".\" &&\n value !== \"..\" &&\n !value.includes(\":\") &&\n !value.includes(\"/\") &&\n !value.includes(\"\\\\\") &&\n !Array.from(value).some((char) => char.charCodeAt(0) < 32)\n );\n}\n\nfunction decodeHashProjectId(value: string): string {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n}\n\nfunction normalizeHashParams(\n params?: URLSearchParams | Record<string, string | null | undefined>,\n): URLSearchParams {\n if (!params) return new URLSearchParams();\n if (params instanceof URLSearchParams) return params;\n\n const next = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (!key || value == null || value === \"\") continue;\n next.set(key, value);\n }\n return next;\n}\n\nexport function encodeProjectId(projectId: string): string {\n if (!isValidProjectId(projectId)) throw new Error(\"Invalid project ID\");\n return encodeURIComponent(projectId);\n}\n\nexport function buildProjectHash(\n projectId: string,\n params?: URLSearchParams | Record<string, string | null | undefined>,\n): string {\n const search = normalizeHashParams(params).toString();\n return `${PROJECT_HASH_PREFIX}${encodeProjectId(projectId)}${search ? `?${search}` : \"\"}`;\n}\n\nexport function parseProjectHashRoute(hash: string): ProjectHashRoute | null {\n if (!hash.startsWith(PROJECT_HASH_PREFIX)) return null;\n\n const route = hash.slice(PROJECT_HASH_PREFIX.length);\n const queryIndex = route.indexOf(\"?\");\n const encodedProjectId = queryIndex >= 0 ? route.slice(0, queryIndex) : route;\n if (!encodedProjectId || encodedProjectId.includes(\"/\")) return null;\n\n const projectId = decodeHashProjectId(encodedProjectId);\n if (!isValidProjectId(projectId)) return null;\n\n const rawParams = queryIndex >= 0 ? route.slice(queryIndex + 1) : \"\";\n return {\n projectId,\n params: new URLSearchParams(rawParams),\n };\n}\n\nexport function parseProjectIdFromHash(hash: string): string | null {\n return parseProjectHashRoute(hash)?.projectId ?? null;\n}\n\nexport function buildProjectApiPath(projectId: string, suffix = \"\"): string {\n const normalizedSuffix = suffix && !suffix.startsWith(\"/\") ? `/${suffix}` : suffix;\n return `/api/projects/${encodeProjectId(projectId)}${normalizedSuffix}`;\n}\n"],"mappings":"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","names":[]}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|