@ai-matrx/media 0.2.3 → 0.3.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/CHANGELOG.md +55 -0
- package/README.md +22 -0
- package/dist/core.cjs +177 -39
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +84 -4
- package/dist/core.d.ts +84 -4
- package/dist/core.js +164 -26
- package/dist/core.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +77 -2
- package/dist/index.d.ts +77 -2
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +230 -79
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +208 -56
- package/dist/react.js.map +1 -1
- package/package.json +2 -2
package/dist/react.js
CHANGED
|
@@ -465,9 +465,10 @@ function FileIcon2({
|
|
|
465
465
|
|
|
466
466
|
// src/react/inline-media-ref.tsx
|
|
467
467
|
import {
|
|
468
|
-
useCallback,
|
|
468
|
+
useCallback as useCallback2,
|
|
469
|
+
useEffect as useEffect4,
|
|
469
470
|
useMemo as useMemo2,
|
|
470
|
-
useRef as
|
|
471
|
+
useRef as useRef3,
|
|
471
472
|
useState as useState3
|
|
472
473
|
} from "react";
|
|
473
474
|
|
|
@@ -503,6 +504,54 @@ function useMediaHostPorts() {
|
|
|
503
504
|
return useContext(portsContext());
|
|
504
505
|
}
|
|
505
506
|
|
|
507
|
+
// src/core/diagnostics.ts
|
|
508
|
+
import { useCallback, useRef } from "react";
|
|
509
|
+
var LATCH_SLOT = /* @__PURE__ */ Symbol.for("ai-matrx.media.failure-latch");
|
|
510
|
+
function latch() {
|
|
511
|
+
const host = globalThis;
|
|
512
|
+
host[LATCH_SLOT] ??= /* @__PURE__ */ new Set();
|
|
513
|
+
return host[LATCH_SLOT];
|
|
514
|
+
}
|
|
515
|
+
function failureMediaIdentity(ref, src) {
|
|
516
|
+
if (ref && typeof ref === "object" && ref.file_id) return `file:${ref.file_id}`;
|
|
517
|
+
const raw = typeof ref === "string" ? ref : (ref && typeof ref === "object" ? ref.url ?? null : null) ?? src ?? null;
|
|
518
|
+
if (!raw) return "<unknown-media>";
|
|
519
|
+
if (raw.startsWith("blob:")) return "blob:object-url";
|
|
520
|
+
try {
|
|
521
|
+
const u = new URL(raw);
|
|
522
|
+
return `url:${u.origin}${u.pathname}`;
|
|
523
|
+
} catch {
|
|
524
|
+
const clean = raw.split(/[?#]/, 1)[0] ?? raw;
|
|
525
|
+
return `ref:${clean}`;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
function emitMediaFailure(capture, info) {
|
|
529
|
+
const key = `${info.terminal ? "terminal" : "warning"}|${info.mediaRef}`;
|
|
530
|
+
const seen = latch();
|
|
531
|
+
if (seen.has(key)) return;
|
|
532
|
+
seen.add(key);
|
|
533
|
+
if (capture) {
|
|
534
|
+
try {
|
|
535
|
+
capture(info);
|
|
536
|
+
return;
|
|
537
|
+
} catch {
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
console.error(
|
|
541
|
+
"[@ai-matrx/media] media failure (no diagnostics sink captured it \u2014 bind MediaHostPorts.diagnostics.capture to your error system)",
|
|
542
|
+
info
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
function useMediaFailureCapture() {
|
|
546
|
+
const ports = useMediaHostPorts();
|
|
547
|
+
const capture = ports.diagnostics?.capture;
|
|
548
|
+
const captureRef = useRef(capture);
|
|
549
|
+
captureRef.current = capture;
|
|
550
|
+
return useCallback((info) => {
|
|
551
|
+
emitMediaFailure(captureRef.current, { source: "media", ...info });
|
|
552
|
+
}, []);
|
|
553
|
+
}
|
|
554
|
+
|
|
506
555
|
// src/core/use-media-blob.ts
|
|
507
556
|
import { useEffect, useState } from "react";
|
|
508
557
|
|
|
@@ -516,8 +565,16 @@ function stableRefKey(ref) {
|
|
|
516
565
|
}
|
|
517
566
|
|
|
518
567
|
// src/core/use-media-blob.ts
|
|
568
|
+
function statusOf(err) {
|
|
569
|
+
if (err && typeof err === "object") {
|
|
570
|
+
const candidate = err.status ?? err.statusCode;
|
|
571
|
+
if (typeof candidate === "number") return candidate;
|
|
572
|
+
}
|
|
573
|
+
return void 0;
|
|
574
|
+
}
|
|
519
575
|
function useMediaBlob(ref) {
|
|
520
576
|
const client = useMediaClient();
|
|
577
|
+
const capture = useMediaFailureCapture();
|
|
521
578
|
const refKey = stableRefKey(ref);
|
|
522
579
|
const [state, setState] = useState({ url: null, blob: null, loading: !!ref, error: null });
|
|
523
580
|
const [retryToken, setRetryToken] = useState(0);
|
|
@@ -542,6 +599,14 @@ function useMediaBlob(ref) {
|
|
|
542
599
|
if (cancelled) return;
|
|
543
600
|
const message = err instanceof Error ? err.message : String(err ?? "Failed to load file");
|
|
544
601
|
setState({ url: null, blob: null, loading: false, error: message });
|
|
602
|
+
const status = statusOf(err);
|
|
603
|
+
capture({
|
|
604
|
+
phase: "blob",
|
|
605
|
+
mediaRef: failureMediaIdentity(ref),
|
|
606
|
+
terminal: true,
|
|
607
|
+
message: `getBlob() failed: ${message}`,
|
|
608
|
+
...status !== void 0 ? { status } : {}
|
|
609
|
+
});
|
|
545
610
|
}
|
|
546
611
|
);
|
|
547
612
|
return () => {
|
|
@@ -556,7 +621,7 @@ function useMediaBlob(ref) {
|
|
|
556
621
|
}
|
|
557
622
|
|
|
558
623
|
// src/core/use-media-load-recovery.ts
|
|
559
|
-
import { useEffect as useEffect2, useRef, useState as useState2 } from "react";
|
|
624
|
+
import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
560
625
|
|
|
561
626
|
// src/types.ts
|
|
562
627
|
function mintDurableSrc(url) {
|
|
@@ -564,26 +629,51 @@ function mintDurableSrc(url) {
|
|
|
564
629
|
}
|
|
565
630
|
|
|
566
631
|
// src/core/use-media-load-recovery.ts
|
|
632
|
+
function normalizeOutcome(outcome) {
|
|
633
|
+
return typeof outcome === "string" ? { verdict: outcome } : outcome;
|
|
634
|
+
}
|
|
635
|
+
var EPHEMERAL_SECRET_IDENTITY = "server:files-session-secret";
|
|
567
636
|
function useMediaLoadRecovery(src, options) {
|
|
568
637
|
const client = useMediaClient();
|
|
638
|
+
const capture = useMediaFailureCapture();
|
|
569
639
|
const recoverable = options?.recoverable !== false;
|
|
570
640
|
const onTerminal = options?.onTerminal;
|
|
641
|
+
const failureRef = options?.failureRef;
|
|
571
642
|
const [result, setResult] = useState2({ source: src, retryKey: 0, failed: false });
|
|
572
|
-
const attemptRef =
|
|
643
|
+
const attemptRef = useRef2(0);
|
|
573
644
|
useEffect2(() => {
|
|
574
645
|
attemptRef.current = 0;
|
|
575
646
|
}, [src]);
|
|
647
|
+
const identity = () => failureMediaIdentity(failureRef ?? null, src);
|
|
576
648
|
const onLoadError = (event) => {
|
|
577
649
|
if (!src || !recoverable) {
|
|
578
650
|
setResult({ source: src, retryKey: 0, failed: true });
|
|
651
|
+
if (src) {
|
|
652
|
+
capture({
|
|
653
|
+
phase: "element-load",
|
|
654
|
+
mediaRef: identity(),
|
|
655
|
+
terminal: true,
|
|
656
|
+
retryOutcome: "not-attempted",
|
|
657
|
+
message: "media element failed to load and the source is unrecoverable (foreign/object URL \u2014 a session refresh cannot fix it)"
|
|
658
|
+
});
|
|
659
|
+
}
|
|
579
660
|
onTerminal?.(event);
|
|
580
661
|
return;
|
|
581
662
|
}
|
|
582
663
|
attemptRef.current += 1;
|
|
583
664
|
const attempt = attemptRef.current;
|
|
584
665
|
client.recoverLoadError(mintDurableSrc(src), attempt).then(
|
|
585
|
-
(
|
|
586
|
-
|
|
666
|
+
(outcome) => {
|
|
667
|
+
const report = normalizeOutcome(outcome);
|
|
668
|
+
if (report.secretSource === "ephemeral") {
|
|
669
|
+
capture({
|
|
670
|
+
phase: "session",
|
|
671
|
+
mediaRef: EPHEMERAL_SECRET_IDENTITY,
|
|
672
|
+
terminal: false,
|
|
673
|
+
message: "server cookie secret is ephemeral \u2014 private renders will break across restarts/instances (set a persistent files-session secret on the server)"
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
if (report.verdict === "retry") {
|
|
587
677
|
setResult((current) => ({
|
|
588
678
|
source: src,
|
|
589
679
|
retryKey: current.source === src ? current.retryKey + 1 : 1,
|
|
@@ -591,11 +681,26 @@ function useMediaLoadRecovery(src, options) {
|
|
|
591
681
|
}));
|
|
592
682
|
} else {
|
|
593
683
|
setResult({ source: src, retryKey: 0, failed: true });
|
|
684
|
+
capture({
|
|
685
|
+
phase: attempt > 1 ? "element-load" : "recovery",
|
|
686
|
+
mediaRef: identity(),
|
|
687
|
+
terminal: true,
|
|
688
|
+
retryOutcome: attempt > 1 ? "retried" : "not-attempted",
|
|
689
|
+
message: attempt > 1 ? "media element failed to load after the session-refresh retry" : "media load recovery declined on the first attempt (session refresh did not produce a usable session)",
|
|
690
|
+
...report.status !== void 0 ? { status: report.status } : {}
|
|
691
|
+
});
|
|
594
692
|
onTerminal?.(event);
|
|
595
693
|
}
|
|
596
694
|
},
|
|
597
|
-
() => {
|
|
695
|
+
(err) => {
|
|
598
696
|
setResult({ source: src, retryKey: 0, failed: true });
|
|
697
|
+
capture({
|
|
698
|
+
phase: "session",
|
|
699
|
+
mediaRef: identity(),
|
|
700
|
+
terminal: true,
|
|
701
|
+
retryOutcome: "recovery-failed",
|
|
702
|
+
message: `session recovery failed: ${err instanceof Error ? err.message : String(err ?? "unknown error")}`
|
|
703
|
+
});
|
|
599
704
|
onTerminal?.(event);
|
|
600
705
|
}
|
|
601
706
|
);
|
|
@@ -608,24 +713,51 @@ function useMediaLoadRecovery(src, options) {
|
|
|
608
713
|
}
|
|
609
714
|
|
|
610
715
|
// src/core/use-media-resolution.ts
|
|
611
|
-
import { useMemo } from "react";
|
|
716
|
+
import { useEffect as useEffect3, useMemo } from "react";
|
|
612
717
|
function useMediaResolution(ref) {
|
|
613
718
|
const client = useMediaClient();
|
|
719
|
+
const capture = useMediaFailureCapture();
|
|
614
720
|
const refKey = stableRefKey(ref);
|
|
615
|
-
|
|
616
|
-
if (!ref)
|
|
721
|
+
const { state, failureMessage } = useMemo(() => {
|
|
722
|
+
if (!ref) {
|
|
723
|
+
return {
|
|
724
|
+
state: { resolution: null, status: "empty", reason: null },
|
|
725
|
+
failureMessage: null
|
|
726
|
+
};
|
|
727
|
+
}
|
|
617
728
|
try {
|
|
618
729
|
const resolution = client.resolve(ref);
|
|
619
|
-
if (!resolution)
|
|
620
|
-
|
|
730
|
+
if (!resolution) {
|
|
731
|
+
return {
|
|
732
|
+
state: { resolution: null, status: "empty", reason: null },
|
|
733
|
+
failureMessage: null
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
return {
|
|
737
|
+
state: { resolution, status: "ready", reason: null },
|
|
738
|
+
failureMessage: null
|
|
739
|
+
};
|
|
621
740
|
} catch (err) {
|
|
622
741
|
return {
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
742
|
+
state: {
|
|
743
|
+
resolution: null,
|
|
744
|
+
status: "unavailable",
|
|
745
|
+
reason: client.classifyError(err)
|
|
746
|
+
},
|
|
747
|
+
failureMessage: err instanceof Error ? err.message : String(err ?? "resolve() failed")
|
|
626
748
|
};
|
|
627
749
|
}
|
|
628
750
|
}, [client, refKey]);
|
|
751
|
+
useEffect3(() => {
|
|
752
|
+
if (state.status !== "unavailable") return;
|
|
753
|
+
capture({
|
|
754
|
+
phase: "resolve",
|
|
755
|
+
mediaRef: failureMediaIdentity(ref ?? null),
|
|
756
|
+
terminal: true,
|
|
757
|
+
message: `resolve() refused (${state.reason ?? "unknown"}): ${failureMessage ?? "no error message"}`
|
|
758
|
+
});
|
|
759
|
+
}, [refKey, state.status]);
|
|
760
|
+
return state;
|
|
629
761
|
}
|
|
630
762
|
|
|
631
763
|
// src/react/inline-media-ref.tsx
|
|
@@ -671,7 +803,7 @@ function prettifyUrl(url) {
|
|
|
671
803
|
}
|
|
672
804
|
var INERT_PLAYBACK = {
|
|
673
805
|
useMediaElementSink(forward) {
|
|
674
|
-
return
|
|
806
|
+
return useCallback2(
|
|
675
807
|
(node) => {
|
|
676
808
|
if (!forward) return;
|
|
677
809
|
if (typeof forward === "function") forward(node);
|
|
@@ -715,10 +847,21 @@ function InformativeErrorFallback({
|
|
|
715
847
|
alt,
|
|
716
848
|
elementType,
|
|
717
849
|
mediaRef,
|
|
850
|
+
failurePhase,
|
|
718
851
|
dimensions,
|
|
719
852
|
rounded,
|
|
720
853
|
className
|
|
721
854
|
}) {
|
|
855
|
+
const capture = useMediaFailureCapture();
|
|
856
|
+
const identity = failureMediaIdentity(mediaRef ?? null, url);
|
|
857
|
+
useEffect4(() => {
|
|
858
|
+
capture({
|
|
859
|
+
phase: failurePhase,
|
|
860
|
+
mediaRef: identity,
|
|
861
|
+
terminal: true,
|
|
862
|
+
message: `media unavailable panel rendered for ${elementType} (${failurePhase} failure)`
|
|
863
|
+
});
|
|
864
|
+
}, [identity, failurePhase]);
|
|
722
865
|
const shortAxis = dimensions === "fill" ? Number.POSITIVE_INFINITY : Math.min(dimensions.width, dimensions.height);
|
|
723
866
|
const isCompact = dimensions !== "fill" && shortAxis < 64;
|
|
724
867
|
const isMidsize = shortAxis < 140;
|
|
@@ -778,7 +921,7 @@ function InformativeErrorPanel({
|
|
|
778
921
|
const [copied, setCopied] = useState3(false);
|
|
779
922
|
const isFill = dimensions === "fill";
|
|
780
923
|
const { host, tail } = prettifyUrl(url);
|
|
781
|
-
const handleCopy =
|
|
924
|
+
const handleCopy = useCallback2(
|
|
782
925
|
(event) => {
|
|
783
926
|
event.stopPropagation();
|
|
784
927
|
event.preventDefault();
|
|
@@ -897,12 +1040,13 @@ function InlineMediaRef({
|
|
|
897
1040
|
const url = needsBlob ? blob.url : resolution?.src ?? null;
|
|
898
1041
|
const recovery = useMediaLoadRecovery(url, {
|
|
899
1042
|
recoverable: !needsBlob && resolution?.recoverable !== false,
|
|
1043
|
+
failureRef: mediaRef,
|
|
900
1044
|
onTerminal: (event) => onError?.(
|
|
901
1045
|
event
|
|
902
1046
|
)
|
|
903
1047
|
});
|
|
904
1048
|
const playback = ports.playbackSession ?? INERT_PLAYBACK;
|
|
905
|
-
const sessionElRef =
|
|
1049
|
+
const sessionElRef = useRef3(null);
|
|
906
1050
|
const [isMediaPlaying, setIsMediaPlaying] = useState3(false);
|
|
907
1051
|
const portSinkRef = playback.useMediaElementSink(
|
|
908
1052
|
mediaElementRef
|
|
@@ -949,6 +1093,7 @@ function InlineMediaRef({
|
|
|
949
1093
|
alt,
|
|
950
1094
|
elementType,
|
|
951
1095
|
mediaRef,
|
|
1096
|
+
failurePhase: status === "unavailable" ? "resolve" : "blob",
|
|
952
1097
|
dimensions,
|
|
953
1098
|
rounded,
|
|
954
1099
|
className
|
|
@@ -1003,6 +1148,7 @@ function InlineMediaRef({
|
|
|
1003
1148
|
alt,
|
|
1004
1149
|
elementType,
|
|
1005
1150
|
mediaRef,
|
|
1151
|
+
failurePhase: "element-load",
|
|
1006
1152
|
dimensions,
|
|
1007
1153
|
rounded,
|
|
1008
1154
|
className
|
|
@@ -1129,7 +1275,7 @@ function InlineMediaRef({
|
|
|
1129
1275
|
}
|
|
1130
1276
|
|
|
1131
1277
|
// src/react/media-thumbnail.tsx
|
|
1132
|
-
import { useEffect as
|
|
1278
|
+
import { useEffect as useEffect5, useRef as useRef4 } from "react";
|
|
1133
1279
|
|
|
1134
1280
|
// src/core/use-thumbnail-source.ts
|
|
1135
1281
|
import { useMemo as useMemo3 } from "react";
|
|
@@ -1227,6 +1373,7 @@ function MediaThumbnail({
|
|
|
1227
1373
|
VideoPosterThumb,
|
|
1228
1374
|
{
|
|
1229
1375
|
url: source.src,
|
|
1376
|
+
mediaRef,
|
|
1230
1377
|
recoverable: source.recoverable,
|
|
1231
1378
|
fallback
|
|
1232
1379
|
}
|
|
@@ -1237,6 +1384,7 @@ function MediaThumbnail({
|
|
|
1237
1384
|
{
|
|
1238
1385
|
url: source.src,
|
|
1239
1386
|
alt: fileName,
|
|
1387
|
+
mediaRef,
|
|
1240
1388
|
recoverable: source.recoverable,
|
|
1241
1389
|
fallback
|
|
1242
1390
|
}
|
|
@@ -1254,11 +1402,13 @@ function MediaThumbnail({
|
|
|
1254
1402
|
function ImageThumb({
|
|
1255
1403
|
url,
|
|
1256
1404
|
alt,
|
|
1405
|
+
mediaRef,
|
|
1257
1406
|
recoverable,
|
|
1258
1407
|
fallback
|
|
1259
1408
|
}) {
|
|
1260
1409
|
const { retryKey, onLoadError, failed } = useMediaLoadRecovery(url, {
|
|
1261
|
-
recoverable
|
|
1410
|
+
recoverable,
|
|
1411
|
+
failureRef: mediaRef
|
|
1262
1412
|
});
|
|
1263
1413
|
if (failed) return /* @__PURE__ */ jsx4(Fragment2, { children: fallback });
|
|
1264
1414
|
return /* @__PURE__ */ jsx4(
|
|
@@ -1275,14 +1425,16 @@ function ImageThumb({
|
|
|
1275
1425
|
}
|
|
1276
1426
|
function VideoPosterThumb({
|
|
1277
1427
|
url,
|
|
1428
|
+
mediaRef,
|
|
1278
1429
|
recoverable,
|
|
1279
1430
|
fallback
|
|
1280
1431
|
}) {
|
|
1281
1432
|
const { retryKey, onLoadError, failed } = useMediaLoadRecovery(url, {
|
|
1282
|
-
recoverable
|
|
1433
|
+
recoverable,
|
|
1434
|
+
failureRef: mediaRef
|
|
1283
1435
|
});
|
|
1284
|
-
const ref =
|
|
1285
|
-
|
|
1436
|
+
const ref = useRef4(null);
|
|
1437
|
+
useEffect5(() => {
|
|
1286
1438
|
const v = ref.current;
|
|
1287
1439
|
if (!v) return void 0;
|
|
1288
1440
|
const onMeta = () => {
|
|
@@ -1312,10 +1464,10 @@ function VideoPosterThumb({
|
|
|
1312
1464
|
|
|
1313
1465
|
// src/react/use-media-dropzone.ts
|
|
1314
1466
|
import {
|
|
1315
|
-
useCallback as
|
|
1316
|
-
useEffect as
|
|
1467
|
+
useCallback as useCallback3,
|
|
1468
|
+
useEffect as useEffect6,
|
|
1317
1469
|
useMemo as useMemo4,
|
|
1318
|
-
useRef as
|
|
1470
|
+
useRef as useRef5,
|
|
1319
1471
|
useState as useState4
|
|
1320
1472
|
} from "react";
|
|
1321
1473
|
function matchesAccept(file, accept) {
|
|
@@ -1343,14 +1495,14 @@ function useMediaDropzone(options) {
|
|
|
1343
1495
|
} = options;
|
|
1344
1496
|
const [isDragActive, setIsDragActive] = useState4(false);
|
|
1345
1497
|
const [pasteHighlight, setPasteHighlight] = useState4(false);
|
|
1346
|
-
const dragDepth =
|
|
1347
|
-
const inputRef =
|
|
1348
|
-
const onFilesRef =
|
|
1498
|
+
const dragDepth = useRef5(0);
|
|
1499
|
+
const inputRef = useRef5(null);
|
|
1500
|
+
const onFilesRef = useRef5(onFiles);
|
|
1349
1501
|
onFilesRef.current = onFiles;
|
|
1350
|
-
const onRejectedRef =
|
|
1502
|
+
const onRejectedRef = useRef5(onRejected);
|
|
1351
1503
|
onRejectedRef.current = onRejected;
|
|
1352
1504
|
const acceptKey = accept?.join(",") ?? "";
|
|
1353
|
-
const filterFiles =
|
|
1505
|
+
const filterFiles = useCallback3(
|
|
1354
1506
|
(files) => {
|
|
1355
1507
|
const accepted = [];
|
|
1356
1508
|
const rejected = [];
|
|
@@ -1368,23 +1520,23 @@ function useMediaDropzone(options) {
|
|
|
1368
1520
|
},
|
|
1369
1521
|
[acceptKey, maxSize]
|
|
1370
1522
|
);
|
|
1371
|
-
const emit =
|
|
1523
|
+
const emit = useCallback3(
|
|
1372
1524
|
(files) => {
|
|
1373
1525
|
const accepted = filterFiles(files);
|
|
1374
1526
|
if (accepted.length) onFilesRef.current(accepted);
|
|
1375
1527
|
},
|
|
1376
1528
|
[filterFiles]
|
|
1377
1529
|
);
|
|
1378
|
-
const openPicker =
|
|
1530
|
+
const openPicker = useCallback3(() => {
|
|
1379
1531
|
if (!disabled) inputRef.current?.click();
|
|
1380
1532
|
}, [disabled]);
|
|
1381
|
-
|
|
1533
|
+
useEffect6(() => {
|
|
1382
1534
|
if (!pickerEventName || typeof window === "undefined") return void 0;
|
|
1383
1535
|
const handler = () => openPicker();
|
|
1384
1536
|
window.addEventListener(pickerEventName, handler);
|
|
1385
1537
|
return () => window.removeEventListener(pickerEventName, handler);
|
|
1386
1538
|
}, [openPicker, pickerEventName]);
|
|
1387
|
-
|
|
1539
|
+
useEffect6(() => {
|
|
1388
1540
|
if (!enablePaste || disabled || typeof window === "undefined") {
|
|
1389
1541
|
return void 0;
|
|
1390
1542
|
}
|
|
@@ -1410,7 +1562,7 @@ function useMediaDropzone(options) {
|
|
|
1410
1562
|
window.addEventListener("paste", handler);
|
|
1411
1563
|
return () => window.removeEventListener("paste", handler);
|
|
1412
1564
|
}, [enablePaste, disabled, emit]);
|
|
1413
|
-
const getRootProps =
|
|
1565
|
+
const getRootProps = useCallback3(
|
|
1414
1566
|
() => ({
|
|
1415
1567
|
onDragEnter: (event) => {
|
|
1416
1568
|
if (disabled) return;
|
|
@@ -1443,7 +1595,7 @@ function useMediaDropzone(options) {
|
|
|
1443
1595
|
() => acceptKey.length ? acceptKey : void 0,
|
|
1444
1596
|
[acceptKey]
|
|
1445
1597
|
);
|
|
1446
|
-
const getInputProps =
|
|
1598
|
+
const getInputProps = useCallback3(
|
|
1447
1599
|
() => ({
|
|
1448
1600
|
ref: (node) => {
|
|
1449
1601
|
inputRef.current = node;
|
|
@@ -1465,10 +1617,10 @@ function useMediaDropzone(options) {
|
|
|
1465
1617
|
}
|
|
1466
1618
|
|
|
1467
1619
|
// src/react/file-upload-dropzone.tsx
|
|
1468
|
-
import { useCallback as
|
|
1620
|
+
import { useCallback as useCallback5 } from "react";
|
|
1469
1621
|
|
|
1470
1622
|
// src/core/use-media-upload.ts
|
|
1471
|
-
import { useCallback as
|
|
1623
|
+
import { useCallback as useCallback4, useRef as useRef6, useState as useState5 } from "react";
|
|
1472
1624
|
var entrySeq = 0;
|
|
1473
1625
|
function useMediaUpload() {
|
|
1474
1626
|
const client = useMediaClient();
|
|
@@ -1477,8 +1629,8 @@ function useMediaUpload() {
|
|
|
1477
1629
|
const [result, setResult] = useState5(null);
|
|
1478
1630
|
const [error, setError] = useState5(null);
|
|
1479
1631
|
const [entries, setEntries] = useState5([]);
|
|
1480
|
-
const activeCount =
|
|
1481
|
-
const patchEntry =
|
|
1632
|
+
const activeCount = useRef6(0);
|
|
1633
|
+
const patchEntry = useCallback4(
|
|
1482
1634
|
(id, patch) => {
|
|
1483
1635
|
setEntries(
|
|
1484
1636
|
(current) => current.map((e) => e.id === id ? { ...e, ...patch } : e)
|
|
@@ -1486,7 +1638,7 @@ function useMediaUpload() {
|
|
|
1486
1638
|
},
|
|
1487
1639
|
[]
|
|
1488
1640
|
);
|
|
1489
|
-
const upload =
|
|
1641
|
+
const upload = useCallback4(
|
|
1490
1642
|
async (file, opts = {}) => {
|
|
1491
1643
|
const id = `up-${++entrySeq}`;
|
|
1492
1644
|
const fileName = opts.fileName ?? (file instanceof File ? file.name : "file");
|
|
@@ -1540,7 +1692,7 @@ function useMediaUpload() {
|
|
|
1540
1692
|
},
|
|
1541
1693
|
[client, patchEntry]
|
|
1542
1694
|
);
|
|
1543
|
-
const uploadMany =
|
|
1695
|
+
const uploadMany = useCallback4(
|
|
1544
1696
|
async (files, opts = {}) => {
|
|
1545
1697
|
if (client.uploadMany) {
|
|
1546
1698
|
activeCount.current += 1;
|
|
@@ -1569,10 +1721,10 @@ function useMediaUpload() {
|
|
|
1569
1721
|
},
|
|
1570
1722
|
[client, upload]
|
|
1571
1723
|
);
|
|
1572
|
-
const clearEntry =
|
|
1724
|
+
const clearEntry = useCallback4((id) => {
|
|
1573
1725
|
setEntries((current) => current.filter((e) => e.id !== id));
|
|
1574
1726
|
}, []);
|
|
1575
|
-
const reset =
|
|
1727
|
+
const reset = useCallback4(() => {
|
|
1576
1728
|
setResult(null);
|
|
1577
1729
|
setError(null);
|
|
1578
1730
|
setProgress(null);
|
|
@@ -1592,7 +1744,7 @@ function useMediaUpload() {
|
|
|
1592
1744
|
}
|
|
1593
1745
|
|
|
1594
1746
|
// src/react/upload-progress-list.tsx
|
|
1595
|
-
import { memo, useEffect as
|
|
1747
|
+
import { memo, useEffect as useEffect7, useRef as useRef7, useState as useState6 } from "react";
|
|
1596
1748
|
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1597
1749
|
var AUTO_DISMISS_MS = 1e4;
|
|
1598
1750
|
function formatFileSize(bytes) {
|
|
@@ -1617,12 +1769,12 @@ function UploadProgressListImpl({
|
|
|
1617
1769
|
const activeCount = uploads.filter((u) => u.status === "uploading").length;
|
|
1618
1770
|
const successCount = uploads.filter((u) => u.status === "success").length;
|
|
1619
1771
|
const errorCount = uploads.filter((u) => u.status === "error").length;
|
|
1620
|
-
const timersRef =
|
|
1772
|
+
const timersRef = useRef7(
|
|
1621
1773
|
/* @__PURE__ */ new Map()
|
|
1622
1774
|
);
|
|
1623
|
-
const onDismissRef =
|
|
1775
|
+
const onDismissRef = useRef7(onDismiss);
|
|
1624
1776
|
onDismissRef.current = onDismiss;
|
|
1625
|
-
|
|
1777
|
+
useEffect7(() => {
|
|
1626
1778
|
const timers = timersRef.current;
|
|
1627
1779
|
if (hovered) {
|
|
1628
1780
|
timers.forEach((t) => clearTimeout(t));
|
|
@@ -1650,7 +1802,7 @@ function UploadProgressListImpl({
|
|
|
1650
1802
|
}
|
|
1651
1803
|
}
|
|
1652
1804
|
}, [uploads, hovered]);
|
|
1653
|
-
|
|
1805
|
+
useEffect7(() => {
|
|
1654
1806
|
const timers = timersRef.current;
|
|
1655
1807
|
return () => {
|
|
1656
1808
|
timers.forEach((t) => clearTimeout(t));
|
|
@@ -1782,7 +1934,7 @@ function FileUploadDropzone({
|
|
|
1782
1934
|
pickerEventName
|
|
1783
1935
|
}) {
|
|
1784
1936
|
const { uploadMany, entries, clearEntry } = useMediaUpload();
|
|
1785
|
-
const handleUpload =
|
|
1937
|
+
const handleUpload = useCallback5(
|
|
1786
1938
|
async (files) => {
|
|
1787
1939
|
if (!files.length) return;
|
|
1788
1940
|
try {
|
|
@@ -1892,7 +2044,7 @@ function FileUploadDropzone({
|
|
|
1892
2044
|
import { useState as useState8 } from "react";
|
|
1893
2045
|
|
|
1894
2046
|
// src/core/use-media-actions.ts
|
|
1895
|
-
import { useCallback as
|
|
2047
|
+
import { useCallback as useCallback6, useMemo as useMemo5, useState as useState7 } from "react";
|
|
1896
2048
|
function useMediaActions(ref, options = {}) {
|
|
1897
2049
|
const ports = useMediaHostPorts();
|
|
1898
2050
|
const { resolution } = useMediaResolution(ref);
|
|
@@ -1904,7 +2056,7 @@ function useMediaActions(ref, options = {}) {
|
|
|
1904
2056
|
if (!ref) return null;
|
|
1905
2057
|
return { ref, resolution, fileName, alt };
|
|
1906
2058
|
}, [ref, resolution, fileName, alt]);
|
|
1907
|
-
const isAvailable =
|
|
2059
|
+
const isAvailable = useCallback6(
|
|
1908
2060
|
(kind) => {
|
|
1909
2061
|
if (!context || !actions) return false;
|
|
1910
2062
|
if (kind === "share") return Boolean(actions.share || actions.SharePopover);
|
|
@@ -1912,7 +2064,7 @@ function useMediaActions(ref, options = {}) {
|
|
|
1912
2064
|
},
|
|
1913
2065
|
[context, actions]
|
|
1914
2066
|
);
|
|
1915
|
-
const run =
|
|
2067
|
+
const run = useCallback6(
|
|
1916
2068
|
async (kind) => {
|
|
1917
2069
|
if (!context || !actions) return;
|
|
1918
2070
|
const handler = actions[kind];
|
|
@@ -2018,7 +2170,7 @@ function MediaActionToolbar({
|
|
|
2018
2170
|
}
|
|
2019
2171
|
|
|
2020
2172
|
// src/react/media-lightbox.tsx
|
|
2021
|
-
import { useEffect as
|
|
2173
|
+
import { useEffect as useEffect8 } from "react";
|
|
2022
2174
|
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2023
2175
|
function MediaLightbox({
|
|
2024
2176
|
open,
|
|
@@ -2036,7 +2188,7 @@ function MediaLightbox({
|
|
|
2036
2188
|
headerExtra,
|
|
2037
2189
|
className
|
|
2038
2190
|
}) {
|
|
2039
|
-
|
|
2191
|
+
useEffect8(() => {
|
|
2040
2192
|
if (!open || typeof window === "undefined") return void 0;
|
|
2041
2193
|
const handler = (event) => {
|
|
2042
2194
|
if (event.key === "Escape") {
|