@ai-matrx/media 0.4.4 → 0.5.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.
package/dist/react.d.cts CHANGED
@@ -280,16 +280,6 @@ interface FileUploadDropzoneProps {
280
280
  }
281
281
  declare function FileUploadDropzone({ uploadOptions, accept, maxSize, enablePaste, mode, children, className, actions, onUploadStart, onUploaded, onError, onOpenFile, pickerEventName, }: FileUploadDropzoneProps): react.JSX.Element;
282
282
 
283
- /**
284
- * Human file size.
285
- *
286
- * `null` / `undefined` / NaN / negative collapse to an em-dash, NOT to
287
- * "0 B" (fixed at 0.4.0 while absorbing the viewer bodies — matrx-frontend's
288
- * `formatFileSize` already had this guard and the package's did not, so a
289
- * corrupt or mid-upload `size` row rendered as a confident, wrong "0 B"
290
- * instead of an honest "size unknown").
291
- */
292
- declare function formatFileSize(bytes: number | null | undefined): string;
293
283
  interface UploadProgressListProps {
294
284
  uploads: MediaUploadEntry[];
295
285
  /** Remove an entry from the tray (wire to `useMediaUpload().clearEntry`). */
@@ -350,4 +340,4 @@ interface MediaLightboxProps {
350
340
  }
351
341
  declare function MediaLightbox({ open, onClose, mediaRef, fileName, alt, as, autoPlay, playsInline, poster, playbackLabel, actions, hideUnavailableActions, headerExtra, className, }: MediaLightboxProps): react.JSX.Element | null;
352
342
 
353
- export { AlertCircleIcon, ArchiveIcon, BracesIcon, CheckCircle2Icon, CheckIcon, CodeIcon, CopyIcon, DatabaseIcon, DownloadIcon, ExternalLinkIcon, FileAudioIcon, FileCodeIcon, FileIcon, type FileIconProps, FileJsonIcon, FileSpreadsheetIcon, FileTextIcon, FileTypeIcon, FileUploadDropzone, type FileUploadDropzoneActions, type FileUploadDropzoneProps, FileVideoIcon, FolderIcon, FolderOpenIcon, GlobeIcon, ImageIcon, ImageOffIcon, InlineMediaRef, type InlineMediaRefFit, type InlineMediaRefProps, type InlineMediaRefSize, Link2Icon, MediaActionToolbar, type MediaActionToolbarProps, type MediaDropzoneOptions, type MediaDropzoneState, type MediaIconProps, MediaLightbox, type MediaLightboxProps, MediaThumbnail, type MediaThumbnailProps, MusicIcon, PackageBoxIcon, PenToolIcon, PlusIcon, SearchIcon, Settings2Icon, Share2Icon, SpinnerIcon, SubtitlesIcon, UploadIcon, UploadProgressList, type UploadProgressListProps, VideoIcon, VideoOffIcon, VolumeXIcon, XIcon, cn, formatFileSize, useMediaDropzone };
343
+ export { AlertCircleIcon, ArchiveIcon, BracesIcon, CheckCircle2Icon, CheckIcon, CodeIcon, CopyIcon, DatabaseIcon, DownloadIcon, ExternalLinkIcon, FileAudioIcon, FileCodeIcon, FileIcon, type FileIconProps, FileJsonIcon, FileSpreadsheetIcon, FileTextIcon, FileTypeIcon, FileUploadDropzone, type FileUploadDropzoneActions, type FileUploadDropzoneProps, FileVideoIcon, FolderIcon, FolderOpenIcon, GlobeIcon, ImageIcon, ImageOffIcon, InlineMediaRef, type InlineMediaRefFit, type InlineMediaRefProps, type InlineMediaRefSize, Link2Icon, MediaActionToolbar, type MediaActionToolbarProps, type MediaDropzoneOptions, type MediaDropzoneState, type MediaIconProps, MediaLightbox, type MediaLightboxProps, MediaThumbnail, type MediaThumbnailProps, MusicIcon, PackageBoxIcon, PenToolIcon, PlusIcon, SearchIcon, Settings2Icon, Share2Icon, SpinnerIcon, SubtitlesIcon, UploadIcon, UploadProgressList, type UploadProgressListProps, VideoIcon, VideoOffIcon, VolumeXIcon, XIcon, cn, useMediaDropzone };
package/dist/react.d.ts CHANGED
@@ -280,16 +280,6 @@ interface FileUploadDropzoneProps {
280
280
  }
281
281
  declare function FileUploadDropzone({ uploadOptions, accept, maxSize, enablePaste, mode, children, className, actions, onUploadStart, onUploaded, onError, onOpenFile, pickerEventName, }: FileUploadDropzoneProps): react.JSX.Element;
282
282
 
283
- /**
284
- * Human file size.
285
- *
286
- * `null` / `undefined` / NaN / negative collapse to an em-dash, NOT to
287
- * "0 B" (fixed at 0.4.0 while absorbing the viewer bodies — matrx-frontend's
288
- * `formatFileSize` already had this guard and the package's did not, so a
289
- * corrupt or mid-upload `size` row rendered as a confident, wrong "0 B"
290
- * instead of an honest "size unknown").
291
- */
292
- declare function formatFileSize(bytes: number | null | undefined): string;
293
283
  interface UploadProgressListProps {
294
284
  uploads: MediaUploadEntry[];
295
285
  /** Remove an entry from the tray (wire to `useMediaUpload().clearEntry`). */
@@ -350,4 +340,4 @@ interface MediaLightboxProps {
350
340
  }
351
341
  declare function MediaLightbox({ open, onClose, mediaRef, fileName, alt, as, autoPlay, playsInline, poster, playbackLabel, actions, hideUnavailableActions, headerExtra, className, }: MediaLightboxProps): react.JSX.Element | null;
352
342
 
353
- export { AlertCircleIcon, ArchiveIcon, BracesIcon, CheckCircle2Icon, CheckIcon, CodeIcon, CopyIcon, DatabaseIcon, DownloadIcon, ExternalLinkIcon, FileAudioIcon, FileCodeIcon, FileIcon, type FileIconProps, FileJsonIcon, FileSpreadsheetIcon, FileTextIcon, FileTypeIcon, FileUploadDropzone, type FileUploadDropzoneActions, type FileUploadDropzoneProps, FileVideoIcon, FolderIcon, FolderOpenIcon, GlobeIcon, ImageIcon, ImageOffIcon, InlineMediaRef, type InlineMediaRefFit, type InlineMediaRefProps, type InlineMediaRefSize, Link2Icon, MediaActionToolbar, type MediaActionToolbarProps, type MediaDropzoneOptions, type MediaDropzoneState, type MediaIconProps, MediaLightbox, type MediaLightboxProps, MediaThumbnail, type MediaThumbnailProps, MusicIcon, PackageBoxIcon, PenToolIcon, PlusIcon, SearchIcon, Settings2Icon, Share2Icon, SpinnerIcon, SubtitlesIcon, UploadIcon, UploadProgressList, type UploadProgressListProps, VideoIcon, VideoOffIcon, VolumeXIcon, XIcon, cn, formatFileSize, useMediaDropzone };
343
+ export { AlertCircleIcon, ArchiveIcon, BracesIcon, CheckCircle2Icon, CheckIcon, CodeIcon, CopyIcon, DatabaseIcon, DownloadIcon, ExternalLinkIcon, FileAudioIcon, FileCodeIcon, FileIcon, type FileIconProps, FileJsonIcon, FileSpreadsheetIcon, FileTextIcon, FileTypeIcon, FileUploadDropzone, type FileUploadDropzoneActions, type FileUploadDropzoneProps, FileVideoIcon, FolderIcon, FolderOpenIcon, GlobeIcon, ImageIcon, ImageOffIcon, InlineMediaRef, type InlineMediaRefFit, type InlineMediaRefProps, type InlineMediaRefSize, Link2Icon, MediaActionToolbar, type MediaActionToolbarProps, type MediaDropzoneOptions, type MediaDropzoneState, type MediaIconProps, MediaLightbox, type MediaLightboxProps, MediaThumbnail, type MediaThumbnailProps, MusicIcon, PackageBoxIcon, PenToolIcon, PlusIcon, SearchIcon, Settings2Icon, Share2Icon, SpinnerIcon, SubtitlesIcon, UploadIcon, UploadProgressList, type UploadProgressListProps, VideoIcon, VideoOffIcon, VolumeXIcon, XIcon, cn, useMediaDropzone };
package/dist/react.js CHANGED
@@ -465,11 +465,11 @@ function FileIcon2({
465
465
 
466
466
  // src/react/inline-media-ref.tsx
467
467
  import {
468
- useCallback as useCallback2,
469
- useEffect as useEffect4,
468
+ useCallback as useCallback3,
469
+ useEffect as useEffect5,
470
470
  useMemo as useMemo2,
471
- useRef as useRef3,
472
- useState as useState3
471
+ useRef as useRef4,
472
+ useState as useState4
473
473
  } from "react";
474
474
 
475
475
  // src/core/context.ts
@@ -620,8 +620,83 @@ function useMediaBlob(ref) {
620
620
  };
621
621
  }
622
622
 
623
+ // src/core/use-media-blob-load-recovery.ts
624
+ import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
625
+ function useMediaBlobLoadRecovery(ref, source, options) {
626
+ const client = useMediaClient();
627
+ const capture = useMediaFailureCapture();
628
+ const refKey = stableRefKey(ref);
629
+ const [failed, setFailed] = useState2(false);
630
+ const attemptRef = useRef2(0);
631
+ const recoveryInFlightRef = useRef2(false);
632
+ const onRetry = options.onRetry;
633
+ const onTerminal = options.onTerminal;
634
+ useEffect2(() => {
635
+ attemptRef.current = 0;
636
+ recoveryInFlightRef.current = false;
637
+ setFailed(false);
638
+ }, [refKey]);
639
+ useEffect2(() => {
640
+ recoveryInFlightRef.current = false;
641
+ }, [source]);
642
+ const finishTerminal = useCallback2(
643
+ (event, attempt, message) => {
644
+ setFailed(true);
645
+ capture({
646
+ phase: "element-load",
647
+ mediaRef: failureMediaIdentity(ref ?? null, source),
648
+ terminal: true,
649
+ retryOutcome: attempt > 1 ? "retried" : "not-attempted",
650
+ message
651
+ });
652
+ onTerminal?.(event);
653
+ },
654
+ [capture, onTerminal, ref, source]
655
+ );
656
+ const onLoadError = useCallback2(
657
+ (event) => {
658
+ if (recoveryInFlightRef.current || !source) return;
659
+ recoveryInFlightRef.current = true;
660
+ attemptRef.current += 1;
661
+ const attempt = attemptRef.current;
662
+ const recover = client.recoverBlobLoadError;
663
+ if (!ref || !recover) {
664
+ finishTerminal(
665
+ event,
666
+ attempt,
667
+ "blob-backed media element failed to load and the client exposes no object-URL recovery policy"
668
+ );
669
+ return;
670
+ }
671
+ recover(ref, attempt).then(
672
+ (verdict) => {
673
+ if (verdict === "retry") {
674
+ setFailed(false);
675
+ onRetry();
676
+ return;
677
+ }
678
+ finishTerminal(
679
+ event,
680
+ attempt,
681
+ attempt > 1 ? "blob-backed media element failed after the client-approved re-read" : "blob-backed media recovery was declined on the first attempt"
682
+ );
683
+ },
684
+ (err) => {
685
+ finishTerminal(
686
+ event,
687
+ attempt,
688
+ `blob-backed media recovery failed: ${err instanceof Error ? err.message : String(err ?? "unknown error")}`
689
+ );
690
+ }
691
+ );
692
+ },
693
+ [client, finishTerminal, onRetry, ref, source]
694
+ );
695
+ return { onLoadError, failed };
696
+ }
697
+
623
698
  // src/core/use-media-load-recovery.ts
624
- import { useEffect as useEffect2, useLayoutEffect, useRef as useRef2, useState as useState2 } from "react";
699
+ import { useEffect as useEffect3, useLayoutEffect, useRef as useRef3, useState as useState3 } from "react";
625
700
 
626
701
  // src/types.ts
627
702
  function mintDurableSrc(url) {
@@ -639,10 +714,10 @@ function useMediaLoadRecovery(src, options) {
639
714
  const recoverable = options?.recoverable !== false;
640
715
  const onTerminal = options?.onTerminal;
641
716
  const failureRef = options?.failureRef;
642
- const [result, setResult] = useState2({ source: src, retryKey: 0, failed: false });
643
- const attemptRef = useRef2(0);
644
- const recoveryInFlightRef = useRef2(false);
645
- useEffect2(() => {
717
+ const [result, setResult] = useState3({ source: src, retryKey: 0, failed: false });
718
+ const attemptRef = useRef3(0);
719
+ const recoveryInFlightRef = useRef3(false);
720
+ useEffect3(() => {
646
721
  attemptRef.current = 0;
647
722
  recoveryInFlightRef.current = false;
648
723
  }, [src]);
@@ -720,7 +795,7 @@ function useMediaLoadRecovery(src, options) {
720
795
  }
721
796
 
722
797
  // src/core/use-media-resolution.ts
723
- import { useEffect as useEffect3, useMemo } from "react";
798
+ import { useEffect as useEffect4, useMemo } from "react";
724
799
  function useMediaResolution(ref) {
725
800
  const client = useMediaClient();
726
801
  const capture = useMediaFailureCapture();
@@ -755,7 +830,7 @@ function useMediaResolution(ref) {
755
830
  };
756
831
  }
757
832
  }, [client, refKey]);
758
- useEffect3(() => {
833
+ useEffect4(() => {
759
834
  if (state.status !== "unavailable") return;
760
835
  capture({
761
836
  phase: "resolve",
@@ -810,7 +885,7 @@ function prettifyUrl(url) {
810
885
  }
811
886
  var INERT_PLAYBACK = {
812
887
  useMediaElementSink(forward) {
813
- return useCallback2(
888
+ return useCallback3(
814
889
  (node) => {
815
890
  if (!forward) return;
816
891
  if (typeof forward === "function") forward(node);
@@ -861,7 +936,7 @@ function InformativeErrorFallback({
861
936
  }) {
862
937
  const capture = useMediaFailureCapture();
863
938
  const identity = failureMediaIdentity(mediaRef ?? null, url);
864
- useEffect4(() => {
939
+ useEffect5(() => {
865
940
  capture({
866
941
  phase: failurePhase,
867
942
  mediaRef: identity,
@@ -925,10 +1000,10 @@ function InformativeErrorPanel({
925
1000
  icon,
926
1001
  tooltip
927
1002
  }) {
928
- const [copied, setCopied] = useState3(false);
1003
+ const [copied, setCopied] = useState4(false);
929
1004
  const isFill = dimensions === "fill";
930
1005
  const { host, tail } = prettifyUrl(url);
931
- const handleCopy = useCallback2(
1006
+ const handleCopy = useCallback3(
932
1007
  (event) => {
933
1008
  event.stopPropagation();
934
1009
  event.preventDefault();
@@ -1045,16 +1120,25 @@ function InlineMediaRef({
1045
1120
  );
1046
1121
  const blob = useMediaBlob(needsBlob ? mediaRef : null);
1047
1122
  const url = needsBlob ? blob.url : resolution?.src ?? null;
1123
+ const terminalCallback = (event) => onError?.(
1124
+ event
1125
+ );
1048
1126
  const recovery = useMediaLoadRecovery(url, {
1049
- recoverable: !needsBlob && resolution?.recoverable !== false,
1127
+ recoverable: resolution?.recoverable !== false,
1050
1128
  failureRef: mediaRef,
1051
- onTerminal: (event) => onError?.(
1052
- event
1053
- )
1129
+ onTerminal: terminalCallback
1054
1130
  });
1131
+ const blobRecovery = useMediaBlobLoadRecovery(
1132
+ needsBlob ? mediaRef : null,
1133
+ needsBlob ? url : null,
1134
+ {
1135
+ onRetry: blob.retry,
1136
+ onTerminal: terminalCallback
1137
+ }
1138
+ );
1055
1139
  const playback = ports.playbackSession ?? INERT_PLAYBACK;
1056
- const sessionElRef = useRef3(null);
1057
- const [isMediaPlaying, setIsMediaPlaying] = useState3(false);
1140
+ const sessionElRef = useRef4(null);
1141
+ const [isMediaPlaying, setIsMediaPlaying] = useState4(false);
1058
1142
  const portSinkRef = playback.useMediaElementSink(
1059
1143
  mediaElementRef
1060
1144
  );
@@ -1085,8 +1169,10 @@ function InlineMediaRef({
1085
1169
  const mediaResolutionError = status === "unavailable" ? reason ?? "unknown" : blob.error ?? null;
1086
1170
  const dimensions = resolveDimensions(size);
1087
1171
  const isFill = dimensions === "fill";
1088
- const hasLoadError = recovery.failed;
1089
- const handleLoadError = recovery.onLoadError;
1172
+ const activeRecovery = needsBlob ? blobRecovery : recovery;
1173
+ const retryKey = needsBlob ? 0 : recovery.retryKey;
1174
+ const hasLoadError = activeRecovery.failed;
1175
+ const handleLoadError = activeRecovery.onLoadError;
1090
1176
  const defaultIcon = elementType === "video" ? /* @__PURE__ */ jsx3(FileVideoIcon, { className: "h-1/2 w-1/2" }) : elementType === "audio" ? /* @__PURE__ */ jsx3(FileAudioIcon, { className: "h-1/2 w-1/2" }) : mediaRef ? /* @__PURE__ */ jsx3(ImageIcon, { className: "h-1/2 w-1/2" }) : /* @__PURE__ */ jsx3(FileIcon, { className: "h-1/2 w-1/2" });
1091
1177
  const unresolvedDisplayUrl = mediaRef && typeof mediaRef === "object" && mediaRef.file_id ? `file:${mediaRef.file_id}` : "unresolved-media";
1092
1178
  if (!url) {
@@ -1222,7 +1308,7 @@ function InlineMediaRef({
1222
1308
  ...mediaSessionEvents,
1223
1309
  ...interactiveProps
1224
1310
  },
1225
- recovery.retryKey
1311
+ retryKey
1226
1312
  );
1227
1313
  }
1228
1314
  if (elementType === "audio") {
@@ -1243,7 +1329,7 @@ function InlineMediaRef({
1243
1329
  crossOrigin,
1244
1330
  ...interactiveProps
1245
1331
  },
1246
- recovery.retryKey
1332
+ retryKey
1247
1333
  );
1248
1334
  }
1249
1335
  const needsPlainImg = !!onLoad || !!mediaElementRef || !!crossOrigin;
@@ -1261,7 +1347,7 @@ function InlineMediaRef({
1261
1347
  onError: handleLoadError,
1262
1348
  ...interactiveProps
1263
1349
  },
1264
- recovery.retryKey
1350
+ retryKey
1265
1351
  );
1266
1352
  }
1267
1353
  return /* @__PURE__ */ jsx3(
@@ -1277,12 +1363,12 @@ function InlineMediaRef({
1277
1363
  crossOrigin,
1278
1364
  ...interactiveProps
1279
1365
  },
1280
- recovery.retryKey
1366
+ retryKey
1281
1367
  );
1282
1368
  }
1283
1369
 
1284
1370
  // src/react/media-thumbnail.tsx
1285
- import { useEffect as useEffect5, useRef as useRef4 } from "react";
1371
+ import { useEffect as useEffect6, useRef as useRef5 } from "react";
1286
1372
 
1287
1373
  // src/core/use-thumbnail-source.ts
1288
1374
  import { useMemo as useMemo3 } from "react";
@@ -1440,8 +1526,8 @@ function VideoPosterThumb({
1440
1526
  recoverable,
1441
1527
  failureRef: mediaRef
1442
1528
  });
1443
- const ref = useRef4(null);
1444
- useEffect5(() => {
1529
+ const ref = useRef5(null);
1530
+ useEffect6(() => {
1445
1531
  const v = ref.current;
1446
1532
  if (!v) return void 0;
1447
1533
  const onMeta = () => {
@@ -1471,11 +1557,11 @@ function VideoPosterThumb({
1471
1557
 
1472
1558
  // src/react/use-media-dropzone.ts
1473
1559
  import {
1474
- useCallback as useCallback3,
1475
- useEffect as useEffect6,
1560
+ useCallback as useCallback4,
1561
+ useEffect as useEffect7,
1476
1562
  useMemo as useMemo4,
1477
- useRef as useRef5,
1478
- useState as useState4
1563
+ useRef as useRef6,
1564
+ useState as useState5
1479
1565
  } from "react";
1480
1566
  function matchesAccept(file, accept) {
1481
1567
  if (!accept || accept.length === 0) return true;
@@ -1500,16 +1586,16 @@ function useMediaDropzone(options) {
1500
1586
  disabled = false,
1501
1587
  multiple = true
1502
1588
  } = options;
1503
- const [isDragActive, setIsDragActive] = useState4(false);
1504
- const [pasteHighlight, setPasteHighlight] = useState4(false);
1505
- const dragDepth = useRef5(0);
1506
- const inputRef = useRef5(null);
1507
- const onFilesRef = useRef5(onFiles);
1589
+ const [isDragActive, setIsDragActive] = useState5(false);
1590
+ const [pasteHighlight, setPasteHighlight] = useState5(false);
1591
+ const dragDepth = useRef6(0);
1592
+ const inputRef = useRef6(null);
1593
+ const onFilesRef = useRef6(onFiles);
1508
1594
  onFilesRef.current = onFiles;
1509
- const onRejectedRef = useRef5(onRejected);
1595
+ const onRejectedRef = useRef6(onRejected);
1510
1596
  onRejectedRef.current = onRejected;
1511
1597
  const acceptKey = accept?.join(",") ?? "";
1512
- const filterFiles = useCallback3(
1598
+ const filterFiles = useCallback4(
1513
1599
  (files) => {
1514
1600
  const accepted = [];
1515
1601
  const rejected = [];
@@ -1527,23 +1613,23 @@ function useMediaDropzone(options) {
1527
1613
  },
1528
1614
  [acceptKey, maxSize]
1529
1615
  );
1530
- const emit = useCallback3(
1616
+ const emit = useCallback4(
1531
1617
  (files) => {
1532
1618
  const accepted = filterFiles(files);
1533
1619
  if (accepted.length) onFilesRef.current(accepted);
1534
1620
  },
1535
1621
  [filterFiles]
1536
1622
  );
1537
- const openPicker = useCallback3(() => {
1623
+ const openPicker = useCallback4(() => {
1538
1624
  if (!disabled) inputRef.current?.click();
1539
1625
  }, [disabled]);
1540
- useEffect6(() => {
1626
+ useEffect7(() => {
1541
1627
  if (!pickerEventName || typeof window === "undefined") return void 0;
1542
1628
  const handler = () => openPicker();
1543
1629
  window.addEventListener(pickerEventName, handler);
1544
1630
  return () => window.removeEventListener(pickerEventName, handler);
1545
1631
  }, [openPicker, pickerEventName]);
1546
- useEffect6(() => {
1632
+ useEffect7(() => {
1547
1633
  if (!enablePaste || disabled || typeof window === "undefined") {
1548
1634
  return void 0;
1549
1635
  }
@@ -1569,7 +1655,7 @@ function useMediaDropzone(options) {
1569
1655
  window.addEventListener("paste", handler);
1570
1656
  return () => window.removeEventListener("paste", handler);
1571
1657
  }, [enablePaste, disabled, emit]);
1572
- const getRootProps = useCallback3(
1658
+ const getRootProps = useCallback4(
1573
1659
  () => ({
1574
1660
  onDragEnter: (event) => {
1575
1661
  if (disabled) return;
@@ -1602,7 +1688,7 @@ function useMediaDropzone(options) {
1602
1688
  () => acceptKey.length ? acceptKey : void 0,
1603
1689
  [acceptKey]
1604
1690
  );
1605
- const getInputProps = useCallback3(
1691
+ const getInputProps = useCallback4(
1606
1692
  () => ({
1607
1693
  ref: (node) => {
1608
1694
  inputRef.current = node;
@@ -1624,20 +1710,20 @@ function useMediaDropzone(options) {
1624
1710
  }
1625
1711
 
1626
1712
  // src/react/file-upload-dropzone.tsx
1627
- import { useCallback as useCallback5 } from "react";
1713
+ import { useCallback as useCallback6 } from "react";
1628
1714
 
1629
1715
  // src/core/use-media-upload.ts
1630
- import { useCallback as useCallback4, useRef as useRef6, useState as useState5 } from "react";
1716
+ import { useCallback as useCallback5, useRef as useRef7, useState as useState6 } from "react";
1631
1717
  var entrySeq = 0;
1632
1718
  function useMediaUpload() {
1633
1719
  const client = useMediaClient();
1634
- const [uploading, setUploading] = useState5(false);
1635
- const [progress, setProgress] = useState5(null);
1636
- const [result, setResult] = useState5(null);
1637
- const [error, setError] = useState5(null);
1638
- const [entries, setEntries] = useState5([]);
1639
- const activeCount = useRef6(0);
1640
- const patchEntry = useCallback4(
1720
+ const [uploading, setUploading] = useState6(false);
1721
+ const [progress, setProgress] = useState6(null);
1722
+ const [result, setResult] = useState6(null);
1723
+ const [error, setError] = useState6(null);
1724
+ const [entries, setEntries] = useState6([]);
1725
+ const activeCount = useRef7(0);
1726
+ const patchEntry = useCallback5(
1641
1727
  (id, patch) => {
1642
1728
  setEntries(
1643
1729
  (current) => current.map((e) => e.id === id ? { ...e, ...patch } : e)
@@ -1645,7 +1731,7 @@ function useMediaUpload() {
1645
1731
  },
1646
1732
  []
1647
1733
  );
1648
- const upload = useCallback4(
1734
+ const upload = useCallback5(
1649
1735
  async (file, opts = {}) => {
1650
1736
  const id = `up-${++entrySeq}`;
1651
1737
  const fileName = opts.fileName ?? (file instanceof File ? file.name : "file");
@@ -1699,7 +1785,7 @@ function useMediaUpload() {
1699
1785
  },
1700
1786
  [client, patchEntry]
1701
1787
  );
1702
- const uploadMany = useCallback4(
1788
+ const uploadMany = useCallback5(
1703
1789
  async (files, opts = {}) => {
1704
1790
  if (client.uploadMany) {
1705
1791
  activeCount.current += 1;
@@ -1728,10 +1814,10 @@ function useMediaUpload() {
1728
1814
  },
1729
1815
  [client, upload]
1730
1816
  );
1731
- const clearEntry = useCallback4((id) => {
1817
+ const clearEntry = useCallback5((id) => {
1732
1818
  setEntries((current) => current.filter((e) => e.id !== id));
1733
1819
  }, []);
1734
- const reset = useCallback4(() => {
1820
+ const reset = useCallback5(() => {
1735
1821
  setResult(null);
1736
1822
  setError(null);
1737
1823
  setProgress(null);
@@ -1751,37 +1837,26 @@ function useMediaUpload() {
1751
1837
  }
1752
1838
 
1753
1839
  // src/react/upload-progress-list.tsx
1754
- import { memo, useEffect as useEffect7, useRef as useRef7, useState as useState6 } from "react";
1840
+ import { memo, useEffect as useEffect8, useRef as useRef8, useState as useState7 } from "react";
1841
+ import { formatFileSize } from "@ai-matrx/kit/format";
1755
1842
  import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1756
1843
  var AUTO_DISMISS_MS = 1e4;
1757
- function formatFileSize(bytes) {
1758
- if (bytes == null || !Number.isFinite(bytes) || bytes < 0) return "\u2014";
1759
- if (bytes < 1024) return `${bytes} B`;
1760
- const units = ["KB", "MB", "GB", "TB"];
1761
- let value = bytes;
1762
- let unit = -1;
1763
- do {
1764
- value /= 1024;
1765
- unit += 1;
1766
- } while (value >= 1024 && unit < units.length - 1);
1767
- return `${value >= 10 ? Math.round(value) : value.toFixed(1)} ${units[unit] ?? "TB"}`;
1768
- }
1769
1844
  function UploadProgressListImpl({
1770
1845
  uploads,
1771
1846
  onDismiss,
1772
1847
  onOpenFile,
1773
1848
  className
1774
1849
  }) {
1775
- const [hovered, setHovered] = useState6(false);
1850
+ const [hovered, setHovered] = useState7(false);
1776
1851
  const activeCount = uploads.filter((u) => u.status === "uploading").length;
1777
1852
  const successCount = uploads.filter((u) => u.status === "success").length;
1778
1853
  const errorCount = uploads.filter((u) => u.status === "error").length;
1779
- const timersRef = useRef7(
1854
+ const timersRef = useRef8(
1780
1855
  /* @__PURE__ */ new Map()
1781
1856
  );
1782
- const onDismissRef = useRef7(onDismiss);
1857
+ const onDismissRef = useRef8(onDismiss);
1783
1858
  onDismissRef.current = onDismiss;
1784
- useEffect7(() => {
1859
+ useEffect8(() => {
1785
1860
  const timers = timersRef.current;
1786
1861
  if (hovered) {
1787
1862
  timers.forEach((t) => clearTimeout(t));
@@ -1809,7 +1884,7 @@ function UploadProgressListImpl({
1809
1884
  }
1810
1885
  }
1811
1886
  }, [uploads, hovered]);
1812
- useEffect7(() => {
1887
+ useEffect8(() => {
1813
1888
  const timers = timersRef.current;
1814
1889
  return () => {
1815
1890
  timers.forEach((t) => clearTimeout(t));
@@ -1941,7 +2016,7 @@ function FileUploadDropzone({
1941
2016
  pickerEventName
1942
2017
  }) {
1943
2018
  const { uploadMany, entries, clearEntry } = useMediaUpload();
1944
- const handleUpload = useCallback5(
2019
+ const handleUpload = useCallback6(
1945
2020
  async (files) => {
1946
2021
  if (!files.length) return;
1947
2022
  try {
@@ -2048,22 +2123,22 @@ function FileUploadDropzone({
2048
2123
  }
2049
2124
 
2050
2125
  // src/react/media-action-toolbar.tsx
2051
- import { useState as useState8 } from "react";
2126
+ import { useState as useState9 } from "react";
2052
2127
 
2053
2128
  // src/core/use-media-actions.ts
2054
- import { useCallback as useCallback6, useMemo as useMemo5, useState as useState7 } from "react";
2129
+ import { useCallback as useCallback7, useMemo as useMemo5, useState as useState8 } from "react";
2055
2130
  function useMediaActions(ref, options = {}) {
2056
2131
  const ports = useMediaHostPorts();
2057
2132
  const { resolution } = useMediaResolution(ref);
2058
- const [busy, setBusy] = useState7(null);
2059
- const [lastError, setLastError] = useState7(null);
2133
+ const [busy, setBusy] = useState8(null);
2134
+ const [lastError, setLastError] = useState8(null);
2060
2135
  const actions = ports.actions;
2061
2136
  const { fileName, alt } = options;
2062
2137
  const context = useMemo5(() => {
2063
2138
  if (!ref) return null;
2064
2139
  return { ref, resolution, fileName, alt };
2065
2140
  }, [ref, resolution, fileName, alt]);
2066
- const isAvailable = useCallback6(
2141
+ const isAvailable = useCallback7(
2067
2142
  (kind) => {
2068
2143
  if (!context || !actions) return false;
2069
2144
  if (kind === "share") return Boolean(actions.share || actions.SharePopover);
@@ -2071,7 +2146,7 @@ function useMediaActions(ref, options = {}) {
2071
2146
  },
2072
2147
  [context, actions]
2073
2148
  );
2074
- const run = useCallback6(
2149
+ const run = useCallback7(
2075
2150
  async (kind) => {
2076
2151
  if (!context || !actions) return;
2077
2152
  const handler = actions[kind];
@@ -2120,7 +2195,7 @@ function MediaActionToolbar({
2120
2195
  buttonClassName
2121
2196
  }) {
2122
2197
  const state = useMediaActions(mediaRef, { fileName, alt });
2123
- const [shareOpen, setShareOpen] = useState8(false);
2198
+ const [shareOpen, setShareOpen] = useState9(false);
2124
2199
  const SharePopover = state.SharePopover;
2125
2200
  const visible = hideUnavailable ? actions.filter((kind) => state.isAvailable(kind)) : actions;
2126
2201
  if (visible.length === 0) return null;
@@ -2177,7 +2252,7 @@ function MediaActionToolbar({
2177
2252
  }
2178
2253
 
2179
2254
  // src/react/media-lightbox.tsx
2180
- import { useEffect as useEffect8 } from "react";
2255
+ import { useEffect as useEffect9 } from "react";
2181
2256
  import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
2182
2257
  function MediaLightbox({
2183
2258
  open,
@@ -2195,7 +2270,7 @@ function MediaLightbox({
2195
2270
  headerExtra,
2196
2271
  className
2197
2272
  }) {
2198
- useEffect8(() => {
2273
+ useEffect9(() => {
2199
2274
  if (!open || typeof window === "undefined") return void 0;
2200
2275
  const handler = (event) => {
2201
2276
  if (event.key === "Escape") {
@@ -2327,7 +2402,6 @@ export {
2327
2402
  VolumeXIcon,
2328
2403
  XIcon,
2329
2404
  cn,
2330
- formatFileSize,
2331
2405
  useMediaDropzone
2332
2406
  };
2333
2407
  //# sourceMappingURL=react.js.map