hyperframes 0.7.5 → 0.7.7
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/cli.js +612 -100
- package/dist/hyperframe-runtime.js +24 -24
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +24 -24
- package/dist/hyperframes-player.global.js +426 -0
- package/dist/hyperframes-slideshow.global.js +149 -0
- package/dist/skills/hyperframes/SKILL.md +9 -1
- package/dist/studio/assets/{index-BwFzbjZQ.js → index-B2YXvFxf.js} +1 -1
- package/dist/studio/assets/index-BSkUuN8g.css +1 -0
- package/dist/studio/assets/index-BeRh2hMe.js +375 -0
- package/dist/studio/assets/{index-C5NAfiPa.js → index-BoASKOeE.js} +1 -1
- package/dist/studio/chunk-KZXYQYIU.js +876 -0
- package/dist/studio/chunk-KZXYQYIU.js.map +1 -0
- package/dist/studio/domEditingLayers-SSXQZHHQ.js +41 -0
- package/dist/studio/domEditingLayers-SSXQZHHQ.js.map +1 -0
- package/dist/studio/index.d.ts +2 -0
- package/dist/studio/index.html +2 -2
- package/dist/studio/index.js +3326 -2939
- package/dist/studio/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/studio/assets/index-D_JGXmfx.js +0 -374
- package/dist/studio/assets/index-DzWIinxk.css +0 -1
|
@@ -0,0 +1,876 @@
|
|
|
1
|
+
// src/components/editor/domEditingTypes.ts
|
|
2
|
+
var CURATED_STYLE_PROPERTIES = [
|
|
3
|
+
"position",
|
|
4
|
+
"display",
|
|
5
|
+
"top",
|
|
6
|
+
"left",
|
|
7
|
+
"right",
|
|
8
|
+
"bottom",
|
|
9
|
+
"inset",
|
|
10
|
+
"width",
|
|
11
|
+
"height",
|
|
12
|
+
"gap",
|
|
13
|
+
"justify-content",
|
|
14
|
+
"align-items",
|
|
15
|
+
"flex-direction",
|
|
16
|
+
"font-size",
|
|
17
|
+
"font-style",
|
|
18
|
+
"font-weight",
|
|
19
|
+
"font-family",
|
|
20
|
+
"line-height",
|
|
21
|
+
"letter-spacing",
|
|
22
|
+
"text-align",
|
|
23
|
+
"text-transform",
|
|
24
|
+
"color",
|
|
25
|
+
"background-color",
|
|
26
|
+
"background-image",
|
|
27
|
+
"opacity",
|
|
28
|
+
"mix-blend-mode",
|
|
29
|
+
"border-radius",
|
|
30
|
+
"border-width",
|
|
31
|
+
"border-style",
|
|
32
|
+
"border-color",
|
|
33
|
+
"border-top-width",
|
|
34
|
+
"border-top-style",
|
|
35
|
+
"border-top-color",
|
|
36
|
+
"outline-color",
|
|
37
|
+
"overflow",
|
|
38
|
+
"clip-path",
|
|
39
|
+
"box-shadow",
|
|
40
|
+
"filter",
|
|
41
|
+
"backdrop-filter",
|
|
42
|
+
"z-index",
|
|
43
|
+
"transform",
|
|
44
|
+
"object-fit",
|
|
45
|
+
"object-position"
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
// src/components/editor/domEditingDom.ts
|
|
49
|
+
function isHtmlElement(value) {
|
|
50
|
+
return typeof value === "object" && value !== null && "nodeType" in value && typeof value.nodeType === "number" && value.nodeType === 1;
|
|
51
|
+
}
|
|
52
|
+
function parsePx(value) {
|
|
53
|
+
if (!value) return null;
|
|
54
|
+
const trimmed = value.trim();
|
|
55
|
+
if (!trimmed.endsWith("px")) return null;
|
|
56
|
+
const parsed = parseFloat(trimmed);
|
|
57
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
58
|
+
}
|
|
59
|
+
function isIdentityTransform(value) {
|
|
60
|
+
const transform = (value ?? "none").trim();
|
|
61
|
+
if (!transform || transform === "none") return true;
|
|
62
|
+
const matrix = transform.match(/^matrix\(([^)]+)\)$/i);
|
|
63
|
+
if (matrix) {
|
|
64
|
+
const values2 = matrix[1].split(",").map((part) => Number.parseFloat(part.trim()));
|
|
65
|
+
if (values2.length !== 6 || values2.some((part) => !Number.isFinite(part))) return false;
|
|
66
|
+
return Math.abs(values2[0] - 1) < 1e-4 && Math.abs(values2[1]) < 1e-4 && Math.abs(values2[2]) < 1e-4 && Math.abs(values2[3] - 1) < 1e-4 && Math.abs(values2[4]) < 1e-4 && Math.abs(values2[5]) < 1e-4;
|
|
67
|
+
}
|
|
68
|
+
const matrix3d = transform.match(/^matrix3d\(([^)]+)\)$/i);
|
|
69
|
+
if (!matrix3d) return false;
|
|
70
|
+
const values = matrix3d[1].split(",").map((part) => Number.parseFloat(part.trim()));
|
|
71
|
+
if (values.length !== 16 || values.some((part) => !Number.isFinite(part))) return false;
|
|
72
|
+
const identity = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
|
73
|
+
return values.every((part, index) => Math.abs(part - identity[index]) < 1e-4);
|
|
74
|
+
}
|
|
75
|
+
function isTextBearingTag(tagName) {
|
|
76
|
+
return ["div", "span", "p", "strong", "h1", "h2", "h3", "h4", "h5", "h6"].includes(tagName);
|
|
77
|
+
}
|
|
78
|
+
var COLOR_GRADING_SOURCE_HIDDEN_ATTR = "data-hf-color-grading-source-hidden";
|
|
79
|
+
function isElementVisibleThroughAncestors(el) {
|
|
80
|
+
const win = el.ownerDocument.defaultView;
|
|
81
|
+
if (!win) return true;
|
|
82
|
+
let current = el;
|
|
83
|
+
while (current) {
|
|
84
|
+
const computed = win.getComputedStyle(current);
|
|
85
|
+
if (computed.display === "none" || computed.visibility === "hidden") return false;
|
|
86
|
+
const opacity = Number.parseFloat(computed.opacity);
|
|
87
|
+
if (Number.isFinite(opacity) && opacity <= 0.01 && !current.hasAttribute(COLOR_GRADING_SOURCE_HIDDEN_ATTR))
|
|
88
|
+
return false;
|
|
89
|
+
current = current.parentElement;
|
|
90
|
+
}
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
function getCuratedComputedStyles(el) {
|
|
94
|
+
const styles = {};
|
|
95
|
+
const computed = el.ownerDocument.defaultView?.getComputedStyle(el);
|
|
96
|
+
if (!computed) return styles;
|
|
97
|
+
for (const prop of CURATED_STYLE_PROPERTIES) {
|
|
98
|
+
const value = computed.getPropertyValue(prop);
|
|
99
|
+
if (value) styles[prop] = value;
|
|
100
|
+
}
|
|
101
|
+
return styles;
|
|
102
|
+
}
|
|
103
|
+
function getInlineStyles(el) {
|
|
104
|
+
const styles = {};
|
|
105
|
+
for (const property of CURATED_STYLE_PROPERTIES) {
|
|
106
|
+
const value = el.style.getPropertyValue(property);
|
|
107
|
+
if (value) styles[property] = value;
|
|
108
|
+
}
|
|
109
|
+
return styles;
|
|
110
|
+
}
|
|
111
|
+
function getDataAttributes(el) {
|
|
112
|
+
const attrs = {};
|
|
113
|
+
for (const attr of el.attributes) {
|
|
114
|
+
if (attr.name.startsWith("data-")) {
|
|
115
|
+
attrs[attr.name.slice(5)] = attr.value;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return attrs;
|
|
119
|
+
}
|
|
120
|
+
function findClosestByAttribute(el, attributeNames) {
|
|
121
|
+
let current = el;
|
|
122
|
+
while (current) {
|
|
123
|
+
const candidate = current;
|
|
124
|
+
if (attributeNames.some((attribute) => candidate.hasAttribute(attribute))) {
|
|
125
|
+
return candidate;
|
|
126
|
+
}
|
|
127
|
+
current = current.parentElement;
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
var compositionSourceMap = /* @__PURE__ */ new Map();
|
|
132
|
+
function setCompositionSourceMap(map) {
|
|
133
|
+
compositionSourceMap = map;
|
|
134
|
+
}
|
|
135
|
+
function sourceFromCompositionId(ownerRoot) {
|
|
136
|
+
if (!ownerRoot || compositionSourceMap.size === 0) return void 0;
|
|
137
|
+
const authored = ownerRoot.getAttribute("data-hf-original-composition-id");
|
|
138
|
+
const current = ownerRoot.getAttribute("data-composition-id");
|
|
139
|
+
return (authored ? compositionSourceMap.get(authored) : void 0) ?? (current ? compositionSourceMap.get(current) : void 0);
|
|
140
|
+
}
|
|
141
|
+
function getSourceFileForElement(el, activeCompositionPath) {
|
|
142
|
+
const sourceHost = findClosestByAttribute(el, ["data-composition-file", "data-composition-src"]);
|
|
143
|
+
const ownerRoot = findClosestByAttribute(el, ["data-composition-id"]);
|
|
144
|
+
const sourceFile = sourceHost?.getAttribute("data-composition-file") ?? sourceHost?.getAttribute("data-composition-src") ?? ownerRoot?.getAttribute("data-composition-file") ?? ownerRoot?.getAttribute("data-composition-src") ?? sourceFromCompositionId(ownerRoot) ?? activeCompositionPath ?? "index.html";
|
|
145
|
+
return {
|
|
146
|
+
sourceFile,
|
|
147
|
+
compositionPath: sourceFile
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function normalizeTimelineCompositionSource(value) {
|
|
151
|
+
const trimmed = value?.trim();
|
|
152
|
+
if (!trimmed) return void 0;
|
|
153
|
+
let pathname = trimmed;
|
|
154
|
+
try {
|
|
155
|
+
pathname = new URL(trimmed, "http://studio.local").pathname;
|
|
156
|
+
} catch {
|
|
157
|
+
pathname = trimmed;
|
|
158
|
+
}
|
|
159
|
+
for (const marker of ["/preview/comp/", "/preview/"]) {
|
|
160
|
+
const markerIndex = pathname.indexOf(marker);
|
|
161
|
+
if (markerIndex < 0) continue;
|
|
162
|
+
const sourcePath = pathname.slice(markerIndex + marker.length).replace(/^\/+/, "");
|
|
163
|
+
return sourcePath || trimmed;
|
|
164
|
+
}
|
|
165
|
+
return trimmed;
|
|
166
|
+
}
|
|
167
|
+
function escapeCssIdentifier(value) {
|
|
168
|
+
const css = globalThis.CSS;
|
|
169
|
+
if (typeof css?.escape === "function") return css.escape(value);
|
|
170
|
+
if (value === "-") return "\\-";
|
|
171
|
+
let escaped = "";
|
|
172
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
173
|
+
const char = value[index] ?? "";
|
|
174
|
+
const code = char.charCodeAt(0);
|
|
175
|
+
if (code === 0) {
|
|
176
|
+
escaped += "\uFFFD";
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
const isDigit = code >= 48 && code <= 57;
|
|
180
|
+
const isUpperAlpha = code >= 65 && code <= 90;
|
|
181
|
+
const isLowerAlpha = code >= 97 && code <= 122;
|
|
182
|
+
const isControl = code >= 1 && code <= 31 || code === 127;
|
|
183
|
+
const isLeadingDigit = index === 0 && isDigit;
|
|
184
|
+
const isSecondDigitAfterDash = index === 1 && value.startsWith("-") && isDigit;
|
|
185
|
+
if (isControl || isLeadingDigit || isSecondDigitAfterDash) {
|
|
186
|
+
escaped += `\\${code.toString(16)} `;
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (isUpperAlpha || isLowerAlpha || isDigit || char === "-" || char === "_" || code >= 128) {
|
|
190
|
+
escaped += char;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
escaped += `\\${char}`;
|
|
194
|
+
}
|
|
195
|
+
return escaped;
|
|
196
|
+
}
|
|
197
|
+
function escapeCssString(value) {
|
|
198
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\a ").replace(/\r/g, "\\d ").replace(/\f/g, "\\c ");
|
|
199
|
+
}
|
|
200
|
+
function querySelectorAllSafely(doc, selector) {
|
|
201
|
+
try {
|
|
202
|
+
return Array.from(doc.querySelectorAll(selector));
|
|
203
|
+
} catch {
|
|
204
|
+
return [];
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function humanizeIdentifier(value) {
|
|
208
|
+
return value.replace(/\.html$/i, "").replace(/^compositions\//i, "").split("/").at(-1)?.replace(/[-_]+/g, " ").replace(/\b\w/g, (char) => char.toUpperCase()) ?? value;
|
|
209
|
+
}
|
|
210
|
+
function buildStableSelector(el) {
|
|
211
|
+
if (el.id) return `#${escapeCssIdentifier(el.id)}`;
|
|
212
|
+
const compositionId = el.getAttribute("data-composition-id");
|
|
213
|
+
if (compositionId) return `[data-composition-id="${escapeCssString(compositionId)}"]`;
|
|
214
|
+
return getPreferredClassSelector(el);
|
|
215
|
+
}
|
|
216
|
+
function getPreferredClassSelector(el) {
|
|
217
|
+
const classes = Array.from(el.classList).map((value) => value.trim()).filter(Boolean);
|
|
218
|
+
if (classes.length === 0) return void 0;
|
|
219
|
+
const preferred = classes.find((value) => value !== "clip" && !value.startsWith("__hf-")) ?? classes[0];
|
|
220
|
+
return preferred ? `.${escapeCssIdentifier(preferred)}` : void 0;
|
|
221
|
+
}
|
|
222
|
+
function getSelectorIndex(doc, el, selector, sourceFile, activeCompositionPath) {
|
|
223
|
+
if (!selector?.startsWith(".")) return void 0;
|
|
224
|
+
const candidates = querySelectorAllSafely(doc, selector).filter(
|
|
225
|
+
(candidate) => isHtmlElement(candidate) && getSourceFileForElement(candidate, activeCompositionPath).sourceFile === sourceFile
|
|
226
|
+
);
|
|
227
|
+
const index = candidates.indexOf(el);
|
|
228
|
+
return index >= 0 ? index : void 0;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// src/components/editor/domEditingElement.ts
|
|
232
|
+
function isElementComputedVisible(el) {
|
|
233
|
+
return isElementVisibleThroughAncestors(el);
|
|
234
|
+
}
|
|
235
|
+
var VISUAL_LEAF_TAGS = /* @__PURE__ */ new Set(["img", "video", "canvas", "svg", "audio"]);
|
|
236
|
+
function hasVisualPresence(el) {
|
|
237
|
+
const win = el.ownerDocument.defaultView;
|
|
238
|
+
if (!win) return false;
|
|
239
|
+
const cs = win.getComputedStyle(el);
|
|
240
|
+
if (cs.backgroundImage !== "none") return true;
|
|
241
|
+
if (cs.backgroundColor && cs.backgroundColor !== "transparent" && cs.backgroundColor !== "rgba(0, 0, 0, 0)")
|
|
242
|
+
return true;
|
|
243
|
+
if (cs.borderWidth && parseFloat(cs.borderWidth) > 0 && cs.borderStyle !== "none") return true;
|
|
244
|
+
if (cs.boxShadow && cs.boxShadow !== "none") return true;
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
function isEmptyVisualContainer(el) {
|
|
248
|
+
const tag = el.tagName.toLowerCase();
|
|
249
|
+
if (VISUAL_LEAF_TAGS.has(tag)) return false;
|
|
250
|
+
if (hasVisualPresence(el)) return false;
|
|
251
|
+
const { children } = el;
|
|
252
|
+
if (children.length === 0) {
|
|
253
|
+
return (el.textContent ?? "").trim().length === 0;
|
|
254
|
+
}
|
|
255
|
+
for (let i = 0; i < children.length; i += 1) {
|
|
256
|
+
const child = children[i];
|
|
257
|
+
if (!isHtmlElement(child)) continue;
|
|
258
|
+
if (VISUAL_LEAF_TAGS.has(child.tagName.toLowerCase())) return false;
|
|
259
|
+
if (isElementComputedVisible(child)) return false;
|
|
260
|
+
}
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
function hasRenderedBox(el) {
|
|
264
|
+
const rect = el.getBoundingClientRect();
|
|
265
|
+
if (rect.width <= 1 || rect.height <= 1) return false;
|
|
266
|
+
if (!isElementComputedVisible(el)) return false;
|
|
267
|
+
if (isEmptyVisualContainer(el)) return false;
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
var DOM_LAYER_IGNORED_TAGS = /* @__PURE__ */ new Set([
|
|
271
|
+
"base",
|
|
272
|
+
"br",
|
|
273
|
+
"canvas",
|
|
274
|
+
"link",
|
|
275
|
+
"meta",
|
|
276
|
+
"script",
|
|
277
|
+
"source",
|
|
278
|
+
"style",
|
|
279
|
+
"template",
|
|
280
|
+
"track",
|
|
281
|
+
"wbr"
|
|
282
|
+
]);
|
|
283
|
+
function isInspectableLayerElement(el) {
|
|
284
|
+
const tagName = el.tagName.toLowerCase();
|
|
285
|
+
if (DOM_LAYER_IGNORED_TAGS.has(tagName)) return false;
|
|
286
|
+
const computed = el.ownerDocument.defaultView?.getComputedStyle(el);
|
|
287
|
+
if (computed?.display === "none" || computed?.visibility === "hidden") return false;
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
function getDomLayerPatchTarget(el, activeCompositionPath) {
|
|
291
|
+
if (!isInspectableLayerElement(el)) return null;
|
|
292
|
+
if (el.hasAttribute("data-composition-id")) return null;
|
|
293
|
+
const selector = buildStableSelector(el);
|
|
294
|
+
if (!selector) return null;
|
|
295
|
+
const { sourceFile } = getSourceFileForElement(el, activeCompositionPath);
|
|
296
|
+
return {
|
|
297
|
+
id: el.id || void 0,
|
|
298
|
+
hfId: el.getAttribute("data-hf-id") || void 0,
|
|
299
|
+
selector,
|
|
300
|
+
selectorIndex: getSelectorIndex(
|
|
301
|
+
el.ownerDocument,
|
|
302
|
+
el,
|
|
303
|
+
selector,
|
|
304
|
+
sourceFile,
|
|
305
|
+
activeCompositionPath
|
|
306
|
+
),
|
|
307
|
+
sourceFile
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
function getPreferredClipAncestor(startEl) {
|
|
311
|
+
let current = startEl;
|
|
312
|
+
while (current) {
|
|
313
|
+
if (current.classList.contains("clip")) {
|
|
314
|
+
const isCompositionHost = current.hasAttribute("data-composition-src") || current.hasAttribute("data-composition-file");
|
|
315
|
+
if (!isCompositionHost || current === startEl) return current;
|
|
316
|
+
}
|
|
317
|
+
current = current.parentElement;
|
|
318
|
+
}
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
function getSelectionCandidate(startEl, options) {
|
|
322
|
+
if (options.preferClipAncestor) {
|
|
323
|
+
const clipAncestor = getPreferredClipAncestor(startEl);
|
|
324
|
+
if (clipAncestor) {
|
|
325
|
+
return clipAncestor;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return startEl;
|
|
329
|
+
}
|
|
330
|
+
function resolveAllVisualDomEditTargets(elementsFromPoint, options) {
|
|
331
|
+
const raw = [];
|
|
332
|
+
for (const entry of elementsFromPoint) {
|
|
333
|
+
if (!isHtmlElement(entry)) continue;
|
|
334
|
+
if (hasRenderedBox(entry) && getDomLayerPatchTarget(entry, options.activeCompositionPath)) {
|
|
335
|
+
raw.push(entry);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
if (raw.length === 0) return [];
|
|
339
|
+
const layers = [];
|
|
340
|
+
let best = raw[0];
|
|
341
|
+
for (let i = 1; i < raw.length; i++) {
|
|
342
|
+
const el = raw[i];
|
|
343
|
+
if (best.contains(el)) {
|
|
344
|
+
best = el;
|
|
345
|
+
} else {
|
|
346
|
+
layers.push(best);
|
|
347
|
+
best = el;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
layers.push(best);
|
|
351
|
+
return layers;
|
|
352
|
+
}
|
|
353
|
+
function findElementForSelection(doc, selection, activeCompositionPath = null) {
|
|
354
|
+
if (selection.hfId) {
|
|
355
|
+
const byHfId = doc.querySelector(`[data-hf-id="${CSS.escape(selection.hfId)}"]`);
|
|
356
|
+
if (isHtmlElement(byHfId)) return byHfId;
|
|
357
|
+
}
|
|
358
|
+
if (selection.id) {
|
|
359
|
+
const byId = doc.getElementById(selection.id);
|
|
360
|
+
if (isHtmlElement(byId) && (!selection.sourceFile || getSourceFileForElement(byId, activeCompositionPath).sourceFile === selection.sourceFile)) {
|
|
361
|
+
return byId;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (!selection.selector) return null;
|
|
365
|
+
if (selection.selector.startsWith(".") && selection.selectorIndex != null) {
|
|
366
|
+
const matches2 = querySelectorAllSafely(doc, selection.selector).filter(
|
|
367
|
+
(candidate) => isHtmlElement(candidate) && (!selection.sourceFile || getSourceFileForElement(candidate, activeCompositionPath).sourceFile === selection.sourceFile)
|
|
368
|
+
);
|
|
369
|
+
return matches2[selection.selectorIndex] ?? null;
|
|
370
|
+
}
|
|
371
|
+
const matches = querySelectorAllSafely(doc, selection.selector).filter(
|
|
372
|
+
(candidate) => isHtmlElement(candidate) && (!selection.sourceFile || getSourceFileForElement(candidate, activeCompositionPath).sourceFile === selection.sourceFile)
|
|
373
|
+
);
|
|
374
|
+
return matches[0] ?? null;
|
|
375
|
+
}
|
|
376
|
+
function findElementForTimelineElement(doc, element, options) {
|
|
377
|
+
const elementId = typeof element.id === "string" ? element.id : "";
|
|
378
|
+
const compositionSource = normalizeTimelineCompositionSource(element.compositionSrc) ?? options.compIdToSrc?.get(elementId);
|
|
379
|
+
const sourceFile = compositionSource ?? normalizeTimelineCompositionSource(element.sourceFile) ?? options.activeCompositionPath ?? "index.html";
|
|
380
|
+
const escapedElementId = escapeCssString(elementId);
|
|
381
|
+
const escapedCompositionSource = compositionSource ? escapeCssString(compositionSource) : null;
|
|
382
|
+
const selector = element.selector ?? (compositionSource ? `[data-composition-src="${escapedCompositionSource}"],[data-composition-file="${escapedCompositionSource}"],[data-composition-id="${escapedElementId}"]` : escapedElementId ? `[data-composition-id="${escapedElementId}"]` : void 0);
|
|
383
|
+
if (selector || element.domId) {
|
|
384
|
+
const targetElement = findElementForSelection(
|
|
385
|
+
doc,
|
|
386
|
+
{
|
|
387
|
+
id: element.domId ?? void 0,
|
|
388
|
+
selector,
|
|
389
|
+
selectorIndex: element.selectorIndex,
|
|
390
|
+
sourceFile
|
|
391
|
+
},
|
|
392
|
+
options.activeCompositionPath
|
|
393
|
+
);
|
|
394
|
+
if (targetElement) return targetElement;
|
|
395
|
+
}
|
|
396
|
+
const hasExplicitDomTarget = Boolean(element.domId || element.selector || compositionSource);
|
|
397
|
+
if (options.isMasterView || hasExplicitDomTarget || !options.activeCompositionPath) {
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
const root = doc.querySelector("[data-composition-id]");
|
|
401
|
+
if (!isHtmlElement(root)) return null;
|
|
402
|
+
return getSourceFileForElement(root, options.activeCompositionPath).sourceFile === sourceFile ? root : null;
|
|
403
|
+
}
|
|
404
|
+
function getDirectLayerChildren(el, options) {
|
|
405
|
+
return Array.from(el.children).filter(
|
|
406
|
+
(child) => isHtmlElement(child) && getDomLayerPatchTarget(child, options.activeCompositionPath) !== null
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// src/components/editor/domEditingRootLayer.ts
|
|
411
|
+
var COMPOSITION_ROOT_LAYER_EPSILON_PX = 1;
|
|
412
|
+
function readPositiveDimension(value) {
|
|
413
|
+
if (!value) return null;
|
|
414
|
+
const parsed = Number.parseFloat(value);
|
|
415
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
416
|
+
}
|
|
417
|
+
function approximatelyEqual(a, b) {
|
|
418
|
+
return Math.abs(a - b) <= COMPOSITION_ROOT_LAYER_EPSILON_PX;
|
|
419
|
+
}
|
|
420
|
+
function getCompositionRootBounds(doc) {
|
|
421
|
+
const root = doc.querySelector("[data-composition-id]") ?? doc.documentElement ?? null;
|
|
422
|
+
const rootWidth = readPositiveDimension(root?.getAttribute("data-width") ?? null);
|
|
423
|
+
const rootHeight = readPositiveDimension(root?.getAttribute("data-height") ?? null);
|
|
424
|
+
if (!root || !rootWidth || !rootHeight) return null;
|
|
425
|
+
return { rect: root.getBoundingClientRect(), width: rootWidth, height: rootHeight };
|
|
426
|
+
}
|
|
427
|
+
function getRenderedLayerSize(element, computedStyles) {
|
|
428
|
+
const rect = element.getBoundingClientRect();
|
|
429
|
+
const width = rect.width || parsePx(computedStyles.width);
|
|
430
|
+
const height = rect.height || parsePx(computedStyles.height);
|
|
431
|
+
return width && height ? { width, height } : null;
|
|
432
|
+
}
|
|
433
|
+
function matchesCompositionRootBounds(elementRect, elementSize, rootBounds) {
|
|
434
|
+
return approximatelyEqual(elementRect.left, rootBounds.rect.left) && approximatelyEqual(elementRect.top, rootBounds.rect.top) && approximatelyEqual(elementSize.width, rootBounds.width) && approximatelyEqual(elementSize.height, rootBounds.height);
|
|
435
|
+
}
|
|
436
|
+
function isExplicitFullBleedLayer(computedStyles) {
|
|
437
|
+
return computedStyles.position === "absolute" || computedStyles.position === "fixed";
|
|
438
|
+
}
|
|
439
|
+
function isCompositionRootLayer(element, doc, computedStyles) {
|
|
440
|
+
if (element.parentElement !== doc.body) return false;
|
|
441
|
+
if (element.hasAttribute("data-hf-allow-root-edit")) return false;
|
|
442
|
+
if (isExplicitFullBleedLayer(computedStyles)) return false;
|
|
443
|
+
const rootBounds = getCompositionRootBounds(doc);
|
|
444
|
+
const elementSize = getRenderedLayerSize(element, computedStyles);
|
|
445
|
+
return Boolean(
|
|
446
|
+
rootBounds && elementSize && matchesCompositionRootBounds(element.getBoundingClientRect(), elementSize, rootBounds)
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// src/components/editor/domEditingLayers.ts
|
|
451
|
+
function isEditableTextLeaf(el) {
|
|
452
|
+
return isTextBearingTag(el.tagName.toLowerCase()) && el.children.length === 0;
|
|
453
|
+
}
|
|
454
|
+
function getTextFieldLabel(_tagName, index, total, source) {
|
|
455
|
+
if (source === "self" || total === 1) return "Content";
|
|
456
|
+
return `Text ${index + 1}`;
|
|
457
|
+
}
|
|
458
|
+
function buildTextField(el, index, total, source) {
|
|
459
|
+
const tagName = el.tagName.toLowerCase();
|
|
460
|
+
const key = el.getAttribute("data-hf-text-key") ?? `${source}:${index}:${tagName}`;
|
|
461
|
+
return {
|
|
462
|
+
key,
|
|
463
|
+
label: getTextFieldLabel(tagName, index, total, source),
|
|
464
|
+
value: el.textContent ?? "",
|
|
465
|
+
tagName,
|
|
466
|
+
attributes: Array.from(el.attributes).filter((attribute) => attribute.name !== "style").map((attribute) => ({
|
|
467
|
+
name: attribute.name,
|
|
468
|
+
value: attribute.value
|
|
469
|
+
})),
|
|
470
|
+
inlineStyles: getInlineStyles(el),
|
|
471
|
+
computedStyles: getCuratedComputedStyles(el),
|
|
472
|
+
source
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
function collectDomEditTextFields(el) {
|
|
476
|
+
const childElements = Array.from(el.children).filter(isHtmlElement).filter(isEditableTextLeaf);
|
|
477
|
+
if (childElements.length > 0) {
|
|
478
|
+
const hasMixedContent = Array.from(el.childNodes).some(
|
|
479
|
+
(node) => node.nodeType === 3 && node.textContent?.trim()
|
|
480
|
+
);
|
|
481
|
+
if (hasMixedContent) {
|
|
482
|
+
const fields = [];
|
|
483
|
+
let childIdx = 0;
|
|
484
|
+
for (const node of el.childNodes) {
|
|
485
|
+
if (node.nodeType === 3) {
|
|
486
|
+
const text = node.textContent ?? "";
|
|
487
|
+
if (!text.trim()) continue;
|
|
488
|
+
fields.push({
|
|
489
|
+
key: `text-node:${childIdx}`,
|
|
490
|
+
label: `Text ${childIdx + 1}`,
|
|
491
|
+
value: text,
|
|
492
|
+
tagName: "#text",
|
|
493
|
+
attributes: [],
|
|
494
|
+
inlineStyles: {},
|
|
495
|
+
computedStyles: {},
|
|
496
|
+
source: "text-node"
|
|
497
|
+
});
|
|
498
|
+
childIdx++;
|
|
499
|
+
} else if (isHtmlElement(node) && isEditableTextLeaf(node)) {
|
|
500
|
+
fields.push(buildTextField(node, childIdx, childElements.length, "child"));
|
|
501
|
+
childIdx++;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return fields;
|
|
505
|
+
}
|
|
506
|
+
return childElements.map(
|
|
507
|
+
(child, index) => buildTextField(child, index, childElements.length, "child")
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
if (isEditableTextLeaf(el)) {
|
|
511
|
+
return [buildTextField(el, 0, 1, "self")];
|
|
512
|
+
}
|
|
513
|
+
return [];
|
|
514
|
+
}
|
|
515
|
+
function escapeHtmlText(value) {
|
|
516
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
517
|
+
}
|
|
518
|
+
function serializeTextFieldStyle(field) {
|
|
519
|
+
const entries = Object.entries(field.inlineStyles).filter(([, value]) => Boolean(value));
|
|
520
|
+
if (entries.length === 0) return "";
|
|
521
|
+
return entries.map(([key, value]) => `${key}: ${value}`).join("; ");
|
|
522
|
+
}
|
|
523
|
+
function serializeDomEditTextFields(fields) {
|
|
524
|
+
return fields.filter((field) => field.source === "child" || field.source === "text-node").map((field) => {
|
|
525
|
+
if (field.source === "text-node") {
|
|
526
|
+
return escapeHtmlText(field.value);
|
|
527
|
+
}
|
|
528
|
+
const attrs = [
|
|
529
|
+
...field.attributes.filter((attribute) => attribute.name !== "data-hf-text-key"),
|
|
530
|
+
{ name: "data-hf-text-key", value: field.key }
|
|
531
|
+
].map((attribute) => ` ${attribute.name}="${attribute.value.replace(/"/g, """)}"`).join("");
|
|
532
|
+
const style = serializeTextFieldStyle(field);
|
|
533
|
+
const styleAttr = style ? ` style="${style.replace(/"/g, """)}"` : "";
|
|
534
|
+
return `<${field.tagName}${attrs}${styleAttr}>${escapeHtmlText(field.value)}</${field.tagName}>`;
|
|
535
|
+
}).join("");
|
|
536
|
+
}
|
|
537
|
+
function buildDefaultDomEditTextField(base) {
|
|
538
|
+
return {
|
|
539
|
+
key: `child:new:${Date.now()}`,
|
|
540
|
+
label: "Text",
|
|
541
|
+
value: "New text",
|
|
542
|
+
tagName: "span",
|
|
543
|
+
attributes: [],
|
|
544
|
+
inlineStyles: {
|
|
545
|
+
"font-family": base?.computedStyles?.["font-family"] ?? "inherit",
|
|
546
|
+
"font-size": base?.computedStyles?.["font-size"] ?? "16px",
|
|
547
|
+
"font-weight": base?.computedStyles?.["font-weight"] ?? "400",
|
|
548
|
+
color: base?.computedStyles?.color ?? "inherit"
|
|
549
|
+
},
|
|
550
|
+
computedStyles: {},
|
|
551
|
+
source: "child"
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
function resolveDomEditCapabilities(args) {
|
|
555
|
+
if (!args.selector && !args.hfId || args.isInsideLockedComposition) {
|
|
556
|
+
return {
|
|
557
|
+
canSelect: !args.isInsideLockedComposition,
|
|
558
|
+
canEditStyles: false,
|
|
559
|
+
canMove: false,
|
|
560
|
+
canResize: false,
|
|
561
|
+
canApplyManualOffset: false,
|
|
562
|
+
canApplyManualSize: false,
|
|
563
|
+
canApplyManualRotation: false,
|
|
564
|
+
reasonIfDisabled: args.isInsideLockedComposition ? "This element belongs to a locked composition." : "Studio could not resolve a stable patch target for this element."
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
if (args.existsInSource === false) {
|
|
568
|
+
return {
|
|
569
|
+
canSelect: true,
|
|
570
|
+
canEditStyles: false,
|
|
571
|
+
canMove: false,
|
|
572
|
+
canResize: false,
|
|
573
|
+
canApplyManualOffset: false,
|
|
574
|
+
canApplyManualSize: false,
|
|
575
|
+
canApplyManualRotation: false,
|
|
576
|
+
reasonIfDisabled: "This element is generated by a script and cannot be edited visually."
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
if (args.isCompositionRoot) {
|
|
580
|
+
return {
|
|
581
|
+
canSelect: true,
|
|
582
|
+
canEditStyles: true,
|
|
583
|
+
canMove: false,
|
|
584
|
+
canResize: false,
|
|
585
|
+
canApplyManualOffset: false,
|
|
586
|
+
canApplyManualSize: false,
|
|
587
|
+
canApplyManualRotation: false,
|
|
588
|
+
reasonIfDisabled: "The root composition defines the preview bounds."
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
const position = args.computedStyles.position;
|
|
592
|
+
const left = parsePx(args.inlineStyles.left) ?? parsePx(args.computedStyles.left);
|
|
593
|
+
const top = parsePx(args.inlineStyles.top) ?? parsePx(args.computedStyles.top);
|
|
594
|
+
const width = parsePx(args.inlineStyles.width) ?? parsePx(args.computedStyles.width);
|
|
595
|
+
const height = parsePx(args.inlineStyles.height) ?? parsePx(args.computedStyles.height);
|
|
596
|
+
const hasTransformDrivenGeometry = !isIdentityTransform(args.computedStyles.transform);
|
|
597
|
+
const canMove = (position === "absolute" || position === "fixed") && left != null && top != null && !hasTransformDrivenGeometry;
|
|
598
|
+
const canResize = canMove && (width != null || height != null);
|
|
599
|
+
const canApplyManualGeometry = !args.isCompositionHost;
|
|
600
|
+
const canApplyManualOffset = canApplyManualGeometry;
|
|
601
|
+
const canApplyManualSize = canApplyManualGeometry;
|
|
602
|
+
const canApplyManualRotation = canApplyManualGeometry;
|
|
603
|
+
const reasonIfDisabled = canApplyManualGeometry ? void 0 : "Select an internal layer to transform it.";
|
|
604
|
+
if (args.isCompositionHost && args.isMasterView) {
|
|
605
|
+
return {
|
|
606
|
+
canSelect: true,
|
|
607
|
+
canEditStyles: false,
|
|
608
|
+
canMove,
|
|
609
|
+
canResize,
|
|
610
|
+
canApplyManualOffset,
|
|
611
|
+
canApplyManualSize,
|
|
612
|
+
canApplyManualRotation,
|
|
613
|
+
reasonIfDisabled
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
return {
|
|
617
|
+
canSelect: true,
|
|
618
|
+
canEditStyles: true,
|
|
619
|
+
canMove,
|
|
620
|
+
canResize,
|
|
621
|
+
canApplyManualOffset,
|
|
622
|
+
canApplyManualSize,
|
|
623
|
+
canApplyManualRotation,
|
|
624
|
+
reasonIfDisabled
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
function buildElementLabel(el) {
|
|
628
|
+
const compositionId = el.getAttribute("data-composition-id");
|
|
629
|
+
if (compositionId && compositionId !== "main") {
|
|
630
|
+
return humanizeIdentifier(compositionId);
|
|
631
|
+
}
|
|
632
|
+
const compositionSrc = el.getAttribute("data-composition-src") ?? el.getAttribute("data-composition-file");
|
|
633
|
+
if (compositionSrc) {
|
|
634
|
+
return humanizeIdentifier(compositionSrc);
|
|
635
|
+
}
|
|
636
|
+
if (el.id) return humanizeIdentifier(el.id);
|
|
637
|
+
const preferredClass = getPreferredClassSelector(el);
|
|
638
|
+
if (preferredClass) {
|
|
639
|
+
return humanizeIdentifier(preferredClass.replace(/^\./, ""));
|
|
640
|
+
}
|
|
641
|
+
const text = (el.textContent ?? "").trim().replace(/\s+/g, " ");
|
|
642
|
+
if (text) return text.length > 40 ? `${text.slice(0, 39)}\u2026` : text;
|
|
643
|
+
return el.tagName.toLowerCase();
|
|
644
|
+
}
|
|
645
|
+
async function probeSourceElement(projectId, sourceFile, target) {
|
|
646
|
+
try {
|
|
647
|
+
const response = await fetch(
|
|
648
|
+
`/api/projects/${projectId}/file-mutations/probe-element/${encodeURIComponent(sourceFile)}`,
|
|
649
|
+
{
|
|
650
|
+
method: "POST",
|
|
651
|
+
headers: { "Content-Type": "application/json" },
|
|
652
|
+
body: JSON.stringify({ target })
|
|
653
|
+
}
|
|
654
|
+
);
|
|
655
|
+
if (!response.ok) return true;
|
|
656
|
+
const data = await response.json();
|
|
657
|
+
return data.exists !== false;
|
|
658
|
+
} catch {
|
|
659
|
+
return true;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
async function resolveDomEditSelection(startEl, options) {
|
|
663
|
+
if (!startEl) return null;
|
|
664
|
+
const doc = startEl.ownerDocument;
|
|
665
|
+
let current = getSelectionCandidate(startEl, options);
|
|
666
|
+
while (current && current !== doc.body && current !== doc.documentElement) {
|
|
667
|
+
const selector = buildStableSelector(current);
|
|
668
|
+
const hfId = readHfId(current);
|
|
669
|
+
if (!selector && !hfId) {
|
|
670
|
+
current = current.parentElement;
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
const { sourceFile, compositionPath } = getSourceFileForElement(
|
|
674
|
+
current,
|
|
675
|
+
options.activeCompositionPath
|
|
676
|
+
);
|
|
677
|
+
const selectorIndex = selector ? getSelectorIndex(doc, current, selector, sourceFile, options.activeCompositionPath) : void 0;
|
|
678
|
+
const compositionSrc = current.getAttribute("data-composition-src") ?? current.getAttribute("data-composition-file") ?? void 0;
|
|
679
|
+
const inlineStyles = getInlineStyles(current);
|
|
680
|
+
const computedStyles = getCuratedComputedStyles(current);
|
|
681
|
+
const isCompositionRoot = current.hasAttribute("data-composition-id") && !compositionSrc || isCompositionRootLayer(current, doc, computedStyles);
|
|
682
|
+
const textFields = collectDomEditTextFields(current);
|
|
683
|
+
const isInsideLocked = Boolean(findClosestByAttribute(current, ["data-timeline-locked"]));
|
|
684
|
+
let existsInSource;
|
|
685
|
+
if (!options.skipSourceProbe && options.projectId && (current.id || selector || hfId)) {
|
|
686
|
+
const probeTarget = {};
|
|
687
|
+
if (current.id) probeTarget.id = current.id;
|
|
688
|
+
if (hfId) probeTarget.hfId = hfId;
|
|
689
|
+
if (selector) probeTarget.selector = selector;
|
|
690
|
+
if (selectorIndex != null) probeTarget.selectorIndex = selectorIndex;
|
|
691
|
+
existsInSource = await probeSourceElement(options.projectId, sourceFile, probeTarget);
|
|
692
|
+
}
|
|
693
|
+
const capabilities = resolveDomEditCapabilities({
|
|
694
|
+
selector,
|
|
695
|
+
hfId,
|
|
696
|
+
tagName: current.tagName.toLowerCase(),
|
|
697
|
+
className: current.className,
|
|
698
|
+
inlineStyles,
|
|
699
|
+
computedStyles,
|
|
700
|
+
isCompositionHost: Boolean(compositionSrc),
|
|
701
|
+
isCompositionRoot,
|
|
702
|
+
isInsideLockedComposition: isInsideLocked,
|
|
703
|
+
isMasterView: options.isMasterView,
|
|
704
|
+
existsInSource
|
|
705
|
+
});
|
|
706
|
+
const rect = current.getBoundingClientRect();
|
|
707
|
+
return {
|
|
708
|
+
element: current,
|
|
709
|
+
id: current.id || void 0,
|
|
710
|
+
hfId,
|
|
711
|
+
selector,
|
|
712
|
+
selectorIndex,
|
|
713
|
+
sourceFile,
|
|
714
|
+
compositionPath,
|
|
715
|
+
compositionSrc,
|
|
716
|
+
isCompositionHost: Boolean(compositionSrc),
|
|
717
|
+
isInsideLockedComposition: isInsideLocked,
|
|
718
|
+
label: buildElementLabel(current),
|
|
719
|
+
tagName: current.tagName.toLowerCase(),
|
|
720
|
+
boundingBox: {
|
|
721
|
+
x: rect.left,
|
|
722
|
+
y: rect.top,
|
|
723
|
+
width: rect.width,
|
|
724
|
+
height: rect.height
|
|
725
|
+
},
|
|
726
|
+
textContent: current.textContent?.trim() || null,
|
|
727
|
+
dataAttributes: getDataAttributes(current),
|
|
728
|
+
inlineStyles,
|
|
729
|
+
computedStyles,
|
|
730
|
+
textFields,
|
|
731
|
+
capabilities
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
return null;
|
|
735
|
+
}
|
|
736
|
+
async function refreshDomEditSelection(selection, activeCompositionPath) {
|
|
737
|
+
const doc = selection.element.ownerDocument;
|
|
738
|
+
const nextElement = findElementForSelection(doc, selection, activeCompositionPath);
|
|
739
|
+
return nextElement ? resolveDomEditSelection(nextElement, {
|
|
740
|
+
activeCompositionPath,
|
|
741
|
+
isMasterView: !activeCompositionPath || activeCompositionPath === "index.html"
|
|
742
|
+
}) : null;
|
|
743
|
+
}
|
|
744
|
+
function getDomEditLayerKey(target) {
|
|
745
|
+
const selectorIndex = target.selectorIndex ?? 0;
|
|
746
|
+
return `${target.sourceFile}:${target.id ?? target.selector ?? "layer"}:${selectorIndex}`;
|
|
747
|
+
}
|
|
748
|
+
function countDomEditChildLayers(root, options, maxCount = 99) {
|
|
749
|
+
if (!root) return 0;
|
|
750
|
+
let count = 0;
|
|
751
|
+
const visit = (el) => {
|
|
752
|
+
for (const child of Array.from(el.children)) {
|
|
753
|
+
if (!isHtmlElement(child)) continue;
|
|
754
|
+
if (getDomLayerPatchTarget(child, options.activeCompositionPath)) {
|
|
755
|
+
count += 1;
|
|
756
|
+
if (count >= maxCount) return;
|
|
757
|
+
}
|
|
758
|
+
visit(child);
|
|
759
|
+
if (count >= maxCount) return;
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
visit(root);
|
|
763
|
+
return count;
|
|
764
|
+
}
|
|
765
|
+
function collectDomEditLayerItems(root, options, maxItems = 80) {
|
|
766
|
+
if (!root) return [];
|
|
767
|
+
const items = [];
|
|
768
|
+
const visit = (el, depth) => {
|
|
769
|
+
if (items.length >= maxItems) return;
|
|
770
|
+
const target = getDomLayerPatchTarget(el, options.activeCompositionPath);
|
|
771
|
+
if (target) {
|
|
772
|
+
items.push({
|
|
773
|
+
key: getDomEditLayerKey(target),
|
|
774
|
+
element: el,
|
|
775
|
+
label: buildElementLabel(el),
|
|
776
|
+
tagName: el.tagName.toLowerCase(),
|
|
777
|
+
depth,
|
|
778
|
+
childCount: getDirectLayerChildren(el, options).length,
|
|
779
|
+
id: target.id ?? void 0,
|
|
780
|
+
hfId: target.hfId ?? void 0,
|
|
781
|
+
selector: target.selector ?? void 0,
|
|
782
|
+
selectorIndex: target.selectorIndex,
|
|
783
|
+
sourceFile: target.sourceFile
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
const nextDepth = target ? depth + 1 : depth;
|
|
787
|
+
for (const child of Array.from(el.children)) {
|
|
788
|
+
if (!isHtmlElement(child)) continue;
|
|
789
|
+
visit(child, nextDepth);
|
|
790
|
+
if (items.length >= maxItems) return;
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
visit(root, 0);
|
|
794
|
+
return items;
|
|
795
|
+
}
|
|
796
|
+
function buildDomEditStylePatchOperation(property, value) {
|
|
797
|
+
return {
|
|
798
|
+
type: "inline-style",
|
|
799
|
+
property,
|
|
800
|
+
value
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
function buildDomEditTextPatchOperation(value) {
|
|
804
|
+
return {
|
|
805
|
+
type: "text-content",
|
|
806
|
+
property: "text",
|
|
807
|
+
value
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
function hasSupportedDirectEdit(capabilities) {
|
|
811
|
+
return capabilities.canEditStyles || capabilities.canMove || capabilities.canResize || capabilities.canApplyManualOffset || capabilities.canApplyManualSize || capabilities.canApplyManualRotation;
|
|
812
|
+
}
|
|
813
|
+
function getDomEditNonEditableReason(element, selection) {
|
|
814
|
+
if (!selection) {
|
|
815
|
+
return "No stable source target";
|
|
816
|
+
}
|
|
817
|
+
if (selection.element !== element) {
|
|
818
|
+
return selection.isCompositionHost ? "Nested composition boundary" : `Selection resolves to ${selection.label}`;
|
|
819
|
+
}
|
|
820
|
+
if (!hasSupportedDirectEdit(selection.capabilities)) {
|
|
821
|
+
return selection.capabilities.reasonIfDisabled ?? "No supported direct edits";
|
|
822
|
+
}
|
|
823
|
+
return null;
|
|
824
|
+
}
|
|
825
|
+
function getDomEditTargetKey(selection) {
|
|
826
|
+
return [
|
|
827
|
+
selection.sourceFile || "index.html",
|
|
828
|
+
selection.hfId ?? "",
|
|
829
|
+
selection.id ?? "",
|
|
830
|
+
selection.selector ?? "",
|
|
831
|
+
selection.selectorIndex ?? ""
|
|
832
|
+
].join("|");
|
|
833
|
+
}
|
|
834
|
+
function isTextEditableSelection(selection) {
|
|
835
|
+
return selection.textFields.length > 0 && !selection.isCompositionHost && !selection.isInsideLockedComposition;
|
|
836
|
+
}
|
|
837
|
+
function readHfId(element) {
|
|
838
|
+
return element.getAttribute("data-hf-id")?.trim() || void 0;
|
|
839
|
+
}
|
|
840
|
+
function buildDomEditPatchTarget(selection) {
|
|
841
|
+
return {
|
|
842
|
+
id: selection.id,
|
|
843
|
+
hfId: selection.hfId,
|
|
844
|
+
selector: selection.selector,
|
|
845
|
+
selectorIndex: selection.selectorIndex
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
export {
|
|
850
|
+
isElementVisibleThroughAncestors,
|
|
851
|
+
setCompositionSourceMap,
|
|
852
|
+
isElementComputedVisible,
|
|
853
|
+
getDomLayerPatchTarget,
|
|
854
|
+
resolveAllVisualDomEditTargets,
|
|
855
|
+
findElementForSelection,
|
|
856
|
+
findElementForTimelineElement,
|
|
857
|
+
isEditableTextLeaf,
|
|
858
|
+
collectDomEditTextFields,
|
|
859
|
+
serializeDomEditTextFields,
|
|
860
|
+
buildDefaultDomEditTextField,
|
|
861
|
+
resolveDomEditCapabilities,
|
|
862
|
+
buildElementLabel,
|
|
863
|
+
resolveDomEditSelection,
|
|
864
|
+
refreshDomEditSelection,
|
|
865
|
+
getDomEditLayerKey,
|
|
866
|
+
countDomEditChildLayers,
|
|
867
|
+
collectDomEditLayerItems,
|
|
868
|
+
buildDomEditStylePatchOperation,
|
|
869
|
+
buildDomEditTextPatchOperation,
|
|
870
|
+
getDomEditNonEditableReason,
|
|
871
|
+
getDomEditTargetKey,
|
|
872
|
+
isTextEditableSelection,
|
|
873
|
+
readHfId,
|
|
874
|
+
buildDomEditPatchTarget
|
|
875
|
+
};
|
|
876
|
+
//# sourceMappingURL=chunk-KZXYQYIU.js.map
|