@proveanything/smartlinks-utils-ui 1.13.8 → 1.13.11
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/{chunk-OTJV62XV.js → chunk-5ZQT2GGU.js} +5 -5
- package/dist/chunk-5ZQT2GGU.js.map +1 -0
- package/dist/{chunk-E3GQ6LNZ.js → chunk-7RWLFKHC.js} +502 -39
- package/dist/chunk-7RWLFKHC.js.map +1 -0
- package/dist/{chunk-7UBXTFZQ.js → chunk-A4YZYKWT.js} +9 -8
- package/dist/chunk-A4YZYKWT.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-JMCV6FOW.js → chunk-WVCNIX7N.js} +3 -3
- package/dist/{chunk-JMCV6FOW.js.map → chunk-WVCNIX7N.js.map} +1 -1
- 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.js +2 -2
- package/dist/index.css +34 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -5
- package/dist/{types-a2DdgZ2H.d.ts → types-BLqki3Zy.d.ts} +11 -0
- package/package.json +3 -3
- 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
|
|
@@ -350,7 +350,7 @@ var AppBadge = ({ appId, appName, size = "sm" }) => {
|
|
|
350
350
|
}
|
|
351
351
|
);
|
|
352
352
|
};
|
|
353
|
-
var CardMenu = ({ onRename, onReplace, onEditTags, onEditImage, onDelete, position = "absolute" }) => {
|
|
353
|
+
var CardMenu = ({ onRename, onReplace, onEditTags, onEditImage, onShowDetails, onDelete, position = "absolute" }) => {
|
|
354
354
|
const [open, setOpen] = useState(false);
|
|
355
355
|
const ref = useRef(null);
|
|
356
356
|
const btnRef = useRef(null);
|
|
@@ -402,7 +402,7 @@ var CardMenu = ({ onRename, onReplace, onEditTags, onEditImage, onDelete, positi
|
|
|
402
402
|
window.removeEventListener("scroll", update, true);
|
|
403
403
|
};
|
|
404
404
|
}, [open]);
|
|
405
|
-
if (!onRename && !onReplace && !onEditTags && !onEditImage && !onDelete) return null;
|
|
405
|
+
if (!onRename && !onReplace && !onEditTags && !onEditImage && !onShowDetails && !onDelete) return null;
|
|
406
406
|
return /* @__PURE__ */ jsxs(
|
|
407
407
|
"div",
|
|
408
408
|
{
|
|
@@ -446,6 +446,23 @@ var CardMenu = ({ onRename, onReplace, onEditTags, onEditImage, onDelete, positi
|
|
|
446
446
|
onClick: (e) => e.stopPropagation(),
|
|
447
447
|
onMouseDown: (e) => e.stopPropagation(),
|
|
448
448
|
children: [
|
|
449
|
+
onShowDetails && /* @__PURE__ */ jsxs(
|
|
450
|
+
"button",
|
|
451
|
+
{
|
|
452
|
+
type: "button",
|
|
453
|
+
role: "menuitem",
|
|
454
|
+
onClick: (e) => {
|
|
455
|
+
e.stopPropagation();
|
|
456
|
+
setOpen(false);
|
|
457
|
+
onShowDetails();
|
|
458
|
+
},
|
|
459
|
+
className: "w-full flex items-center gap-2 px-2.5 py-1.5 text-xs hover:bg-accent",
|
|
460
|
+
children: [
|
|
461
|
+
/* @__PURE__ */ jsx(Info, { className: "w-3 h-3" }),
|
|
462
|
+
" Details"
|
|
463
|
+
]
|
|
464
|
+
}
|
|
465
|
+
),
|
|
449
466
|
onRename && /* @__PURE__ */ jsxs(
|
|
450
467
|
"button",
|
|
451
468
|
{
|
|
@@ -540,7 +557,7 @@ var CardMenu = ({ onRename, onReplace, onEditTags, onEditImage, onDelete, positi
|
|
|
540
557
|
}
|
|
541
558
|
);
|
|
542
559
|
};
|
|
543
|
-
var AssetGridItem = ({ asset: asset2, selected, onToggle, onDoubleClick, onDelete, onRename, onReplace, onEditTags, onEditImage, allowDelete, currentAppId, getAppName, activeLabels, onToggleLabel }) => {
|
|
560
|
+
var AssetGridItem = ({ asset: asset2, selected, onToggle, onDoubleClick, onDelete, onRename, onReplace, onEditTags, onEditImage, onShowDetails, allowDelete, currentAppId, getAppName, activeLabels, onToggleLabel }) => {
|
|
544
561
|
const thumb = getThumbnail(asset2);
|
|
545
562
|
const Icon = getIcon(asset2.mimeType);
|
|
546
563
|
const ownerAppId = getAssetAppId(asset2);
|
|
@@ -622,6 +639,7 @@ var AssetGridItem = ({ asset: asset2, selected, onToggle, onDoubleClick, onDelet
|
|
|
622
639
|
onReplace,
|
|
623
640
|
onEditTags,
|
|
624
641
|
onEditImage,
|
|
642
|
+
onShowDetails,
|
|
625
643
|
onDelete: allowDelete ? onDelete : void 0
|
|
626
644
|
}
|
|
627
645
|
)
|
|
@@ -629,7 +647,7 @@ var AssetGridItem = ({ asset: asset2, selected, onToggle, onDoubleClick, onDelet
|
|
|
629
647
|
}
|
|
630
648
|
);
|
|
631
649
|
};
|
|
632
|
-
var AssetListItem = ({ asset: asset2, selected, onToggle, onDoubleClick, onDelete, onRename, onReplace, onEditTags, onEditImage, allowDelete, currentAppId, getAppName, activeLabels, onToggleLabel }) => {
|
|
650
|
+
var AssetListItem = ({ asset: asset2, selected, onToggle, onDoubleClick, onDelete, onRename, onReplace, onEditTags, onEditImage, onShowDetails, allowDelete, currentAppId, getAppName, activeLabels, onToggleLabel }) => {
|
|
633
651
|
const thumb = getThumbnail(asset2);
|
|
634
652
|
const Icon = getIcon(asset2.mimeType);
|
|
635
653
|
const ownerAppId = getAssetAppId(asset2);
|
|
@@ -699,6 +717,7 @@ var AssetListItem = ({ asset: asset2, selected, onToggle, onDoubleClick, onDelet
|
|
|
699
717
|
onReplace,
|
|
700
718
|
onEditTags,
|
|
701
719
|
onEditImage,
|
|
720
|
+
onShowDetails,
|
|
702
721
|
onDelete: allowDelete ? onDelete : void 0,
|
|
703
722
|
position: "inline"
|
|
704
723
|
}
|
|
@@ -718,6 +737,7 @@ var AssetGrid = ({
|
|
|
718
737
|
onReplace,
|
|
719
738
|
onEditTags,
|
|
720
739
|
onEditImage,
|
|
740
|
+
onShowDetails,
|
|
721
741
|
allowDelete,
|
|
722
742
|
currentAppId,
|
|
723
743
|
getAppName,
|
|
@@ -738,6 +758,7 @@ var AssetGrid = ({
|
|
|
738
758
|
onReplace: onReplace ? () => onReplace(asset2) : void 0,
|
|
739
759
|
onEditTags: onEditTags ? () => onEditTags(asset2) : void 0,
|
|
740
760
|
onEditImage: onEditImage ? () => onEditImage(asset2) : void 0,
|
|
761
|
+
onShowDetails: onShowDetails ? () => onShowDetails(asset2) : void 0,
|
|
741
762
|
allowDelete,
|
|
742
763
|
currentAppId,
|
|
743
764
|
getAppName,
|
|
@@ -759,6 +780,7 @@ var AssetGrid = ({
|
|
|
759
780
|
onReplace: onReplace ? () => onReplace(asset2) : void 0,
|
|
760
781
|
onEditTags: onEditTags ? () => onEditTags(asset2) : void 0,
|
|
761
782
|
onEditImage: onEditImage ? () => onEditImage(asset2) : void 0,
|
|
783
|
+
onShowDetails: onShowDetails ? () => onShowDetails(asset2) : void 0,
|
|
762
784
|
allowDelete,
|
|
763
785
|
currentAppId,
|
|
764
786
|
getAppName,
|
|
@@ -779,6 +801,51 @@ function isProcessableImage(file) {
|
|
|
779
801
|
if (file.type === "image/svg+xml" || file.type === "image/gif") return false;
|
|
780
802
|
return true;
|
|
781
803
|
}
|
|
804
|
+
async function sniffImageMime(file) {
|
|
805
|
+
try {
|
|
806
|
+
const head = new Uint8Array(await file.slice(0, 16).arrayBuffer());
|
|
807
|
+
if (head.length < 4) return null;
|
|
808
|
+
if (head[0] === 137 && head[1] === 80 && head[2] === 78 && head[3] === 71) {
|
|
809
|
+
return { mime: "image/png", ext: "png" };
|
|
810
|
+
}
|
|
811
|
+
if (head[0] === 255 && head[1] === 216 && head[2] === 255) {
|
|
812
|
+
return { mime: "image/jpeg", ext: "jpg" };
|
|
813
|
+
}
|
|
814
|
+
if (head[0] === 71 && head[1] === 73 && head[2] === 70 && head[3] === 56) {
|
|
815
|
+
return { mime: "image/gif", ext: "gif" };
|
|
816
|
+
}
|
|
817
|
+
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) {
|
|
818
|
+
return { mime: "image/webp", ext: "webp" };
|
|
819
|
+
}
|
|
820
|
+
if (head[0] === 66 && head[1] === 77) {
|
|
821
|
+
return { mime: "image/bmp", ext: "bmp" };
|
|
822
|
+
}
|
|
823
|
+
if (head[4] === 102 && head[5] === 116 && head[6] === 121 && head[7] === 112) {
|
|
824
|
+
const brand = String.fromCharCode(head[8], head[9], head[10], head[11]);
|
|
825
|
+
if (brand === "avif" || brand === "avis") return { mime: "image/avif", ext: "avif" };
|
|
826
|
+
if (brand === "heic" || brand === "heix" || brand === "mif1" || brand === "msf1") {
|
|
827
|
+
return { mime: "image/heic", ext: "heic" };
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
if (head[0] === 60) {
|
|
831
|
+
const text = new TextDecoder().decode(head);
|
|
832
|
+
if (/^<\?xml|^<svg/i.test(text)) return { mime: "image/svg+xml", ext: "svg" };
|
|
833
|
+
}
|
|
834
|
+
return null;
|
|
835
|
+
} catch {
|
|
836
|
+
return null;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
async function normalizeFileType(file, fallbackBaseName) {
|
|
840
|
+
const declaredType = (file.type || "").toLowerCase();
|
|
841
|
+
const hasExt = /\.[a-z0-9]+$/i.test(file.name);
|
|
842
|
+
if (declaredType.startsWith("image/") && hasExt) return file;
|
|
843
|
+
const sniffed = await sniffImageMime(file);
|
|
844
|
+
if (!sniffed) return file;
|
|
845
|
+
const base = (fallbackBaseName || file.name.replace(/\.[^.]+$/, "") || "image").trim() || "image";
|
|
846
|
+
const newName = `${base}.${sniffed.ext}`;
|
|
847
|
+
return new File([file], newName, { type: sniffed.mime, lastModified: file.lastModified });
|
|
848
|
+
}
|
|
782
849
|
async function loadImage(file) {
|
|
783
850
|
const url = URL.createObjectURL(file);
|
|
784
851
|
const img = new Image();
|
|
@@ -1350,18 +1417,22 @@ var UploadZone = ({
|
|
|
1350
1417
|
if (!items) return;
|
|
1351
1418
|
for (const item of Array.from(items)) {
|
|
1352
1419
|
if (item.kind === "file") {
|
|
1353
|
-
const
|
|
1354
|
-
if (!
|
|
1355
|
-
if (accept && !file.type.startsWith(accept.replace("*", ""))) continue;
|
|
1420
|
+
const raw = item.getAsFile();
|
|
1421
|
+
if (!raw) continue;
|
|
1356
1422
|
e.preventDefault();
|
|
1357
|
-
const
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1423
|
+
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(/\.[^.]+$/, "");
|
|
1424
|
+
void (async () => {
|
|
1425
|
+
const file = await normalizeFileType(raw, defaultBase);
|
|
1426
|
+
if (accept && file.type && !file.type.startsWith(accept.replace("*", ""))) return;
|
|
1427
|
+
const previewUrl = file.type.startsWith("image/") ? URL.createObjectURL(file) : "";
|
|
1428
|
+
const nameForDisplay = file.name.replace(/\.[^.]+$/, "") || defaultBase;
|
|
1429
|
+
setPastedFile({ file, previewUrl, name: nameForDisplay, origSize: file.size });
|
|
1430
|
+
setFileName(nameForDisplay);
|
|
1431
|
+
setEditingName(false);
|
|
1432
|
+
getImageDimensions(file).then((dims) => {
|
|
1433
|
+
if (dims) setPastedFile((prev) => prev && prev.file === file ? { ...prev, origDims: dims } : prev);
|
|
1434
|
+
});
|
|
1435
|
+
})();
|
|
1365
1436
|
return;
|
|
1366
1437
|
}
|
|
1367
1438
|
}
|
|
@@ -1410,7 +1481,8 @@ var UploadZone = ({
|
|
|
1410
1481
|
e.stopPropagation();
|
|
1411
1482
|
setDragOver(false);
|
|
1412
1483
|
}, []);
|
|
1413
|
-
const presentForRename = useCallback((
|
|
1484
|
+
const presentForRename = useCallback(async (raw) => {
|
|
1485
|
+
const file = await normalizeFileType(raw);
|
|
1414
1486
|
const previewUrl = file.type.startsWith("image/") ? URL.createObjectURL(file) : "";
|
|
1415
1487
|
const defaultName = file.name.replace(/\.[^.]+$/, "") || "file";
|
|
1416
1488
|
setPastedFile({ file, previewUrl, name: defaultName, origSize: file.size });
|
|
@@ -1426,7 +1498,7 @@ var UploadZone = ({
|
|
|
1426
1498
|
setDragOver(false);
|
|
1427
1499
|
const files = Array.from(e.dataTransfer.files);
|
|
1428
1500
|
if (files.length === 1 && !multiple) {
|
|
1429
|
-
presentForRename(files[0]);
|
|
1501
|
+
void presentForRename(files[0]);
|
|
1430
1502
|
} else if (files.length > 0) {
|
|
1431
1503
|
void handleBatchFiles(multiple ? files : [files[0]]);
|
|
1432
1504
|
}
|
|
@@ -1434,21 +1506,22 @@ var UploadZone = ({
|
|
|
1434
1506
|
const handleInputChange = useCallback((e) => {
|
|
1435
1507
|
const files = Array.from(e.target.files || []);
|
|
1436
1508
|
if (files.length === 1 && !multiple) {
|
|
1437
|
-
presentForRename(files[0]);
|
|
1509
|
+
void presentForRename(files[0]);
|
|
1438
1510
|
} else if (files.length > 0) {
|
|
1439
1511
|
void handleBatchFiles(multiple ? files : [files[0]]);
|
|
1440
1512
|
}
|
|
1441
1513
|
e.target.value = "";
|
|
1442
1514
|
}, [onFiles, multiple, presentForRename]);
|
|
1443
1515
|
const handleBatchFiles = useCallback(async (files) => {
|
|
1516
|
+
const normalized = await Promise.all(files.map((f) => normalizeFileType(f)));
|
|
1444
1517
|
if (!autoOptimize) {
|
|
1445
|
-
onFiles(
|
|
1518
|
+
onFiles(normalized);
|
|
1446
1519
|
return;
|
|
1447
1520
|
}
|
|
1448
1521
|
setOptimizing(true);
|
|
1449
1522
|
try {
|
|
1450
1523
|
const out = [];
|
|
1451
|
-
for (const f of
|
|
1524
|
+
for (const f of normalized) {
|
|
1452
1525
|
if (isProcessableImage(f)) {
|
|
1453
1526
|
const r = await processImage(f, {
|
|
1454
1527
|
maxDimension: optConfig.maxDimension,
|
|
@@ -1668,12 +1741,8 @@ var UploadZone = ({
|
|
|
1668
1741
|
onDragEnter: handleDragIn,
|
|
1669
1742
|
onDragLeave: handleDragOut,
|
|
1670
1743
|
onDrop: handleDrop,
|
|
1671
|
-
onClick: () =>
|
|
1672
|
-
role: "button",
|
|
1744
|
+
onClick: () => zoneRef.current?.focus(),
|
|
1673
1745
|
tabIndex: 0,
|
|
1674
|
-
onKeyDown: (e) => {
|
|
1675
|
-
if (e.key === "Enter" || e.key === " ") inputRef.current?.click();
|
|
1676
|
-
},
|
|
1677
1746
|
children: [
|
|
1678
1747
|
/* @__PURE__ */ jsx(
|
|
1679
1748
|
"input",
|
|
@@ -1699,8 +1768,20 @@ var UploadZone = ({
|
|
|
1699
1768
|
] }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-1 py-2", children: [
|
|
1700
1769
|
/* @__PURE__ */ jsx(Upload, { className: "w-6 h-6 text-muted-foreground" }),
|
|
1701
1770
|
/* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
|
|
1702
|
-
"Drop files here,
|
|
1703
|
-
|
|
1771
|
+
"Drop files here,",
|
|
1772
|
+
" ",
|
|
1773
|
+
/* @__PURE__ */ jsx(
|
|
1774
|
+
"button",
|
|
1775
|
+
{
|
|
1776
|
+
type: "button",
|
|
1777
|
+
onClick: (e) => {
|
|
1778
|
+
e.stopPropagation();
|
|
1779
|
+
inputRef.current?.click();
|
|
1780
|
+
},
|
|
1781
|
+
className: "text-primary underline hover:no-underline focus:outline-none focus:ring-1 focus:ring-ring rounded",
|
|
1782
|
+
children: "browse"
|
|
1783
|
+
}
|
|
1784
|
+
),
|
|
1704
1785
|
", or paste from clipboard"
|
|
1705
1786
|
] }),
|
|
1706
1787
|
/* @__PURE__ */ jsxs("p", { className: "text-[10px] text-muted-foreground flex items-center gap-1", children: [
|
|
@@ -2329,6 +2410,10 @@ var StockPhotoSearch = ({
|
|
|
2329
2410
|
}
|
|
2330
2411
|
}, [query, orientation, collectionId]);
|
|
2331
2412
|
const handlePick = useCallback((photo) => {
|
|
2413
|
+
setStaged((prev) => {
|
|
2414
|
+
if (prev?.previewUrl) URL.revokeObjectURL(prev.previewUrl);
|
|
2415
|
+
return null;
|
|
2416
|
+
});
|
|
2332
2417
|
const baseName = `stock-${(photo.alt || query.trim() || "photo").slice(0, 60).replace(/\s+/g, "-")}`;
|
|
2333
2418
|
setStaged({ photo, name: baseName, file: null, previewUrl: null });
|
|
2334
2419
|
}, [query]);
|
|
@@ -2543,7 +2628,7 @@ var StockPhotoSearch = ({
|
|
|
2543
2628
|
{
|
|
2544
2629
|
type: "button",
|
|
2545
2630
|
onClick: () => handlePick(photo),
|
|
2546
|
-
disabled:
|
|
2631
|
+
disabled: isStaged || saving,
|
|
2547
2632
|
className: cn(
|
|
2548
2633
|
"absolute inset-0 flex items-center justify-center text-xs font-medium",
|
|
2549
2634
|
"bg-background/80 opacity-0 group-hover:opacity-100 transition-opacity",
|
|
@@ -2717,8 +2802,359 @@ var TagEditor = ({ initial, suggestions, assetName, onCancel, onSave }) => {
|
|
|
2717
2802
|
}
|
|
2718
2803
|
);
|
|
2719
2804
|
};
|
|
2805
|
+
function getIcon2(mimeType) {
|
|
2806
|
+
if (!mimeType) return FileIcon;
|
|
2807
|
+
if (mimeType.startsWith("image/")) return Image$1;
|
|
2808
|
+
if (mimeType.startsWith("video/")) return Film;
|
|
2809
|
+
if (mimeType.startsWith("audio/")) return Music;
|
|
2810
|
+
return FileText;
|
|
2811
|
+
}
|
|
2812
|
+
function formatSize2(bytes) {
|
|
2813
|
+
if (bytes == null) return "\u2014";
|
|
2814
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
2815
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
2816
|
+
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
2817
|
+
}
|
|
2818
|
+
function formatAge(iso) {
|
|
2819
|
+
if (!iso) return "\u2014";
|
|
2820
|
+
const t = Date.parse(iso);
|
|
2821
|
+
if (Number.isNaN(t)) return iso;
|
|
2822
|
+
const diff = Date.now() - t;
|
|
2823
|
+
const day = 24 * 60 * 60 * 1e3;
|
|
2824
|
+
if (diff < 6e4) return "just now";
|
|
2825
|
+
if (diff < 36e5) return `${Math.floor(diff / 6e4)} min ago`;
|
|
2826
|
+
if (diff < day) return `${Math.floor(diff / 36e5)} h ago`;
|
|
2827
|
+
if (diff < 30 * day) return `${Math.floor(diff / day)} d ago`;
|
|
2828
|
+
if (diff < 365 * day) return `${Math.floor(diff / (30 * day))} mo ago`;
|
|
2829
|
+
return `${Math.floor(diff / (365 * day))} y ago`;
|
|
2830
|
+
}
|
|
2831
|
+
function isUnknownMime(asset2) {
|
|
2832
|
+
const m = (asset2.mimeType || "").toLowerCase();
|
|
2833
|
+
if (!m || m === "application/octet-stream" || m === "unknown" || m.endsWith("/unknown")) return true;
|
|
2834
|
+
const name = (asset2.name || asset2.cleanName || "").toLowerCase();
|
|
2835
|
+
if (/\.unknown(\?|$)/.test(name)) return true;
|
|
2836
|
+
return false;
|
|
2837
|
+
}
|
|
2838
|
+
function getThumbnailUrl(asset2) {
|
|
2839
|
+
if (asset2.thumbnail) return asset2.thumbnail;
|
|
2840
|
+
if (asset2.thumbnails?.x200) return asset2.thumbnails.x200;
|
|
2841
|
+
if (asset2.thumbnails?.x100) return asset2.thumbnails.x100;
|
|
2842
|
+
if (asset2.thumbnails?.x512) return asset2.thumbnails.x512;
|
|
2843
|
+
return null;
|
|
2844
|
+
}
|
|
2845
|
+
async function probeSize(url) {
|
|
2846
|
+
try {
|
|
2847
|
+
const res = await fetch(url, { method: "HEAD" });
|
|
2848
|
+
const len = res.headers.get("content-length");
|
|
2849
|
+
return len ? Number(len) : null;
|
|
2850
|
+
} catch {
|
|
2851
|
+
return null;
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
function probeImageDimensions(url) {
|
|
2855
|
+
return new Promise((resolve) => {
|
|
2856
|
+
const img = new Image();
|
|
2857
|
+
img.crossOrigin = "anonymous";
|
|
2858
|
+
img.onload = () => resolve({ w: img.naturalWidth, h: img.naturalHeight });
|
|
2859
|
+
img.onerror = () => resolve(null);
|
|
2860
|
+
img.src = url;
|
|
2861
|
+
});
|
|
2862
|
+
}
|
|
2863
|
+
var Row = ({ label, value, mono }) => /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-3 py-1", children: [
|
|
2864
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground", children: label }),
|
|
2865
|
+
/* @__PURE__ */ jsx("span", { className: cn("text-xs text-foreground text-right truncate", mono && "font-mono"), children: value })
|
|
2866
|
+
] });
|
|
2867
|
+
var AssetDetails = ({
|
|
2868
|
+
asset: asset2,
|
|
2869
|
+
onClose,
|
|
2870
|
+
onRename,
|
|
2871
|
+
onReplace,
|
|
2872
|
+
onEditTags,
|
|
2873
|
+
onEditImage,
|
|
2874
|
+
onDelete,
|
|
2875
|
+
onFix
|
|
2876
|
+
}) => {
|
|
2877
|
+
const Icon = getIcon2(asset2.mimeType);
|
|
2878
|
+
const thumbUrl = getThumbnailUrl(asset2);
|
|
2879
|
+
const isImage = (asset2.mimeType || "").startsWith("image/");
|
|
2880
|
+
const unknownMime = isUnknownMime(asset2);
|
|
2881
|
+
const missingThumbnail = !thumbUrl && isImage;
|
|
2882
|
+
const [dims, setDims] = useState(null);
|
|
2883
|
+
const [thumbDims, setThumbDims] = useState(null);
|
|
2884
|
+
const [thumbBytes, setThumbBytes] = useState(null);
|
|
2885
|
+
const [sourceMimeProbe, setSourceMimeProbe] = useState(null);
|
|
2886
|
+
const [fixing, setFixing] = useState(false);
|
|
2887
|
+
useEffect(() => {
|
|
2888
|
+
let cancelled = false;
|
|
2889
|
+
(async () => {
|
|
2890
|
+
if (isImage) {
|
|
2891
|
+
const d = await probeImageDimensions(asset2.url);
|
|
2892
|
+
if (!cancelled) setDims(d);
|
|
2893
|
+
}
|
|
2894
|
+
if (thumbUrl) {
|
|
2895
|
+
const [d, b] = await Promise.all([probeImageDimensions(thumbUrl), probeSize(thumbUrl)]);
|
|
2896
|
+
if (cancelled) return;
|
|
2897
|
+
setThumbDims(d);
|
|
2898
|
+
setThumbBytes(b);
|
|
2899
|
+
}
|
|
2900
|
+
if (unknownMime) {
|
|
2901
|
+
try {
|
|
2902
|
+
const res = await fetch(asset2.url);
|
|
2903
|
+
if (!res.ok) return;
|
|
2904
|
+
const blob = await res.blob();
|
|
2905
|
+
const file = new File([blob], asset2.name || "file", { type: blob.type });
|
|
2906
|
+
const sniffed = await sniffImageMime(file);
|
|
2907
|
+
if (!cancelled) setSourceMimeProbe(sniffed);
|
|
2908
|
+
} catch {
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
})();
|
|
2912
|
+
return () => {
|
|
2913
|
+
cancelled = true;
|
|
2914
|
+
};
|
|
2915
|
+
}, [asset2.id, asset2.url, thumbUrl, isImage, unknownMime]);
|
|
2916
|
+
useEffect(() => {
|
|
2917
|
+
const onKey = (e) => {
|
|
2918
|
+
if (e.key === "Escape") {
|
|
2919
|
+
e.stopPropagation();
|
|
2920
|
+
onClose();
|
|
2921
|
+
}
|
|
2922
|
+
};
|
|
2923
|
+
window.addEventListener("keydown", onKey, true);
|
|
2924
|
+
return () => window.removeEventListener("keydown", onKey, true);
|
|
2925
|
+
}, [onClose]);
|
|
2926
|
+
const copyId = () => {
|
|
2927
|
+
try {
|
|
2928
|
+
navigator.clipboard?.writeText(asset2.id);
|
|
2929
|
+
} catch {
|
|
2930
|
+
}
|
|
2931
|
+
};
|
|
2932
|
+
const handleFix = async () => {
|
|
2933
|
+
if (!onFix || fixing) return;
|
|
2934
|
+
setFixing(true);
|
|
2935
|
+
try {
|
|
2936
|
+
await onFix(asset2);
|
|
2937
|
+
} finally {
|
|
2938
|
+
setFixing(false);
|
|
2939
|
+
}
|
|
2940
|
+
};
|
|
2941
|
+
if (typeof document === "undefined") return null;
|
|
2942
|
+
const warnings = [];
|
|
2943
|
+
if (unknownMime) {
|
|
2944
|
+
const suggestion = sourceMimeProbe ? ` Detected as ${sourceMimeProbe.mime}.` : "";
|
|
2945
|
+
warnings.push({ kind: "mime", text: `File type is missing or unknown.${suggestion}` });
|
|
2946
|
+
}
|
|
2947
|
+
if (missingThumbnail) warnings.push({ kind: "thumb", text: "Thumbnail has not been generated." });
|
|
2948
|
+
return createPortal(
|
|
2949
|
+
/* @__PURE__ */ jsxs(
|
|
2950
|
+
"div",
|
|
2951
|
+
{
|
|
2952
|
+
className: "fixed inset-0 z-[2147483646] flex items-center justify-center p-4",
|
|
2953
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
2954
|
+
onClick: (e) => e.stopPropagation(),
|
|
2955
|
+
children: [
|
|
2956
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/50", onClick: onClose }),
|
|
2957
|
+
/* @__PURE__ */ jsxs(
|
|
2958
|
+
"div",
|
|
2959
|
+
{
|
|
2960
|
+
role: "dialog",
|
|
2961
|
+
"aria-modal": "true",
|
|
2962
|
+
"aria-label": "Asset details",
|
|
2963
|
+
className: "relative w-full max-w-lg max-h-[90vh] overflow-auto rounded-lg border border-border bg-popover text-popover-foreground shadow-xl",
|
|
2964
|
+
children: [
|
|
2965
|
+
/* @__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: [
|
|
2966
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold truncate", children: asset2.cleanName || asset2.name || "Asset details" }),
|
|
2967
|
+
/* @__PURE__ */ jsx(
|
|
2968
|
+
"button",
|
|
2969
|
+
{
|
|
2970
|
+
type: "button",
|
|
2971
|
+
onClick: onClose,
|
|
2972
|
+
className: "p-1 rounded hover:bg-accent text-muted-foreground",
|
|
2973
|
+
"aria-label": "Close",
|
|
2974
|
+
children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" })
|
|
2975
|
+
}
|
|
2976
|
+
)
|
|
2977
|
+
] }),
|
|
2978
|
+
/* @__PURE__ */ jsxs("div", { className: "p-4 space-y-4", children: [
|
|
2979
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
|
|
2980
|
+
/* @__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: () => {
|
|
2981
|
+
} }) : /* @__PURE__ */ jsx(Icon, { className: "w-10 h-10 text-muted-foreground" }) }),
|
|
2982
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
2983
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium truncate", title: asset2.name, children: asset2.cleanName || asset2.name || asset2.id }),
|
|
2984
|
+
/* @__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: [
|
|
2985
|
+
/* @__PURE__ */ jsx(Copy, { className: "w-3 h-3" }),
|
|
2986
|
+
" ",
|
|
2987
|
+
asset2.id
|
|
2988
|
+
] }) }),
|
|
2989
|
+
/* @__PURE__ */ jsxs(
|
|
2990
|
+
"a",
|
|
2991
|
+
{
|
|
2992
|
+
href: asset2.url,
|
|
2993
|
+
target: "_blank",
|
|
2994
|
+
rel: "noreferrer",
|
|
2995
|
+
className: "mt-1 inline-flex items-center gap-1 text-[11px] text-primary hover:underline",
|
|
2996
|
+
children: [
|
|
2997
|
+
/* @__PURE__ */ jsx(ExternalLink, { className: "w-3 h-3" }),
|
|
2998
|
+
" Open original"
|
|
2999
|
+
]
|
|
3000
|
+
}
|
|
3001
|
+
)
|
|
3002
|
+
] })
|
|
3003
|
+
] }),
|
|
3004
|
+
warnings.length > 0 && /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-amber-500/40 bg-amber-500/10 p-3 space-y-2", children: [
|
|
3005
|
+
warnings.map((w, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 text-xs text-foreground", children: [
|
|
3006
|
+
/* @__PURE__ */ jsx(AlertTriangle, { className: "w-3.5 h-3.5 text-amber-600 dark:text-amber-400 flex-shrink-0 mt-0.5" }),
|
|
3007
|
+
/* @__PURE__ */ jsx("span", { children: w.text })
|
|
3008
|
+
] }, i)),
|
|
3009
|
+
onFix && /* @__PURE__ */ jsxs(
|
|
3010
|
+
"button",
|
|
3011
|
+
{
|
|
3012
|
+
type: "button",
|
|
3013
|
+
onClick: handleFix,
|
|
3014
|
+
disabled: fixing,
|
|
3015
|
+
className: cn(
|
|
3016
|
+
"mt-1 inline-flex items-center gap-1.5 px-2.5 py-1 text-xs rounded-md",
|
|
3017
|
+
"bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
|
|
3018
|
+
),
|
|
3019
|
+
title: "Re-saves the file with the correct type so the backend regenerates the thumbnail.",
|
|
3020
|
+
children: [
|
|
3021
|
+
fixing ? /* @__PURE__ */ jsx(Loader2, { className: "w-3 h-3 animate-spin" }) : /* @__PURE__ */ jsx(Wrench, { className: "w-3 h-3" }),
|
|
3022
|
+
fixing ? "Fixing\u2026" : "Fix file type & thumbnail"
|
|
3023
|
+
]
|
|
3024
|
+
}
|
|
3025
|
+
)
|
|
3026
|
+
] }),
|
|
3027
|
+
warnings.length === 0 && isImage && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-emerald-700 dark:text-emerald-400", children: [
|
|
3028
|
+
/* @__PURE__ */ jsx(CheckCircle2, { className: "w-3.5 h-3.5" }),
|
|
3029
|
+
" File type and thumbnail look healthy."
|
|
3030
|
+
] }),
|
|
3031
|
+
/* @__PURE__ */ jsxs("section", { children: [
|
|
3032
|
+
/* @__PURE__ */ jsx("h4", { className: "text-[11px] font-semibold uppercase tracking-wide text-muted-foreground mb-1", children: "File" }),
|
|
3033
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-md border border-border divide-y divide-border px-3", children: [
|
|
3034
|
+
/* @__PURE__ */ jsx(Row, { label: "Type", value: asset2.mimeType || /* @__PURE__ */ jsx("span", { className: "text-amber-600 dark:text-amber-400", children: "unknown" }), mono: true }),
|
|
3035
|
+
/* @__PURE__ */ jsx(Row, { label: "Size", value: formatSize2(asset2.size) }),
|
|
3036
|
+
/* @__PURE__ */ jsx(Row, { label: "Dimensions", value: dims ? `${dims.w} \xD7 ${dims.h}` : isImage ? "\u2014" : "n/a" }),
|
|
3037
|
+
/* @__PURE__ */ jsx(Row, { label: "Uploaded", value: formatAge(asset2.createdAt) }),
|
|
3038
|
+
asset2.app && /* @__PURE__ */ jsx(Row, { label: "App", value: asset2.app, mono: true }),
|
|
3039
|
+
asset2.labels && asset2.labels.length > 0 && /* @__PURE__ */ jsx(Row, { label: "Labels", value: asset2.labels.join(", ") })
|
|
3040
|
+
] })
|
|
3041
|
+
] }),
|
|
3042
|
+
isImage && /* @__PURE__ */ jsxs("section", { children: [
|
|
3043
|
+
/* @__PURE__ */ jsx("h4", { className: "text-[11px] font-semibold uppercase tracking-wide text-muted-foreground mb-1", children: "Thumbnail" }),
|
|
3044
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-md border border-border divide-y divide-border px-3", children: [
|
|
3045
|
+
/* @__PURE__ */ jsx(
|
|
3046
|
+
Row,
|
|
3047
|
+
{
|
|
3048
|
+
label: "Status",
|
|
3049
|
+
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" })
|
|
3050
|
+
}
|
|
3051
|
+
),
|
|
3052
|
+
thumbUrl && /* @__PURE__ */ jsx(Row, { label: "Dimensions", value: thumbDims ? `${thumbDims.w} \xD7 ${thumbDims.h}` : "\u2014" }),
|
|
3053
|
+
thumbUrl && /* @__PURE__ */ jsx(Row, { label: "Size", value: formatSize2(thumbBytes) })
|
|
3054
|
+
] })
|
|
3055
|
+
] }),
|
|
3056
|
+
/* @__PURE__ */ jsxs("section", { className: "flex flex-wrap gap-2 pt-1 border-t border-border", children: [
|
|
3057
|
+
onEditImage && (asset2.mimeType || "").startsWith("image/") && /* @__PURE__ */ jsxs(
|
|
3058
|
+
"button",
|
|
3059
|
+
{
|
|
3060
|
+
type: "button",
|
|
3061
|
+
onClick: () => {
|
|
3062
|
+
onClose();
|
|
3063
|
+
onEditImage(asset2);
|
|
3064
|
+
},
|
|
3065
|
+
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",
|
|
3066
|
+
children: [
|
|
3067
|
+
/* @__PURE__ */ jsx(Crop, { className: "w-3 h-3" }),
|
|
3068
|
+
" Edit image"
|
|
3069
|
+
]
|
|
3070
|
+
}
|
|
3071
|
+
),
|
|
3072
|
+
onRename && /* @__PURE__ */ jsxs(
|
|
3073
|
+
"button",
|
|
3074
|
+
{
|
|
3075
|
+
type: "button",
|
|
3076
|
+
onClick: () => {
|
|
3077
|
+
onClose();
|
|
3078
|
+
onRename(asset2);
|
|
3079
|
+
},
|
|
3080
|
+
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",
|
|
3081
|
+
children: [
|
|
3082
|
+
/* @__PURE__ */ jsx(Pencil, { className: "w-3 h-3" }),
|
|
3083
|
+
" Rename"
|
|
3084
|
+
]
|
|
3085
|
+
}
|
|
3086
|
+
),
|
|
3087
|
+
onReplace && /* @__PURE__ */ jsxs(
|
|
3088
|
+
"button",
|
|
3089
|
+
{
|
|
3090
|
+
type: "button",
|
|
3091
|
+
onClick: () => {
|
|
3092
|
+
onClose();
|
|
3093
|
+
onReplace(asset2);
|
|
3094
|
+
},
|
|
3095
|
+
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",
|
|
3096
|
+
children: [
|
|
3097
|
+
/* @__PURE__ */ jsx(Upload, { className: "w-3 h-3" }),
|
|
3098
|
+
" Replace file"
|
|
3099
|
+
]
|
|
3100
|
+
}
|
|
3101
|
+
),
|
|
3102
|
+
onEditTags && /* @__PURE__ */ jsxs(
|
|
3103
|
+
"button",
|
|
3104
|
+
{
|
|
3105
|
+
type: "button",
|
|
3106
|
+
onClick: () => {
|
|
3107
|
+
onClose();
|
|
3108
|
+
onEditTags(asset2);
|
|
3109
|
+
},
|
|
3110
|
+
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",
|
|
3111
|
+
children: [
|
|
3112
|
+
/* @__PURE__ */ jsx(Tag, { className: "w-3 h-3" }),
|
|
3113
|
+
" Edit tags"
|
|
3114
|
+
]
|
|
3115
|
+
}
|
|
3116
|
+
),
|
|
3117
|
+
onDelete && /* @__PURE__ */ jsxs(
|
|
3118
|
+
"button",
|
|
3119
|
+
{
|
|
3120
|
+
type: "button",
|
|
3121
|
+
onClick: () => {
|
|
3122
|
+
onClose();
|
|
3123
|
+
onDelete(asset2);
|
|
3124
|
+
},
|
|
3125
|
+
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",
|
|
3126
|
+
children: [
|
|
3127
|
+
/* @__PURE__ */ jsx(Trash2, { className: "w-3 h-3" }),
|
|
3128
|
+
" Delete"
|
|
3129
|
+
]
|
|
3130
|
+
}
|
|
3131
|
+
)
|
|
3132
|
+
] })
|
|
3133
|
+
] })
|
|
3134
|
+
]
|
|
3135
|
+
}
|
|
3136
|
+
)
|
|
3137
|
+
]
|
|
3138
|
+
}
|
|
3139
|
+
),
|
|
3140
|
+
document.body
|
|
3141
|
+
);
|
|
3142
|
+
};
|
|
3143
|
+
async function buildFixFile(asset2) {
|
|
3144
|
+
try {
|
|
3145
|
+
const res = await fetch(asset2.url);
|
|
3146
|
+
if (!res.ok) return null;
|
|
3147
|
+
const blob = await res.blob();
|
|
3148
|
+
const baseName = (asset2.cleanName || asset2.name || "file").replace(/\.[^.]+$/, "") || "file";
|
|
3149
|
+
const raw = new File([blob], asset2.name || baseName, { type: blob.type || "" });
|
|
3150
|
+
const normalised = await normalizeFileType(raw, baseName);
|
|
3151
|
+
return normalised;
|
|
3152
|
+
} catch {
|
|
3153
|
+
return null;
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
2720
3156
|
var InlineConfirm = ({ open, title, body, confirmLabel = "Confirm", cancelLabel = "Cancel", destructive, onConfirm, onCancel }) => {
|
|
2721
|
-
|
|
3157
|
+
React9.useEffect(() => {
|
|
2722
3158
|
if (!open) return;
|
|
2723
3159
|
const onKey = (e) => {
|
|
2724
3160
|
if (e.key === "Escape") {
|
|
@@ -2823,8 +3259,8 @@ var AttachToContextToggle = ({ checked, onChange, contextLabel }) => /* @__PURE_
|
|
|
2823
3259
|
] })
|
|
2824
3260
|
] });
|
|
2825
3261
|
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 =
|
|
3262
|
+
const replaceInputRef = React9.useRef(null);
|
|
3263
|
+
const replaceTargetRef = React9.useRef(null);
|
|
2828
3264
|
const handleRename = useCallback(async (asset2) => {
|
|
2829
3265
|
const current = asset2.cleanName || asset2.name || "";
|
|
2830
3266
|
const next = window.prompt("Rename asset", current);
|
|
@@ -2880,6 +3316,16 @@ var ScopedAssetBrowser = ({ scope: _scope, accept: _accept, pageSize: _pageSize,
|
|
|
2880
3316
|
await updateAsset(tagEditorAsset.id, { labels: next });
|
|
2881
3317
|
setTagEditorAsset(null);
|
|
2882
3318
|
}, [tagEditorAsset, updateAsset]);
|
|
3319
|
+
const [detailsAsset, setDetailsAsset] = useState(null);
|
|
3320
|
+
const handleShowDetails = useCallback((asset2) => {
|
|
3321
|
+
setDetailsAsset(asset2);
|
|
3322
|
+
}, []);
|
|
3323
|
+
const handleFixAsset = useCallback(async (asset2) => {
|
|
3324
|
+
const fixed = await buildFixFile(asset2);
|
|
3325
|
+
if (!fixed) return;
|
|
3326
|
+
const result = await replaceFile(asset2.id, fixed);
|
|
3327
|
+
if (result) setDetailsAsset(result);
|
|
3328
|
+
}, [replaceFile]);
|
|
2883
3329
|
const [activeLabels, setActiveLabels] = useState(/* @__PURE__ */ new Set());
|
|
2884
3330
|
const toggleLabel = useCallback((label) => {
|
|
2885
3331
|
setActiveLabels((prev) => {
|
|
@@ -2979,6 +3425,7 @@ var ScopedAssetBrowser = ({ scope: _scope, accept: _accept, pageSize: _pageSize,
|
|
|
2979
3425
|
onReplace: handleReplace,
|
|
2980
3426
|
onEditTags: handleEditTags,
|
|
2981
3427
|
onEditImage: handleEditImage,
|
|
3428
|
+
onShowDetails: handleShowDetails,
|
|
2982
3429
|
allowDelete,
|
|
2983
3430
|
currentAppId,
|
|
2984
3431
|
currentAppName,
|
|
@@ -3024,6 +3471,19 @@ var ScopedAssetBrowser = ({ scope: _scope, accept: _accept, pageSize: _pageSize,
|
|
|
3024
3471
|
onSave: handleSaveTags
|
|
3025
3472
|
}
|
|
3026
3473
|
),
|
|
3474
|
+
detailsAsset && /* @__PURE__ */ jsx(
|
|
3475
|
+
AssetDetails,
|
|
3476
|
+
{
|
|
3477
|
+
asset: detailsAsset,
|
|
3478
|
+
onClose: () => setDetailsAsset(null),
|
|
3479
|
+
onRename: handleRename,
|
|
3480
|
+
onReplace: handleReplace,
|
|
3481
|
+
onEditTags: handleEditTags,
|
|
3482
|
+
onEditImage: handleEditImage,
|
|
3483
|
+
onDelete: allowDelete ? (a) => handleDeleteWithConfirm(a.id) : void 0,
|
|
3484
|
+
onFix: handleFixAsset
|
|
3485
|
+
}
|
|
3486
|
+
),
|
|
3027
3487
|
/* @__PURE__ */ jsx(
|
|
3028
3488
|
InlineConfirm,
|
|
3029
3489
|
{
|
|
@@ -3228,11 +3688,14 @@ var AssetPickerContent = ({
|
|
|
3228
3688
|
name,
|
|
3229
3689
|
...sameAsActive ? {} : { scopeOverride: targetScope }
|
|
3230
3690
|
});
|
|
3231
|
-
if (result
|
|
3232
|
-
|
|
3233
|
-
|
|
3691
|
+
if (result) {
|
|
3692
|
+
setTab("browse");
|
|
3693
|
+
if (!multiple) {
|
|
3694
|
+
setSelectedIds(/* @__PURE__ */ new Set([result.id]));
|
|
3695
|
+
onSelect?.(toSelection(result));
|
|
3696
|
+
}
|
|
3697
|
+
await reconcileAfterUpload(targetScope);
|
|
3234
3698
|
}
|
|
3235
|
-
if (result) await reconcileAfterUpload(targetScope);
|
|
3236
3699
|
return result;
|
|
3237
3700
|
}, [uploadFromRemoteUrl, multiple, onSelect, toSelection, uploadScope, activeScope, reconcileAfterUpload]);
|
|
3238
3701
|
const handleDelete = useCallback(async (assetId) => {
|
|
@@ -3634,5 +4097,5 @@ var AssetPicker = (props) => {
|
|
|
3634
4097
|
assertStylesLoaded();
|
|
3635
4098
|
|
|
3636
4099
|
export { ASSET_MIME_FILTERS, AssetPicker, useAppRegistry, useAssets };
|
|
3637
|
-
//# sourceMappingURL=chunk-
|
|
3638
|
-
//# sourceMappingURL=chunk-
|
|
4100
|
+
//# sourceMappingURL=chunk-7RWLFKHC.js.map
|
|
4101
|
+
//# sourceMappingURL=chunk-7RWLFKHC.js.map
|