@hyperframes/studio 0.8.141 → 0.8.143
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-5Vx2AiUz.js → hyperframes-player-BXSuksFh.js} +3 -3
- package/dist/assets/{index-BAnh7cSQ.js → index-BgOVCqjo.js} +1 -1
- package/dist/assets/{index-P8fgj_Xa.js → index-FgyO_5Kv.js} +1 -1
- package/dist/assets/{index-C0thd4eM.js → index-J7jpL4pj.js} +157 -157
- package/dist/index.html +1 -1
- package/dist/index.js +148 -175
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
- package/src/components/MediaPreview.tsx +2 -1
- package/src/components/TimelineToolbar.test.tsx +1 -1
- package/src/components/editor/mediaAudioEdits.test.ts +12 -0
- package/src/components/editor/mediaAudioEdits.ts +5 -5
- package/src/components/editor/propertyPanelFill.tsx +2 -1
- package/src/components/editor/propertyPanelFlatMediaSection.tsx +3 -4
- package/src/components/editor/propertyPanelMediaSection.tsx +3 -4
- package/src/components/editor/useColorGradingController.ts +2 -1
- package/src/hooks/useDomEditCommits.ts +2 -1
- package/src/hooks/useFileManager.ts +2 -1
- package/src/hooks/useFileTree.ts +2 -1
- package/src/hooks/useRenderClipContent.ts +15 -3
- package/src/player/components/clipMenuNormalize.ts +2 -2
- package/src/player/components/clipPeakMap.ts +2 -2
- package/src/player/components/thumbnailUtils.test.ts +33 -0
- package/src/player/components/thumbnailUtils.ts +5 -0
- package/src/player/components/timelineLayout.test.ts +3 -3
- package/src/player/components/timelineZoomInput.test.ts +27 -6
- package/src/player/components/timelineZoomInput.ts +5 -1
- package/src/player/components/useTimelinePlayhead.test.tsx +90 -4
- package/src/player/components/useTimelinePlayhead.ts +18 -3
- package/src/player/components/useTimelineProviderState.tsx +1 -0
- package/src/player/lib/mediaProbe.test.ts +12 -0
- package/src/player/lib/mediaProbe.ts +4 -2
- package/src/player/lib/timelineViewportBudgets.test.ts +1 -1
- package/src/player/lib/timelineViewportBudgets.ts +1 -1
- package/src/utils/sourcePatcher.test.ts +135 -0
- package/src/utils/sourcePatcher.ts +102 -186
- package/src/utils/studioHelpers.test.ts +23 -0
- package/src/utils/studioHelpers.ts +3 -2
- package/src/utils/studioSelectionSnapshot.test.ts +31 -0
- package/src/utils/timelineAssetDrop.test.ts +14 -0
- package/src/utils/timelineAssetDrop.ts +3 -16
package/dist/index.js
CHANGED
|
@@ -2272,7 +2272,7 @@ var MAX_VISIBLE_THUMBNAIL_FRAMES = Math.ceil(3840 / (66 * (16 / 9)));
|
|
|
2272
2272
|
var TIMELINE_VIEWPORT_BUDGETS = Object.freeze({
|
|
2273
2273
|
directScrollSafetyPx: 8e6,
|
|
2274
2274
|
rowOverscanPerSide: 2,
|
|
2275
|
-
timeOverscanViewportRatio: 0.
|
|
2275
|
+
timeOverscanViewportRatio: 0.25,
|
|
2276
2276
|
maxMountedRows: 64,
|
|
2277
2277
|
maxMountedClipRoots: 512,
|
|
2278
2278
|
maxMountedClipRootsPerRow: 128,
|
|
@@ -2486,7 +2486,10 @@ function commitPreview() {
|
|
|
2486
2486
|
);
|
|
2487
2487
|
}
|
|
2488
2488
|
preview = null;
|
|
2489
|
-
if (Math.abs(view.scroll.scrollLeft - left) >= 0.5)
|
|
2489
|
+
if (Math.abs(view.scroll.scrollLeft - left) >= 0.5) {
|
|
2490
|
+
view.scroll.scrollLeft = left;
|
|
2491
|
+
flushSync(() => view.publishScroll(view.scroll));
|
|
2492
|
+
}
|
|
2490
2493
|
clearScaled(view.scroll);
|
|
2491
2494
|
emitPreview();
|
|
2492
2495
|
}
|
|
@@ -3359,6 +3362,7 @@ function revealPlayheadScrollLeft(scroll, playheadX, contentOrigin, from = scrol
|
|
|
3359
3362
|
function useTimelinePlayhead({
|
|
3360
3363
|
playheadRef,
|
|
3361
3364
|
scrollRef,
|
|
3365
|
+
syncScrollViewport,
|
|
3362
3366
|
ppsRef,
|
|
3363
3367
|
durationRef,
|
|
3364
3368
|
isDragging,
|
|
@@ -3395,6 +3399,7 @@ function useTimelinePlayhead({
|
|
|
3395
3399
|
const maxScrollLeft2 = Math.max(0, scroll.scrollWidth - scroll.clientWidth);
|
|
3396
3400
|
const left = anchor2.time * pps + contentOrigin - anchor2.x;
|
|
3397
3401
|
scroll.scrollLeft = Math.max(0, Math.min(maxScrollLeft2, left));
|
|
3402
|
+
syncScrollViewport(scroll);
|
|
3398
3403
|
return;
|
|
3399
3404
|
}
|
|
3400
3405
|
const zoomed = userZoomCount !== prevZoomCount;
|
|
@@ -3413,7 +3418,8 @@ function useTimelinePlayhead({
|
|
|
3413
3418
|
const maxScrollLeft = Math.max(0, scroll.scrollWidth - scroll.clientWidth);
|
|
3414
3419
|
const anchored = Math.max(0, Math.min(maxScrollLeft, nextScrollLeft));
|
|
3415
3420
|
scroll.scrollLeft = zoomed ? revealPlayheadScrollLeft(scroll, contentOrigin + time * pps, contentOrigin, anchored) : anchored;
|
|
3416
|
-
|
|
3421
|
+
syncScrollViewport(scroll);
|
|
3422
|
+
}, [pps, userZoomCount, scrollRef, durationRef, contentOrigin, syncScrollViewport]);
|
|
3417
3423
|
const syncPlayheadPosition = useCallback4(
|
|
3418
3424
|
(time) => {
|
|
3419
3425
|
if (!playheadRef.current || durationRef.current <= 0) return;
|
|
@@ -3553,14 +3559,25 @@ function useTimelinePlayhead({
|
|
|
3553
3559
|
scroll.addEventListener("wheel", handlePinchWheel, { passive: false, capture: true });
|
|
3554
3560
|
scroll.addEventListener("pointerdown", settleTimelineZoom, { capture: true });
|
|
3555
3561
|
scroll.addEventListener("scroll", redrawTimelineZoomPreview, { passive: true });
|
|
3556
|
-
const unregisterZoomViewport = registerTimelineZoomViewport({
|
|
3562
|
+
const unregisterZoomViewport = registerTimelineZoomViewport({
|
|
3563
|
+
scroll,
|
|
3564
|
+
contentOrigin,
|
|
3565
|
+
publishScroll: syncScrollViewport
|
|
3566
|
+
});
|
|
3557
3567
|
return () => {
|
|
3558
3568
|
scroll.removeEventListener("wheel", handlePinchWheel, { capture: true });
|
|
3559
3569
|
scroll.removeEventListener("pointerdown", settleTimelineZoom, { capture: true });
|
|
3560
3570
|
scroll.removeEventListener("scroll", redrawTimelineZoomPreview);
|
|
3561
3571
|
unregisterZoomViewport();
|
|
3562
3572
|
};
|
|
3563
|
-
}, [
|
|
3573
|
+
}, [
|
|
3574
|
+
handlePinchWheel,
|
|
3575
|
+
scrollRef,
|
|
3576
|
+
timelineReady,
|
|
3577
|
+
elementsLength,
|
|
3578
|
+
contentOrigin,
|
|
3579
|
+
syncScrollViewport
|
|
3580
|
+
]);
|
|
3564
3581
|
return { seekFromX, autoScrollDuringDrag, dragScrollRaf };
|
|
3565
3582
|
}
|
|
3566
3583
|
|
|
@@ -3579,6 +3596,10 @@ function useTimelineZoom() {
|
|
|
3579
3596
|
// src/player/components/timelineDragDrop.ts
|
|
3580
3597
|
import { useCallback as useCallback5, useEffect as useEffect6, useRef as useRef6, useState as useState4 } from "react";
|
|
3581
3598
|
|
|
3599
|
+
// src/utils/timelineAssetDrop.ts
|
|
3600
|
+
import { encodeUrlPath } from "@hyperframes/parsers";
|
|
3601
|
+
import { buildTimelineAssetId } from "@hyperframes/core/timeline-asset-id";
|
|
3602
|
+
|
|
3582
3603
|
// src/utils/mediaTypes.ts
|
|
3583
3604
|
var IMAGE_EXTS = ["jpg", "jpeg", "png", "gif", "webp", "avif", "svg", "ico"];
|
|
3584
3605
|
var VIDEO_EXTS = ["mp4", "webm", "mov", "m4v"];
|
|
@@ -3636,16 +3657,6 @@ function getTimelineAssetKind(assetPath) {
|
|
|
3636
3657
|
if (AUDIO_EXT.test(assetPath)) return "audio";
|
|
3637
3658
|
return null;
|
|
3638
3659
|
}
|
|
3639
|
-
function buildTimelineAssetId(assetPath, existingIds) {
|
|
3640
|
-
const baseName = assetPath.split("/").pop() ?? "asset";
|
|
3641
|
-
const normalized = baseName.replace(/\.[^.]+$/, "").replace(/[^a-zA-Z0-9_-]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
|
|
3642
|
-
const baseId = normalized || "asset";
|
|
3643
|
-
const ids = new Set(existingIds);
|
|
3644
|
-
if (!ids.has(baseId)) return baseId;
|
|
3645
|
-
let suffix = 2;
|
|
3646
|
-
while (ids.has(`${baseId}_${suffix}`)) suffix += 1;
|
|
3647
|
-
return `${baseId}_${suffix}`;
|
|
3648
|
-
}
|
|
3649
3660
|
function resolveTimelineAssetSrc(targetPath, assetPath) {
|
|
3650
3661
|
const targetDir = targetPath.includes("/") ? targetPath.slice(0, targetPath.lastIndexOf("/")) : "";
|
|
3651
3662
|
if (!targetDir) return assetPath;
|
|
@@ -3691,7 +3702,7 @@ function fitTimelineAssetGeometry(natural, comp) {
|
|
|
3691
3702
|
};
|
|
3692
3703
|
}
|
|
3693
3704
|
function buildTimelineAssetInsertHtml(input) {
|
|
3694
|
-
const sharedAttrs = `id="${input.id}" data-hf-id="${input.hfId}" class="clip" src="${input.assetPath}" data-start="${input.start}" data-duration="${input.duration}" data-track-index="${input.track}"`;
|
|
3705
|
+
const sharedAttrs = `id="${input.id}" data-hf-id="${input.hfId}" class="clip" src="${encodeUrlPath(input.assetPath)}" data-start="${input.start}" data-duration="${input.duration}" data-track-index="${input.track}"`;
|
|
3695
3706
|
const geometry = input.geometry ?? { left: 0, top: 0, width: 640, height: 360 };
|
|
3696
3707
|
const visualStyles = `position: absolute; left: ${geometry.left}px; top: ${geometry.top}px; width: ${geometry.width}px; height: ${geometry.height}px; object-fit: contain; z-index: ${input.zIndex}`;
|
|
3697
3708
|
if (input.kind === "image") {
|
|
@@ -14852,6 +14863,7 @@ function useTimelineProviderState({
|
|
|
14852
14863
|
const { seekFromX, autoScrollDuringDrag, dragScrollRaf } = useTimelinePlayhead({
|
|
14853
14864
|
playheadRef,
|
|
14854
14865
|
scrollRef,
|
|
14866
|
+
syncScrollViewport,
|
|
14855
14867
|
ppsRef,
|
|
14856
14868
|
durationRef,
|
|
14857
14869
|
isDragging,
|
|
@@ -17132,17 +17144,41 @@ function useOwnPreviewIframe() {
|
|
|
17132
17144
|
import { isHtmlElement as isHtmlElement4 } from "@hyperframes/core/runtime/dom-realm";
|
|
17133
17145
|
|
|
17134
17146
|
// src/utils/sourcePatcher.ts
|
|
17147
|
+
import {
|
|
17148
|
+
scanHtmlOpeningTags,
|
|
17149
|
+
decodeAuthoredAttribute,
|
|
17150
|
+
HTML_ATTRIBUTE_ENTITIES
|
|
17151
|
+
} from "@hyperframes/parsers";
|
|
17135
17152
|
function escapeRegex(s) {
|
|
17136
17153
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
17137
17154
|
}
|
|
17155
|
+
function tagAttribute(tag, name) {
|
|
17156
|
+
return scanHtmlOpeningTags(tag)[0]?.attributes.find((attr) => attr.name === name.toLowerCase());
|
|
17157
|
+
}
|
|
17158
|
+
function patchTagAttribute(tag, name, edit) {
|
|
17159
|
+
if (edit.kind === "remove") {
|
|
17160
|
+
const attributes = scanHtmlOpeningTags(tag)[0]?.attributes.filter((attr2) => attr2.name === name.toLowerCase()) ?? [];
|
|
17161
|
+
const parts = [];
|
|
17162
|
+
let cursor = 0;
|
|
17163
|
+
for (const attribute of attributes) {
|
|
17164
|
+
let start = attribute.start;
|
|
17165
|
+
while (start > cursor && /[\t\n\f\r ]/.test(tag[start - 1])) start--;
|
|
17166
|
+
parts.push(tag.slice(cursor, start));
|
|
17167
|
+
cursor = attribute.end;
|
|
17168
|
+
}
|
|
17169
|
+
parts.push(tag.slice(cursor));
|
|
17170
|
+
return parts.join("");
|
|
17171
|
+
}
|
|
17172
|
+
const attr = tagAttribute(tag, name);
|
|
17173
|
+
const replacement = edit.kind === "boolean" ? name : `${name}="${escapeHtmlAttribute(edit.value)}"`;
|
|
17174
|
+
if (attr) return tag.slice(0, attr.start) + replacement + tag.slice(attr.end);
|
|
17175
|
+
return tag.endsWith("/") ? `${tag.slice(0, -1).trimEnd()} ${replacement} /` : `${tag} ${replacement}`;
|
|
17176
|
+
}
|
|
17138
17177
|
function escapeStyleAttributeValue(value, quote) {
|
|
17139
17178
|
return quote === '"' ? value.replace(/"/g, """) : value.replace(/'/g, "'");
|
|
17140
17179
|
}
|
|
17141
17180
|
function escapeHtmlAttribute(value) {
|
|
17142
|
-
return value.replace(
|
|
17143
|
-
}
|
|
17144
|
-
function unescapeHtmlAttribute(value) {
|
|
17145
|
-
return value.replace(/"/g, '"').replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
17181
|
+
return value.replace(/[&"<>]/g, (char) => HTML_ATTRIBUTE_ENTITIES[char][0]);
|
|
17146
17182
|
}
|
|
17147
17183
|
function splitInlineStyleDeclarations(style) {
|
|
17148
17184
|
const declarations = [];
|
|
@@ -17195,7 +17231,7 @@ function resolveSourceFile(elementId2, selector, files) {
|
|
|
17195
17231
|
if (!elementId2 && !selector) return null;
|
|
17196
17232
|
if (elementId2) {
|
|
17197
17233
|
for (const [path, content] of Object.entries(files)) {
|
|
17198
|
-
if (content
|
|
17234
|
+
if (findTagByAttribute(content, "id", elementId2)) {
|
|
17199
17235
|
return path;
|
|
17200
17236
|
}
|
|
17201
17237
|
}
|
|
@@ -17204,7 +17240,7 @@ function resolveSourceFile(elementId2, selector, files) {
|
|
|
17204
17240
|
if (compIdMatch) {
|
|
17205
17241
|
const compId = compIdMatch[1];
|
|
17206
17242
|
for (const [path, content] of Object.entries(files)) {
|
|
17207
|
-
if (content
|
|
17243
|
+
if (findTagByAttribute(content, "data-composition-id", compId)) {
|
|
17208
17244
|
return path;
|
|
17209
17245
|
}
|
|
17210
17246
|
}
|
|
@@ -17213,7 +17249,7 @@ function resolveSourceFile(elementId2, selector, files) {
|
|
|
17213
17249
|
if (classMatch) {
|
|
17214
17250
|
const cls = classMatch[1];
|
|
17215
17251
|
for (const [path, content] of Object.entries(files)) {
|
|
17216
|
-
if (content
|
|
17252
|
+
if (findTagByClass(content, { selector: `.${cls}` })) {
|
|
17217
17253
|
return path;
|
|
17218
17254
|
}
|
|
17219
17255
|
}
|
|
@@ -17222,18 +17258,14 @@ function resolveSourceFile(elementId2, selector, files) {
|
|
|
17222
17258
|
return null;
|
|
17223
17259
|
}
|
|
17224
17260
|
function patchInlineStyle(html2, elementId2, prop, value) {
|
|
17225
|
-
|
|
17226
|
-
|
|
17227
|
-
|
|
17228
|
-
|
|
17229
|
-
|
|
17230
|
-
|
|
17231
|
-
|
|
17232
|
-
|
|
17233
|
-
const styleMatch = /\bstyle=(")([^"]*)"|\bstyle=(')([^']*)'/.exec(tag);
|
|
17234
|
-
if (styleMatch) {
|
|
17235
|
-
const existingStyle = styleMatch[2] ?? styleMatch[4];
|
|
17236
|
-
const quote = styleMatch[1] ?? styleMatch[3];
|
|
17261
|
+
return patchInlineStyleByTarget(html2, { id: elementId2 }, prop, value);
|
|
17262
|
+
}
|
|
17263
|
+
function patchInlineStyleInTag(tag, prop, value) {
|
|
17264
|
+
if (!tag) return tag;
|
|
17265
|
+
const styleMatch = tagAttribute(tag, "style");
|
|
17266
|
+
if (styleMatch?.kind === "value") {
|
|
17267
|
+
const existingStyle = styleMatch.value;
|
|
17268
|
+
const quote = styleMatch.quote || '"';
|
|
17237
17269
|
const props = /* @__PURE__ */ new Map();
|
|
17238
17270
|
for (const part of splitInlineStyleDeclarations(existingStyle)) {
|
|
17239
17271
|
const colon = part.indexOf(":");
|
|
@@ -17248,67 +17280,54 @@ function patchInlineStyleInTag(html2, tag, prop, value) {
|
|
|
17248
17280
|
props.set(prop, value);
|
|
17249
17281
|
}
|
|
17250
17282
|
const newStyle = Array.from(props.entries()).map(([k, v]) => `${k}: ${escapeStyleAttributeValue(v, quote)}`).join("; ");
|
|
17251
|
-
const newTag = tag.
|
|
17252
|
-
return
|
|
17283
|
+
const newTag = tag.slice(0, styleMatch.start) + `style=${quote}${newStyle}${quote}` + tag.slice(styleMatch.end);
|
|
17284
|
+
return newTag;
|
|
17253
17285
|
} else {
|
|
17254
|
-
if (value === null) return
|
|
17286
|
+
if (value === null) return tag;
|
|
17255
17287
|
const selfClosing = tag.endsWith("/");
|
|
17256
17288
|
const base2 = selfClosing ? tag.slice(0, -1).trimEnd() : tag;
|
|
17257
17289
|
const newTag = `${base2} style="${prop}: ${escapeStyleAttributeValue(value, '"')}"${selfClosing ? " /" : ""}`;
|
|
17258
|
-
return
|
|
17290
|
+
return newTag;
|
|
17259
17291
|
}
|
|
17260
17292
|
}
|
|
17261
17293
|
function patchInlineStyleByTarget(html2, target, prop, value) {
|
|
17262
17294
|
const match = findTagByTarget(html2, target);
|
|
17263
17295
|
if (!match) return html2;
|
|
17264
|
-
const newTag = patchInlineStyleInTag(match.tag,
|
|
17296
|
+
const newTag = patchInlineStyleInTag(match.tag, prop, value);
|
|
17265
17297
|
return replaceTagAtMatch(html2, match, newTag);
|
|
17266
17298
|
}
|
|
17267
17299
|
function replaceTagAtMatch(html2, match, newTag) {
|
|
17268
17300
|
return `${html2.slice(0, match.start)}${newTag}${html2.slice(match.end)}`;
|
|
17269
17301
|
}
|
|
17270
|
-
function
|
|
17271
|
-
const
|
|
17272
|
-
|
|
17273
|
-
|
|
17274
|
-
|
|
17302
|
+
function findTagByAttribute(html2, name, value) {
|
|
17303
|
+
const found = scanHtmlOpeningTags(html2).find((tag) => {
|
|
17304
|
+
const attr = tag.attributes.find((attr2) => attr2.name === name);
|
|
17305
|
+
return tag.closed && attr?.kind === "value" && decodeAuthoredAttribute(attr.value) === value;
|
|
17306
|
+
});
|
|
17307
|
+
return found ? { tag: html2.slice(found.start, found.bodyEnd), start: found.start, end: found.bodyEnd } : null;
|
|
17275
17308
|
}
|
|
17276
17309
|
function findTagByClass(html2, target) {
|
|
17277
17310
|
const classMatch = target.selector?.match(/^\.([a-zA-Z0-9_-]+)$/);
|
|
17278
17311
|
if (!classMatch) return null;
|
|
17279
|
-
const
|
|
17280
|
-
|
|
17281
|
-
|
|
17282
|
-
|
|
17283
|
-
);
|
|
17284
|
-
const selectorIndex = target.selectorIndex ?? 0;
|
|
17285
|
-
let match;
|
|
17286
|
-
let currentIndex = 0;
|
|
17287
|
-
while ((match = pattern.exec(html2)) !== null) {
|
|
17288
|
-
if (currentIndex === selectorIndex && match.index != null) {
|
|
17289
|
-
return {
|
|
17290
|
-
tag: match[1],
|
|
17291
|
-
start: match.index,
|
|
17292
|
-
end: match.index + match[1].length
|
|
17293
|
-
};
|
|
17294
|
-
}
|
|
17295
|
-
currentIndex += 1;
|
|
17296
|
-
}
|
|
17297
|
-
return null;
|
|
17312
|
+
const found = scanHtmlOpeningTags(html2).filter((tag) => {
|
|
17313
|
+
const attr = tag.attributes.find((attr2) => attr2.name === "class");
|
|
17314
|
+
return tag.closed && attr?.kind === "value" && decodeAuthoredAttribute(attr.value).split(/\s+/).includes(classMatch[1]);
|
|
17315
|
+
})[target.selectorIndex ?? 0];
|
|
17316
|
+
return found ? { tag: html2.slice(found.start, found.bodyEnd), start: found.start, end: found.bodyEnd } : null;
|
|
17298
17317
|
}
|
|
17299
17318
|
function findTagByTarget(html2, target) {
|
|
17300
17319
|
if (target.hfId) {
|
|
17301
|
-
const result =
|
|
17320
|
+
const result = findTagByAttribute(html2, "data-hf-id", target.hfId);
|
|
17302
17321
|
if (result) return result;
|
|
17303
17322
|
}
|
|
17304
17323
|
if (target.id) {
|
|
17305
|
-
const result =
|
|
17324
|
+
const result = findTagByAttribute(html2, "id", target.id);
|
|
17306
17325
|
if (result) return result;
|
|
17307
17326
|
}
|
|
17308
17327
|
if (!target.selector) return null;
|
|
17309
17328
|
const compositionIdMatch = target.selector.match(/^\[data-composition-id="([^"]+)"\]$/);
|
|
17310
17329
|
if (compositionIdMatch) {
|
|
17311
|
-
const result =
|
|
17330
|
+
const result = findTagByAttribute(html2, "data-composition-id", compositionIdMatch[1]);
|
|
17312
17331
|
if (result) return result;
|
|
17313
17332
|
}
|
|
17314
17333
|
return findTagByClass(html2, target);
|
|
@@ -17316,9 +17335,11 @@ function findTagByTarget(html2, target) {
|
|
|
17316
17335
|
function readAttributeByTarget(html2, target, attr) {
|
|
17317
17336
|
const match = findTagByTarget(html2, target);
|
|
17318
17337
|
if (!match) return void 0;
|
|
17319
|
-
|
|
17320
|
-
|
|
17321
|
-
|
|
17338
|
+
return readTagAttribute(match.tag, attr.startsWith("data-") ? attr : `data-${attr}`);
|
|
17339
|
+
}
|
|
17340
|
+
function readTagAttribute(tag, attr) {
|
|
17341
|
+
const value = tagAttribute(tag, attr);
|
|
17342
|
+
return value?.kind === "value" ? decodeAuthoredAttribute(value.value) : void 0;
|
|
17322
17343
|
}
|
|
17323
17344
|
function readTagSnippetByTarget(html2, target) {
|
|
17324
17345
|
const match = findTagByTarget(html2, target);
|
|
@@ -17327,60 +17348,15 @@ function readTagSnippetByTarget(html2, target) {
|
|
|
17327
17348
|
function patchAttributeByTarget(html2, target, attr, value) {
|
|
17328
17349
|
const match = findTagByTarget(html2, target);
|
|
17329
17350
|
if (!match) return html2;
|
|
17330
|
-
const
|
|
17331
|
-
const
|
|
17332
|
-
|
|
17333
|
-
if (value === null) {
|
|
17334
|
-
const boolAttrPattern = new RegExp(`\\b${escapeRegex(fullAttr)}(?:=(["'])[^"']*\\1)?`);
|
|
17335
|
-
if (!boolAttrPattern.test(tag)) return html2;
|
|
17336
|
-
const removePattern = new RegExp(`\\s+${escapeRegex(fullAttr)}(?:=(["'])[^"']*\\1)?`);
|
|
17337
|
-
const newTag2 = tag.replace(removePattern, "");
|
|
17338
|
-
return replaceTagAtMatch(html2, match, newTag2);
|
|
17339
|
-
}
|
|
17340
|
-
const escaped = escapeHtmlAttribute(value);
|
|
17341
|
-
if (attrPattern.test(tag)) {
|
|
17342
|
-
const newTag2 = tag.replace(attrPattern, `${fullAttr}="${escaped}"`);
|
|
17343
|
-
return replaceTagAtMatch(html2, match, newTag2);
|
|
17344
|
-
}
|
|
17345
|
-
const newTag = tag + ` ${fullAttr}="${escaped}"`;
|
|
17346
|
-
return replaceTagAtMatch(html2, match, newTag);
|
|
17351
|
+
const name = attr.startsWith("data-") ? attr : `data-${attr}`;
|
|
17352
|
+
const edit = value === null ? { kind: "remove" } : { kind: "value", value };
|
|
17353
|
+
return replaceTagAtMatch(html2, match, patchTagAttribute(match.tag, name, edit));
|
|
17347
17354
|
}
|
|
17348
17355
|
function patchAttribute(html2, elementId2, attr, value) {
|
|
17349
|
-
|
|
17350
|
-
const match = idPattern.exec(html2);
|
|
17351
|
-
if (!match) return html2;
|
|
17352
|
-
const tag = match[1];
|
|
17353
|
-
const fullAttr = attr.startsWith("data-") ? attr : `data-${attr}`;
|
|
17354
|
-
const attrPattern = new RegExp(`\\b${escapeRegex(fullAttr)}=(["'])([^"']*)\\1`);
|
|
17355
|
-
if (value === null) {
|
|
17356
|
-
const boolAttrPattern = new RegExp(`\\b${escapeRegex(fullAttr)}(?:=(["'])[^"']*\\1)?`);
|
|
17357
|
-
if (!boolAttrPattern.test(tag)) return html2;
|
|
17358
|
-
const removePattern = new RegExp(`\\s+${escapeRegex(fullAttr)}(?:=(["'])[^"']*\\1)?`);
|
|
17359
|
-
const newTag = tag.replace(removePattern, "");
|
|
17360
|
-
return html2.replace(tag, newTag);
|
|
17361
|
-
}
|
|
17362
|
-
const escaped = escapeHtmlAttribute(value);
|
|
17363
|
-
if (attrPattern.test(tag)) {
|
|
17364
|
-
const newTag = tag.replace(attrPattern, `${fullAttr}="${escaped}"`);
|
|
17365
|
-
return html2.replace(tag, newTag);
|
|
17366
|
-
} else {
|
|
17367
|
-
const newTag = tag + ` ${fullAttr}="${escaped}"`;
|
|
17368
|
-
return html2.replace(tag, newTag);
|
|
17369
|
-
}
|
|
17356
|
+
return patchAttributeByTarget(html2, { id: elementId2 }, attr, value);
|
|
17370
17357
|
}
|
|
17371
17358
|
function patchTextContent(html2, elementId2, value) {
|
|
17372
|
-
|
|
17373
|
-
`(<([a-z0-9-]+)[^>]*\\bid=(["'])${escapeRegex(elementId2)}\\3[^>]*>)`,
|
|
17374
|
-
"i"
|
|
17375
|
-
);
|
|
17376
|
-
const match = openTagPattern.exec(html2);
|
|
17377
|
-
if (!match || match.index == null) return html2;
|
|
17378
|
-
const openingTag = match[1];
|
|
17379
|
-
const tagName = match[2];
|
|
17380
|
-
const contentStart = match.index + openingTag.length;
|
|
17381
|
-
const closingIndex = findMatchingClosingTagIndex(html2, tagName, contentStart);
|
|
17382
|
-
if (closingIndex < 0) return html2;
|
|
17383
|
-
return `${html2.slice(0, contentStart)}${value}${html2.slice(closingIndex)}`;
|
|
17359
|
+
return patchTextContentByTarget(html2, { id: elementId2 }, value);
|
|
17384
17360
|
}
|
|
17385
17361
|
function findMatchingClosingTagIndex(html2, tagName, contentStart) {
|
|
17386
17362
|
const tagPattern = new RegExp(`</?${escapeRegex(tagName)}\\b[^>]*>`, "gi");
|
|
@@ -17415,47 +17391,26 @@ var HTML_BOOLEAN_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
|
17415
17391
|
"reversed",
|
|
17416
17392
|
"selected"
|
|
17417
17393
|
]);
|
|
17418
|
-
function patchHtmlAttributeInTag(
|
|
17419
|
-
if (!tag) return
|
|
17420
|
-
|
|
17421
|
-
if (
|
|
17422
|
-
|
|
17423
|
-
|
|
17424
|
-
|
|
17425
|
-
|
|
17426
|
-
|
|
17427
|
-
|
|
17428
|
-
|
|
17429
|
-
|
|
17430
|
-
if (hasBoolAttr) return html2;
|
|
17431
|
-
const newTag2 = tag + ` ${attr}`;
|
|
17432
|
-
return html2.replace(tag, newTag2);
|
|
17433
|
-
}
|
|
17434
|
-
const attrPattern = new RegExp(`\\b${escapeRegex(attr)}=(["'])([^"']*)\\1`);
|
|
17435
|
-
if (value === null) {
|
|
17436
|
-
if (!attrPattern.test(tag)) return html2;
|
|
17437
|
-
const removePattern = new RegExp(`\\s+${escapeRegex(attr)}=(["'])[^"']*\\1`);
|
|
17438
|
-
const newTag2 = tag.replace(removePattern, "");
|
|
17439
|
-
return html2.replace(tag, newTag2);
|
|
17440
|
-
}
|
|
17441
|
-
const escaped = escapeHtmlAttribute(value);
|
|
17442
|
-
if (attrPattern.test(tag)) {
|
|
17443
|
-
const newTag2 = tag.replace(attrPattern, `${attr}="${escaped}"`);
|
|
17444
|
-
return html2.replace(tag, newTag2);
|
|
17445
|
-
}
|
|
17446
|
-
const newTag = tag + ` ${attr}="${escaped}"`;
|
|
17447
|
-
return html2.replace(tag, newTag);
|
|
17394
|
+
function patchHtmlAttributeInTag(tag, attr, value) {
|
|
17395
|
+
if (!tag) return tag;
|
|
17396
|
+
let edit;
|
|
17397
|
+
if (HTML_BOOLEAN_ATTRIBUTES.has(attr)) {
|
|
17398
|
+
if (value === null || value === "" || value === "false") edit = { kind: "remove" };
|
|
17399
|
+
else {
|
|
17400
|
+
if (tagAttribute(tag, attr)) return tag;
|
|
17401
|
+
edit = { kind: "boolean" };
|
|
17402
|
+
}
|
|
17403
|
+
} else edit = value === null ? { kind: "remove" } : { kind: "value", value };
|
|
17404
|
+
const newTag = patchTagAttribute(tag, attr, edit);
|
|
17405
|
+
return newTag;
|
|
17448
17406
|
}
|
|
17449
17407
|
function patchHtmlAttribute(html2, elementId2, attr, value) {
|
|
17450
|
-
|
|
17451
|
-
const match = idPattern.exec(html2);
|
|
17452
|
-
if (!match) return html2;
|
|
17453
|
-
return patchHtmlAttributeInTag(html2, match[1], attr, value);
|
|
17408
|
+
return patchHtmlAttributeByTarget(html2, { id: elementId2 }, attr, value);
|
|
17454
17409
|
}
|
|
17455
17410
|
function patchHtmlAttributeByTarget(html2, target, attr, value) {
|
|
17456
17411
|
const match = findTagByTarget(html2, target);
|
|
17457
17412
|
if (!match) return html2;
|
|
17458
|
-
const newTag = patchHtmlAttributeInTag(match.tag,
|
|
17413
|
+
const newTag = patchHtmlAttributeInTag(match.tag, attr, value);
|
|
17459
17414
|
return replaceTagAtMatch(html2, match, newTag);
|
|
17460
17415
|
}
|
|
17461
17416
|
function patchTextContentByTarget(html2, target, value) {
|
|
@@ -21826,6 +21781,7 @@ import {
|
|
|
21826
21781
|
} from "@hyperframes/core/audio-automation";
|
|
21827
21782
|
|
|
21828
21783
|
// src/utils/studioHelpers.ts
|
|
21784
|
+
import { encodeUrlPath as encodeUrlPath2 } from "@hyperframes/parsers";
|
|
21829
21785
|
function getTimelineElementLabel(element) {
|
|
21830
21786
|
return element.label || element.id || element.tag;
|
|
21831
21787
|
}
|
|
@@ -21980,7 +21936,7 @@ async function resolveDroppedAssetDuration(projectId, assetPath, kind) {
|
|
|
21980
21936
|
if (kind === "image") return DEFAULT_TIMELINE_ASSET_DURATION.image;
|
|
21981
21937
|
const media = document.createElement(kind === "video" ? "video" : "audio");
|
|
21982
21938
|
media.preload = "metadata";
|
|
21983
|
-
media.src = buildProjectApiPath(projectId, `/preview/${assetPath}`);
|
|
21939
|
+
media.src = buildProjectApiPath(projectId, `/preview/${encodeUrlPath2(assetPath)}`);
|
|
21984
21940
|
const duration = await new Promise((resolve) => {
|
|
21985
21941
|
const timeout = window.setTimeout(() => resolve(DEFAULT_TIMELINE_ASSET_DURATION[kind]), 3e3);
|
|
21986
21942
|
const finalize = (value) => {
|
|
@@ -25296,6 +25252,7 @@ function isPeakMap(value) {
|
|
|
25296
25252
|
}
|
|
25297
25253
|
|
|
25298
25254
|
// src/player/components/thumbnailUtils.ts
|
|
25255
|
+
import { decodedUrlPath } from "@hyperframes/parsers";
|
|
25299
25256
|
function quantizeThumbnailFrameCount(frameCount) {
|
|
25300
25257
|
const safeCount = Math.max(1, Number.isFinite(frameCount) ? Math.ceil(frameCount) : 1);
|
|
25301
25258
|
const cap = 2 ** Math.floor(Math.log2(MAX_VISIBLE_THUMBNAIL_FRAMES));
|
|
@@ -25346,6 +25303,9 @@ function computeThumbnailStrip(containerWidth, aspect, clipHeight, minFrameWidth
|
|
|
25346
25303
|
const frameCount = measured ? Math.max(1, Math.ceil(containerWidth / frameW)) : 1;
|
|
25347
25304
|
return { frameW, frameCount };
|
|
25348
25305
|
}
|
|
25306
|
+
function authoredSrcPath(src) {
|
|
25307
|
+
return /^(?:[a-z][a-z\d+.-]*:|\/\/)/i.test(src) ? src : decodedUrlPath(src);
|
|
25308
|
+
}
|
|
25349
25309
|
function encodePreviewPath(relativePath) {
|
|
25350
25310
|
return relativePath.split("/").map(encodeURIComponent).join("/");
|
|
25351
25311
|
}
|
|
@@ -25390,7 +25350,7 @@ function loadPeakMap(url) {
|
|
|
25390
25350
|
function clipPeaksUrl(src, projectId) {
|
|
25391
25351
|
const origin2 = window.location.origin;
|
|
25392
25352
|
const relative = resolvePreviewRelative(
|
|
25393
|
-
resolveMediaPreviewUrl(src ?? "", projectId, origin2),
|
|
25353
|
+
resolveMediaPreviewUrl(authoredSrcPath(src ?? ""), projectId, origin2),
|
|
25394
25354
|
projectId,
|
|
25395
25355
|
origin2
|
|
25396
25356
|
);
|
|
@@ -25461,7 +25421,7 @@ function isNormalizePlan(value) {
|
|
|
25461
25421
|
async function requestNormalizePlan(projectId, el, fetchImpl = studioApiFetch) {
|
|
25462
25422
|
const origin2 = window.location.origin;
|
|
25463
25423
|
const relative = resolvePreviewRelative(
|
|
25464
|
-
resolveMediaPreviewUrl(el.src ?? "", projectId, origin2),
|
|
25424
|
+
resolveMediaPreviewUrl(authoredSrcPath(el.src ?? ""), projectId, origin2),
|
|
25465
25425
|
projectId,
|
|
25466
25426
|
origin2
|
|
25467
25427
|
);
|
|
@@ -26975,10 +26935,10 @@ function formatAttrs(attrs) {
|
|
|
26975
26935
|
function readAuthoredSrc(source, target) {
|
|
26976
26936
|
const tag = findTagByTarget(source, target);
|
|
26977
26937
|
if (!tag) return "";
|
|
26978
|
-
const own =
|
|
26979
|
-
if (own
|
|
26980
|
-
const firstSource = /^\s*<source\b[^>]
|
|
26981
|
-
return firstSource
|
|
26938
|
+
const own = readTagAttribute(tag.tag, "src");
|
|
26939
|
+
if (own) return own;
|
|
26940
|
+
const firstSource = /^\s*<source\b[^>]*>/i.exec(source.slice(tag.end + 1));
|
|
26941
|
+
return firstSource && readTagAttribute(firstSource[0], "src") || "";
|
|
26982
26942
|
}
|
|
26983
26943
|
function insertBeforeTarget(source, target, markup) {
|
|
26984
26944
|
const match = findTagByTarget(source, target);
|
|
@@ -32173,7 +32133,7 @@ function getCachedProbe(url) {
|
|
|
32173
32133
|
return cached5?.result;
|
|
32174
32134
|
}
|
|
32175
32135
|
function resolveProbeSource(src, projectId) {
|
|
32176
|
-
return projectId ? resolveMediaPreviewUrl(src, projectId, window.location.origin) : src;
|
|
32136
|
+
return projectId ? resolveMediaPreviewUrl(authoredSrcPath(src), projectId, window.location.origin) : src;
|
|
32177
32137
|
}
|
|
32178
32138
|
function evictMetadataOverflow() {
|
|
32179
32139
|
const overflow = cache.size + failed.size - TIMELINE_VIEWPORT_BUDGETS.metadataRegistryEntries;
|
|
@@ -52871,7 +52831,11 @@ function trimFractions(el) {
|
|
|
52871
52831
|
return { start, end };
|
|
52872
52832
|
}
|
|
52873
52833
|
function renderAudioClip(el, pid, sessionEpoch, labelColor, context, elements, labelInset) {
|
|
52874
|
-
const audioUrl = resolveMediaPreviewUrl(
|
|
52834
|
+
const audioUrl = resolveMediaPreviewUrl(
|
|
52835
|
+
authoredSrcPath(el.src ?? ""),
|
|
52836
|
+
pid,
|
|
52837
|
+
window.location.origin
|
|
52838
|
+
);
|
|
52875
52839
|
const srcRelative = resolvePreviewRelative(audioUrl, pid, window.location.origin);
|
|
52876
52840
|
const encodedRelative = srcRelative ? encodePreviewPath(srcRelative) : null;
|
|
52877
52841
|
const waveformUrl = encodedRelative ? buildProjectApiPath(pid, `/waveform/${encodedRelative}`) : void 0;
|
|
@@ -52971,7 +52935,11 @@ function useRenderClipContent({
|
|
|
52971
52935
|
}
|
|
52972
52936
|
const htmlPreviewEligible = el.duration > 0 && effectiveTimelineDuration > 0;
|
|
52973
52937
|
if ((el.tag === "video" || el.tag === "img") && el.src) {
|
|
52974
|
-
const mediaSrc = resolveMediaPreviewUrl(
|
|
52938
|
+
const mediaSrc = resolveMediaPreviewUrl(
|
|
52939
|
+
authoredSrcPath(el.src),
|
|
52940
|
+
pid,
|
|
52941
|
+
window.location.origin
|
|
52942
|
+
);
|
|
52975
52943
|
if (el.tag === "img") {
|
|
52976
52944
|
return createElement(ImageThumbnail, {
|
|
52977
52945
|
imageSrc: mediaSrc,
|
|
@@ -54689,8 +54657,8 @@ function MediaSection({
|
|
|
54689
54657
|
const [removeProgress, setRemoveProgress] = useState65(null);
|
|
54690
54658
|
const [createPlate, setCreatePlate] = useState65(false);
|
|
54691
54659
|
const [quality, setQuality] = useState65("balanced");
|
|
54692
|
-
const
|
|
54693
|
-
const
|
|
54660
|
+
const projectSrc = srcAttr && !/^(?:https?:|data:|blob:)/i.test(srcAttr) ? authoredSrcPath(srcAttr.startsWith("./") ? srcAttr.slice(2) : srcAttr) : "";
|
|
54661
|
+
const absoluteSrc = projectDir && projectSrc ? `${projectDir}/${projectSrc}` : srcAttr;
|
|
54694
54662
|
const canRemoveBackground = Boolean(onRemoveBackground && isVisualMedia && projectSrc);
|
|
54695
54663
|
const panelTitle = isImage ? "Image" : isVideo ? "Video" : "Audio";
|
|
54696
54664
|
useEffect72(() => {
|
|
@@ -56013,7 +55981,7 @@ function resolveProjectAssetPath(sourceFile, src, projectId) {
|
|
|
56013
55981
|
return stripPreviewAssetPath(trimmed, projectId);
|
|
56014
55982
|
}
|
|
56015
55983
|
const sourceDir = sourceFile.includes("/") ? sourceFile.slice(0, sourceFile.lastIndexOf("/")) : "";
|
|
56016
|
-
const parts = `${sourceDir}/${trimmed}`.split("/");
|
|
55984
|
+
const parts = `${sourceDir}/${authoredSrcPath(trimmed)}`.split("/");
|
|
56017
55985
|
const normalized = [];
|
|
56018
55986
|
for (const part of parts) {
|
|
56019
55987
|
if (!part || part === ".") continue;
|
|
@@ -58487,6 +58455,7 @@ function ReverseGradientIcon({ size = 16 }) {
|
|
|
58487
58455
|
}
|
|
58488
58456
|
|
|
58489
58457
|
// src/components/editor/propertyPanelFill.tsx
|
|
58458
|
+
import { encodeUrlPath as encodeUrlPath3 } from "@hyperframes/parsers";
|
|
58490
58459
|
import { jsx as jsx137, jsxs as jsxs99 } from "react/jsx-runtime";
|
|
58491
58460
|
function normalizeProjectPath(value) {
|
|
58492
58461
|
const trimmed = value.trim();
|
|
@@ -58613,7 +58582,7 @@ function ImageFillField({
|
|
|
58613
58582
|
selectedAsset && /* @__PURE__ */ jsx137("div", { className: "overflow-hidden rounded-xl border border-neutral-800 bg-neutral-900/80", children: /* @__PURE__ */ jsx137(
|
|
58614
58583
|
"img",
|
|
58615
58584
|
{
|
|
58616
|
-
src: buildProjectApiPath(projectId, `/preview/${selectedAsset}`),
|
|
58585
|
+
src: buildProjectApiPath(projectId, `/preview/${encodeUrlPath3(selectedAsset)}`),
|
|
58617
58586
|
alt: selectedAsset.split("/").pop() ?? selectedAsset,
|
|
58618
58587
|
className: "h-28 w-full object-contain bg-neutral-950/80"
|
|
58619
58588
|
}
|
|
@@ -67079,8 +67048,8 @@ function FlatMediaSection({
|
|
|
67079
67048
|
const [removeProgress, setRemoveProgress] = useState94(null);
|
|
67080
67049
|
const [createPlate, setCreatePlate] = useState94(false);
|
|
67081
67050
|
const [quality, setQuality] = useState94("balanced");
|
|
67082
|
-
const
|
|
67083
|
-
const
|
|
67051
|
+
const projectSrc = srcAttr && !/^(?:https?:|data:|blob:)/i.test(srcAttr) ? authoredSrcPath(srcAttr.startsWith("./") ? srcAttr.slice(2) : srcAttr) : "";
|
|
67052
|
+
const absoluteSrc = projectDir && projectSrc ? `${projectDir}/${projectSrc}` : srcAttr;
|
|
67084
67053
|
const canRemoveBackground = Boolean(onRemoveBackground && isVisualMedia && projectSrc);
|
|
67085
67054
|
useEffect94(() => {
|
|
67086
67055
|
setRemoveProgress(null);
|
|
@@ -75107,6 +75076,7 @@ import { useState as useState117, useCallback as useCallback104, useRef as useRe
|
|
|
75107
75076
|
|
|
75108
75077
|
// src/hooks/useFileTree.ts
|
|
75109
75078
|
import { useState as useState116, useCallback as useCallback101, useEffect as useEffect113, useMemo as useMemo71, useRef as useRef128 } from "react";
|
|
75079
|
+
import { encodeUrlPath as encodeUrlPath4 } from "@hyperframes/parsers";
|
|
75110
75080
|
var EMPTY_FILE_LIST = [];
|
|
75111
75081
|
function useFileTree({ projectId, projectIdRef }) {
|
|
75112
75082
|
const [fetched, setFetched] = useState116(null);
|
|
@@ -75178,7 +75148,7 @@ function useFileTree({ projectId, projectIdRef }) {
|
|
|
75178
75148
|
() => (projectId ? assets : []).filter((asset) => FONT_EXT.test(asset)).map((asset) => ({
|
|
75179
75149
|
family: fontFamilyFromAssetPath(asset),
|
|
75180
75150
|
path: asset,
|
|
75181
|
-
url: projectId ? buildProjectApiPath(projectId, `/preview/${asset}`) : ""
|
|
75151
|
+
url: projectId ? buildProjectApiPath(projectId, `/preview/${encodeUrlPath4(asset)}`) : ""
|
|
75182
75152
|
})),
|
|
75183
75153
|
[assets, projectId]
|
|
75184
75154
|
);
|
|
@@ -75443,6 +75413,7 @@ function useProjectFileWriter({ projectId }) {
|
|
|
75443
75413
|
}
|
|
75444
75414
|
|
|
75445
75415
|
// src/hooks/useFileManager.ts
|
|
75416
|
+
import { encodeUrlPath as encodeUrlPath5 } from "@hyperframes/parsers";
|
|
75446
75417
|
function useFileManager({
|
|
75447
75418
|
projectId,
|
|
75448
75419
|
showToast,
|
|
@@ -75752,7 +75723,7 @@ function useFileManager({
|
|
|
75752
75723
|
const imported = uploaded.filter((asset) => FONT_EXT.test(asset)).map((asset) => ({
|
|
75753
75724
|
family: fontFamilyFromAssetPath(asset),
|
|
75754
75725
|
path: asset,
|
|
75755
|
-
url: `/api/projects/${encodeURIComponent(pid)}/preview/${asset}`
|
|
75726
|
+
url: `/api/projects/${encodeURIComponent(pid)}/preview/${encodeUrlPath5(asset)}`
|
|
75756
75727
|
}));
|
|
75757
75728
|
importedFontAssetsRef.current = [
|
|
75758
75729
|
...imported,
|
|
@@ -81788,6 +81759,7 @@ function useDomGeometryCommits({
|
|
|
81788
81759
|
}
|
|
81789
81760
|
|
|
81790
81761
|
// src/hooks/useDomEditCommits.ts
|
|
81762
|
+
import { encodeUrlPath as encodeUrlPath6 } from "@hyperframes/parsers";
|
|
81791
81763
|
function useDomEditCommits({
|
|
81792
81764
|
activeCompPath,
|
|
81793
81765
|
previewIframeRef,
|
|
@@ -81826,7 +81798,7 @@ function useDomEditCommits({
|
|
|
81826
81798
|
return {
|
|
81827
81799
|
family: fontFamilyFromAssetPath(asset),
|
|
81828
81800
|
path: asset,
|
|
81829
|
-
url: buildProjectApiPath(projectId, `/preview/${asset}`)
|
|
81801
|
+
url: buildProjectApiPath(projectId, `/preview/${encodeUrlPath6(asset)}`)
|
|
81830
81802
|
};
|
|
81831
81803
|
},
|
|
81832
81804
|
[fileTree, projectId, importedFontAssetsRef]
|
|
@@ -91341,6 +91313,7 @@ import { memo as memo50, useCallback as useCallback171 } from "react";
|
|
|
91341
91313
|
|
|
91342
91314
|
// src/components/MediaPreview.tsx
|
|
91343
91315
|
import { useState as useState136 } from "react";
|
|
91316
|
+
import { encodeUrlPath as encodeUrlPath7 } from "@hyperframes/parsers";
|
|
91344
91317
|
import { jsx as jsx212, jsxs as jsxs166 } from "react/jsx-runtime";
|
|
91345
91318
|
function MediaErrorPanel({ name, filePath }) {
|
|
91346
91319
|
return /* @__PURE__ */ jsxs166("div", { className: "flex flex-col items-center justify-center h-full p-4 bg-neutral-950 gap-2", children: [
|
|
@@ -91368,7 +91341,7 @@ function MediaErrorPanel({ name, filePath }) {
|
|
|
91368
91341
|
] });
|
|
91369
91342
|
}
|
|
91370
91343
|
function MediaPreview({ projectId, filePath }) {
|
|
91371
|
-
const serveUrl = buildProjectApiPath(projectId, `/preview/${filePath}`);
|
|
91344
|
+
const serveUrl = buildProjectApiPath(projectId, `/preview/${encodeUrlPath7(filePath)}`);
|
|
91372
91345
|
const name = filePath.split("/").pop() ?? filePath;
|
|
91373
91346
|
const [failedPath, setFailedPath] = useState136(null);
|
|
91374
91347
|
const failed2 = failedPath === filePath;
|