@hyperframes/studio 0.8.102 → 0.8.104
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-BuRiFtrF.js → hyperframes-player-DAoaajuM.js} +1 -1
- package/dist/assets/{index-BT3sauee.js → index-BDK4rTMb.js} +1 -1
- package/dist/assets/{index-xB5O0hPw.css → index-CQN3M56N.css} +1 -1
- package/dist/assets/{index-C2utDZt-.js → index-De2aO4XM.js} +179 -179
- package/dist/assets/{index-BM35fjFf.js → index-WV9LroAD.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 +183 -170
- package/dist/index.html +2 -2
- package/dist/index.js +550 -389
- 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/DomEditOverlay.tsx +7 -7
- package/src/components/editor/PropertyPanel.tsx +5 -5
- package/src/components/editor/anchoredResizeCommitFeedsOffset.test.ts +46 -6
- package/src/components/editor/domEditOverlayGestures.ts +14 -20
- package/src/components/editor/domEditOverlayStartGesture.test.ts +170 -13
- package/src/components/editor/domEditOverlayStartGesture.ts +17 -27
- package/src/components/editor/manualEdits.test.ts +3 -9
- package/src/components/editor/manualEdits.ts +0 -1
- package/src/components/editor/manualEditsDom.ts +0 -10
- package/src/components/editor/manualEditsDomPatches.test.ts +1 -45
- package/src/components/editor/manualEditsDomPatches.ts +1 -23
- package/src/components/editor/manualEditsSnapshot.ts +2 -0
- package/src/components/editor/manualEditsTypes.ts +2 -0
- package/src/components/editor/manualOffsetDrag.test.ts +57 -0
- package/src/components/editor/manualOffsetDrag.ts +11 -32
- package/src/components/editor/plainTranslate.test.ts +38 -22
- package/src/components/editor/plainTranslate.ts +75 -40
- package/src/components/editor/previewReadOnly.overlay.test.tsx +30 -0
- package/src/components/editor/previewReadOnlyContext.tsx +3 -1
- package/src/components/editor/propertyPanelTransformCommit.test.ts +80 -0
- package/src/components/editor/propertyPanelTransformCommit.ts +11 -6
- package/src/components/editor/propertyPanelTypes.ts +1 -0
- package/src/components/editor/rotateInlineSave.test.ts +111 -0
- package/src/components/editor/rotationDraft.test.ts +111 -0
- package/src/components/editor/rotationDraft.ts +145 -0
- package/src/components/editor/useColorGradingController.test.ts +36 -1
- package/src/components/editor/useColorGradingController.ts +2 -2
- package/src/components/editor/useDomEditNudge.test.tsx +34 -0
- package/src/components/editor/useDomEditNudge.ts +12 -5
- package/src/components/editor/useDomEditOverlayGestures.ts +22 -24
- package/src/hooks/draggedGsapPosition.ts +28 -0
- package/src/hooks/elementOffsetStager.ts +26 -6
- package/src/hooks/gsapRuntimeBridge.ts +5 -3
- package/src/hooks/gsapRuntimeKeyframes.ts +36 -2
- package/src/hooks/plainRotation.test.ts +122 -0
- package/src/hooks/plainRotation.ts +42 -0
- package/src/hooks/useDomEditSession.ts +5 -5
- package/src/hooks/useDomGeometryCommit.test.tsx +75 -0
- package/src/hooks/useDomGeometryCommit.ts +17 -17
- package/src/hooks/useDomGeometryCommits.test.tsx +56 -1
- package/src/hooks/useDomGeometryCommits.ts +28 -36
- package/src/hooks/useGsapAwareEditing.test.tsx +194 -11
- package/src/hooks/useGsapAwareEditing.ts +43 -42
- package/src/hooks/useInlineTextEdit.test.tsx +33 -3
- package/src/hooks/useInlineTextEdit.ts +2 -2
- package/src/hooks/usePreviewInteraction.test.ts +32 -0
- package/src/hooks/usePreviewInteraction.ts +3 -2
- 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
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
buildProjectApiPath,
|
|
3
3
|
getSourceScopedSelectorIndex,
|
|
4
4
|
withSelectorIndexPass
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-DI4LCJHL.js";
|
|
6
6
|
|
|
7
7
|
// src/components/editor/probeSourceElement.ts
|
|
8
8
|
async function probeSourceElement(projectId, sourceFile, target) {
|
|
@@ -1061,4 +1061,4 @@ export {
|
|
|
1061
1061
|
readHfId,
|
|
1062
1062
|
buildDomEditPatchTarget
|
|
1063
1063
|
};
|
|
1064
|
-
//# sourceMappingURL=chunk-
|
|
1064
|
+
//# sourceMappingURL=chunk-46XP7J3B.js.map
|
|
@@ -40,8 +40,9 @@ function getSourceScopedSelectorIndex(doc, el, selector, sourceFile, resolveSour
|
|
|
40
40
|
|
|
41
41
|
// src/utils/projectRouting.ts
|
|
42
42
|
var PROJECT_HASH_PREFIX = "#project/";
|
|
43
|
+
var WINDOWS_DRIVE_PREFIX = /^[a-z]:/i;
|
|
43
44
|
function isValidProjectId(value) {
|
|
44
|
-
return value.length > 0 && value !== "." && value !== ".." && !
|
|
45
|
+
return value.length > 0 && value !== "." && value !== ".." && !WINDOWS_DRIVE_PREFIX.test(value) && !value.includes("/") && !value.includes("\\") && !Array.from(value).some((char) => char.charCodeAt(0) < 32);
|
|
45
46
|
}
|
|
46
47
|
function decodeHashProjectId(value) {
|
|
47
48
|
try {
|
|
@@ -98,4 +99,4 @@ export {
|
|
|
98
99
|
withSelectorIndexPass,
|
|
99
100
|
getSourceScopedSelectorIndex
|
|
100
101
|
};
|
|
101
|
-
//# sourceMappingURL=chunk-
|
|
102
|
+
//# sourceMappingURL=chunk-DI4LCJHL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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\nconst WINDOWS_DRIVE_PREFIX = /^[a-z]:/i;\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 !WINDOWS_DRIVE_PREFIX.test(value) &&\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":[]}
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
buildProjectApiPath,
|
|
6
6
|
getSourceScopedSelectorIndex
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-DI4LCJHL.js";
|
|
8
8
|
|
|
9
9
|
// src/player/lib/time.ts
|
|
10
10
|
var STUDIO_PREVIEW_FPS = 30;
|
|
@@ -1890,4 +1890,4 @@ export {
|
|
|
1890
1890
|
whenPreviewBooted,
|
|
1891
1891
|
Player
|
|
1892
1892
|
};
|
|
1893
|
-
//# sourceMappingURL=chunk-
|
|
1893
|
+
//# sourceMappingURL=chunk-HIGHVWYD.js.map
|
|
@@ -20,8 +20,8 @@ import {
|
|
|
20
20
|
resolveDomEditCapabilities,
|
|
21
21
|
resolveDomEditSelection,
|
|
22
22
|
serializeDomEditTextFields
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import "./chunk-
|
|
23
|
+
} from "./chunk-46XP7J3B.js";
|
|
24
|
+
import "./chunk-DI4LCJHL.js";
|
|
25
25
|
import "./chunk-PZ5AY32C.js";
|
|
26
26
|
export {
|
|
27
27
|
buildDefaultDomEditTextField,
|
|
@@ -46,4 +46,4 @@ export {
|
|
|
46
46
|
resolveDomEditSelection,
|
|
47
47
|
serializeDomEditTextFields
|
|
48
48
|
};
|
|
49
|
-
//# sourceMappingURL=domEditingLayers-
|
|
49
|
+
//# sourceMappingURL=domEditingLayers-AQBDFNUC.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -2285,6 +2285,8 @@ interface PropertyPanelProps {
|
|
|
2285
2285
|
onSetManualOffset: (element: DomEditSelection, next: {
|
|
2286
2286
|
x: number;
|
|
2287
2287
|
y: number;
|
|
2288
|
+
}, route?: {
|
|
2289
|
+
plainTranslate: boolean;
|
|
2288
2290
|
}) => void | Promise<void>;
|
|
2289
2291
|
onSetManualSize: (element: DomEditSelection, next: {
|
|
2290
2292
|
width: number;
|
|
@@ -2922,24 +2924,150 @@ interface InlineTextEditCommit {
|
|
|
2922
2924
|
previousHtml: string;
|
|
2923
2925
|
}
|
|
2924
2926
|
|
|
2927
|
+
/** Where a plain rotate draws its turn, read once at press: the element's own `rotate`, or, when its
|
|
2928
|
+
* transform translates it (often the translate(-50%, -50%) centring), a rotate() right after that
|
|
2929
|
+
* translate, so the box turns in place in screen space. `share` is what the rest already turns. */
|
|
2930
|
+
interface CssRotationTarget {
|
|
2931
|
+
property: "rotate" | "transform";
|
|
2932
|
+
before: string;
|
|
2933
|
+
after: string;
|
|
2934
|
+
share: number;
|
|
2935
|
+
/** -1 when a mirroring `scale` property flips a turn made inside the transform. */
|
|
2936
|
+
sign: number;
|
|
2937
|
+
/** An inline box does not transform, so the turn makes it inline-block. */
|
|
2938
|
+
inline: boolean;
|
|
2939
|
+
}
|
|
2940
|
+
type RotationCommit = {
|
|
2941
|
+
angle: number;
|
|
2942
|
+
plain?: CssRotationTarget;
|
|
2943
|
+
};
|
|
2944
|
+
|
|
2945
|
+
declare const SNAP_THRESHOLD_PX = 6;
|
|
2946
|
+
/** Whether a move of this size has travelled far enough for snapping to apply. */
|
|
2947
|
+
declare function snapEngagedForTravel(dx: number, dy: number): boolean;
|
|
2948
|
+
interface SnapEdge {
|
|
2949
|
+
position: number;
|
|
2950
|
+
source: "grid";
|
|
2951
|
+
id: string;
|
|
2952
|
+
}
|
|
2953
|
+
interface SnapTarget {
|
|
2954
|
+
left: number;
|
|
2955
|
+
top: number;
|
|
2956
|
+
right: number;
|
|
2957
|
+
bottom: number;
|
|
2958
|
+
centerX: number;
|
|
2959
|
+
centerY: number;
|
|
2960
|
+
id: string;
|
|
2961
|
+
}
|
|
2962
|
+
interface SnapGuide {
|
|
2963
|
+
axis: "x" | "y";
|
|
2964
|
+
position: number;
|
|
2965
|
+
/** Extent of the guide line (min of involved elements). */
|
|
2966
|
+
from: number;
|
|
2967
|
+
/** Extent of the guide line (max of involved elements). */
|
|
2968
|
+
to: number;
|
|
2969
|
+
}
|
|
2970
|
+
interface SpacingGuide {
|
|
2971
|
+
axis: "x" | "y";
|
|
2972
|
+
/** Position of the gap (start of gap). */
|
|
2973
|
+
position: number;
|
|
2974
|
+
/** Size of the gap in pixels. */
|
|
2975
|
+
size: number;
|
|
2976
|
+
/** Extent for rendering the indicator. */
|
|
2977
|
+
from: number;
|
|
2978
|
+
/** Extent for rendering the indicator. */
|
|
2979
|
+
to: number;
|
|
2980
|
+
}
|
|
2981
|
+
interface SnapResult {
|
|
2982
|
+
dx: number;
|
|
2983
|
+
dy: number;
|
|
2984
|
+
guides: SnapGuide[];
|
|
2985
|
+
spacingGuides: SpacingGuide[];
|
|
2986
|
+
}
|
|
2987
|
+
interface Rect {
|
|
2988
|
+
left: number;
|
|
2989
|
+
top: number;
|
|
2990
|
+
width: number;
|
|
2991
|
+
height: number;
|
|
2992
|
+
}
|
|
2925
2993
|
/**
|
|
2926
|
-
*
|
|
2927
|
-
*
|
|
2928
|
-
* `runDomEditCommit` resolves on persist failure by design (see its contract
|
|
2929
|
-
* above), so a caller cannot learn whether the write landed by awaiting it — a
|
|
2930
|
-
* failed persist and a successful one are indistinguishable. Capture and apply
|
|
2931
|
-
* bugs still reject. The human path does not need to ask about handled persist
|
|
2932
|
-
* failures, because `onError` already put a toast on screen. A programmatic
|
|
2933
|
-
* caller has no screen, so it has to be told.
|
|
2994
|
+
* Convert overlay rects to snap targets with precomputed edges & centers.
|
|
2934
2995
|
*/
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2996
|
+
declare function extractSnapTargets(entries: Array<{
|
|
2997
|
+
rect: Rect;
|
|
2998
|
+
id: string;
|
|
2999
|
+
}>): SnapTarget[];
|
|
3000
|
+
/**
|
|
3001
|
+
* Create a snap target from the composition/overlay boundary.
|
|
3002
|
+
*/
|
|
3003
|
+
declare function buildCompositionSnapTarget(rect: Rect): SnapTarget;
|
|
3004
|
+
/**
|
|
3005
|
+
* Generate grid-line snap edges.
|
|
3006
|
+
* `gridSpacing` is in composition pixels; `scale` converts to overlay pixels.
|
|
3007
|
+
* X edges are vertical grid lines; Y edges are horizontal grid lines.
|
|
3008
|
+
*/
|
|
3009
|
+
declare function buildGridSnapEdges(compositionRect: Rect, gridSpacing: number, scale: number): {
|
|
3010
|
+
x: SnapEdge[];
|
|
3011
|
+
y: SnapEdge[];
|
|
2942
3012
|
};
|
|
3013
|
+
declare function resolveSnapAdjustment(input: {
|
|
3014
|
+
movingRect: Rect;
|
|
3015
|
+
proposedDx: number;
|
|
3016
|
+
proposedDy: number;
|
|
3017
|
+
targets: SnapTarget[];
|
|
3018
|
+
gridEdges?: {
|
|
3019
|
+
x: SnapEdge[];
|
|
3020
|
+
y: SnapEdge[];
|
|
3021
|
+
};
|
|
3022
|
+
threshold: number;
|
|
3023
|
+
disabled: boolean;
|
|
3024
|
+
/** Set when the gesture has not travelled far enough for snapping yet. */
|
|
3025
|
+
disabledForTravel?: boolean;
|
|
3026
|
+
lockedAxis?: "x" | "y";
|
|
3027
|
+
}): SnapResult;
|
|
3028
|
+
/**
|
|
3029
|
+
* Full-length guide line spanning the composition: a vertical line (axis "x")
|
|
3030
|
+
* runs the composition's height at the snapped x position; a horizontal line
|
|
3031
|
+
* (axis "y") runs the composition's width. `composition` is the composition
|
|
3032
|
+
* rect in overlay space — guide positions are already overlay-space, so the
|
|
3033
|
+
* line must be offset by the composition's left/top (the canvas is usually
|
|
3034
|
+
* letterboxed inside the overlay).
|
|
3035
|
+
*/
|
|
3036
|
+
declare function resolveGuideLineRect(guide: SnapGuide, composition: Rect): Rect;
|
|
3037
|
+
declare function resolveEquidistanceGuides(input: {
|
|
3038
|
+
movingRect: Rect;
|
|
3039
|
+
targets: SnapTarget[];
|
|
3040
|
+
threshold: number;
|
|
3041
|
+
}): SpacingGuide[];
|
|
3042
|
+
|
|
3043
|
+
interface SnapContext {
|
|
3044
|
+
targets: SnapTarget[];
|
|
3045
|
+
compositionTarget: SnapTarget | null;
|
|
3046
|
+
gridEdges: {
|
|
3047
|
+
x: SnapEdge[];
|
|
3048
|
+
y: SnapEdge[];
|
|
3049
|
+
} | null;
|
|
3050
|
+
snapEnabled: boolean;
|
|
3051
|
+
}
|
|
3052
|
+
declare function collectSnapContext(input: {
|
|
3053
|
+
overlayEl: HTMLDivElement;
|
|
3054
|
+
iframe: HTMLIFrameElement;
|
|
3055
|
+
excludeElements: Set<HTMLElement>;
|
|
3056
|
+
}): SnapContext;
|
|
3057
|
+
|
|
3058
|
+
interface SnapGuidesState {
|
|
3059
|
+
guides: SnapGuide[];
|
|
3060
|
+
spacingGuides: SpacingGuide[];
|
|
3061
|
+
}
|
|
3062
|
+
interface SnapGuideOverlayProps {
|
|
3063
|
+
snapGuidesRef: RefObject<SnapGuidesState | null>;
|
|
3064
|
+
/** Composition rect in overlay space — guide lines span exactly this rect. */
|
|
3065
|
+
compositionLeft: number;
|
|
3066
|
+
compositionTop: number;
|
|
3067
|
+
compositionWidth: number;
|
|
3068
|
+
compositionHeight: number;
|
|
3069
|
+
}
|
|
3070
|
+
declare const SnapGuideOverlay: react.NamedExoticComponent<SnapGuideOverlayProps>;
|
|
2943
3071
|
|
|
2944
3072
|
interface ApplyDomSelectionOptions {
|
|
2945
3073
|
revealPanel?: boolean;
|
|
@@ -2974,6 +3102,38 @@ interface PreviewMouseDownOptions {
|
|
|
2974
3102
|
hoverSelection?: DomEditSelection | null;
|
|
2975
3103
|
}
|
|
2976
3104
|
|
|
3105
|
+
interface MoveCommitOptions {
|
|
3106
|
+
altKey?: boolean;
|
|
3107
|
+
plainTranslate?: boolean;
|
|
3108
|
+
}
|
|
3109
|
+
interface DomEditGroupPathOffsetCommit {
|
|
3110
|
+
selection: DomEditSelection;
|
|
3111
|
+
next: {
|
|
3112
|
+
x: number;
|
|
3113
|
+
y: number;
|
|
3114
|
+
};
|
|
3115
|
+
plainTranslate?: boolean;
|
|
3116
|
+
}
|
|
3117
|
+
|
|
3118
|
+
/**
|
|
3119
|
+
* Why a DOM edit commit did not change the file.
|
|
3120
|
+
*
|
|
3121
|
+
* `runDomEditCommit` resolves on persist failure by design (see its contract
|
|
3122
|
+
* above), so a caller cannot learn whether the write landed by awaiting it — a
|
|
3123
|
+
* failed persist and a successful one are indistinguishable. Capture and apply
|
|
3124
|
+
* bugs still reject. The human path does not need to ask about handled persist
|
|
3125
|
+
* failures, because `onError` already put a toast on screen. A programmatic
|
|
3126
|
+
* caller has no screen, so it has to be told.
|
|
3127
|
+
*/
|
|
3128
|
+
type DomEditCommitDeclineReason = "no-project" | "no-selection" | "geometry-property" | "styles-not-editable" | "not-text-editable" | "preview-stale" | "persist-failed";
|
|
3129
|
+
type DomEditCommitOutcome = {
|
|
3130
|
+
ok: true;
|
|
3131
|
+
persistence?: DomEditPersistOutcome;
|
|
3132
|
+
} | {
|
|
3133
|
+
ok: false;
|
|
3134
|
+
reason: DomEditCommitDeclineReason;
|
|
3135
|
+
};
|
|
3136
|
+
|
|
2977
3137
|
/**
|
|
2978
3138
|
* Which elements a delete acts on.
|
|
2979
3139
|
*
|
|
@@ -3056,9 +3216,7 @@ declare function useDomEditSession({ projectId, activeCompPath, compIdToSrc, cap
|
|
|
3056
3216
|
handleDomPathOffsetCommit: (selection: DomEditSelection, next: {
|
|
3057
3217
|
x: number;
|
|
3058
3218
|
y: number;
|
|
3059
|
-
}, modifiers?:
|
|
3060
|
-
altKey?: boolean;
|
|
3061
|
-
}) => Promise<void>;
|
|
3219
|
+
}, modifiers?: MoveCommitOptions) => Promise<void>;
|
|
3062
3220
|
handleDomGroupPathOffsetCommit: (updates: DomEditGroupPathOffsetCommit[]) => Promise<void>;
|
|
3063
3221
|
handleDomZIndexReorderCommit: (entries: Array<{
|
|
3064
3222
|
element: HTMLElement;
|
|
@@ -3076,9 +3234,7 @@ declare function useDomEditSession({ projectId, activeCompPath, compIdToSrc, cap
|
|
|
3076
3234
|
x: number;
|
|
3077
3235
|
y: number;
|
|
3078
3236
|
}, restore?: () => void) => Promise<void>;
|
|
3079
|
-
handleDomRotationCommit: (selection: DomEditSelection, next:
|
|
3080
|
-
angle: number;
|
|
3081
|
-
}) => Promise<void>;
|
|
3237
|
+
handleDomRotationCommit: (selection: DomEditSelection, next: RotationCommit) => Promise<void>;
|
|
3082
3238
|
handleDomManualEditsReset: (selection: DomEditSelection) => Promise<void>;
|
|
3083
3239
|
handleDomTextCommit: (value: string, fieldKey?: string) => Promise<DomEditCommitOutcome>;
|
|
3084
3240
|
handleDomTextCommitForSelection: (selection: DomEditSelection, value: string, fieldKey?: string) => Promise<DomEditCommitOutcome>;
|
|
@@ -3089,7 +3245,7 @@ declare function useDomEditSession({ projectId, activeCompPath, compIdToSrc, cap
|
|
|
3089
3245
|
getGsapAnimationsForSelection: (selection: DomEditSelection) => _hyperframes_parsers.GsapAnimation[] | Promise<_hyperframes_parsers.GsapAnimation[]>;
|
|
3090
3246
|
handleAskAgent: () => void;
|
|
3091
3247
|
handleAgentModalSubmit: (userInstruction: string) => Promise<void>;
|
|
3092
|
-
handleBlockedDomMove: (selection: DomEditSelection) => void;
|
|
3248
|
+
handleBlockedDomMove: (selection: DomEditSelection, reason?: string) => void;
|
|
3093
3249
|
handleDomManualDragStart: () => void;
|
|
3094
3250
|
handleDomEditElementDelete: (selection: DomEditSelection, options?: {
|
|
3095
3251
|
expandGroup?: boolean;
|
|
@@ -3244,141 +3400,6 @@ declare function PreviewReadOnlyProvider({ readOnly, reason, children, }: {
|
|
|
3244
3400
|
children: ReactNode;
|
|
3245
3401
|
}): react_jsx_runtime.JSX.Element;
|
|
3246
3402
|
|
|
3247
|
-
declare const SNAP_THRESHOLD_PX = 6;
|
|
3248
|
-
/** Whether a move of this size has travelled far enough for snapping to apply. */
|
|
3249
|
-
declare function snapEngagedForTravel(dx: number, dy: number): boolean;
|
|
3250
|
-
interface SnapEdge {
|
|
3251
|
-
position: number;
|
|
3252
|
-
source: "grid";
|
|
3253
|
-
id: string;
|
|
3254
|
-
}
|
|
3255
|
-
interface SnapTarget {
|
|
3256
|
-
left: number;
|
|
3257
|
-
top: number;
|
|
3258
|
-
right: number;
|
|
3259
|
-
bottom: number;
|
|
3260
|
-
centerX: number;
|
|
3261
|
-
centerY: number;
|
|
3262
|
-
id: string;
|
|
3263
|
-
}
|
|
3264
|
-
interface SnapGuide {
|
|
3265
|
-
axis: "x" | "y";
|
|
3266
|
-
position: number;
|
|
3267
|
-
/** Extent of the guide line (min of involved elements). */
|
|
3268
|
-
from: number;
|
|
3269
|
-
/** Extent of the guide line (max of involved elements). */
|
|
3270
|
-
to: number;
|
|
3271
|
-
}
|
|
3272
|
-
interface SpacingGuide {
|
|
3273
|
-
axis: "x" | "y";
|
|
3274
|
-
/** Position of the gap (start of gap). */
|
|
3275
|
-
position: number;
|
|
3276
|
-
/** Size of the gap in pixels. */
|
|
3277
|
-
size: number;
|
|
3278
|
-
/** Extent for rendering the indicator. */
|
|
3279
|
-
from: number;
|
|
3280
|
-
/** Extent for rendering the indicator. */
|
|
3281
|
-
to: number;
|
|
3282
|
-
}
|
|
3283
|
-
interface SnapResult {
|
|
3284
|
-
dx: number;
|
|
3285
|
-
dy: number;
|
|
3286
|
-
guides: SnapGuide[];
|
|
3287
|
-
spacingGuides: SpacingGuide[];
|
|
3288
|
-
}
|
|
3289
|
-
interface Rect {
|
|
3290
|
-
left: number;
|
|
3291
|
-
top: number;
|
|
3292
|
-
width: number;
|
|
3293
|
-
height: number;
|
|
3294
|
-
}
|
|
3295
|
-
/**
|
|
3296
|
-
* Convert overlay rects to snap targets with precomputed edges & centers.
|
|
3297
|
-
*/
|
|
3298
|
-
declare function extractSnapTargets(entries: Array<{
|
|
3299
|
-
rect: Rect;
|
|
3300
|
-
id: string;
|
|
3301
|
-
}>): SnapTarget[];
|
|
3302
|
-
/**
|
|
3303
|
-
* Create a snap target from the composition/overlay boundary.
|
|
3304
|
-
*/
|
|
3305
|
-
declare function buildCompositionSnapTarget(rect: Rect): SnapTarget;
|
|
3306
|
-
/**
|
|
3307
|
-
* Generate grid-line snap edges.
|
|
3308
|
-
* `gridSpacing` is in composition pixels; `scale` converts to overlay pixels.
|
|
3309
|
-
* X edges are vertical grid lines; Y edges are horizontal grid lines.
|
|
3310
|
-
*/
|
|
3311
|
-
declare function buildGridSnapEdges(compositionRect: Rect, gridSpacing: number, scale: number): {
|
|
3312
|
-
x: SnapEdge[];
|
|
3313
|
-
y: SnapEdge[];
|
|
3314
|
-
};
|
|
3315
|
-
declare function resolveSnapAdjustment(input: {
|
|
3316
|
-
movingRect: Rect;
|
|
3317
|
-
proposedDx: number;
|
|
3318
|
-
proposedDy: number;
|
|
3319
|
-
targets: SnapTarget[];
|
|
3320
|
-
gridEdges?: {
|
|
3321
|
-
x: SnapEdge[];
|
|
3322
|
-
y: SnapEdge[];
|
|
3323
|
-
};
|
|
3324
|
-
threshold: number;
|
|
3325
|
-
disabled: boolean;
|
|
3326
|
-
/** Set when the gesture has not travelled far enough for snapping yet. */
|
|
3327
|
-
disabledForTravel?: boolean;
|
|
3328
|
-
lockedAxis?: "x" | "y";
|
|
3329
|
-
}): SnapResult;
|
|
3330
|
-
/**
|
|
3331
|
-
* Full-length guide line spanning the composition: a vertical line (axis "x")
|
|
3332
|
-
* runs the composition's height at the snapped x position; a horizontal line
|
|
3333
|
-
* (axis "y") runs the composition's width. `composition` is the composition
|
|
3334
|
-
* rect in overlay space — guide positions are already overlay-space, so the
|
|
3335
|
-
* line must be offset by the composition's left/top (the canvas is usually
|
|
3336
|
-
* letterboxed inside the overlay).
|
|
3337
|
-
*/
|
|
3338
|
-
declare function resolveGuideLineRect(guide: SnapGuide, composition: Rect): Rect;
|
|
3339
|
-
declare function resolveEquidistanceGuides(input: {
|
|
3340
|
-
movingRect: Rect;
|
|
3341
|
-
targets: SnapTarget[];
|
|
3342
|
-
threshold: number;
|
|
3343
|
-
}): SpacingGuide[];
|
|
3344
|
-
|
|
3345
|
-
interface SnapContext {
|
|
3346
|
-
targets: SnapTarget[];
|
|
3347
|
-
compositionTarget: SnapTarget | null;
|
|
3348
|
-
gridEdges: {
|
|
3349
|
-
x: SnapEdge[];
|
|
3350
|
-
y: SnapEdge[];
|
|
3351
|
-
} | null;
|
|
3352
|
-
snapEnabled: boolean;
|
|
3353
|
-
}
|
|
3354
|
-
declare function collectSnapContext(input: {
|
|
3355
|
-
overlayEl: HTMLDivElement;
|
|
3356
|
-
iframe: HTMLIFrameElement;
|
|
3357
|
-
excludeElements: Set<HTMLElement>;
|
|
3358
|
-
}): SnapContext;
|
|
3359
|
-
|
|
3360
|
-
interface SnapGuidesState {
|
|
3361
|
-
guides: SnapGuide[];
|
|
3362
|
-
spacingGuides: SpacingGuide[];
|
|
3363
|
-
}
|
|
3364
|
-
interface SnapGuideOverlayProps {
|
|
3365
|
-
snapGuidesRef: RefObject<SnapGuidesState | null>;
|
|
3366
|
-
/** Composition rect in overlay space — guide lines span exactly this rect. */
|
|
3367
|
-
compositionLeft: number;
|
|
3368
|
-
compositionTop: number;
|
|
3369
|
-
compositionWidth: number;
|
|
3370
|
-
compositionHeight: number;
|
|
3371
|
-
}
|
|
3372
|
-
declare const SnapGuideOverlay: react.NamedExoticComponent<SnapGuideOverlayProps>;
|
|
3373
|
-
|
|
3374
|
-
interface DomEditGroupPathOffsetCommit {
|
|
3375
|
-
selection: DomEditSelection;
|
|
3376
|
-
next: {
|
|
3377
|
-
x: number;
|
|
3378
|
-
y: number;
|
|
3379
|
-
};
|
|
3380
|
-
}
|
|
3381
|
-
|
|
3382
3403
|
/**
|
|
3383
3404
|
* Pure z-order helpers for the canvas right-click context menu.
|
|
3384
3405
|
*
|
|
@@ -3450,14 +3471,12 @@ interface DomEditOverlayProps {
|
|
|
3450
3471
|
revealPanel?: boolean;
|
|
3451
3472
|
additive?: boolean;
|
|
3452
3473
|
}) => void;
|
|
3453
|
-
onBlockedMove: (selection: DomEditSelection) => void;
|
|
3474
|
+
onBlockedMove: (selection: DomEditSelection, reason?: string) => void;
|
|
3454
3475
|
onManualDragStart?: () => void;
|
|
3455
3476
|
onPathOffsetCommit: (selection: DomEditSelection, next: {
|
|
3456
3477
|
x: number;
|
|
3457
3478
|
y: number;
|
|
3458
|
-
}, modifiers?:
|
|
3459
|
-
altKey?: boolean;
|
|
3460
|
-
}) => Promise<unknown> | void;
|
|
3479
|
+
}, modifiers?: MoveCommitOptions) => Promise<unknown> | void;
|
|
3461
3480
|
onGroupPathOffsetCommit: (updates: DomEditGroupPathOffsetCommit[]) => Promise<unknown> | void;
|
|
3462
3481
|
onBoxSizeCommit: (selection: DomEditSelection, next: {
|
|
3463
3482
|
width: number;
|
|
@@ -3466,9 +3485,7 @@ interface DomEditOverlayProps {
|
|
|
3466
3485
|
x: number;
|
|
3467
3486
|
y: number;
|
|
3468
3487
|
}, restore?: () => void) => Promise<unknown> | void;
|
|
3469
|
-
onRotationCommit: (selection: DomEditSelection, next:
|
|
3470
|
-
angle: number;
|
|
3471
|
-
}) => Promise<unknown> | void;
|
|
3488
|
+
onRotationCommit: (selection: DomEditSelection, next: RotationCommit) => Promise<unknown> | void;
|
|
3472
3489
|
onStyleCommit?: (property: string, value: string) => Promise<unknown> | void;
|
|
3473
3490
|
recordingState?: GestureRecordingState;
|
|
3474
3491
|
onToggleRecording?: () => void;
|
|
@@ -3552,9 +3569,7 @@ interface DomGeometryCommits {
|
|
|
3552
3569
|
commitPathOffset: (selection: DomEditSelection, next: {
|
|
3553
3570
|
x: number;
|
|
3554
3571
|
y: number;
|
|
3555
|
-
}, modifiers?:
|
|
3556
|
-
altKey?: boolean;
|
|
3557
|
-
}) => Promise<DomEditCommitOutcome>;
|
|
3572
|
+
}, modifiers?: MoveCommitOptions) => Promise<DomEditCommitOutcome>;
|
|
3558
3573
|
commitGroupPathOffset: (updates: DomEditGroupPathOffsetCommit[]) => Promise<DomEditCommitOutcome>;
|
|
3559
3574
|
commitBoxSize: (selection: DomEditSelection, next: {
|
|
3560
3575
|
width: number;
|
|
@@ -3563,9 +3578,7 @@ interface DomGeometryCommits {
|
|
|
3563
3578
|
x: number;
|
|
3564
3579
|
y: number;
|
|
3565
3580
|
}, restore?: () => void) => Promise<DomEditCommitOutcome>;
|
|
3566
|
-
commitRotation: (selection: DomEditSelection, next:
|
|
3567
|
-
angle: number;
|
|
3568
|
-
}) => Promise<DomEditCommitOutcome>;
|
|
3581
|
+
commitRotation: (selection: DomEditSelection, next: RotationCommit) => Promise<DomEditCommitOutcome>;
|
|
3569
3582
|
waitForPendingSaves: () => Promise<void>;
|
|
3570
3583
|
}
|
|
3571
3584
|
/**
|
package/dist/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
|
6
6
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
7
7
|
<title>HyperFrames Studio</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-De2aO4XM.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CQN3M56N.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div data-hf-id="hf-aph5" id="root"></div>
|