@proveanything/smartlinks-utils-ui 1.13.8 → 1.13.13
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/README.md +6 -6
- package/dist/ErrorBoundary-J9iKgF_H.d.ts +40 -0
- package/dist/{chunk-OTJV62XV.js → chunk-5ZQT2GGU.js} +5 -5
- package/dist/chunk-5ZQT2GGU.js.map +1 -0
- package/dist/{chunk-4LHF5JB7.js → chunk-DH5HG5DW.js} +15 -6
- package/dist/chunk-DH5HG5DW.js.map +1 -0
- package/dist/{chunk-E3GQ6LNZ.js → chunk-I3T36FSI.js} +528 -49
- package/dist/chunk-I3T36FSI.js.map +1 -0
- package/dist/{chunk-7UBXTFZQ.js → chunk-JNCRSL2H.js} +13 -12
- package/dist/chunk-JNCRSL2H.js.map +1 -0
- package/dist/{chunk-JMCV6FOW.js → chunk-WVCNIX7N.js} +3 -3
- package/dist/{chunk-JMCV6FOW.js.map → chunk-WVCNIX7N.js.map} +1 -1
- package/dist/{chunk-3RRHM4LP.js → chunk-XASZS7EA.js} +131 -4
- package/dist/chunk-XASZS7EA.js.map +1 -0
- package/dist/components/AssetPicker/index.css +34 -0
- package/dist/components/AssetPicker/index.css.map +1 -1
- package/dist/components/AssetPicker/index.js +1 -1
- package/dist/components/ConditionsEditor/index.css +34 -0
- package/dist/components/ConditionsEditor/index.css.map +1 -1
- package/dist/components/ConditionsEditor/index.d.ts +2 -2
- package/dist/components/ConditionsEditor/index.js +2 -2
- package/dist/components/FacetRuleEditor/index.d.ts +1 -1
- package/dist/components/FacetRuleEditor/index.js +2 -2
- package/dist/components/FontPicker/index.css +34 -0
- package/dist/components/FontPicker/index.css.map +1 -1
- package/dist/components/FontPicker/index.js +1 -1
- package/dist/components/IconPicker/index.css +34 -0
- package/dist/components/IconPicker/index.css.map +1 -1
- package/dist/components/LinkPicker/index.css +34 -0
- package/dist/components/LinkPicker/index.css.map +1 -1
- package/dist/components/RecordsAdmin/index.css +34 -0
- package/dist/components/RecordsAdmin/index.css.map +1 -1
- package/dist/components/RecordsAdmin/index.d.ts +1 -0
- package/dist/components/RecordsAdmin/index.js +15 -11
- package/dist/components/RecordsAdmin/index.js.map +1 -1
- package/dist/index.css +34 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +7 -7
- package/dist/index.js.map +1 -1
- package/dist/{types-a2DdgZ2H.d.ts → types-BLqki3Zy.d.ts} +11 -0
- package/package.json +3 -3
- package/dist/chunk-3RRHM4LP.js.map +0 -1
- package/dist/chunk-4LHF5JB7.js.map +0 -1
- package/dist/chunk-7UBXTFZQ.js.map +0 -1
- package/dist/chunk-E3GQ6LNZ.js.map +0 -1
- package/dist/chunk-OTJV62XV.js.map +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { assertStylesLoaded } from './chunk-OLYC54YT.js';
|
|
2
2
|
import { cn } from './chunk-L7FQ52F5.js';
|
|
3
|
-
import
|
|
3
|
+
import React9, { useState, useRef, useEffect, useCallback, useMemo, useLayoutEffect } from 'react';
|
|
4
4
|
import { createPortal } from 'react-dom';
|
|
5
5
|
import * as SL from '@proveanything/smartlinks';
|
|
6
|
-
import { Filter, Search, LayoutGrid, List, Loader2, AlertCircle, Tag, X, ImageOff, Wand2, Maximize2, Clipboard, Pencil, Crop, Check, Upload, Link, MicOff, Mic, ChevronDown, ChevronRight, Sparkles, Image as Image$1, RotateCcw, RotateCw, FlipHorizontal, FlipVertical, RefreshCw, Plus, AlertTriangle, FileIcon, Film, Music, FileText, AppWindow, MoreVertical,
|
|
6
|
+
import { Filter, Search, LayoutGrid, List, Loader2, AlertCircle, Tag, X, ImageOff, Wand2, Maximize2, Clipboard, Pencil, Crop, Check, Upload, Link, MicOff, Mic, ChevronDown, ChevronRight, Sparkles, Image as Image$1, RotateCcw, RotateCw, FlipHorizontal, FlipVertical, RefreshCw, Plus, Copy, ExternalLink, AlertTriangle, Wrench, CheckCircle2, Trash2, FileIcon, Film, Music, FileText, AppWindow, MoreVertical, Info } from 'lucide-react';
|
|
7
7
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
8
|
|
|
9
9
|
// src/components/AssetPicker/types.ts
|
|
@@ -32,17 +32,33 @@ function useAssets({ scope, accept, pageSize, appId, listAppId }) {
|
|
|
32
32
|
setLoading(true);
|
|
33
33
|
setError(null);
|
|
34
34
|
try {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
const collectionId = scope.collectionId;
|
|
36
|
+
const productId = scope.productId;
|
|
37
|
+
const proofId = scope.proofId;
|
|
38
|
+
const assetType = accept?.startsWith("image/") ? "Image" : accept?.startsWith("video/") ? "Video" : accept?.startsWith("audio/") ? "Audio" : void 0;
|
|
39
|
+
const adminList = SL.asset.listAdmin;
|
|
40
|
+
let items = [];
|
|
41
|
+
if (adminList) {
|
|
42
|
+
const res = await adminList({
|
|
43
|
+
collectionId,
|
|
44
|
+
...productId ? { productId } : {},
|
|
45
|
+
...proofId ? { proofId } : {},
|
|
46
|
+
...listAppId ? { appId: listAppId } : {},
|
|
47
|
+
...assetType ? { assetType } : {},
|
|
48
|
+
...pageSize ? { limit: pageSize } : {}
|
|
49
|
+
});
|
|
50
|
+
items = Array.isArray(res) ? res : res?.data ?? [];
|
|
51
|
+
} else {
|
|
52
|
+
const res = await SL.asset.list({
|
|
53
|
+
scope,
|
|
54
|
+
mimeTypePrefix: accept,
|
|
55
|
+
limit: pageSize,
|
|
56
|
+
...listAppId ? { appId: listAppId } : {}
|
|
57
|
+
});
|
|
58
|
+
items = res;
|
|
59
|
+
}
|
|
44
60
|
if (mountedRef.current) {
|
|
45
|
-
setAssets(
|
|
61
|
+
setAssets(items);
|
|
46
62
|
}
|
|
47
63
|
} catch (err) {
|
|
48
64
|
if (mountedRef.current) {
|
|
@@ -350,7 +366,7 @@ var AppBadge = ({ appId, appName, size = "sm" }) => {
|
|
|
350
366
|
}
|
|
351
367
|
);
|
|
352
368
|
};
|
|
353
|
-
var CardMenu = ({ onRename, onReplace, onEditTags, onEditImage, onDelete, position = "absolute" }) => {
|
|
369
|
+
var CardMenu = ({ onRename, onReplace, onEditTags, onEditImage, onShowDetails, onDelete, position = "absolute" }) => {
|
|
354
370
|
const [open, setOpen] = useState(false);
|
|
355
371
|
const ref = useRef(null);
|
|
356
372
|
const btnRef = useRef(null);
|
|
@@ -402,7 +418,7 @@ var CardMenu = ({ onRename, onReplace, onEditTags, onEditImage, onDelete, positi
|
|
|
402
418
|
window.removeEventListener("scroll", update, true);
|
|
403
419
|
};
|
|
404
420
|
}, [open]);
|
|
405
|
-
if (!onRename && !onReplace && !onEditTags && !onEditImage && !onDelete) return null;
|
|
421
|
+
if (!onRename && !onReplace && !onEditTags && !onEditImage && !onShowDetails && !onDelete) return null;
|
|
406
422
|
return /* @__PURE__ */ jsxs(
|
|
407
423
|
"div",
|
|
408
424
|
{
|
|
@@ -446,6 +462,23 @@ var CardMenu = ({ onRename, onReplace, onEditTags, onEditImage, onDelete, positi
|
|
|
446
462
|
onClick: (e) => e.stopPropagation(),
|
|
447
463
|
onMouseDown: (e) => e.stopPropagation(),
|
|
448
464
|
children: [
|
|
465
|
+
onShowDetails && /* @__PURE__ */ jsxs(
|
|
466
|
+
"button",
|
|
467
|
+
{
|
|
468
|
+
type: "button",
|
|
469
|
+
role: "menuitem",
|
|
470
|
+
onClick: (e) => {
|
|
471
|
+
e.stopPropagation();
|
|
472
|
+
setOpen(false);
|
|
473
|
+
onShowDetails();
|
|
474
|
+
},
|
|
475
|
+
className: "w-full flex items-center gap-2 px-2.5 py-1.5 text-xs hover:bg-accent",
|
|
476
|
+
children: [
|
|
477
|
+
/* @__PURE__ */ jsx(Info, { className: "w-3 h-3" }),
|
|
478
|
+
" Details"
|
|
479
|
+
]
|
|
480
|
+
}
|
|
481
|
+
),
|
|
449
482
|
onRename && /* @__PURE__ */ jsxs(
|
|
450
483
|
"button",
|
|
451
484
|
{
|
|
@@ -540,7 +573,7 @@ var CardMenu = ({ onRename, onReplace, onEditTags, onEditImage, onDelete, positi
|
|
|
540
573
|
}
|
|
541
574
|
);
|
|
542
575
|
};
|
|
543
|
-
var AssetGridItem = ({ asset: asset2, selected, onToggle, onDoubleClick, onDelete, onRename, onReplace, onEditTags, onEditImage, allowDelete, currentAppId, getAppName, activeLabels, onToggleLabel }) => {
|
|
576
|
+
var AssetGridItem = ({ asset: asset2, selected, onToggle, onDoubleClick, onDelete, onRename, onReplace, onEditTags, onEditImage, onShowDetails, allowDelete, currentAppId, getAppName, activeLabels, onToggleLabel }) => {
|
|
544
577
|
const thumb = getThumbnail(asset2);
|
|
545
578
|
const Icon = getIcon(asset2.mimeType);
|
|
546
579
|
const ownerAppId = getAssetAppId(asset2);
|
|
@@ -622,6 +655,7 @@ var AssetGridItem = ({ asset: asset2, selected, onToggle, onDoubleClick, onDelet
|
|
|
622
655
|
onReplace,
|
|
623
656
|
onEditTags,
|
|
624
657
|
onEditImage,
|
|
658
|
+
onShowDetails,
|
|
625
659
|
onDelete: allowDelete ? onDelete : void 0
|
|
626
660
|
}
|
|
627
661
|
)
|
|
@@ -629,7 +663,7 @@ var AssetGridItem = ({ asset: asset2, selected, onToggle, onDoubleClick, onDelet
|
|
|
629
663
|
}
|
|
630
664
|
);
|
|
631
665
|
};
|
|
632
|
-
var AssetListItem = ({ asset: asset2, selected, onToggle, onDoubleClick, onDelete, onRename, onReplace, onEditTags, onEditImage, allowDelete, currentAppId, getAppName, activeLabels, onToggleLabel }) => {
|
|
666
|
+
var AssetListItem = ({ asset: asset2, selected, onToggle, onDoubleClick, onDelete, onRename, onReplace, onEditTags, onEditImage, onShowDetails, allowDelete, currentAppId, getAppName, activeLabels, onToggleLabel }) => {
|
|
633
667
|
const thumb = getThumbnail(asset2);
|
|
634
668
|
const Icon = getIcon(asset2.mimeType);
|
|
635
669
|
const ownerAppId = getAssetAppId(asset2);
|
|
@@ -699,6 +733,7 @@ var AssetListItem = ({ asset: asset2, selected, onToggle, onDoubleClick, onDelet
|
|
|
699
733
|
onReplace,
|
|
700
734
|
onEditTags,
|
|
701
735
|
onEditImage,
|
|
736
|
+
onShowDetails,
|
|
702
737
|
onDelete: allowDelete ? onDelete : void 0,
|
|
703
738
|
position: "inline"
|
|
704
739
|
}
|
|
@@ -718,6 +753,7 @@ var AssetGrid = ({
|
|
|
718
753
|
onReplace,
|
|
719
754
|
onEditTags,
|
|
720
755
|
onEditImage,
|
|
756
|
+
onShowDetails,
|
|
721
757
|
allowDelete,
|
|
722
758
|
currentAppId,
|
|
723
759
|
getAppName,
|
|
@@ -738,6 +774,7 @@ var AssetGrid = ({
|
|
|
738
774
|
onReplace: onReplace ? () => onReplace(asset2) : void 0,
|
|
739
775
|
onEditTags: onEditTags ? () => onEditTags(asset2) : void 0,
|
|
740
776
|
onEditImage: onEditImage ? () => onEditImage(asset2) : void 0,
|
|
777
|
+
onShowDetails: onShowDetails ? () => onShowDetails(asset2) : void 0,
|
|
741
778
|
allowDelete,
|
|
742
779
|
currentAppId,
|
|
743
780
|
getAppName,
|
|
@@ -759,6 +796,7 @@ var AssetGrid = ({
|
|
|
759
796
|
onReplace: onReplace ? () => onReplace(asset2) : void 0,
|
|
760
797
|
onEditTags: onEditTags ? () => onEditTags(asset2) : void 0,
|
|
761
798
|
onEditImage: onEditImage ? () => onEditImage(asset2) : void 0,
|
|
799
|
+
onShowDetails: onShowDetails ? () => onShowDetails(asset2) : void 0,
|
|
762
800
|
allowDelete,
|
|
763
801
|
currentAppId,
|
|
764
802
|
getAppName,
|
|
@@ -779,6 +817,51 @@ function isProcessableImage(file) {
|
|
|
779
817
|
if (file.type === "image/svg+xml" || file.type === "image/gif") return false;
|
|
780
818
|
return true;
|
|
781
819
|
}
|
|
820
|
+
async function sniffImageMime(file) {
|
|
821
|
+
try {
|
|
822
|
+
const head = new Uint8Array(await file.slice(0, 16).arrayBuffer());
|
|
823
|
+
if (head.length < 4) return null;
|
|
824
|
+
if (head[0] === 137 && head[1] === 80 && head[2] === 78 && head[3] === 71) {
|
|
825
|
+
return { mime: "image/png", ext: "png" };
|
|
826
|
+
}
|
|
827
|
+
if (head[0] === 255 && head[1] === 216 && head[2] === 255) {
|
|
828
|
+
return { mime: "image/jpeg", ext: "jpg" };
|
|
829
|
+
}
|
|
830
|
+
if (head[0] === 71 && head[1] === 73 && head[2] === 70 && head[3] === 56) {
|
|
831
|
+
return { mime: "image/gif", ext: "gif" };
|
|
832
|
+
}
|
|
833
|
+
if (head[0] === 82 && head[1] === 73 && head[2] === 70 && head[3] === 70 && head[8] === 87 && head[9] === 69 && head[10] === 66 && head[11] === 80) {
|
|
834
|
+
return { mime: "image/webp", ext: "webp" };
|
|
835
|
+
}
|
|
836
|
+
if (head[0] === 66 && head[1] === 77) {
|
|
837
|
+
return { mime: "image/bmp", ext: "bmp" };
|
|
838
|
+
}
|
|
839
|
+
if (head[4] === 102 && head[5] === 116 && head[6] === 121 && head[7] === 112) {
|
|
840
|
+
const brand = String.fromCharCode(head[8], head[9], head[10], head[11]);
|
|
841
|
+
if (brand === "avif" || brand === "avis") return { mime: "image/avif", ext: "avif" };
|
|
842
|
+
if (brand === "heic" || brand === "heix" || brand === "mif1" || brand === "msf1") {
|
|
843
|
+
return { mime: "image/heic", ext: "heic" };
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
if (head[0] === 60) {
|
|
847
|
+
const text = new TextDecoder().decode(head);
|
|
848
|
+
if (/^<\?xml|^<svg/i.test(text)) return { mime: "image/svg+xml", ext: "svg" };
|
|
849
|
+
}
|
|
850
|
+
return null;
|
|
851
|
+
} catch {
|
|
852
|
+
return null;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
async function normalizeFileType(file, fallbackBaseName) {
|
|
856
|
+
const declaredType = (file.type || "").toLowerCase();
|
|
857
|
+
const hasExt = /\.[a-z0-9]+$/i.test(file.name);
|
|
858
|
+
if (declaredType.startsWith("image/") && hasExt) return file;
|
|
859
|
+
const sniffed = await sniffImageMime(file);
|
|
860
|
+
if (!sniffed) return file;
|
|
861
|
+
const base = (fallbackBaseName || file.name.replace(/\.[^.]+$/, "") || "image").trim() || "image";
|
|
862
|
+
const newName = `${base}.${sniffed.ext}`;
|
|
863
|
+
return new File([file], newName, { type: sniffed.mime, lastModified: file.lastModified });
|
|
864
|
+
}
|
|
782
865
|
async function loadImage(file) {
|
|
783
866
|
const url = URL.createObjectURL(file);
|
|
784
867
|
const img = new Image();
|
|
@@ -1350,18 +1433,22 @@ var UploadZone = ({
|
|
|
1350
1433
|
if (!items) return;
|
|
1351
1434
|
for (const item of Array.from(items)) {
|
|
1352
1435
|
if (item.kind === "file") {
|
|
1353
|
-
const
|
|
1354
|
-
if (!
|
|
1355
|
-
if (accept && !file.type.startsWith(accept.replace("*", ""))) continue;
|
|
1436
|
+
const raw = item.getAsFile();
|
|
1437
|
+
if (!raw) continue;
|
|
1356
1438
|
e.preventDefault();
|
|
1357
|
-
const
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1439
|
+
const defaultBase = !raw.name || raw.name === "image.png" || raw.name === "image" ? `pasted-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace(/[T:]/g, "-")}` : raw.name.replace(/\.[^.]+$/, "");
|
|
1440
|
+
void (async () => {
|
|
1441
|
+
const file = await normalizeFileType(raw, defaultBase);
|
|
1442
|
+
if (accept && file.type && !file.type.startsWith(accept.replace("*", ""))) return;
|
|
1443
|
+
const previewUrl = file.type.startsWith("image/") ? URL.createObjectURL(file) : "";
|
|
1444
|
+
const nameForDisplay = file.name.replace(/\.[^.]+$/, "") || defaultBase;
|
|
1445
|
+
setPastedFile({ file, previewUrl, name: nameForDisplay, origSize: file.size });
|
|
1446
|
+
setFileName(nameForDisplay);
|
|
1447
|
+
setEditingName(false);
|
|
1448
|
+
getImageDimensions(file).then((dims) => {
|
|
1449
|
+
if (dims) setPastedFile((prev) => prev && prev.file === file ? { ...prev, origDims: dims } : prev);
|
|
1450
|
+
});
|
|
1451
|
+
})();
|
|
1365
1452
|
return;
|
|
1366
1453
|
}
|
|
1367
1454
|
}
|
|
@@ -1410,7 +1497,8 @@ var UploadZone = ({
|
|
|
1410
1497
|
e.stopPropagation();
|
|
1411
1498
|
setDragOver(false);
|
|
1412
1499
|
}, []);
|
|
1413
|
-
const presentForRename = useCallback((
|
|
1500
|
+
const presentForRename = useCallback(async (raw) => {
|
|
1501
|
+
const file = await normalizeFileType(raw);
|
|
1414
1502
|
const previewUrl = file.type.startsWith("image/") ? URL.createObjectURL(file) : "";
|
|
1415
1503
|
const defaultName = file.name.replace(/\.[^.]+$/, "") || "file";
|
|
1416
1504
|
setPastedFile({ file, previewUrl, name: defaultName, origSize: file.size });
|
|
@@ -1426,7 +1514,7 @@ var UploadZone = ({
|
|
|
1426
1514
|
setDragOver(false);
|
|
1427
1515
|
const files = Array.from(e.dataTransfer.files);
|
|
1428
1516
|
if (files.length === 1 && !multiple) {
|
|
1429
|
-
presentForRename(files[0]);
|
|
1517
|
+
void presentForRename(files[0]);
|
|
1430
1518
|
} else if (files.length > 0) {
|
|
1431
1519
|
void handleBatchFiles(multiple ? files : [files[0]]);
|
|
1432
1520
|
}
|
|
@@ -1434,21 +1522,22 @@ var UploadZone = ({
|
|
|
1434
1522
|
const handleInputChange = useCallback((e) => {
|
|
1435
1523
|
const files = Array.from(e.target.files || []);
|
|
1436
1524
|
if (files.length === 1 && !multiple) {
|
|
1437
|
-
presentForRename(files[0]);
|
|
1525
|
+
void presentForRename(files[0]);
|
|
1438
1526
|
} else if (files.length > 0) {
|
|
1439
1527
|
void handleBatchFiles(multiple ? files : [files[0]]);
|
|
1440
1528
|
}
|
|
1441
1529
|
e.target.value = "";
|
|
1442
1530
|
}, [onFiles, multiple, presentForRename]);
|
|
1443
1531
|
const handleBatchFiles = useCallback(async (files) => {
|
|
1532
|
+
const normalized = await Promise.all(files.map((f) => normalizeFileType(f)));
|
|
1444
1533
|
if (!autoOptimize) {
|
|
1445
|
-
onFiles(
|
|
1534
|
+
onFiles(normalized);
|
|
1446
1535
|
return;
|
|
1447
1536
|
}
|
|
1448
1537
|
setOptimizing(true);
|
|
1449
1538
|
try {
|
|
1450
1539
|
const out = [];
|
|
1451
|
-
for (const f of
|
|
1540
|
+
for (const f of normalized) {
|
|
1452
1541
|
if (isProcessableImage(f)) {
|
|
1453
1542
|
const r = await processImage(f, {
|
|
1454
1543
|
maxDimension: optConfig.maxDimension,
|
|
@@ -1668,12 +1757,8 @@ var UploadZone = ({
|
|
|
1668
1757
|
onDragEnter: handleDragIn,
|
|
1669
1758
|
onDragLeave: handleDragOut,
|
|
1670
1759
|
onDrop: handleDrop,
|
|
1671
|
-
onClick: () =>
|
|
1672
|
-
role: "button",
|
|
1760
|
+
onClick: () => zoneRef.current?.focus(),
|
|
1673
1761
|
tabIndex: 0,
|
|
1674
|
-
onKeyDown: (e) => {
|
|
1675
|
-
if (e.key === "Enter" || e.key === " ") inputRef.current?.click();
|
|
1676
|
-
},
|
|
1677
1762
|
children: [
|
|
1678
1763
|
/* @__PURE__ */ jsx(
|
|
1679
1764
|
"input",
|
|
@@ -1699,8 +1784,20 @@ var UploadZone = ({
|
|
|
1699
1784
|
] }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-1 py-2", children: [
|
|
1700
1785
|
/* @__PURE__ */ jsx(Upload, { className: "w-6 h-6 text-muted-foreground" }),
|
|
1701
1786
|
/* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
|
|
1702
|
-
"Drop files here,
|
|
1703
|
-
|
|
1787
|
+
"Drop files here,",
|
|
1788
|
+
" ",
|
|
1789
|
+
/* @__PURE__ */ jsx(
|
|
1790
|
+
"button",
|
|
1791
|
+
{
|
|
1792
|
+
type: "button",
|
|
1793
|
+
onClick: (e) => {
|
|
1794
|
+
e.stopPropagation();
|
|
1795
|
+
inputRef.current?.click();
|
|
1796
|
+
},
|
|
1797
|
+
className: "text-primary underline hover:no-underline focus:outline-none focus:ring-1 focus:ring-ring rounded",
|
|
1798
|
+
children: "browse"
|
|
1799
|
+
}
|
|
1800
|
+
),
|
|
1704
1801
|
", or paste from clipboard"
|
|
1705
1802
|
] }),
|
|
1706
1803
|
/* @__PURE__ */ jsxs("p", { className: "text-[10px] text-muted-foreground flex items-center gap-1", children: [
|
|
@@ -2329,6 +2426,10 @@ var StockPhotoSearch = ({
|
|
|
2329
2426
|
}
|
|
2330
2427
|
}, [query, orientation, collectionId]);
|
|
2331
2428
|
const handlePick = useCallback((photo) => {
|
|
2429
|
+
setStaged((prev) => {
|
|
2430
|
+
if (prev?.previewUrl) URL.revokeObjectURL(prev.previewUrl);
|
|
2431
|
+
return null;
|
|
2432
|
+
});
|
|
2332
2433
|
const baseName = `stock-${(photo.alt || query.trim() || "photo").slice(0, 60).replace(/\s+/g, "-")}`;
|
|
2333
2434
|
setStaged({ photo, name: baseName, file: null, previewUrl: null });
|
|
2334
2435
|
}, [query]);
|
|
@@ -2543,7 +2644,7 @@ var StockPhotoSearch = ({
|
|
|
2543
2644
|
{
|
|
2544
2645
|
type: "button",
|
|
2545
2646
|
onClick: () => handlePick(photo),
|
|
2546
|
-
disabled:
|
|
2647
|
+
disabled: isStaged || saving,
|
|
2547
2648
|
className: cn(
|
|
2548
2649
|
"absolute inset-0 flex items-center justify-center text-xs font-medium",
|
|
2549
2650
|
"bg-background/80 opacity-0 group-hover:opacity-100 transition-opacity",
|
|
@@ -2717,8 +2818,359 @@ var TagEditor = ({ initial, suggestions, assetName, onCancel, onSave }) => {
|
|
|
2717
2818
|
}
|
|
2718
2819
|
);
|
|
2719
2820
|
};
|
|
2821
|
+
function getIcon2(mimeType) {
|
|
2822
|
+
if (!mimeType) return FileIcon;
|
|
2823
|
+
if (mimeType.startsWith("image/")) return Image$1;
|
|
2824
|
+
if (mimeType.startsWith("video/")) return Film;
|
|
2825
|
+
if (mimeType.startsWith("audio/")) return Music;
|
|
2826
|
+
return FileText;
|
|
2827
|
+
}
|
|
2828
|
+
function formatSize2(bytes) {
|
|
2829
|
+
if (bytes == null) return "\u2014";
|
|
2830
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
2831
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
2832
|
+
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
2833
|
+
}
|
|
2834
|
+
function formatAge(iso) {
|
|
2835
|
+
if (!iso) return "\u2014";
|
|
2836
|
+
const t = Date.parse(iso);
|
|
2837
|
+
if (Number.isNaN(t)) return iso;
|
|
2838
|
+
const diff = Date.now() - t;
|
|
2839
|
+
const day = 24 * 60 * 60 * 1e3;
|
|
2840
|
+
if (diff < 6e4) return "just now";
|
|
2841
|
+
if (diff < 36e5) return `${Math.floor(diff / 6e4)} min ago`;
|
|
2842
|
+
if (diff < day) return `${Math.floor(diff / 36e5)} h ago`;
|
|
2843
|
+
if (diff < 30 * day) return `${Math.floor(diff / day)} d ago`;
|
|
2844
|
+
if (diff < 365 * day) return `${Math.floor(diff / (30 * day))} mo ago`;
|
|
2845
|
+
return `${Math.floor(diff / (365 * day))} y ago`;
|
|
2846
|
+
}
|
|
2847
|
+
function isUnknownMime(asset2) {
|
|
2848
|
+
const m = (asset2.mimeType || "").toLowerCase();
|
|
2849
|
+
if (!m || m === "application/octet-stream" || m === "unknown" || m.endsWith("/unknown")) return true;
|
|
2850
|
+
const name = (asset2.name || asset2.cleanName || "").toLowerCase();
|
|
2851
|
+
if (/\.unknown(\?|$)/.test(name)) return true;
|
|
2852
|
+
return false;
|
|
2853
|
+
}
|
|
2854
|
+
function getThumbnailUrl(asset2) {
|
|
2855
|
+
if (asset2.thumbnail) return asset2.thumbnail;
|
|
2856
|
+
if (asset2.thumbnails?.x200) return asset2.thumbnails.x200;
|
|
2857
|
+
if (asset2.thumbnails?.x100) return asset2.thumbnails.x100;
|
|
2858
|
+
if (asset2.thumbnails?.x512) return asset2.thumbnails.x512;
|
|
2859
|
+
return null;
|
|
2860
|
+
}
|
|
2861
|
+
async function probeSize(url) {
|
|
2862
|
+
try {
|
|
2863
|
+
const res = await fetch(url, { method: "HEAD" });
|
|
2864
|
+
const len = res.headers.get("content-length");
|
|
2865
|
+
return len ? Number(len) : null;
|
|
2866
|
+
} catch {
|
|
2867
|
+
return null;
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
function probeImageDimensions(url) {
|
|
2871
|
+
return new Promise((resolve) => {
|
|
2872
|
+
const img = new Image();
|
|
2873
|
+
img.crossOrigin = "anonymous";
|
|
2874
|
+
img.onload = () => resolve({ w: img.naturalWidth, h: img.naturalHeight });
|
|
2875
|
+
img.onerror = () => resolve(null);
|
|
2876
|
+
img.src = url;
|
|
2877
|
+
});
|
|
2878
|
+
}
|
|
2879
|
+
var Row = ({ label, value, mono }) => /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-3 py-1", children: [
|
|
2880
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground", children: label }),
|
|
2881
|
+
/* @__PURE__ */ jsx("span", { className: cn("text-xs text-foreground text-right truncate", mono && "font-mono"), children: value })
|
|
2882
|
+
] });
|
|
2883
|
+
var AssetDetails = ({
|
|
2884
|
+
asset: asset2,
|
|
2885
|
+
onClose,
|
|
2886
|
+
onRename,
|
|
2887
|
+
onReplace,
|
|
2888
|
+
onEditTags,
|
|
2889
|
+
onEditImage,
|
|
2890
|
+
onDelete,
|
|
2891
|
+
onFix
|
|
2892
|
+
}) => {
|
|
2893
|
+
const Icon = getIcon2(asset2.mimeType);
|
|
2894
|
+
const thumbUrl = getThumbnailUrl(asset2);
|
|
2895
|
+
const isImage = (asset2.mimeType || "").startsWith("image/");
|
|
2896
|
+
const unknownMime = isUnknownMime(asset2);
|
|
2897
|
+
const missingThumbnail = !thumbUrl && isImage;
|
|
2898
|
+
const [dims, setDims] = useState(null);
|
|
2899
|
+
const [thumbDims, setThumbDims] = useState(null);
|
|
2900
|
+
const [thumbBytes, setThumbBytes] = useState(null);
|
|
2901
|
+
const [sourceMimeProbe, setSourceMimeProbe] = useState(null);
|
|
2902
|
+
const [fixing, setFixing] = useState(false);
|
|
2903
|
+
useEffect(() => {
|
|
2904
|
+
let cancelled = false;
|
|
2905
|
+
(async () => {
|
|
2906
|
+
if (isImage) {
|
|
2907
|
+
const d = await probeImageDimensions(asset2.url);
|
|
2908
|
+
if (!cancelled) setDims(d);
|
|
2909
|
+
}
|
|
2910
|
+
if (thumbUrl) {
|
|
2911
|
+
const [d, b] = await Promise.all([probeImageDimensions(thumbUrl), probeSize(thumbUrl)]);
|
|
2912
|
+
if (cancelled) return;
|
|
2913
|
+
setThumbDims(d);
|
|
2914
|
+
setThumbBytes(b);
|
|
2915
|
+
}
|
|
2916
|
+
if (unknownMime) {
|
|
2917
|
+
try {
|
|
2918
|
+
const res = await fetch(asset2.url);
|
|
2919
|
+
if (!res.ok) return;
|
|
2920
|
+
const blob = await res.blob();
|
|
2921
|
+
const file = new File([blob], asset2.name || "file", { type: blob.type });
|
|
2922
|
+
const sniffed = await sniffImageMime(file);
|
|
2923
|
+
if (!cancelled) setSourceMimeProbe(sniffed);
|
|
2924
|
+
} catch {
|
|
2925
|
+
}
|
|
2926
|
+
}
|
|
2927
|
+
})();
|
|
2928
|
+
return () => {
|
|
2929
|
+
cancelled = true;
|
|
2930
|
+
};
|
|
2931
|
+
}, [asset2.id, asset2.url, thumbUrl, isImage, unknownMime]);
|
|
2932
|
+
useEffect(() => {
|
|
2933
|
+
const onKey = (e) => {
|
|
2934
|
+
if (e.key === "Escape") {
|
|
2935
|
+
e.stopPropagation();
|
|
2936
|
+
onClose();
|
|
2937
|
+
}
|
|
2938
|
+
};
|
|
2939
|
+
window.addEventListener("keydown", onKey, true);
|
|
2940
|
+
return () => window.removeEventListener("keydown", onKey, true);
|
|
2941
|
+
}, [onClose]);
|
|
2942
|
+
const copyId = () => {
|
|
2943
|
+
try {
|
|
2944
|
+
navigator.clipboard?.writeText(asset2.id);
|
|
2945
|
+
} catch {
|
|
2946
|
+
}
|
|
2947
|
+
};
|
|
2948
|
+
const handleFix = async () => {
|
|
2949
|
+
if (!onFix || fixing) return;
|
|
2950
|
+
setFixing(true);
|
|
2951
|
+
try {
|
|
2952
|
+
await onFix(asset2);
|
|
2953
|
+
} finally {
|
|
2954
|
+
setFixing(false);
|
|
2955
|
+
}
|
|
2956
|
+
};
|
|
2957
|
+
if (typeof document === "undefined") return null;
|
|
2958
|
+
const warnings = [];
|
|
2959
|
+
if (unknownMime) {
|
|
2960
|
+
const suggestion = sourceMimeProbe ? ` Detected as ${sourceMimeProbe.mime}.` : "";
|
|
2961
|
+
warnings.push({ kind: "mime", text: `File type is missing or unknown.${suggestion}` });
|
|
2962
|
+
}
|
|
2963
|
+
if (missingThumbnail) warnings.push({ kind: "thumb", text: "Thumbnail has not been generated." });
|
|
2964
|
+
return createPortal(
|
|
2965
|
+
/* @__PURE__ */ jsxs(
|
|
2966
|
+
"div",
|
|
2967
|
+
{
|
|
2968
|
+
className: "fixed inset-0 z-[2147483646] flex items-center justify-center p-4",
|
|
2969
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
2970
|
+
onClick: (e) => e.stopPropagation(),
|
|
2971
|
+
children: [
|
|
2972
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/50", onClick: onClose }),
|
|
2973
|
+
/* @__PURE__ */ jsxs(
|
|
2974
|
+
"div",
|
|
2975
|
+
{
|
|
2976
|
+
role: "dialog",
|
|
2977
|
+
"aria-modal": "true",
|
|
2978
|
+
"aria-label": "Asset details",
|
|
2979
|
+
className: "relative w-full max-w-lg max-h-[90vh] overflow-auto rounded-lg border border-border bg-popover text-popover-foreground shadow-xl",
|
|
2980
|
+
children: [
|
|
2981
|
+
/* @__PURE__ */ jsxs("div", { className: "sticky top-0 z-10 flex items-center justify-between gap-2 px-4 py-3 border-b border-border bg-popover", children: [
|
|
2982
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold truncate", children: asset2.cleanName || asset2.name || "Asset details" }),
|
|
2983
|
+
/* @__PURE__ */ jsx(
|
|
2984
|
+
"button",
|
|
2985
|
+
{
|
|
2986
|
+
type: "button",
|
|
2987
|
+
onClick: onClose,
|
|
2988
|
+
className: "p-1 rounded hover:bg-accent text-muted-foreground",
|
|
2989
|
+
"aria-label": "Close",
|
|
2990
|
+
children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" })
|
|
2991
|
+
}
|
|
2992
|
+
)
|
|
2993
|
+
] }),
|
|
2994
|
+
/* @__PURE__ */ jsxs("div", { className: "p-4 space-y-4", children: [
|
|
2995
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
|
|
2996
|
+
/* @__PURE__ */ jsx("div", { className: "w-28 h-28 rounded-md bg-muted flex items-center justify-center overflow-hidden flex-shrink-0 border border-border", children: thumbUrl ? /* @__PURE__ */ jsx("img", { src: thumbUrl, alt: "", className: "w-full h-full object-cover" }) : isImage ? /* @__PURE__ */ jsx("img", { src: asset2.url, alt: "", className: "w-full h-full object-cover", onError: () => {
|
|
2997
|
+
} }) : /* @__PURE__ */ jsx(Icon, { className: "w-10 h-10 text-muted-foreground" }) }),
|
|
2998
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
2999
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium truncate", title: asset2.name, children: asset2.cleanName || asset2.name || asset2.id }),
|
|
3000
|
+
/* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground truncate", title: asset2.id, children: /* @__PURE__ */ jsxs("button", { type: "button", onClick: copyId, className: "inline-flex items-center gap-1 hover:text-foreground", children: [
|
|
3001
|
+
/* @__PURE__ */ jsx(Copy, { className: "w-3 h-3" }),
|
|
3002
|
+
" ",
|
|
3003
|
+
asset2.id
|
|
3004
|
+
] }) }),
|
|
3005
|
+
/* @__PURE__ */ jsxs(
|
|
3006
|
+
"a",
|
|
3007
|
+
{
|
|
3008
|
+
href: asset2.url,
|
|
3009
|
+
target: "_blank",
|
|
3010
|
+
rel: "noreferrer",
|
|
3011
|
+
className: "mt-1 inline-flex items-center gap-1 text-[11px] text-primary hover:underline",
|
|
3012
|
+
children: [
|
|
3013
|
+
/* @__PURE__ */ jsx(ExternalLink, { className: "w-3 h-3" }),
|
|
3014
|
+
" Open original"
|
|
3015
|
+
]
|
|
3016
|
+
}
|
|
3017
|
+
)
|
|
3018
|
+
] })
|
|
3019
|
+
] }),
|
|
3020
|
+
warnings.length > 0 && /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-amber-500/40 bg-amber-500/10 p-3 space-y-2", children: [
|
|
3021
|
+
warnings.map((w, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 text-xs text-foreground", children: [
|
|
3022
|
+
/* @__PURE__ */ jsx(AlertTriangle, { className: "w-3.5 h-3.5 text-amber-600 dark:text-amber-400 flex-shrink-0 mt-0.5" }),
|
|
3023
|
+
/* @__PURE__ */ jsx("span", { children: w.text })
|
|
3024
|
+
] }, i)),
|
|
3025
|
+
onFix && /* @__PURE__ */ jsxs(
|
|
3026
|
+
"button",
|
|
3027
|
+
{
|
|
3028
|
+
type: "button",
|
|
3029
|
+
onClick: handleFix,
|
|
3030
|
+
disabled: fixing,
|
|
3031
|
+
className: cn(
|
|
3032
|
+
"mt-1 inline-flex items-center gap-1.5 px-2.5 py-1 text-xs rounded-md",
|
|
3033
|
+
"bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
|
|
3034
|
+
),
|
|
3035
|
+
title: "Re-saves the file with the correct type so the backend regenerates the thumbnail.",
|
|
3036
|
+
children: [
|
|
3037
|
+
fixing ? /* @__PURE__ */ jsx(Loader2, { className: "w-3 h-3 animate-spin" }) : /* @__PURE__ */ jsx(Wrench, { className: "w-3 h-3" }),
|
|
3038
|
+
fixing ? "Fixing\u2026" : "Fix file type & thumbnail"
|
|
3039
|
+
]
|
|
3040
|
+
}
|
|
3041
|
+
)
|
|
3042
|
+
] }),
|
|
3043
|
+
warnings.length === 0 && isImage && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-emerald-700 dark:text-emerald-400", children: [
|
|
3044
|
+
/* @__PURE__ */ jsx(CheckCircle2, { className: "w-3.5 h-3.5" }),
|
|
3045
|
+
" File type and thumbnail look healthy."
|
|
3046
|
+
] }),
|
|
3047
|
+
/* @__PURE__ */ jsxs("section", { children: [
|
|
3048
|
+
/* @__PURE__ */ jsx("h4", { className: "text-[11px] font-semibold uppercase tracking-wide text-muted-foreground mb-1", children: "File" }),
|
|
3049
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-md border border-border divide-y divide-border px-3", children: [
|
|
3050
|
+
/* @__PURE__ */ jsx(Row, { label: "Type", value: asset2.mimeType || /* @__PURE__ */ jsx("span", { className: "text-amber-600 dark:text-amber-400", children: "unknown" }), mono: true }),
|
|
3051
|
+
/* @__PURE__ */ jsx(Row, { label: "Size", value: formatSize2(asset2.size) }),
|
|
3052
|
+
/* @__PURE__ */ jsx(Row, { label: "Dimensions", value: dims ? `${dims.w} \xD7 ${dims.h}` : isImage ? "\u2014" : "n/a" }),
|
|
3053
|
+
/* @__PURE__ */ jsx(Row, { label: "Uploaded", value: formatAge(asset2.createdAt) }),
|
|
3054
|
+
asset2.app && /* @__PURE__ */ jsx(Row, { label: "App", value: asset2.app, mono: true }),
|
|
3055
|
+
asset2.labels && asset2.labels.length > 0 && /* @__PURE__ */ jsx(Row, { label: "Labels", value: asset2.labels.join(", ") })
|
|
3056
|
+
] })
|
|
3057
|
+
] }),
|
|
3058
|
+
isImage && /* @__PURE__ */ jsxs("section", { children: [
|
|
3059
|
+
/* @__PURE__ */ jsx("h4", { className: "text-[11px] font-semibold uppercase tracking-wide text-muted-foreground mb-1", children: "Thumbnail" }),
|
|
3060
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-md border border-border divide-y divide-border px-3", children: [
|
|
3061
|
+
/* @__PURE__ */ jsx(
|
|
3062
|
+
Row,
|
|
3063
|
+
{
|
|
3064
|
+
label: "Status",
|
|
3065
|
+
value: thumbUrl ? /* @__PURE__ */ jsx("span", { className: "text-emerald-700 dark:text-emerald-400", children: "Generated" }) : /* @__PURE__ */ jsx("span", { className: "text-amber-600 dark:text-amber-400", children: "Missing" })
|
|
3066
|
+
}
|
|
3067
|
+
),
|
|
3068
|
+
thumbUrl && /* @__PURE__ */ jsx(Row, { label: "Dimensions", value: thumbDims ? `${thumbDims.w} \xD7 ${thumbDims.h}` : "\u2014" }),
|
|
3069
|
+
thumbUrl && /* @__PURE__ */ jsx(Row, { label: "Size", value: formatSize2(thumbBytes) })
|
|
3070
|
+
] })
|
|
3071
|
+
] }),
|
|
3072
|
+
/* @__PURE__ */ jsxs("section", { className: "flex flex-wrap gap-2 pt-1 border-t border-border", children: [
|
|
3073
|
+
onEditImage && (asset2.mimeType || "").startsWith("image/") && /* @__PURE__ */ jsxs(
|
|
3074
|
+
"button",
|
|
3075
|
+
{
|
|
3076
|
+
type: "button",
|
|
3077
|
+
onClick: () => {
|
|
3078
|
+
onClose();
|
|
3079
|
+
onEditImage(asset2);
|
|
3080
|
+
},
|
|
3081
|
+
className: "inline-flex items-center gap-1.5 px-2.5 py-1.5 text-xs rounded-md border border-border bg-background hover:bg-accent",
|
|
3082
|
+
children: [
|
|
3083
|
+
/* @__PURE__ */ jsx(Crop, { className: "w-3 h-3" }),
|
|
3084
|
+
" Edit image"
|
|
3085
|
+
]
|
|
3086
|
+
}
|
|
3087
|
+
),
|
|
3088
|
+
onRename && /* @__PURE__ */ jsxs(
|
|
3089
|
+
"button",
|
|
3090
|
+
{
|
|
3091
|
+
type: "button",
|
|
3092
|
+
onClick: () => {
|
|
3093
|
+
onClose();
|
|
3094
|
+
onRename(asset2);
|
|
3095
|
+
},
|
|
3096
|
+
className: "inline-flex items-center gap-1.5 px-2.5 py-1.5 text-xs rounded-md border border-border bg-background hover:bg-accent",
|
|
3097
|
+
children: [
|
|
3098
|
+
/* @__PURE__ */ jsx(Pencil, { className: "w-3 h-3" }),
|
|
3099
|
+
" Rename"
|
|
3100
|
+
]
|
|
3101
|
+
}
|
|
3102
|
+
),
|
|
3103
|
+
onReplace && /* @__PURE__ */ jsxs(
|
|
3104
|
+
"button",
|
|
3105
|
+
{
|
|
3106
|
+
type: "button",
|
|
3107
|
+
onClick: () => {
|
|
3108
|
+
onClose();
|
|
3109
|
+
onReplace(asset2);
|
|
3110
|
+
},
|
|
3111
|
+
className: "inline-flex items-center gap-1.5 px-2.5 py-1.5 text-xs rounded-md border border-border bg-background hover:bg-accent",
|
|
3112
|
+
children: [
|
|
3113
|
+
/* @__PURE__ */ jsx(Upload, { className: "w-3 h-3" }),
|
|
3114
|
+
" Replace file"
|
|
3115
|
+
]
|
|
3116
|
+
}
|
|
3117
|
+
),
|
|
3118
|
+
onEditTags && /* @__PURE__ */ jsxs(
|
|
3119
|
+
"button",
|
|
3120
|
+
{
|
|
3121
|
+
type: "button",
|
|
3122
|
+
onClick: () => {
|
|
3123
|
+
onClose();
|
|
3124
|
+
onEditTags(asset2);
|
|
3125
|
+
},
|
|
3126
|
+
className: "inline-flex items-center gap-1.5 px-2.5 py-1.5 text-xs rounded-md border border-border bg-background hover:bg-accent",
|
|
3127
|
+
children: [
|
|
3128
|
+
/* @__PURE__ */ jsx(Tag, { className: "w-3 h-3" }),
|
|
3129
|
+
" Edit tags"
|
|
3130
|
+
]
|
|
3131
|
+
}
|
|
3132
|
+
),
|
|
3133
|
+
onDelete && /* @__PURE__ */ jsxs(
|
|
3134
|
+
"button",
|
|
3135
|
+
{
|
|
3136
|
+
type: "button",
|
|
3137
|
+
onClick: () => {
|
|
3138
|
+
onClose();
|
|
3139
|
+
onDelete(asset2);
|
|
3140
|
+
},
|
|
3141
|
+
className: "ml-auto inline-flex items-center gap-1.5 px-2.5 py-1.5 text-xs rounded-md border border-destructive/40 text-destructive bg-background hover:bg-destructive/10",
|
|
3142
|
+
children: [
|
|
3143
|
+
/* @__PURE__ */ jsx(Trash2, { className: "w-3 h-3" }),
|
|
3144
|
+
" Delete"
|
|
3145
|
+
]
|
|
3146
|
+
}
|
|
3147
|
+
)
|
|
3148
|
+
] })
|
|
3149
|
+
] })
|
|
3150
|
+
]
|
|
3151
|
+
}
|
|
3152
|
+
)
|
|
3153
|
+
]
|
|
3154
|
+
}
|
|
3155
|
+
),
|
|
3156
|
+
document.body
|
|
3157
|
+
);
|
|
3158
|
+
};
|
|
3159
|
+
async function buildFixFile(asset2) {
|
|
3160
|
+
try {
|
|
3161
|
+
const res = await fetch(asset2.url);
|
|
3162
|
+
if (!res.ok) return null;
|
|
3163
|
+
const blob = await res.blob();
|
|
3164
|
+
const baseName = (asset2.cleanName || asset2.name || "file").replace(/\.[^.]+$/, "") || "file";
|
|
3165
|
+
const raw = new File([blob], asset2.name || baseName, { type: blob.type || "" });
|
|
3166
|
+
const normalised = await normalizeFileType(raw, baseName);
|
|
3167
|
+
return normalised;
|
|
3168
|
+
} catch {
|
|
3169
|
+
return null;
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
2720
3172
|
var InlineConfirm = ({ open, title, body, confirmLabel = "Confirm", cancelLabel = "Cancel", destructive, onConfirm, onCancel }) => {
|
|
2721
|
-
|
|
3173
|
+
React9.useEffect(() => {
|
|
2722
3174
|
if (!open) return;
|
|
2723
3175
|
const onKey = (e) => {
|
|
2724
3176
|
if (e.key === "Escape") {
|
|
@@ -2823,8 +3275,8 @@ var AttachToContextToggle = ({ checked, onChange, contextLabel }) => /* @__PURE_
|
|
|
2823
3275
|
] })
|
|
2824
3276
|
] });
|
|
2825
3277
|
var ScopedAssetBrowser = ({ scope: _scope, accept: _accept, pageSize: _pageSize, viewMode, search, selectedIds, onToggleSelect, onDoubleClickSelect, onDelete, allowDelete, emptyText, listAppId: _listAppId, currentAppId, currentAppName, getAppName, assets, loading, error, refresh, remove, updateAsset, replaceFile }) => {
|
|
2826
|
-
const replaceInputRef =
|
|
2827
|
-
const replaceTargetRef =
|
|
3278
|
+
const replaceInputRef = React9.useRef(null);
|
|
3279
|
+
const replaceTargetRef = React9.useRef(null);
|
|
2828
3280
|
const handleRename = useCallback(async (asset2) => {
|
|
2829
3281
|
const current = asset2.cleanName || asset2.name || "";
|
|
2830
3282
|
const next = window.prompt("Rename asset", current);
|
|
@@ -2880,6 +3332,16 @@ var ScopedAssetBrowser = ({ scope: _scope, accept: _accept, pageSize: _pageSize,
|
|
|
2880
3332
|
await updateAsset(tagEditorAsset.id, { labels: next });
|
|
2881
3333
|
setTagEditorAsset(null);
|
|
2882
3334
|
}, [tagEditorAsset, updateAsset]);
|
|
3335
|
+
const [detailsAsset, setDetailsAsset] = useState(null);
|
|
3336
|
+
const handleShowDetails = useCallback((asset2) => {
|
|
3337
|
+
setDetailsAsset(asset2);
|
|
3338
|
+
}, []);
|
|
3339
|
+
const handleFixAsset = useCallback(async (asset2) => {
|
|
3340
|
+
const fixed = await buildFixFile(asset2);
|
|
3341
|
+
if (!fixed) return;
|
|
3342
|
+
const result = await replaceFile(asset2.id, fixed);
|
|
3343
|
+
if (result) setDetailsAsset(result);
|
|
3344
|
+
}, [replaceFile]);
|
|
2883
3345
|
const [activeLabels, setActiveLabels] = useState(/* @__PURE__ */ new Set());
|
|
2884
3346
|
const toggleLabel = useCallback((label) => {
|
|
2885
3347
|
setActiveLabels((prev) => {
|
|
@@ -2979,6 +3441,7 @@ var ScopedAssetBrowser = ({ scope: _scope, accept: _accept, pageSize: _pageSize,
|
|
|
2979
3441
|
onReplace: handleReplace,
|
|
2980
3442
|
onEditTags: handleEditTags,
|
|
2981
3443
|
onEditImage: handleEditImage,
|
|
3444
|
+
onShowDetails: handleShowDetails,
|
|
2982
3445
|
allowDelete,
|
|
2983
3446
|
currentAppId,
|
|
2984
3447
|
currentAppName,
|
|
@@ -3024,6 +3487,19 @@ var ScopedAssetBrowser = ({ scope: _scope, accept: _accept, pageSize: _pageSize,
|
|
|
3024
3487
|
onSave: handleSaveTags
|
|
3025
3488
|
}
|
|
3026
3489
|
),
|
|
3490
|
+
detailsAsset && /* @__PURE__ */ jsx(
|
|
3491
|
+
AssetDetails,
|
|
3492
|
+
{
|
|
3493
|
+
asset: detailsAsset,
|
|
3494
|
+
onClose: () => setDetailsAsset(null),
|
|
3495
|
+
onRename: handleRename,
|
|
3496
|
+
onReplace: handleReplace,
|
|
3497
|
+
onEditTags: handleEditTags,
|
|
3498
|
+
onEditImage: handleEditImage,
|
|
3499
|
+
onDelete: allowDelete ? (a) => handleDeleteWithConfirm(a.id) : void 0,
|
|
3500
|
+
onFix: handleFixAsset
|
|
3501
|
+
}
|
|
3502
|
+
),
|
|
3027
3503
|
/* @__PURE__ */ jsx(
|
|
3028
3504
|
InlineConfirm,
|
|
3029
3505
|
{
|
|
@@ -3228,11 +3704,14 @@ var AssetPickerContent = ({
|
|
|
3228
3704
|
name,
|
|
3229
3705
|
...sameAsActive ? {} : { scopeOverride: targetScope }
|
|
3230
3706
|
});
|
|
3231
|
-
if (result
|
|
3232
|
-
|
|
3233
|
-
|
|
3707
|
+
if (result) {
|
|
3708
|
+
setTab("browse");
|
|
3709
|
+
if (!multiple) {
|
|
3710
|
+
setSelectedIds(/* @__PURE__ */ new Set([result.id]));
|
|
3711
|
+
onSelect?.(toSelection(result));
|
|
3712
|
+
}
|
|
3713
|
+
await reconcileAfterUpload(targetScope);
|
|
3234
3714
|
}
|
|
3235
|
-
if (result) await reconcileAfterUpload(targetScope);
|
|
3236
3715
|
return result;
|
|
3237
3716
|
}, [uploadFromRemoteUrl, multiple, onSelect, toSelection, uploadScope, activeScope, reconcileAfterUpload]);
|
|
3238
3717
|
const handleDelete = useCallback(async (assetId) => {
|
|
@@ -3634,5 +4113,5 @@ var AssetPicker = (props) => {
|
|
|
3634
4113
|
assertStylesLoaded();
|
|
3635
4114
|
|
|
3636
4115
|
export { ASSET_MIME_FILTERS, AssetPicker, useAppRegistry, useAssets };
|
|
3637
|
-
//# sourceMappingURL=chunk-
|
|
3638
|
-
//# sourceMappingURL=chunk-
|
|
4116
|
+
//# sourceMappingURL=chunk-I3T36FSI.js.map
|
|
4117
|
+
//# sourceMappingURL=chunk-I3T36FSI.js.map
|