@proveanything/smartlinks-utils-ui 1.13.7 → 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 +14 -6
- package/dist/{chunk-OTJV62XV.js → chunk-5ZQT2GGU.js} +5 -5
- package/dist/chunk-5ZQT2GGU.js.map +1 -0
- package/dist/{chunk-WLN4WW7K.js → chunk-7RWLFKHC.js} +505 -40
- 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.d.ts +6 -2
- package/dist/components/RecordsAdmin/index.js +4 -3
- 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 +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-OTJV62XV.js.map +0 -1
- package/dist/chunk-WLN4WW7K.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",
|
|
@@ -2610,7 +2695,9 @@ var TagEditor = ({ initial, suggestions, assetName, onCancel, onSave }) => {
|
|
|
2610
2695
|
const handleSave = async () => {
|
|
2611
2696
|
setSaving(true);
|
|
2612
2697
|
try {
|
|
2613
|
-
|
|
2698
|
+
const pending = input.trim();
|
|
2699
|
+
const finalLabels = pending && !labels.some((l) => l.toLowerCase() === pending.toLowerCase()) ? [...labels, pending] : labels;
|
|
2700
|
+
await onSave(finalLabels);
|
|
2614
2701
|
} finally {
|
|
2615
2702
|
setSaving(false);
|
|
2616
2703
|
}
|
|
@@ -2715,8 +2802,359 @@ var TagEditor = ({ initial, suggestions, assetName, onCancel, onSave }) => {
|
|
|
2715
2802
|
}
|
|
2716
2803
|
);
|
|
2717
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
|
+
}
|
|
2718
3156
|
var InlineConfirm = ({ open, title, body, confirmLabel = "Confirm", cancelLabel = "Cancel", destructive, onConfirm, onCancel }) => {
|
|
2719
|
-
|
|
3157
|
+
React9.useEffect(() => {
|
|
2720
3158
|
if (!open) return;
|
|
2721
3159
|
const onKey = (e) => {
|
|
2722
3160
|
if (e.key === "Escape") {
|
|
@@ -2821,8 +3259,8 @@ var AttachToContextToggle = ({ checked, onChange, contextLabel }) => /* @__PURE_
|
|
|
2821
3259
|
] })
|
|
2822
3260
|
] });
|
|
2823
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 }) => {
|
|
2824
|
-
const replaceInputRef =
|
|
2825
|
-
const replaceTargetRef =
|
|
3262
|
+
const replaceInputRef = React9.useRef(null);
|
|
3263
|
+
const replaceTargetRef = React9.useRef(null);
|
|
2826
3264
|
const handleRename = useCallback(async (asset2) => {
|
|
2827
3265
|
const current = asset2.cleanName || asset2.name || "";
|
|
2828
3266
|
const next = window.prompt("Rename asset", current);
|
|
@@ -2878,6 +3316,16 @@ var ScopedAssetBrowser = ({ scope: _scope, accept: _accept, pageSize: _pageSize,
|
|
|
2878
3316
|
await updateAsset(tagEditorAsset.id, { labels: next });
|
|
2879
3317
|
setTagEditorAsset(null);
|
|
2880
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]);
|
|
2881
3329
|
const [activeLabels, setActiveLabels] = useState(/* @__PURE__ */ new Set());
|
|
2882
3330
|
const toggleLabel = useCallback((label) => {
|
|
2883
3331
|
setActiveLabels((prev) => {
|
|
@@ -2977,6 +3425,7 @@ var ScopedAssetBrowser = ({ scope: _scope, accept: _accept, pageSize: _pageSize,
|
|
|
2977
3425
|
onReplace: handleReplace,
|
|
2978
3426
|
onEditTags: handleEditTags,
|
|
2979
3427
|
onEditImage: handleEditImage,
|
|
3428
|
+
onShowDetails: handleShowDetails,
|
|
2980
3429
|
allowDelete,
|
|
2981
3430
|
currentAppId,
|
|
2982
3431
|
currentAppName,
|
|
@@ -3022,6 +3471,19 @@ var ScopedAssetBrowser = ({ scope: _scope, accept: _accept, pageSize: _pageSize,
|
|
|
3022
3471
|
onSave: handleSaveTags
|
|
3023
3472
|
}
|
|
3024
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
|
+
),
|
|
3025
3487
|
/* @__PURE__ */ jsx(
|
|
3026
3488
|
InlineConfirm,
|
|
3027
3489
|
{
|
|
@@ -3226,11 +3688,14 @@ var AssetPickerContent = ({
|
|
|
3226
3688
|
name,
|
|
3227
3689
|
...sameAsActive ? {} : { scopeOverride: targetScope }
|
|
3228
3690
|
});
|
|
3229
|
-
if (result
|
|
3230
|
-
|
|
3231
|
-
|
|
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);
|
|
3232
3698
|
}
|
|
3233
|
-
if (result) await reconcileAfterUpload(targetScope);
|
|
3234
3699
|
return result;
|
|
3235
3700
|
}, [uploadFromRemoteUrl, multiple, onSelect, toSelection, uploadScope, activeScope, reconcileAfterUpload]);
|
|
3236
3701
|
const handleDelete = useCallback(async (assetId) => {
|
|
@@ -3632,5 +4097,5 @@ var AssetPicker = (props) => {
|
|
|
3632
4097
|
assertStylesLoaded();
|
|
3633
4098
|
|
|
3634
4099
|
export { ASSET_MIME_FILTERS, AssetPicker, useAppRegistry, useAssets };
|
|
3635
|
-
//# sourceMappingURL=chunk-
|
|
3636
|
-
//# sourceMappingURL=chunk-
|
|
4100
|
+
//# sourceMappingURL=chunk-7RWLFKHC.js.map
|
|
4101
|
+
//# sourceMappingURL=chunk-7RWLFKHC.js.map
|