@proveanything/smartlinks-utils-ui 1.13.1 → 1.13.3

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,9 @@
1
1
  import { assertStylesLoaded } from './chunk-OLYC54YT.js';
2
2
  import { cn } from './chunk-L7FQ52F5.js';
3
- import React7, { useState, useRef, useEffect, useCallback, useMemo } from 'react';
3
+ import React7, { useState, useRef, useEffect, useCallback, useMemo, useLayoutEffect } from 'react';
4
+ import { createPortal } from 'react-dom';
4
5
  import * as SL from '@proveanything/smartlinks';
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
+ import { Filter, Search, LayoutGrid, List, Loader2, AlertCircle, Tag, X, 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
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
8
 
8
9
  // src/components/AssetPicker/types.ts
@@ -14,6 +15,66 @@ var ASSET_MIME_FILTERS = [
14
15
  { value: "document", label: "Documents", prefix: "application/" },
15
16
  { value: "pdf", label: "PDFs", prefix: "application/pdf" }
16
17
  ];
18
+ function safeDecode(value) {
19
+ if (!value) return "";
20
+ try {
21
+ return decodeURIComponent(value);
22
+ } catch {
23
+ return value;
24
+ }
25
+ }
26
+ function getAssetIdentityKeys(asset2) {
27
+ const keys = [];
28
+ if (asset2.id) keys.push(`id:${asset2.id}`);
29
+ const normalizedUrl = safeDecode(asset2.url).trim().toLowerCase();
30
+ if (normalizedUrl) keys.push(`url:${normalizedUrl}`);
31
+ const normalizedThumb = safeDecode(asset2.thumbnail).trim().toLowerCase();
32
+ if (normalizedThumb) keys.push(`thumb:${normalizedThumb}`);
33
+ const normalizedHash = asset2.hash?.trim().toLowerCase();
34
+ if (normalizedHash) keys.push(`hash:${normalizedHash}`);
35
+ return keys;
36
+ }
37
+ function scoreAsset(asset2) {
38
+ const productId = asset2.productId ?? asset2.metadata?.productId;
39
+ const proofId = asset2.proofId ?? asset2.metadata?.proofId;
40
+ const plainName = asset2.name && !asset2.name.startsWith("sites/") ? 1 : 0;
41
+ const plainCleanName = asset2.cleanName && !asset2.cleanName.startsWith("sites/") ? 1 : 0;
42
+ return (proofId ? 32 : 0) + (productId ? 16 : 0) + (asset2.thumbnail ? 8 : 0) + (asset2.mimeType ? 4 : 0) + (asset2.size ? 2 : 0) + plainName + plainCleanName;
43
+ }
44
+ function mergeAssets(a, b) {
45
+ const keepB = scoreAsset(b) > scoreAsset(a);
46
+ const primary = keepB ? b : a;
47
+ const secondary = keepB ? a : b;
48
+ return {
49
+ ...secondary,
50
+ ...primary,
51
+ metadata: {
52
+ ...secondary.metadata || {},
53
+ ...primary.metadata || {}
54
+ },
55
+ labels: Array.from(/* @__PURE__ */ new Set([...secondary.labels || [], ...primary.labels || []])),
56
+ thumbnail: primary.thumbnail ?? secondary.thumbnail ?? null,
57
+ app: primary.app ?? secondary.app ?? null,
58
+ thumbnails: primary.thumbnails ?? secondary.thumbnails
59
+ };
60
+ }
61
+ function dedupeAssetList(items) {
62
+ const deduped = [];
63
+ const keyToIndex = /* @__PURE__ */ new Map();
64
+ for (const asset2 of items) {
65
+ const keys = getAssetIdentityKeys(asset2);
66
+ const existingIndex = keys.map((key) => keyToIndex.get(key)).find((index) => index !== void 0);
67
+ if (existingIndex === void 0) {
68
+ const nextIndex = deduped.push(asset2) - 1;
69
+ keys.forEach((key) => keyToIndex.set(key, nextIndex));
70
+ continue;
71
+ }
72
+ const merged = mergeAssets(deduped[existingIndex], asset2);
73
+ deduped[existingIndex] = merged;
74
+ getAssetIdentityKeys(merged).forEach((key) => keyToIndex.set(key, existingIndex));
75
+ }
76
+ return deduped;
77
+ }
17
78
  function useAssets({ scope, accept, pageSize, appId, listAppId }) {
18
79
  const [assets, setAssets] = useState([]);
19
80
  const [loading, setLoading] = useState(true);
@@ -41,7 +102,19 @@ function useAssets({ scope, accept, pageSize, appId, listAppId }) {
41
102
  ...listAppId ? { appId: listAppId } : {}
42
103
  });
43
104
  if (mountedRef.current) {
44
- setAssets(result);
105
+ const raw = result;
106
+ const deduped = dedupeAssetList(raw);
107
+ const idCounts = /* @__PURE__ */ new Map();
108
+ for (const a of raw) idCounts.set(a.id, (idCounts.get(a.id) || 0) + 1);
109
+ const dupIds = Array.from(idCounts.entries()).filter(([, n]) => n > 1);
110
+ console.debug("[AssetPicker] list", {
111
+ scope,
112
+ listAppId,
113
+ rawCount: raw.length,
114
+ dedupedCount: deduped.length,
115
+ duplicateIds: dupIds
116
+ });
117
+ setAssets(deduped);
45
118
  }
46
119
  } catch (err) {
47
120
  if (mountedRef.current) {
@@ -55,13 +128,13 @@ function useAssets({ scope, accept, pageSize, appId, listAppId }) {
55
128
  useEffect(() => {
56
129
  fetchAssets();
57
130
  }, [fetchAssets]);
58
- const upload = useCallback(async (file, onProgress) => {
131
+ const upload = useCallback(async (file, onProgress, scopeOverride) => {
59
132
  setUploading(true);
60
133
  setUploadProgress(0);
61
134
  try {
62
135
  const result = await SL.asset.upload({
63
136
  file,
64
- scope,
137
+ scope: scopeOverride || scope,
65
138
  name: file.name,
66
139
  admin: true,
67
140
  ...appId ? { appId } : {},
@@ -70,8 +143,8 @@ function useAssets({ scope, accept, pageSize, appId, listAppId }) {
70
143
  onProgress?.(pct);
71
144
  }
72
145
  });
73
- if (mountedRef.current) {
74
- setAssets((prev) => [result, ...prev]);
146
+ if (mountedRef.current && !scopeOverride) {
147
+ setAssets((prev) => dedupeAssetList([result, ...prev]));
75
148
  }
76
149
  return result;
77
150
  } catch (err) {
@@ -84,7 +157,7 @@ function useAssets({ scope, accept, pageSize, appId, listAppId }) {
84
157
  }
85
158
  }
86
159
  }, [scope, appId]);
87
- const uploadFromUrl = useCallback(async (url, name) => {
160
+ const uploadFromUrl = useCallback(async (url, name, scopeOverride) => {
88
161
  setUploading(true);
89
162
  setUploadProgress(0);
90
163
  try {
@@ -93,7 +166,7 @@ function useAssets({ scope, accept, pageSize, appId, listAppId }) {
93
166
  const blob = await response.blob();
94
167
  const fileName = name || url.split("/").pop()?.split("?")[0] || "imported-file";
95
168
  const file = new File([blob], fileName, { type: blob.type });
96
- return await upload(file);
169
+ return await upload(file, void 0, scopeOverride);
97
170
  } catch (err) {
98
171
  if (mountedRef.current) setError(err?.message || "URL import failed");
99
172
  return null;
@@ -110,13 +183,13 @@ function useAssets({ scope, accept, pageSize, appId, listAppId }) {
110
183
  try {
111
184
  const result = await SL.asset.uploadFromUrl({
112
185
  url,
113
- scope,
186
+ scope: opts?.scopeOverride || scope,
114
187
  metadata: { name: opts?.name, ...opts?.metadata || {} },
115
188
  ...appId ? { appId } : {},
116
189
  admin: true
117
190
  });
118
- if (mountedRef.current) {
119
- setAssets((prev) => [result, ...prev]);
191
+ if (mountedRef.current && !opts?.scopeOverride) {
192
+ setAssets((prev) => dedupeAssetList([result, ...prev]));
120
193
  }
121
194
  return result;
122
195
  } catch (err) {
@@ -155,10 +228,10 @@ function useAssets({ scope, accept, pageSize, appId, listAppId }) {
155
228
  try {
156
229
  const result = await SL.asset.restoreAdmin(collectionId, assetId);
157
230
  if (mountedRef.current) {
158
- setAssets((prev) => {
231
+ setAssets((prev) => dedupeAssetList((() => {
159
232
  const exists = prev.some((a) => a.id === assetId);
160
233
  return exists ? prev.map((a) => a.id === assetId ? result : a) : [result, ...prev];
161
- });
234
+ })()));
162
235
  }
163
236
  return result;
164
237
  } catch (err) {
@@ -185,7 +258,7 @@ function useAssets({ scope, accept, pageSize, appId, listAppId }) {
185
258
  }
186
259
  });
187
260
  if (mountedRef.current) {
188
- setAssets((prev) => prev.map((a) => a.id === assetId ? result : a));
261
+ setAssets((prev) => dedupeAssetList(prev.map((a) => a.id === assetId ? result : a)));
189
262
  }
190
263
  return result;
191
264
  } catch (err) {
@@ -207,7 +280,7 @@ function useAssets({ scope, accept, pageSize, appId, listAppId }) {
207
280
  try {
208
281
  const result = await SL.asset.updateAdmin({ collectionId, assetId, ...patch });
209
282
  if (mountedRef.current) {
210
- setAssets((prev) => prev.map((a) => a.id === assetId ? result : a));
283
+ setAssets((prev) => dedupeAssetList(prev.map((a) => a.id === assetId ? result : a)));
211
284
  }
212
285
  return result;
213
286
  } catch (err) {
@@ -352,13 +425,54 @@ var AppBadge = ({ appId, appName, size = "sm" }) => {
352
425
  var CardMenu = ({ onRename, onReplace, onEditTags, onDelete, position = "absolute" }) => {
353
426
  const [open, setOpen] = useState(false);
354
427
  const ref = useRef(null);
428
+ const btnRef = useRef(null);
429
+ const menuRef = useRef(null);
430
+ const [pos, setPos] = useState(null);
355
431
  useEffect(() => {
356
432
  if (!open) return;
357
433
  const handler = (e) => {
358
- if (!ref.current?.contains(e.target)) setOpen(false);
434
+ const t = e.target;
435
+ if (ref.current?.contains(t)) return;
436
+ if (menuRef.current?.contains(t)) return;
437
+ setOpen(false);
438
+ };
439
+ const onKey = (e) => {
440
+ if (e.key === "Escape") setOpen(false);
359
441
  };
360
442
  document.addEventListener("mousedown", handler);
361
- return () => document.removeEventListener("mousedown", handler);
443
+ document.addEventListener("keydown", onKey);
444
+ return () => {
445
+ document.removeEventListener("mousedown", handler);
446
+ document.removeEventListener("keydown", onKey);
447
+ };
448
+ }, [open]);
449
+ useLayoutEffect(() => {
450
+ if (!open) {
451
+ setPos(null);
452
+ return;
453
+ }
454
+ const update = () => {
455
+ const el = btnRef.current;
456
+ if (!el) return;
457
+ const r = el.getBoundingClientRect();
458
+ const menuW = menuRef.current?.offsetWidth ?? 160;
459
+ const menuH = menuRef.current?.offsetHeight ?? 160;
460
+ const margin = 8;
461
+ let left = Math.min(window.innerWidth - menuW - margin, r.right - menuW);
462
+ left = Math.max(margin, left);
463
+ let top = r.bottom + 4;
464
+ if (top + menuH > window.innerHeight - margin) {
465
+ top = Math.max(margin, r.top - menuH - 4);
466
+ }
467
+ setPos({ top, left });
468
+ };
469
+ update();
470
+ window.addEventListener("resize", update);
471
+ window.addEventListener("scroll", update, true);
472
+ return () => {
473
+ window.removeEventListener("resize", update);
474
+ window.removeEventListener("scroll", update, true);
475
+ };
362
476
  }, [open]);
363
477
  if (!onRename && !onReplace && !onEditTags && !onDelete) return null;
364
478
  return /* @__PURE__ */ jsxs(
@@ -366,7 +480,7 @@ var CardMenu = ({ onRename, onReplace, onEditTags, onDelete, position = "absolut
366
480
  {
367
481
  ref,
368
482
  className: cn(
369
- position === "absolute" ? "absolute bottom-1.5 right-1.5" : "relative flex-shrink-0"
483
+ position === "absolute" ? "absolute top-1.5 right-1.5 z-10" : "relative flex-shrink-0"
370
484
  ),
371
485
  onClick: (e) => e.stopPropagation(),
372
486
  onDoubleClick: (e) => e.stopPropagation(),
@@ -374,6 +488,7 @@ var CardMenu = ({ onRename, onReplace, onEditTags, onDelete, position = "absolut
374
488
  /* @__PURE__ */ jsx(
375
489
  "button",
376
490
  {
491
+ ref: btnRef,
377
492
  type: "button",
378
493
  onClick: (e) => {
379
494
  e.stopPropagation();
@@ -381,91 +496,96 @@ var CardMenu = ({ onRename, onReplace, onEditTags, onDelete, position = "absolut
381
496
  },
382
497
  className: cn(
383
498
  "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"
499
+ "bg-background/90 border border-border text-foreground hover:bg-background shadow-sm"
387
500
  ),
388
501
  title: "Asset actions",
389
502
  "aria-label": "Asset actions",
390
503
  children: /* @__PURE__ */ jsx(MoreVertical, { className: "w-3 h-3" })
391
504
  }
392
505
  ),
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
- }
506
+ open && typeof document !== "undefined" && createPortal(
507
+ /* @__PURE__ */ jsxs(
508
+ "div",
509
+ {
510
+ ref: menuRef,
511
+ className: "fixed z-[2147483647] min-w-[160px] rounded-md border border-border bg-popover text-popover-foreground shadow-lg py-1",
512
+ style: pos ? { top: pos.top, left: pos.left } : { visibility: "hidden", top: 0, left: 0 },
513
+ role: "menu",
514
+ onClick: (e) => e.stopPropagation(),
515
+ onMouseDown: (e) => e.stopPropagation(),
516
+ children: [
517
+ onRename && /* @__PURE__ */ jsxs(
518
+ "button",
519
+ {
520
+ type: "button",
521
+ role: "menuitem",
522
+ onClick: (e) => {
523
+ e.stopPropagation();
524
+ setOpen(false);
525
+ onRename();
526
+ },
527
+ className: "w-full flex items-center gap-2 px-2.5 py-1.5 text-xs hover:bg-accent",
528
+ children: [
529
+ /* @__PURE__ */ jsx(Pencil, { className: "w-3 h-3" }),
530
+ " Rename"
531
+ ]
532
+ }
533
+ ),
534
+ onReplace && /* @__PURE__ */ jsxs(
535
+ "button",
536
+ {
537
+ type: "button",
538
+ role: "menuitem",
539
+ onClick: (e) => {
540
+ e.stopPropagation();
541
+ setOpen(false);
542
+ onReplace();
543
+ },
544
+ className: "w-full flex items-center gap-2 px-2.5 py-1.5 text-xs hover:bg-accent",
545
+ children: [
546
+ /* @__PURE__ */ jsx(Upload, { className: "w-3 h-3" }),
547
+ " Replace file"
548
+ ]
549
+ }
550
+ ),
551
+ onEditTags && /* @__PURE__ */ jsxs(
552
+ "button",
553
+ {
554
+ type: "button",
555
+ role: "menuitem",
556
+ onClick: (e) => {
557
+ e.stopPropagation();
558
+ setOpen(false);
559
+ onEditTags();
560
+ },
561
+ className: "w-full flex items-center gap-2 px-2.5 py-1.5 text-xs hover:bg-accent",
562
+ children: [
563
+ /* @__PURE__ */ jsx(Tag, { className: "w-3 h-3" }),
564
+ " Edit tags"
565
+ ]
566
+ }
567
+ ),
568
+ onDelete && /* @__PURE__ */ jsxs(
569
+ "button",
570
+ {
571
+ type: "button",
572
+ role: "menuitem",
573
+ onClick: (e) => {
574
+ e.stopPropagation();
575
+ setOpen(false);
576
+ onDelete();
577
+ },
578
+ className: "w-full flex items-center gap-2 px-2.5 py-1.5 text-xs text-destructive hover:bg-destructive/10",
579
+ children: [
580
+ /* @__PURE__ */ jsx(Trash2, { className: "w-3 h-3" }),
581
+ " Delete"
582
+ ]
583
+ }
584
+ )
585
+ ]
586
+ }
587
+ ),
588
+ document.body
469
589
  )
470
590
  ]
471
591
  }
@@ -545,7 +665,7 @@ var AssetGridItem = ({ asset: asset2, selected, onToggle, onDoubleClick, onDelet
545
665
  ] })
546
666
  ] })
547
667
  ] }),
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" }) }),
668
+ selected && /* @__PURE__ */ jsx("div", { className: "absolute top-2 left-2 w-5 h-5 rounded-full bg-primary flex items-center justify-center z-10", children: /* @__PURE__ */ jsx(Check, { className: "w-3 h-3 text-primary-foreground" }) }),
549
669
  /* @__PURE__ */ jsx(
550
670
  CardMenu,
551
671
  {
@@ -2025,8 +2145,25 @@ var GlobalUploadToggle = ({ checked, onChange, appName }) => /* @__PURE__ */ jsx
2025
2145
  /* @__PURE__ */ jsx("span", { className: "block", children: checked ? `Asset will be available to every app in this collection.` : `Asset will be tagged to ${appName}. Tick to share with every app in the collection instead.` })
2026
2146
  ] })
2027
2147
  ] });
2028
- var ScopedAssetBrowser = ({ scope, accept, pageSize, viewMode, search, selectedIds, onToggleSelect, onDoubleClickSelect, onDelete, allowDelete, emptyText, listAppId, requireProductId, currentAppId, currentAppName, getAppName }) => {
2029
- const { assets, loading, error, refresh, remove, updateAsset, replaceFile } = useAssets({ scope, accept, pageSize, listAppId });
2148
+ var AttachToContextToggle = ({ checked, onChange, contextLabel }) => /* @__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: [
2149
+ /* @__PURE__ */ jsx(
2150
+ "input",
2151
+ {
2152
+ type: "checkbox",
2153
+ checked,
2154
+ onChange: (e) => onChange(e.target.checked),
2155
+ className: "mt-0.5 cursor-pointer"
2156
+ }
2157
+ ),
2158
+ /* @__PURE__ */ jsxs("span", { children: [
2159
+ /* @__PURE__ */ jsxs("span", { className: "font-medium text-foreground", children: [
2160
+ "Attach to ",
2161
+ contextLabel
2162
+ ] }),
2163
+ /* @__PURE__ */ jsx("span", { className: "block", children: checked ? `Asset will be tagged to ${contextLabel}.` : `Asset will be added to the collection (available everywhere). Tick to attach it to ${contextLabel} instead.` })
2164
+ ] })
2165
+ ] });
2166
+ var ScopedAssetBrowser = ({ scope: _scope, accept: _accept, pageSize: _pageSize, viewMode, search, selectedIds, onToggleSelect, onDoubleClickSelect, onDelete, allowDelete, emptyText, listAppId: _listAppId, requireProductId, currentAppId, currentAppName, getAppName, assets, loading, error, refresh, remove, updateAsset, replaceFile }) => {
2030
2167
  const replaceInputRef = React7.useRef(null);
2031
2168
  const replaceTargetRef = React7.useRef(null);
2032
2169
  const handleRename = useCallback(async (asset2) => {
@@ -2250,13 +2387,55 @@ var AssetPickerContent = ({
2250
2387
  }
2251
2388
  return scope;
2252
2389
  }, [scope, productScope, scopeTab, hasProductScope]);
2253
- const { assets, upload, uploadFromUrl, uploadFromRemoteUrl, uploading, uploadProgress } = useAssets({
2390
+ const collectionScope = useMemo(() => {
2391
+ const cId = scope.collectionId;
2392
+ return cId ? { type: "collection", collectionId: cId } : null;
2393
+ }, [scope]);
2394
+ const contextScope = useMemo(() => {
2395
+ if (scope.type === "proof") return scope;
2396
+ if (scope.type === "product") return scope;
2397
+ if (productScope) {
2398
+ return { type: "product", collectionId: productScope.collectionId, productId: productScope.productId };
2399
+ }
2400
+ return null;
2401
+ }, [scope, productScope]);
2402
+ const contextLabel = contextScope?.type === "proof" ? "this proof" : "this product";
2403
+ const hasContext = !!contextScope && !!collectionScope;
2404
+ const [attachToContext, setAttachToContext] = useState(true);
2405
+ const uploadScope = useMemo(() => {
2406
+ if (hasContext) return attachToContext ? contextScope : collectionScope;
2407
+ return activeScope;
2408
+ }, [hasContext, attachToContext, contextScope, collectionScope, activeScope]);
2409
+ const {
2410
+ assets,
2411
+ loading: assetsLoading,
2412
+ error: assetsError,
2413
+ refresh: refreshAssets,
2414
+ upload,
2415
+ uploadFromUrl,
2416
+ uploadFromRemoteUrl,
2417
+ remove,
2418
+ updateAsset,
2419
+ replaceFile,
2420
+ uploading,
2421
+ uploadProgress
2422
+ } = useAssets({
2254
2423
  scope: activeScope,
2255
2424
  accept: acceptProp,
2256
2425
  pageSize,
2257
2426
  appId: uploadGlobal ? void 0 : appId,
2258
2427
  listAppId
2259
2428
  });
2429
+ const reconcileAfterUpload = useCallback(async (uploadedScope) => {
2430
+ if (hasProductScope) {
2431
+ const desiredTab = uploadedScope.type === "product" ? "product" : "collection";
2432
+ if (desiredTab !== scopeTab) {
2433
+ setScopeTab(desiredTab);
2434
+ return;
2435
+ }
2436
+ }
2437
+ await refreshAssets();
2438
+ }, [hasProductScope, scopeTab, refreshAssets]);
2260
2439
  const toSelection = useCallback((asset2) => ({
2261
2440
  id: asset2.id,
2262
2441
  url: asset2.url,
@@ -2293,34 +2472,46 @@ var AssetPickerContent = ({
2293
2472
  });
2294
2473
  }, [multiple, onSelect, onConfirm, toSelection]);
2295
2474
  const handleUploadFiles = useCallback(async (files) => {
2475
+ const targetScope = uploadScope;
2476
+ const sameAsActive = targetScope === activeScope;
2296
2477
  for (const file of files) {
2297
- const result = await upload(file);
2478
+ const result = await upload(file, void 0, sameAsActive ? void 0 : targetScope);
2298
2479
  if (result && !multiple) {
2299
2480
  setSelectedIds(/* @__PURE__ */ new Set([result.id]));
2300
2481
  onSelect?.(toSelection(result));
2301
2482
  }
2302
2483
  }
2303
2484
  setTab("browse");
2304
- }, [upload, multiple, onSelect, toSelection]);
2485
+ await reconcileAfterUpload(targetScope);
2486
+ }, [upload, multiple, onSelect, toSelection, uploadScope, activeScope, reconcileAfterUpload]);
2305
2487
  const handleUrlImport = useCallback(async (url, name) => {
2306
- const result = await uploadFromUrl(url, name);
2488
+ const targetScope = uploadScope;
2489
+ const sameAsActive = targetScope === activeScope;
2490
+ const result = await uploadFromUrl(url, name, sameAsActive ? void 0 : targetScope);
2307
2491
  if (result) {
2308
2492
  setTab("browse");
2309
2493
  if (!multiple) {
2310
2494
  setSelectedIds(/* @__PURE__ */ new Set([result.id]));
2311
2495
  onSelect?.(toSelection(result));
2312
2496
  }
2497
+ await reconcileAfterUpload(targetScope);
2313
2498
  }
2314
2499
  return result;
2315
- }, [uploadFromUrl, multiple, onSelect, toSelection]);
2500
+ }, [uploadFromUrl, multiple, onSelect, toSelection, uploadScope, activeScope, reconcileAfterUpload]);
2316
2501
  const handleRemoteIngest = useCallback(async (url, name) => {
2317
- const result = await uploadFromRemoteUrl(url, { name });
2502
+ const targetScope = uploadScope;
2503
+ const sameAsActive = targetScope === activeScope;
2504
+ const result = await uploadFromRemoteUrl(url, {
2505
+ name,
2506
+ ...sameAsActive ? {} : { scopeOverride: targetScope }
2507
+ });
2318
2508
  if (result && !multiple) {
2319
2509
  setSelectedIds(/* @__PURE__ */ new Set([result.id]));
2320
2510
  onSelect?.(toSelection(result));
2321
2511
  }
2512
+ if (result) await reconcileAfterUpload(targetScope);
2322
2513
  return result;
2323
- }, [uploadFromRemoteUrl, multiple, onSelect, toSelection]);
2514
+ }, [uploadFromRemoteUrl, multiple, onSelect, toSelection, uploadScope, activeScope, reconcileAfterUpload]);
2324
2515
  const handleDelete = useCallback(async (assetId) => {
2325
2516
  setSelectedIds((prev) => {
2326
2517
  const next = new Set(prev);
@@ -2508,11 +2699,26 @@ var AssetPickerContent = ({
2508
2699
  requireProductId: hasProductScope && scopeTab === "product" ? productScope.productId : void 0,
2509
2700
  currentAppId: appId,
2510
2701
  currentAppName: resolvedAppName,
2511
- getAppName
2702
+ getAppName,
2703
+ assets,
2704
+ loading: assetsLoading,
2705
+ error: assetsError,
2706
+ refresh: refreshAssets,
2707
+ remove,
2708
+ updateAsset,
2709
+ replaceFile
2512
2710
  },
2513
2711
  `${activeScope.type}-${activeScope.productId || ""}-${effectiveAccept || "all"}-${listAppId || "no-app-filter"}`
2514
2712
  ),
2515
2713
  tab === "upload" && /* @__PURE__ */ jsxs(Fragment, { children: [
2714
+ hasContext && /* @__PURE__ */ jsx(
2715
+ AttachToContextToggle,
2716
+ {
2717
+ checked: attachToContext,
2718
+ onChange: setAttachToContext,
2719
+ contextLabel
2720
+ }
2721
+ ),
2516
2722
  hasAppFilter && /* @__PURE__ */ jsx(
2517
2723
  GlobalUploadToggle,
2518
2724
  {
@@ -2534,6 +2740,14 @@ var AssetPickerContent = ({
2534
2740
  )
2535
2741
  ] }),
2536
2742
  tab === "url" && /* @__PURE__ */ jsxs(Fragment, { children: [
2743
+ hasContext && /* @__PURE__ */ jsx(
2744
+ AttachToContextToggle,
2745
+ {
2746
+ checked: attachToContext,
2747
+ onChange: setAttachToContext,
2748
+ contextLabel
2749
+ }
2750
+ ),
2537
2751
  hasAppFilter && /* @__PURE__ */ jsx(
2538
2752
  GlobalUploadToggle,
2539
2753
  {
@@ -2551,6 +2765,14 @@ var AssetPickerContent = ({
2551
2765
  )
2552
2766
  ] }),
2553
2767
  tab === "ai" && aiEnabled && /* @__PURE__ */ jsxs(Fragment, { children: [
2768
+ hasContext && /* @__PURE__ */ jsx(
2769
+ AttachToContextToggle,
2770
+ {
2771
+ checked: attachToContext,
2772
+ onChange: setAttachToContext,
2773
+ contextLabel
2774
+ }
2775
+ ),
2554
2776
  hasAppFilter && /* @__PURE__ */ jsx(
2555
2777
  GlobalUploadToggle,
2556
2778
  {
@@ -2569,6 +2791,14 @@ var AssetPickerContent = ({
2569
2791
  )
2570
2792
  ] }),
2571
2793
  tab === "stock" && stockEnabled && /* @__PURE__ */ jsxs(Fragment, { children: [
2794
+ hasContext && /* @__PURE__ */ jsx(
2795
+ AttachToContextToggle,
2796
+ {
2797
+ checked: attachToContext,
2798
+ onChange: setAttachToContext,
2799
+ contextLabel
2800
+ }
2801
+ ),
2572
2802
  hasAppFilter && /* @__PURE__ */ jsx(
2573
2803
  GlobalUploadToggle,
2574
2804
  {
@@ -2611,7 +2841,7 @@ var AssetPickerContent = ({
2611
2841
  };
2612
2842
  var PickerDialog = ({ open, onClose, maxWidth, children }) => {
2613
2843
  if (!open) return null;
2614
- return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
2844
+ const node = /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-[2147483600] flex items-center justify-center", children: [
2615
2845
  /* @__PURE__ */ jsx(
2616
2846
  "div",
2617
2847
  {
@@ -2645,6 +2875,8 @@ var PickerDialog = ({ open, onClose, maxWidth, children }) => {
2645
2875
  }
2646
2876
  )
2647
2877
  ] });
2878
+ if (typeof document === "undefined") return node;
2879
+ return createPortal(node, document.body);
2648
2880
  };
2649
2881
  var AssetPicker = (props) => {
2650
2882
  const { mode = "inline", open: controlledOpen, onClose, trigger, onSelect, multiple, className, dialogMaxWidth } = props;
@@ -2678,5 +2910,5 @@ var AssetPicker = (props) => {
2678
2910
  assertStylesLoaded();
2679
2911
 
2680
2912
  export { ASSET_MIME_FILTERS, AssetPicker, useAppRegistry, useAssets };
2681
- //# sourceMappingURL=chunk-HKL24TFC.js.map
2682
- //# sourceMappingURL=chunk-HKL24TFC.js.map
2913
+ //# sourceMappingURL=chunk-VP4LZEEZ.js.map
2914
+ //# sourceMappingURL=chunk-VP4LZEEZ.js.map