@proveanything/smartlinks-utils-ui 1.0.1 → 1.13.0

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.
@@ -1,8 +1,8 @@
1
1
  import { assertStylesLoaded } from './chunk-OLYC54YT.js';
2
2
  import { cn } from './chunk-L7FQ52F5.js';
3
- import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
3
+ import React7, { useState, useRef, useEffect, useCallback, useMemo } from 'react';
4
4
  import * as SL from '@proveanything/smartlinks';
5
- import { Filter, Search, LayoutGrid, List, X, Loader2, AlertCircle, Tag, ImageOff, Clipboard, Pencil, Check, Upload, Link, MicOff, Mic, ChevronDown, ChevronRight, Sparkles, Image, Trash2, FileIcon, Film, Music, FileText, AppWindow } from 'lucide-react';
5
+ import { Filter, Search, LayoutGrid, List, X, Loader2, AlertCircle, Tag, ImageOff, Wand2, Maximize2, Clipboard, Pencil, Check, Upload, Link, MicOff, Mic, ChevronDown, ChevronRight, Sparkles, Image as Image$1, Plus, FileIcon, Film, Music, FileText, AppWindow, MoreVertical, Trash2 } from 'lucide-react';
6
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
7
 
8
8
  // src/components/AssetPicker/types.ts
@@ -309,7 +309,7 @@ function useAppRegistry(collectionId) {
309
309
  }
310
310
  function getIcon(mimeType) {
311
311
  if (!mimeType) return FileIcon;
312
- if (mimeType.startsWith("image/")) return Image;
312
+ if (mimeType.startsWith("image/")) return Image$1;
313
313
  if (mimeType.startsWith("video/")) return Film;
314
314
  if (mimeType.startsWith("audio/")) return Music;
315
315
  return FileText;
@@ -349,7 +349,129 @@ var AppBadge = ({ appId, appName, size = "sm" }) => {
349
349
  }
350
350
  );
351
351
  };
352
- var AssetGridItem = ({ asset: asset2, selected, onToggle, onDoubleClick, onDelete, allowDelete, currentAppId, getAppName, activeLabels, onToggleLabel }) => {
352
+ var CardMenu = ({ onRename, onReplace, onEditTags, onDelete, position = "absolute" }) => {
353
+ const [open, setOpen] = useState(false);
354
+ const ref = useRef(null);
355
+ useEffect(() => {
356
+ if (!open) return;
357
+ const handler = (e) => {
358
+ if (!ref.current?.contains(e.target)) setOpen(false);
359
+ };
360
+ document.addEventListener("mousedown", handler);
361
+ return () => document.removeEventListener("mousedown", handler);
362
+ }, [open]);
363
+ if (!onRename && !onReplace && !onEditTags && !onDelete) return null;
364
+ return /* @__PURE__ */ jsxs(
365
+ "div",
366
+ {
367
+ ref,
368
+ className: cn(
369
+ position === "absolute" ? "absolute bottom-1.5 right-1.5" : "relative flex-shrink-0"
370
+ ),
371
+ onClick: (e) => e.stopPropagation(),
372
+ onDoubleClick: (e) => e.stopPropagation(),
373
+ children: [
374
+ /* @__PURE__ */ jsx(
375
+ "button",
376
+ {
377
+ type: "button",
378
+ onClick: (e) => {
379
+ e.stopPropagation();
380
+ setOpen((o) => !o);
381
+ },
382
+ className: cn(
383
+ "w-6 h-6 rounded-full flex items-center justify-center transition-all",
384
+ "bg-background/90 border border-border text-foreground hover:bg-background shadow-sm",
385
+ position === "absolute" && "opacity-0 group-hover:opacity-100",
386
+ open && "opacity-100"
387
+ ),
388
+ title: "Asset actions",
389
+ "aria-label": "Asset actions",
390
+ children: /* @__PURE__ */ jsx(MoreVertical, { className: "w-3 h-3" })
391
+ }
392
+ ),
393
+ open && /* @__PURE__ */ jsxs(
394
+ "div",
395
+ {
396
+ className: "absolute bottom-full right-0 mb-1 z-20 min-w-[140px] rounded-md border border-border bg-popover text-popover-foreground shadow-md py-1",
397
+ role: "menu",
398
+ children: [
399
+ onRename && /* @__PURE__ */ jsxs(
400
+ "button",
401
+ {
402
+ type: "button",
403
+ role: "menuitem",
404
+ onClick: (e) => {
405
+ e.stopPropagation();
406
+ setOpen(false);
407
+ onRename();
408
+ },
409
+ className: "w-full flex items-center gap-2 px-2.5 py-1.5 text-xs hover:bg-accent",
410
+ children: [
411
+ /* @__PURE__ */ jsx(Pencil, { className: "w-3 h-3" }),
412
+ " Rename"
413
+ ]
414
+ }
415
+ ),
416
+ onReplace && /* @__PURE__ */ jsxs(
417
+ "button",
418
+ {
419
+ type: "button",
420
+ role: "menuitem",
421
+ onClick: (e) => {
422
+ e.stopPropagation();
423
+ setOpen(false);
424
+ onReplace();
425
+ },
426
+ className: "w-full flex items-center gap-2 px-2.5 py-1.5 text-xs hover:bg-accent",
427
+ children: [
428
+ /* @__PURE__ */ jsx(Upload, { className: "w-3 h-3" }),
429
+ " Replace file"
430
+ ]
431
+ }
432
+ ),
433
+ onEditTags && /* @__PURE__ */ jsxs(
434
+ "button",
435
+ {
436
+ type: "button",
437
+ role: "menuitem",
438
+ onClick: (e) => {
439
+ e.stopPropagation();
440
+ setOpen(false);
441
+ onEditTags();
442
+ },
443
+ className: "w-full flex items-center gap-2 px-2.5 py-1.5 text-xs hover:bg-accent",
444
+ children: [
445
+ /* @__PURE__ */ jsx(Tag, { className: "w-3 h-3" }),
446
+ " Edit tags"
447
+ ]
448
+ }
449
+ ),
450
+ onDelete && /* @__PURE__ */ jsxs(
451
+ "button",
452
+ {
453
+ type: "button",
454
+ role: "menuitem",
455
+ onClick: (e) => {
456
+ e.stopPropagation();
457
+ setOpen(false);
458
+ onDelete();
459
+ },
460
+ className: "w-full flex items-center gap-2 px-2.5 py-1.5 text-xs text-destructive hover:bg-destructive/10",
461
+ children: [
462
+ /* @__PURE__ */ jsx(Trash2, { className: "w-3 h-3" }),
463
+ " Delete"
464
+ ]
465
+ }
466
+ )
467
+ ]
468
+ }
469
+ )
470
+ ]
471
+ }
472
+ );
473
+ };
474
+ var AssetGridItem = ({ asset: asset2, selected, onToggle, onDoubleClick, onDelete, onRename, onReplace, onEditTags, allowDelete, currentAppId, getAppName, activeLabels, onToggleLabel }) => {
353
475
  const thumb = getThumbnail(asset2);
354
476
  const Icon = getIcon(asset2.mimeType);
355
477
  const ownerAppId = getAssetAppId(asset2);
@@ -424,24 +546,20 @@ var AssetGridItem = ({ asset: asset2, selected, onToggle, onDoubleClick, onDelet
424
546
  ] })
425
547
  ] }),
426
548
  selected && /* @__PURE__ */ jsx("div", { className: "absolute top-2 right-2 w-5 h-5 rounded-full bg-primary flex items-center justify-center", children: /* @__PURE__ */ jsx(Check, { className: "w-3 h-3 text-primary-foreground" }) }),
427
- allowDelete && onDelete && /* @__PURE__ */ jsx(
428
- "button",
549
+ /* @__PURE__ */ jsx(
550
+ CardMenu,
429
551
  {
430
- type: "button",
431
- className: "absolute top-2 left-2 w-6 h-6 rounded-full bg-destructive/80 hover:bg-destructive text-destructive-foreground flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity",
432
- onClick: (e) => {
433
- e.stopPropagation();
434
- onDelete();
435
- },
436
- title: "Delete asset",
437
- children: /* @__PURE__ */ jsx(Trash2, { className: "w-3 h-3" })
552
+ onRename,
553
+ onReplace,
554
+ onEditTags,
555
+ onDelete: allowDelete ? onDelete : void 0
438
556
  }
439
557
  )
440
558
  ]
441
559
  }
442
560
  );
443
561
  };
444
- var AssetListItem = ({ asset: asset2, selected, onToggle, onDoubleClick, onDelete, allowDelete, currentAppId, getAppName, activeLabels, onToggleLabel }) => {
562
+ var AssetListItem = ({ asset: asset2, selected, onToggle, onDoubleClick, onDelete, onRename, onReplace, onEditTags, allowDelete, currentAppId, getAppName, activeLabels, onToggleLabel }) => {
445
563
  const thumb = getThumbnail(asset2);
446
564
  const Icon = getIcon(asset2.mimeType);
447
565
  const ownerAppId = getAssetAppId(asset2);
@@ -504,17 +622,14 @@ var AssetListItem = ({ asset: asset2, selected, onToggle, onDoubleClick, onDelet
504
622
  }) })
505
623
  ] }),
506
624
  selected && /* @__PURE__ */ jsx("div", { className: "w-5 h-5 rounded-full bg-primary flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ jsx(Check, { className: "w-3 h-3 text-primary-foreground" }) }),
507
- allowDelete && onDelete && /* @__PURE__ */ jsx(
508
- "button",
625
+ /* @__PURE__ */ jsx(
626
+ CardMenu,
509
627
  {
510
- type: "button",
511
- className: "w-6 h-6 rounded-full bg-destructive/80 hover:bg-destructive text-destructive-foreground flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity flex-shrink-0",
512
- onClick: (e) => {
513
- e.stopPropagation();
514
- onDelete();
515
- },
516
- title: "Delete",
517
- children: /* @__PURE__ */ jsx(Trash2, { className: "w-3 h-3" })
628
+ onRename,
629
+ onReplace,
630
+ onEditTags,
631
+ onDelete: allowDelete ? onDelete : void 0,
632
+ position: "inline"
518
633
  }
519
634
  )
520
635
  ]
@@ -528,6 +643,9 @@ var AssetGrid = ({
528
643
  onToggleSelect,
529
644
  onDoubleClickSelect,
530
645
  onDelete,
646
+ onRename,
647
+ onReplace,
648
+ onEditTags,
531
649
  allowDelete,
532
650
  currentAppId,
533
651
  getAppName,
@@ -544,6 +662,9 @@ var AssetGrid = ({
544
662
  onToggle: () => onToggleSelect(asset2),
545
663
  onDoubleClick: onDoubleClickSelect ? () => onDoubleClickSelect(asset2) : void 0,
546
664
  onDelete: allowDelete && onDelete ? () => onDelete(asset2.id) : void 0,
665
+ onRename: onRename ? () => onRename(asset2) : void 0,
666
+ onReplace: onReplace ? () => onReplace(asset2) : void 0,
667
+ onEditTags: onEditTags ? () => onEditTags(asset2) : void 0,
547
668
  allowDelete,
548
669
  currentAppId,
549
670
  getAppName,
@@ -561,6 +682,9 @@ var AssetGrid = ({
561
682
  onToggle: () => onToggleSelect(asset2),
562
683
  onDoubleClick: onDoubleClickSelect ? () => onDoubleClickSelect(asset2) : void 0,
563
684
  onDelete: allowDelete && onDelete ? () => onDelete(asset2.id) : void 0,
685
+ onRename: onRename ? () => onRename(asset2) : void 0,
686
+ onReplace: onReplace ? () => onReplace(asset2) : void 0,
687
+ onEditTags: onEditTags ? () => onEditTags(asset2) : void 0,
564
688
  allowDelete,
565
689
  currentAppId,
566
690
  getAppName,
@@ -570,18 +694,144 @@ var AssetGrid = ({
570
694
  asset2.id
571
695
  )) });
572
696
  };
697
+
698
+ // src/components/AssetPicker/imageProcessing.ts
699
+ var WEBP_CANDIDATES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/jpg", "image/bmp"]);
700
+ function isWebpCandidate(file) {
701
+ return WEBP_CANDIDATES.has(file.type.toLowerCase());
702
+ }
703
+ function isProcessableImage(file) {
704
+ if (!file.type.startsWith("image/")) return false;
705
+ if (file.type === "image/svg+xml" || file.type === "image/gif") return false;
706
+ return true;
707
+ }
708
+ async function loadImage(file) {
709
+ const url = URL.createObjectURL(file);
710
+ const img = new Image();
711
+ img.decoding = "async";
712
+ await new Promise((resolve, reject) => {
713
+ img.onload = () => resolve();
714
+ img.onerror = () => reject(new Error("Failed to decode image"));
715
+ img.src = url;
716
+ });
717
+ return { img, url };
718
+ }
719
+ async function getImageDimensions(file) {
720
+ if (!isProcessableImage(file)) return null;
721
+ try {
722
+ const { img, url } = await loadImage(file);
723
+ const out = { width: img.naturalWidth, height: img.naturalHeight };
724
+ URL.revokeObjectURL(url);
725
+ return out;
726
+ } catch {
727
+ return null;
728
+ }
729
+ }
730
+ async function processImage(file, opts = {}) {
731
+ const toWebp = opts.toWebp ?? isWebpCandidate(file);
732
+ const maxDim = opts.maxDimension ?? 2048;
733
+ const quality = opts.quality ?? 0.85;
734
+ const fallback = {
735
+ file,
736
+ changed: false,
737
+ originalSize: file.size,
738
+ newSize: file.size,
739
+ width: 0,
740
+ height: 0
741
+ };
742
+ if (!isProcessableImage(file)) return fallback;
743
+ let img;
744
+ let url;
745
+ try {
746
+ ({ img, url } = await loadImage(file));
747
+ } catch {
748
+ return fallback;
749
+ }
750
+ try {
751
+ const w = img.naturalWidth;
752
+ const h = img.naturalHeight;
753
+ const longest = Math.max(w, h);
754
+ const needsResize = maxDim > 0 && longest > maxDim;
755
+ const needsReencode = toWebp && file.type !== "image/webp";
756
+ if (!needsResize && !needsReencode) {
757
+ return { ...fallback, width: w, height: h };
758
+ }
759
+ const scale = needsResize ? maxDim / longest : 1;
760
+ const targetW = Math.round(w * scale);
761
+ const targetH = Math.round(h * scale);
762
+ const canvas = document.createElement("canvas");
763
+ canvas.width = targetW;
764
+ canvas.height = targetH;
765
+ const ctx = canvas.getContext("2d");
766
+ if (!ctx) return { ...fallback, width: w, height: h };
767
+ ctx.drawImage(img, 0, 0, targetW, targetH);
768
+ const outType = needsReencode ? "image/webp" : file.type;
769
+ const blob = await new Promise(
770
+ (resolve) => canvas.toBlob((b) => resolve(b), outType, quality)
771
+ );
772
+ if (!blob) return { ...fallback, width: w, height: h };
773
+ const base = file.name.replace(/\.[^.]+$/, "") || "image";
774
+ const ext = needsReencode ? "webp" : file.name.match(/\.([^.]+)$/)?.[1] || "png";
775
+ const newName = `${base}.${ext}`;
776
+ const newFile = new File([blob], newName, { type: outType });
777
+ return {
778
+ file: newFile,
779
+ changed: true,
780
+ originalSize: file.size,
781
+ newSize: newFile.size,
782
+ width: targetW,
783
+ height: targetH
784
+ };
785
+ } finally {
786
+ URL.revokeObjectURL(url);
787
+ }
788
+ }
573
789
  var UploadZone = ({
574
790
  onFiles,
575
791
  accept,
576
792
  multiple,
577
793
  uploading,
578
794
  uploadProgress = 0,
579
- className
795
+ className,
796
+ imageOptimization
580
797
  }) => {
798
+ const optConfig = useMemo(() => {
799
+ if (imageOptimization === false) return { forced: "off" };
800
+ if (imageOptimization === true) return { forced: "on", maxDimension: 2048, quality: 0.85 };
801
+ if (typeof imageOptimization === "object" && imageOptimization) {
802
+ return {
803
+ forced: imageOptimization.userToggleable === false ? imageOptimization.defaultEnabled === false ? "off" : "on" : null,
804
+ defaultEnabled: imageOptimization.defaultEnabled ?? true,
805
+ maxDimension: imageOptimization.maxDimension ?? 2048,
806
+ quality: imageOptimization.quality ?? 0.85
807
+ };
808
+ }
809
+ return { forced: null, defaultEnabled: true, maxDimension: 2048, quality: 0.85 };
810
+ }, [imageOptimization]);
581
811
  const [dragOver, setDragOver] = useState(false);
582
812
  const [pastedFile, setPastedFile] = useState(null);
583
813
  const [editingName, setEditingName] = useState(false);
584
814
  const [fileName, setFileName] = useState("");
815
+ const [lightboxOpen, setLightboxOpen] = useState(false);
816
+ const [autoOptimizeUser, setAutoOptimizeUser] = useState(() => {
817
+ if (optConfig.forced) return optConfig.forced === "on";
818
+ try {
819
+ const v = localStorage.getItem("smartlinks.assetPicker.autoOptimize");
820
+ if (v !== null) return v === "1";
821
+ } catch {
822
+ }
823
+ return optConfig.defaultEnabled ?? true;
824
+ });
825
+ const autoOptimize = optConfig.forced ? optConfig.forced === "on" : autoOptimizeUser;
826
+ const showOptimizeToggle = optConfig.forced === null;
827
+ const setAutoOptimizePersist = useCallback((v) => {
828
+ setAutoOptimizeUser(v);
829
+ try {
830
+ localStorage.setItem("smartlinks.assetPicker.autoOptimize", v ? "1" : "0");
831
+ } catch {
832
+ }
833
+ }, []);
834
+ const [optimizing, setOptimizing] = useState(false);
585
835
  const inputRef = useRef(null);
586
836
  const nameInputRef = useRef(null);
587
837
  const zoneRef = useRef(null);
@@ -606,9 +856,12 @@ var UploadZone = ({
606
856
  e.preventDefault();
607
857
  const previewUrl = file.type.startsWith("image/") ? URL.createObjectURL(file) : "";
608
858
  const defaultName = file.name === "image.png" ? `pasted-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace(/[T:]/g, "-")}` : file.name.replace(/\.[^.]+$/, "");
609
- setPastedFile({ file, previewUrl, name: defaultName });
859
+ setPastedFile({ file, previewUrl, name: defaultName, origSize: file.size });
610
860
  setFileName(defaultName);
611
861
  setEditingName(false);
862
+ getImageDimensions(file).then((dims) => {
863
+ if (dims) setPastedFile((prev) => prev && prev.file === file ? { ...prev, origDims: dims } : prev);
864
+ });
612
865
  return;
613
866
  }
614
867
  }
@@ -616,15 +869,28 @@ var UploadZone = ({
616
869
  document.addEventListener("paste", handlePaste);
617
870
  return () => document.removeEventListener("paste", handlePaste);
618
871
  }, [uploading, accept]);
619
- const handleConfirmPaste = useCallback(() => {
872
+ const handleConfirmPaste = useCallback(async () => {
620
873
  if (!pastedFile) return;
621
- const ext = pastedFile.file.name.includes(".") ? pastedFile.file.name.split(".").pop() : "png";
874
+ let working = pastedFile.file;
875
+ if (autoOptimize && isProcessableImage(working)) {
876
+ setOptimizing(true);
877
+ try {
878
+ const result = await processImage(working, {
879
+ maxDimension: optConfig.maxDimension,
880
+ quality: optConfig.quality
881
+ });
882
+ if (result.changed) working = result.file;
883
+ } finally {
884
+ setOptimizing(false);
885
+ }
886
+ }
887
+ const ext = working.name.includes(".") ? working.name.split(".").pop() : "png";
622
888
  const finalName = `${fileName.trim() || "pasted-image"}.${ext}`;
623
- const renamedFile = new File([pastedFile.file], finalName, { type: pastedFile.file.type });
889
+ const renamedFile = new File([working], finalName, { type: working.type });
624
890
  onFiles([renamedFile]);
625
891
  if (pastedFile.previewUrl) URL.revokeObjectURL(pastedFile.previewUrl);
626
892
  setPastedFile(null);
627
- }, [pastedFile, fileName, onFiles]);
893
+ }, [pastedFile, fileName, onFiles, autoOptimize, optConfig]);
628
894
  const handleCancelPaste = useCallback(() => {
629
895
  if (pastedFile?.previewUrl) URL.revokeObjectURL(pastedFile.previewUrl);
630
896
  setPastedFile(null);
@@ -647,9 +913,12 @@ var UploadZone = ({
647
913
  const presentForRename = useCallback((file) => {
648
914
  const previewUrl = file.type.startsWith("image/") ? URL.createObjectURL(file) : "";
649
915
  const defaultName = file.name.replace(/\.[^.]+$/, "") || "file";
650
- setPastedFile({ file, previewUrl, name: defaultName });
916
+ setPastedFile({ file, previewUrl, name: defaultName, origSize: file.size });
651
917
  setFileName(defaultName);
652
918
  setEditingName(false);
919
+ getImageDimensions(file).then((dims) => {
920
+ if (dims) setPastedFile((prev) => prev && prev.file === file ? { ...prev, origDims: dims } : prev);
921
+ });
653
922
  }, []);
654
923
  const handleDrop = useCallback((e) => {
655
924
  e.preventDefault();
@@ -659,7 +928,7 @@ var UploadZone = ({
659
928
  if (files.length === 1 && !multiple) {
660
929
  presentForRename(files[0]);
661
930
  } else if (files.length > 0) {
662
- onFiles(multiple ? files : [files[0]]);
931
+ void handleBatchFiles(multiple ? files : [files[0]]);
663
932
  }
664
933
  }, [onFiles, multiple, presentForRename]);
665
934
  const handleInputChange = useCallback((e) => {
@@ -667,87 +936,189 @@ var UploadZone = ({
667
936
  if (files.length === 1 && !multiple) {
668
937
  presentForRename(files[0]);
669
938
  } else if (files.length > 0) {
670
- onFiles(multiple ? files : [files[0]]);
939
+ void handleBatchFiles(multiple ? files : [files[0]]);
671
940
  }
672
941
  e.target.value = "";
673
942
  }, [onFiles, multiple, presentForRename]);
943
+ const handleBatchFiles = useCallback(async (files) => {
944
+ if (!autoOptimize) {
945
+ onFiles(files);
946
+ return;
947
+ }
948
+ setOptimizing(true);
949
+ try {
950
+ const out = [];
951
+ for (const f of files) {
952
+ if (isProcessableImage(f)) {
953
+ const r = await processImage(f, {
954
+ maxDimension: optConfig.maxDimension,
955
+ quality: optConfig.quality
956
+ });
957
+ out.push(r.changed ? r.file : f);
958
+ } else {
959
+ out.push(f);
960
+ }
961
+ }
962
+ onFiles(out);
963
+ } finally {
964
+ setOptimizing(false);
965
+ }
966
+ }, [autoOptimize, onFiles, optConfig]);
967
+ const fmtKB = (n) => n >= 1024 * 1024 ? `${(n / 1024 / 1024).toFixed(1)} MB` : `${(n / 1024).toFixed(1)} KB`;
968
+ const optimizeToggle = showOptimizeToggle ? /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-1.5 text-[10px] text-muted-foreground cursor-pointer select-none", children: [
969
+ /* @__PURE__ */ jsx(
970
+ "input",
971
+ {
972
+ type: "checkbox",
973
+ checked: autoOptimize,
974
+ onChange: (e) => setAutoOptimizePersist(e.target.checked),
975
+ className: "cursor-pointer"
976
+ }
977
+ ),
978
+ /* @__PURE__ */ jsx(Wand2, { className: "w-3 h-3" }),
979
+ /* @__PURE__ */ jsxs("span", { children: [
980
+ "Optimize for web (WebP, max ",
981
+ optConfig.maxDimension ?? 2048,
982
+ "px)"
983
+ ] })
984
+ ] }) : null;
674
985
  if (pastedFile) {
675
- return /* @__PURE__ */ jsx("div", { className: cn(
986
+ const willOptimize = autoOptimize && isProcessableImage(pastedFile.file);
987
+ return /* @__PURE__ */ jsxs("div", { className: cn(
676
988
  "border-2 border-solid border-primary rounded-lg p-4 transition-colors",
677
989
  className
678
- ), children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3", children: [
679
- pastedFile.previewUrl ? /* @__PURE__ */ jsx(
680
- "img",
681
- {
682
- src: pastedFile.previewUrl,
683
- alt: "Pasted content",
684
- className: "max-h-32 max-w-full rounded-md object-contain border border-border"
685
- }
686
- ) : /* @__PURE__ */ jsx("div", { className: "w-16 h-16 rounded-md bg-muted flex items-center justify-center", children: /* @__PURE__ */ jsx(Clipboard, { className: "w-6 h-6 text-muted-foreground" }) }),
687
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1.5 w-full max-w-xs", children: editingName ? /* @__PURE__ */ jsx(
688
- "input",
689
- {
690
- ref: nameInputRef,
691
- type: "text",
692
- value: fileName,
693
- onChange: (e) => setFileName(e.target.value),
694
- onKeyDown: (e) => {
695
- if (e.key === "Enter") {
696
- setEditingName(false);
697
- handleConfirmPaste();
698
- }
699
- if (e.key === "Escape") setEditingName(false);
700
- },
701
- onBlur: () => setEditingName(false),
702
- className: "flex-1 px-2 py-1 text-sm rounded border border-border bg-transparent focus:outline-none focus:ring-1 focus:ring-ring text-center",
703
- placeholder: "File name"
704
- }
705
- ) : /* @__PURE__ */ jsxs(
706
- "button",
707
- {
708
- type: "button",
709
- onClick: () => setEditingName(true),
710
- className: "flex items-center gap-1 mx-auto px-2 py-1 text-sm text-muted-foreground hover:text-foreground rounded hover:bg-accent transition-colors",
711
- title: "Rename",
712
- children: [
713
- /* @__PURE__ */ jsx("span", { className: "truncate max-w-[200px]", children: fileName }),
714
- /* @__PURE__ */ jsx(Pencil, { className: "w-3 h-3 flex-shrink-0 opacity-50" })
715
- ]
716
- }
717
- ) }),
718
- /* @__PURE__ */ jsxs("p", { className: "text-[10px] text-muted-foreground", children: [
719
- pastedFile.file.type,
720
- " \xB7 ",
721
- (pastedFile.file.size / 1024).toFixed(1),
722
- " KB"
723
- ] }),
724
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
725
- /* @__PURE__ */ jsxs(
990
+ ), children: [
991
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3", children: [
992
+ pastedFile.previewUrl ? /* @__PURE__ */ jsxs(
726
993
  "button",
727
994
  {
728
995
  type: "button",
729
- onClick: handleCancelPaste,
730
- className: "px-3 py-1.5 text-xs font-medium rounded-md border border-border text-muted-foreground hover:bg-accent transition-colors flex items-center gap-1",
996
+ onClick: () => setLightboxOpen(true),
997
+ className: "group relative w-24 h-24 rounded-md border border-border bg-muted overflow-hidden flex items-center justify-center",
998
+ title: "Click to preview full size",
731
999
  children: [
732
- /* @__PURE__ */ jsx(X, { className: "w-3 h-3" }),
733
- " Cancel"
1000
+ /* @__PURE__ */ jsx(
1001
+ "img",
1002
+ {
1003
+ src: pastedFile.previewUrl,
1004
+ alt: "Pasted content",
1005
+ className: "w-full h-full object-cover",
1006
+ loading: "lazy",
1007
+ decoding: "async"
1008
+ }
1009
+ ),
1010
+ /* @__PURE__ */ jsx("span", { className: "absolute inset-0 bg-black/0 group-hover:bg-black/40 transition-colors flex items-center justify-center opacity-0 group-hover:opacity-100", children: /* @__PURE__ */ jsx(Maximize2, { className: "w-4 h-4 text-white" }) })
734
1011
  ]
735
1012
  }
736
- ),
737
- /* @__PURE__ */ jsxs(
1013
+ ) : /* @__PURE__ */ jsx("div", { className: "w-16 h-16 rounded-md bg-muted flex items-center justify-center", children: /* @__PURE__ */ jsx(Clipboard, { className: "w-6 h-6 text-muted-foreground" }) }),
1014
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1.5 w-full max-w-xs", children: editingName ? /* @__PURE__ */ jsx(
1015
+ "input",
1016
+ {
1017
+ ref: nameInputRef,
1018
+ type: "text",
1019
+ value: fileName,
1020
+ onChange: (e) => setFileName(e.target.value),
1021
+ onKeyDown: (e) => {
1022
+ if (e.key === "Enter") {
1023
+ setEditingName(false);
1024
+ handleConfirmPaste();
1025
+ }
1026
+ if (e.key === "Escape") setEditingName(false);
1027
+ },
1028
+ onBlur: () => setEditingName(false),
1029
+ className: "flex-1 px-2 py-1 text-sm rounded border border-border bg-transparent focus:outline-none focus:ring-1 focus:ring-ring text-center",
1030
+ placeholder: "File name"
1031
+ }
1032
+ ) : /* @__PURE__ */ jsxs(
738
1033
  "button",
739
1034
  {
740
1035
  type: "button",
741
- onClick: handleConfirmPaste,
742
- className: "px-3 py-1.5 text-xs font-medium rounded-md bg-primary text-primary-foreground hover:bg-primary/90 transition-colors flex items-center gap-1",
1036
+ onClick: () => setEditingName(true),
1037
+ className: "flex items-center gap-1 mx-auto px-2 py-1 text-sm text-muted-foreground hover:text-foreground rounded hover:bg-accent transition-colors",
1038
+ title: "Rename",
743
1039
  children: [
744
- /* @__PURE__ */ jsx(Check, { className: "w-3 h-3" }),
745
- " Upload"
1040
+ /* @__PURE__ */ jsx("span", { className: "truncate max-w-[200px]", children: fileName }),
1041
+ /* @__PURE__ */ jsx(Pencil, { className: "w-3 h-3 flex-shrink-0 opacity-50" })
746
1042
  ]
747
1043
  }
748
- )
749
- ] })
750
- ] }) });
1044
+ ) }),
1045
+ /* @__PURE__ */ jsxs("p", { className: "text-[10px] text-muted-foreground text-center", children: [
1046
+ pastedFile.file.type,
1047
+ " \xB7 ",
1048
+ fmtKB(pastedFile.origSize),
1049
+ pastedFile.origDims && ` \xB7 ${pastedFile.origDims.width}\xD7${pastedFile.origDims.height}`,
1050
+ willOptimize && pastedFile.origDims && (pastedFile.origDims.width > 2048 || pastedFile.origDims.height > 2048 || pastedFile.file.type !== "image/webp") && /* @__PURE__ */ jsxs("span", { className: "block text-primary mt-0.5", children: [
1051
+ /* @__PURE__ */ jsx(Wand2, { className: "w-3 h-3 inline -mt-0.5 mr-1" }),
1052
+ "Will be optimized to WebP",
1053
+ pastedFile.origDims.width > 2048 || pastedFile.origDims.height > 2048 ? ", max 2048px" : ""
1054
+ ] })
1055
+ ] }),
1056
+ isProcessableImage(pastedFile.file) && optimizeToggle,
1057
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
1058
+ /* @__PURE__ */ jsxs(
1059
+ "button",
1060
+ {
1061
+ type: "button",
1062
+ onClick: handleCancelPaste,
1063
+ className: "px-3 py-1.5 text-xs font-medium rounded-md border border-border text-muted-foreground hover:bg-accent transition-colors flex items-center gap-1",
1064
+ disabled: optimizing,
1065
+ children: [
1066
+ /* @__PURE__ */ jsx(X, { className: "w-3 h-3" }),
1067
+ " Cancel"
1068
+ ]
1069
+ }
1070
+ ),
1071
+ /* @__PURE__ */ jsxs(
1072
+ "button",
1073
+ {
1074
+ type: "button",
1075
+ onClick: handleConfirmPaste,
1076
+ className: "px-3 py-1.5 text-xs font-medium rounded-md bg-primary text-primary-foreground hover:bg-primary/90 transition-colors flex items-center gap-1",
1077
+ disabled: optimizing,
1078
+ children: [
1079
+ optimizing ? /* @__PURE__ */ jsx(Loader2, { className: "w-3 h-3 animate-spin" }) : /* @__PURE__ */ jsx(Check, { className: "w-3 h-3" }),
1080
+ optimizing ? "Optimizing\u2026" : "Upload"
1081
+ ]
1082
+ }
1083
+ )
1084
+ ] })
1085
+ ] }),
1086
+ lightboxOpen && pastedFile.previewUrl && /* @__PURE__ */ jsxs(
1087
+ "div",
1088
+ {
1089
+ className: "fixed inset-0 z-[100] bg-black/80 flex items-center justify-center p-6",
1090
+ onClick: () => setLightboxOpen(false),
1091
+ onMouseDown: (e) => e.stopPropagation(),
1092
+ onTouchStart: (e) => e.stopPropagation(),
1093
+ role: "dialog",
1094
+ "aria-label": "Image preview",
1095
+ children: [
1096
+ /* @__PURE__ */ jsx(
1097
+ "button",
1098
+ {
1099
+ type: "button",
1100
+ onClick: (e) => {
1101
+ e.stopPropagation();
1102
+ setLightboxOpen(false);
1103
+ },
1104
+ className: "absolute top-4 right-4 p-2 rounded-md bg-white/10 hover:bg-white/20 text-white",
1105
+ "aria-label": "Close preview",
1106
+ children: /* @__PURE__ */ jsx(X, { className: "w-5 h-5" })
1107
+ }
1108
+ ),
1109
+ /* @__PURE__ */ jsx(
1110
+ "img",
1111
+ {
1112
+ src: pastedFile.previewUrl,
1113
+ alt: "Full preview",
1114
+ className: "max-w-full max-h-full object-contain",
1115
+ onClick: (e) => e.stopPropagation()
1116
+ }
1117
+ )
1118
+ ]
1119
+ }
1120
+ )
1121
+ ] });
751
1122
  }
752
1123
  return /* @__PURE__ */ jsxs(
753
1124
  "div",
@@ -782,13 +1153,10 @@ var UploadZone = ({
782
1153
  className: "hidden"
783
1154
  }
784
1155
  ),
785
- uploading ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2 py-2", children: [
1156
+ uploading || optimizing ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2 py-2", children: [
786
1157
  /* @__PURE__ */ jsx(Loader2, { className: "w-6 h-6 text-primary animate-spin" }),
787
- /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
788
- "Uploading\u2026 ",
789
- uploadProgress > 0 ? `${Math.round(uploadProgress)}%` : ""
790
- ] }),
791
- uploadProgress > 0 && /* @__PURE__ */ jsx("div", { className: "w-full max-w-xs h-1.5 bg-muted rounded-full overflow-hidden", children: /* @__PURE__ */ jsx(
1158
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: optimizing ? "Optimizing\u2026" : `Uploading\u2026 ${uploadProgress > 0 ? `${Math.round(uploadProgress)}%` : ""}` }),
1159
+ !optimizing && uploadProgress > 0 && /* @__PURE__ */ jsx("div", { className: "w-full max-w-xs h-1.5 bg-muted rounded-full overflow-hidden", children: /* @__PURE__ */ jsx(
792
1160
  "div",
793
1161
  {
794
1162
  className: "h-full bg-primary rounded-full transition-all duration-300",
@@ -809,7 +1177,15 @@ var UploadZone = ({
809
1177
  accept && /* @__PURE__ */ jsxs("p", { className: "text-[10px] text-muted-foreground", children: [
810
1178
  "Accepts: ",
811
1179
  accept
812
- ] })
1180
+ ] }),
1181
+ /* @__PURE__ */ jsx(
1182
+ "div",
1183
+ {
1184
+ className: "mt-2 pt-2 border-t border-border/50 w-full flex justify-center",
1185
+ onClick: (e) => e.stopPropagation(),
1186
+ children: optimizeToggle
1187
+ }
1188
+ )
813
1189
  ] })
814
1190
  ]
815
1191
  }
@@ -1461,7 +1837,7 @@ var StockPhotoSearch = ({
1461
1837
  /* @__PURE__ */ jsx(Check, { className: "w-3.5 h-3.5" }),
1462
1838
  " Saved"
1463
1839
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1464
- /* @__PURE__ */ jsx(Image, { className: "w-3.5 h-3.5" }),
1840
+ /* @__PURE__ */ jsx(Image$1, { className: "w-3.5 h-3.5" }),
1465
1841
  " Save"
1466
1842
  ] })
1467
1843
  }
@@ -1496,6 +1872,144 @@ var StockPhotoSearch = ({
1496
1872
  }) })
1497
1873
  ] });
1498
1874
  };
1875
+ var TagEditor = ({ initial, suggestions, assetName, onCancel, onSave }) => {
1876
+ const [labels, setLabels] = useState(() => Array.from(new Set(initial.map((l) => l.trim()).filter(Boolean))));
1877
+ const [input, setInput] = useState("");
1878
+ const [saving, setSaving] = useState(false);
1879
+ const inputRef = useRef(null);
1880
+ useEffect(() => {
1881
+ inputRef.current?.focus();
1882
+ }, []);
1883
+ const filteredSuggestions = useMemo(() => {
1884
+ const q = input.trim().toLowerCase();
1885
+ const have = new Set(labels.map((l) => l.toLowerCase()));
1886
+ return suggestions.filter((s) => !have.has(s.toLowerCase())).filter((s) => !q || s.toLowerCase().includes(q)).slice(0, 8);
1887
+ }, [suggestions, input, labels]);
1888
+ const addLabel = (raw) => {
1889
+ const v = raw.trim();
1890
+ if (!v) return;
1891
+ setLabels((prev) => prev.some((p) => p.toLowerCase() === v.toLowerCase()) ? prev : [...prev, v]);
1892
+ setInput("");
1893
+ };
1894
+ const removeLabel = (label) => {
1895
+ setLabels((prev) => prev.filter((l) => l !== label));
1896
+ };
1897
+ const handleKey = (e) => {
1898
+ if (e.key === "Enter" || e.key === ",") {
1899
+ e.preventDefault();
1900
+ addLabel(input);
1901
+ } else if (e.key === "Backspace" && !input && labels.length > 0) {
1902
+ setLabels((prev) => prev.slice(0, -1));
1903
+ }
1904
+ };
1905
+ const handleSave = async () => {
1906
+ setSaving(true);
1907
+ try {
1908
+ await onSave(labels);
1909
+ } finally {
1910
+ setSaving(false);
1911
+ }
1912
+ };
1913
+ return /* @__PURE__ */ jsx(
1914
+ "div",
1915
+ {
1916
+ className: "fixed inset-0 z-[100] flex items-center justify-center bg-black/60",
1917
+ onClick: onCancel,
1918
+ onMouseDown: (e) => e.stopPropagation(),
1919
+ onTouchStart: (e) => e.stopPropagation(),
1920
+ children: /* @__PURE__ */ jsxs(
1921
+ "div",
1922
+ {
1923
+ className: "w-[min(28rem,92vw)] rounded-lg bg-background border border-border shadow-xl p-4 flex flex-col gap-3",
1924
+ onClick: (e) => e.stopPropagation(),
1925
+ children: [
1926
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1927
+ /* @__PURE__ */ jsx(Tag, { className: "w-4 h-4 text-muted-foreground" }),
1928
+ /* @__PURE__ */ jsxs("h3", { className: "text-sm font-semibold text-foreground truncate", children: [
1929
+ "Edit tags",
1930
+ assetName ? ` \u2014 ${assetName}` : ""
1931
+ ] }),
1932
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: onCancel, className: "ml-auto p-1 rounded hover:bg-muted", "aria-label": "Close", children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4 text-muted-foreground" }) })
1933
+ ] }),
1934
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1 p-2 rounded-md border border-border min-h-[2.5rem]", children: [
1935
+ labels.map((label) => /* @__PURE__ */ jsxs(
1936
+ "span",
1937
+ {
1938
+ className: "inline-flex items-center gap-1 rounded-full bg-primary/10 text-primary border border-primary/30 px-2 py-0.5 text-xs",
1939
+ children: [
1940
+ label,
1941
+ /* @__PURE__ */ jsx(
1942
+ "button",
1943
+ {
1944
+ type: "button",
1945
+ onClick: () => removeLabel(label),
1946
+ className: "opacity-70 hover:opacity-100",
1947
+ "aria-label": `Remove ${label}`,
1948
+ children: /* @__PURE__ */ jsx(X, { className: "w-3 h-3" })
1949
+ }
1950
+ )
1951
+ ]
1952
+ },
1953
+ label
1954
+ )),
1955
+ /* @__PURE__ */ jsx(
1956
+ "input",
1957
+ {
1958
+ ref: inputRef,
1959
+ type: "text",
1960
+ value: input,
1961
+ onChange: (e) => setInput(e.target.value),
1962
+ onKeyDown: handleKey,
1963
+ placeholder: labels.length === 0 ? "Add a tag and press Enter\u2026" : "Add another\u2026",
1964
+ className: "flex-1 min-w-[8rem] bg-transparent text-sm focus:outline-none text-foreground"
1965
+ }
1966
+ )
1967
+ ] }),
1968
+ filteredSuggestions.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-1", children: [
1969
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground self-center mr-1", children: "Suggestions:" }),
1970
+ filteredSuggestions.map((s) => /* @__PURE__ */ jsxs(
1971
+ "button",
1972
+ {
1973
+ type: "button",
1974
+ onClick: () => addLabel(s),
1975
+ className: cn(
1976
+ "inline-flex items-center gap-0.5 rounded-full border border-border bg-muted text-muted-foreground hover:bg-muted/70 px-2 py-0.5 text-[11px]"
1977
+ ),
1978
+ children: [
1979
+ /* @__PURE__ */ jsx(Plus, { className: "w-2.5 h-2.5" }),
1980
+ s
1981
+ ]
1982
+ },
1983
+ s
1984
+ ))
1985
+ ] }),
1986
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2 pt-1", children: [
1987
+ /* @__PURE__ */ jsx(
1988
+ "button",
1989
+ {
1990
+ type: "button",
1991
+ onClick: onCancel,
1992
+ className: "px-3 py-1.5 text-xs rounded-md border border-border text-foreground hover:bg-muted",
1993
+ children: "Cancel"
1994
+ }
1995
+ ),
1996
+ /* @__PURE__ */ jsx(
1997
+ "button",
1998
+ {
1999
+ type: "button",
2000
+ onClick: handleSave,
2001
+ disabled: saving,
2002
+ className: "px-3 py-1.5 text-xs rounded-md bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50",
2003
+ children: saving ? "Saving\u2026" : "Save tags"
2004
+ }
2005
+ )
2006
+ ] })
2007
+ ]
2008
+ }
2009
+ )
2010
+ }
2011
+ );
2012
+ };
1499
2013
  var GlobalUploadToggle = ({ checked, onChange, appName }) => /* @__PURE__ */ jsxs("label", { className: "flex items-start gap-2 text-xs text-muted-foreground cursor-pointer select-none p-2 rounded-md border border-border bg-muted/30", children: [
1500
2014
  /* @__PURE__ */ jsx(
1501
2015
  "input",
@@ -1512,7 +2026,43 @@ var GlobalUploadToggle = ({ checked, onChange, appName }) => /* @__PURE__ */ jsx
1512
2026
  ] })
1513
2027
  ] });
1514
2028
  var ScopedAssetBrowser = ({ scope, accept, pageSize, viewMode, search, selectedIds, onToggleSelect, onDoubleClickSelect, onDelete, allowDelete, emptyText, listAppId, currentAppId, currentAppName, getAppName }) => {
1515
- const { assets, loading, error, refresh } = useAssets({ scope, accept, pageSize, listAppId });
2029
+ const { assets, loading, error, refresh, remove, updateAsset, replaceFile } = useAssets({ scope, accept, pageSize, listAppId });
2030
+ const replaceInputRef = React7.useRef(null);
2031
+ const replaceTargetRef = React7.useRef(null);
2032
+ const handleRename = useCallback(async (asset2) => {
2033
+ const current = asset2.cleanName || asset2.name || "";
2034
+ const next = window.prompt("Rename asset", current);
2035
+ if (next === null) return;
2036
+ const trimmed = next.trim();
2037
+ if (!trimmed || trimmed === current) return;
2038
+ await updateAsset(asset2.id, { name: trimmed });
2039
+ }, [updateAsset]);
2040
+ const handleReplace = useCallback((asset2) => {
2041
+ replaceTargetRef.current = asset2.id;
2042
+ replaceInputRef.current?.click();
2043
+ }, []);
2044
+ const handleReplaceFiles = useCallback(async (e) => {
2045
+ const file = e.target.files?.[0];
2046
+ const assetId = replaceTargetRef.current;
2047
+ e.target.value = "";
2048
+ replaceTargetRef.current = null;
2049
+ if (!file || !assetId) return;
2050
+ await replaceFile(assetId, file);
2051
+ }, [replaceFile]);
2052
+ const handleDeleteWithConfirm = useCallback(async (assetId) => {
2053
+ if (!window.confirm("Delete this asset? It can be restored from the asset manager within 30 days.")) return;
2054
+ const ok = await remove(assetId);
2055
+ if (ok) onDelete?.(assetId);
2056
+ }, [remove, onDelete]);
2057
+ const [tagEditorAsset, setTagEditorAsset] = useState(null);
2058
+ const handleEditTags = useCallback((asset2) => {
2059
+ setTagEditorAsset(asset2);
2060
+ }, []);
2061
+ const handleSaveTags = useCallback(async (next) => {
2062
+ if (!tagEditorAsset) return;
2063
+ await updateAsset(tagEditorAsset.id, { labels: next });
2064
+ setTagEditorAsset(null);
2065
+ }, [tagEditorAsset, updateAsset]);
1516
2066
  const [activeLabels, setActiveLabels] = useState(/* @__PURE__ */ new Set());
1517
2067
  const toggleLabel = useCallback((label) => {
1518
2068
  setActiveLabels((prev) => {
@@ -1607,7 +2157,10 @@ var ScopedAssetBrowser = ({ scope, accept, pageSize, viewMode, search, selectedI
1607
2157
  selectedIds,
1608
2158
  onToggleSelect,
1609
2159
  onDoubleClickSelect,
1610
- onDelete: allowDelete ? onDelete : void 0,
2160
+ onDelete: allowDelete ? handleDeleteWithConfirm : void 0,
2161
+ onRename: allowDelete ? handleRename : void 0,
2162
+ onReplace: allowDelete ? handleReplace : void 0,
2163
+ onEditTags: allowDelete ? handleEditTags : void 0,
1611
2164
  allowDelete,
1612
2165
  currentAppId,
1613
2166
  currentAppName,
@@ -1615,6 +2168,25 @@ var ScopedAssetBrowser = ({ scope, accept, pageSize, viewMode, search, selectedI
1615
2168
  activeLabels,
1616
2169
  onToggleLabel: toggleLabel
1617
2170
  }
2171
+ ),
2172
+ /* @__PURE__ */ jsx(
2173
+ "input",
2174
+ {
2175
+ ref: replaceInputRef,
2176
+ type: "file",
2177
+ className: "hidden",
2178
+ onChange: handleReplaceFiles
2179
+ }
2180
+ ),
2181
+ tagEditorAsset && /* @__PURE__ */ jsx(
2182
+ TagEditor,
2183
+ {
2184
+ initial: tagEditorAsset.labels || [],
2185
+ suggestions: allLabels.map((l) => l.label),
2186
+ assetName: tagEditorAsset.cleanName || tagEditorAsset.name,
2187
+ onCancel: () => setTagEditorAsset(null),
2188
+ onSave: handleSaveTags
2189
+ }
1618
2190
  )
1619
2191
  ] });
1620
2192
  };
@@ -1637,7 +2209,8 @@ var AssetPickerContent = ({
1637
2209
  defaultView = "grid",
1638
2210
  emptyText,
1639
2211
  pageSize = 50,
1640
- onConfirm
2212
+ onConfirm,
2213
+ imageOptimization
1641
2214
  }) => {
1642
2215
  const [appFilter, setAppFilter] = useState(defaultAppFilter);
1643
2216
  const hasAppFilter = !!appId;
@@ -1647,13 +2220,6 @@ var AssetPickerContent = ({
1647
2220
  const { apps: registryApps, getAppName } = useAppRegistry(collectionIdForRegistry);
1648
2221
  const resolvedAppName = appName || (appId ? getAppName(appId) : void 0);
1649
2222
  const listAppId = hasAppFilter ? appFilter === "app" ? appId : otherAppFilter || void 0 : void 0;
1650
- const { assets, upload, uploadFromUrl, uploadFromRemoteUrl, uploading, uploadProgress } = useAssets({
1651
- scope,
1652
- accept: acceptProp,
1653
- pageSize,
1654
- appId: uploadGlobal ? void 0 : appId,
1655
- listAppId
1656
- });
1657
2223
  const [tab, setTab] = useState("browse");
1658
2224
  const [viewMode, setViewMode] = useState(defaultView);
1659
2225
  const [search, setSearch] = useState("");
@@ -1663,7 +2229,7 @@ var AssetPickerContent = ({
1663
2229
  return new Set(Array.isArray(value) ? value : [value]);
1664
2230
  });
1665
2231
  const hasProductScope = !!productScope;
1666
- const [scopeTab, setScopeTab] = useState("collection");
2232
+ const [scopeTab, setScopeTab] = useState(hasProductScope ? "product" : "collection");
1667
2233
  const effectiveAccept = useMemo(() => {
1668
2234
  if (acceptProp) return acceptProp;
1669
2235
  const entry = ASSET_MIME_FILTERS.find((f) => f.value === mimeFilter);
@@ -1680,6 +2246,13 @@ var AssetPickerContent = ({
1680
2246
  }
1681
2247
  return scope;
1682
2248
  }, [scope, productScope, scopeTab, hasProductScope]);
2249
+ const { assets, upload, uploadFromUrl, uploadFromRemoteUrl, uploading, uploadProgress } = useAssets({
2250
+ scope: activeScope,
2251
+ accept: acceptProp,
2252
+ pageSize,
2253
+ appId: uploadGlobal ? void 0 : appId,
2254
+ listAppId
2255
+ });
1683
2256
  const toSelection = useCallback((asset2) => ({
1684
2257
  id: asset2.id,
1685
2258
  url: asset2.url,
@@ -1950,7 +2523,8 @@ var AssetPickerContent = ({
1950
2523
  accept: acceptProp,
1951
2524
  multiple,
1952
2525
  uploading,
1953
- uploadProgress
2526
+ uploadProgress,
2527
+ imageOptimization
1954
2528
  }
1955
2529
  )
1956
2530
  ] }),
@@ -2099,5 +2673,5 @@ var AssetPicker = (props) => {
2099
2673
  assertStylesLoaded();
2100
2674
 
2101
2675
  export { ASSET_MIME_FILTERS, AssetPicker, useAppRegistry, useAssets };
2102
- //# sourceMappingURL=chunk-PQD2B6DA.js.map
2103
- //# sourceMappingURL=chunk-PQD2B6DA.js.map
2676
+ //# sourceMappingURL=chunk-PSVYUVZC.js.map
2677
+ //# sourceMappingURL=chunk-PSVYUVZC.js.map