@ai-matrx/media 0.4.2 → 0.4.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @ai-matrx/media
2
2
 
3
+ ## 0.4.3 — 2026-08-31
4
+
5
+ ### Consumer action (C28)
6
+
7
+ - Upgrade. No API change; private SVG refs now render from the client's
8
+ authenticated blob lifecycle instead of binding a bearer-less download URL
9
+ directly to an image element.
10
+
11
+ ### Fixed
12
+
13
+ - The built-in SVG viewer now resolves refs through `MediaClient.getBlob()`
14
+ and renders the lifecycle-owned object URL. This makes personal/private SVG
15
+ files render in ordinary image elements while retaining the existing source
16
+ view and avoiding any host-side viewer fork.
17
+ - Blob sources remain client-free, and real byte-fetch errors are shown in the
18
+ viewer instead of being collapsed into a generic render failure.
19
+
3
20
  ## 0.4.2 — 2026-08-31
4
21
 
5
22
  ### Consumer action (C28)
package/dist/viewers.cjs CHANGED
@@ -47,7 +47,7 @@ __export(viewers_exports, {
47
47
  module.exports = __toCommonJS(viewers_exports);
48
48
 
49
49
  // src/viewers/media-file-viewer.tsx
50
- var import_react12 = require("react");
50
+ var import_react11 = require("react");
51
51
 
52
52
  // src/viewers/registry.ts
53
53
  var VIEWER_SLOT = /* @__PURE__ */ Symbol.for("ai-matrx.media.viewer-registry");
@@ -1433,105 +1433,6 @@ function HtmlSourceView({ source, maxBytes = 1024 * 1024 }) {
1433
1433
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("pre", { className: "h-full w-full overflow-auto bg-muted/10 p-4 font-mono text-xs leading-relaxed break-all whitespace-pre-wrap", children: text });
1434
1434
  }
1435
1435
 
1436
- // src/core/use-media-load-recovery.ts
1437
- var import_react11 = require("react");
1438
-
1439
- // src/types.ts
1440
- function mintDurableSrc(url) {
1441
- return url;
1442
- }
1443
-
1444
- // src/core/use-media-load-recovery.ts
1445
- function normalizeOutcome(outcome) {
1446
- return typeof outcome === "string" ? { verdict: outcome } : outcome;
1447
- }
1448
- var EPHEMERAL_SECRET_IDENTITY = "server:files-session-secret";
1449
- function useMediaLoadRecovery(src, options) {
1450
- const client = useMediaClient();
1451
- const capture = useMediaFailureCapture();
1452
- const recoverable = options?.recoverable !== false;
1453
- const onTerminal = options?.onTerminal;
1454
- const failureRef = options?.failureRef;
1455
- const [result, setResult] = (0, import_react11.useState)({ source: src, retryKey: 0, failed: false });
1456
- const attemptRef = (0, import_react11.useRef)(0);
1457
- const recoveryInFlightRef = (0, import_react11.useRef)(false);
1458
- (0, import_react11.useEffect)(() => {
1459
- attemptRef.current = 0;
1460
- recoveryInFlightRef.current = false;
1461
- }, [src]);
1462
- (0, import_react11.useLayoutEffect)(() => {
1463
- recoveryInFlightRef.current = false;
1464
- }, [result.retryKey]);
1465
- const identity = () => failureMediaIdentity(failureRef ?? null, src);
1466
- const onLoadError = (event) => {
1467
- if (recoveryInFlightRef.current) return;
1468
- if (!src || !recoverable) {
1469
- setResult({ source: src, retryKey: 0, failed: true });
1470
- if (src) {
1471
- capture({
1472
- phase: "element-load",
1473
- mediaRef: identity(),
1474
- terminal: true,
1475
- retryOutcome: "not-attempted",
1476
- message: "media element failed to load and the source is unrecoverable (foreign/object URL \u2014 a session refresh cannot fix it)"
1477
- });
1478
- }
1479
- onTerminal?.(event);
1480
- return;
1481
- }
1482
- recoveryInFlightRef.current = true;
1483
- attemptRef.current += 1;
1484
- const attempt = attemptRef.current;
1485
- client.recoverLoadError(mintDurableSrc(src), attempt).then(
1486
- (outcome) => {
1487
- const report = normalizeOutcome(outcome);
1488
- if (report.secretSource === "ephemeral") {
1489
- capture({
1490
- phase: "session",
1491
- mediaRef: EPHEMERAL_SECRET_IDENTITY,
1492
- terminal: false,
1493
- message: "server cookie secret is ephemeral \u2014 private renders will break across restarts/instances (set a persistent files-session secret on the server)"
1494
- });
1495
- }
1496
- if (report.verdict === "retry") {
1497
- setResult((current) => ({
1498
- source: src,
1499
- retryKey: current.source === src ? current.retryKey + 1 : 1,
1500
- failed: false
1501
- }));
1502
- } else {
1503
- setResult({ source: src, retryKey: 0, failed: true });
1504
- capture({
1505
- phase: attempt > 1 ? "element-load" : "recovery",
1506
- mediaRef: identity(),
1507
- terminal: true,
1508
- retryOutcome: attempt > 1 ? "retried" : "not-attempted",
1509
- 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)",
1510
- ...report.status !== void 0 ? { status: report.status } : {}
1511
- });
1512
- onTerminal?.(event);
1513
- }
1514
- },
1515
- (err) => {
1516
- setResult({ source: src, retryKey: 0, failed: true });
1517
- capture({
1518
- phase: "session",
1519
- mediaRef: identity(),
1520
- terminal: true,
1521
- retryOutcome: "recovery-failed",
1522
- message: `session recovery failed: ${err instanceof Error ? err.message : String(err ?? "unknown error")}`
1523
- });
1524
- onTerminal?.(event);
1525
- }
1526
- );
1527
- };
1528
- return {
1529
- retryKey: result.source === src ? result.retryKey : 0,
1530
- onLoadError,
1531
- failed: result.source === src ? result.failed : false
1532
- };
1533
- }
1534
-
1535
1436
  // src/viewers/transparency-grid.ts
1536
1437
  var TRANSPARENCY_GRID_STYLE = {
1537
1438
  backgroundImage: "linear-gradient(45deg, rgb(0 0 0 / 0.08) 25%, transparent 25%), linear-gradient(-45deg, rgb(0 0 0 / 0.08) 25%, transparent 25%), linear-gradient(45deg, transparent 75%, rgb(0 0 0 / 0.08) 75%), linear-gradient(-45deg, transparent 75%, rgb(0 0 0 / 0.08) 75%)",
@@ -1566,30 +1467,21 @@ function SvgViewer(props) {
1566
1467
  );
1567
1468
  }
1568
1469
  function SvgRenderedView({ source, fileName }) {
1569
- const { url, recoverable, unavailable } = useViewerDisplayUrl(source);
1570
- const { retryKey, onLoadError, failed } = useMediaLoadRecovery(url, {
1571
- recoverable,
1572
- ...source.kind === "ref" ? { failureRef: source.ref } : {}
1573
- });
1574
- if (unavailable) {
1575
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1576
- ViewerError,
1577
- {
1578
- title: "This SVG isn't available",
1579
- hint: "You may not have access to it, or it may have been deleted."
1580
- }
1581
- );
1582
- }
1583
- if (!url) return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ViewerSkeleton, { className: "flex-1" });
1584
- if (failed) {
1470
+ const fromClient = useMediaBlob(source.kind === "ref" ? source.ref : null);
1471
+ const url = source.kind === "blob" ? source.url : fromClient.url;
1472
+ const loading = source.kind === "ref" && fromClient.loading;
1473
+ const error = source.kind === "ref" ? fromClient.error : null;
1474
+ if (error) {
1585
1475
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1586
1476
  ViewerError,
1587
1477
  {
1588
1478
  title: "SVG could not be rendered",
1479
+ message: error,
1589
1480
  hint: "Switch to the Source view to inspect the markup."
1590
1481
  }
1591
1482
  );
1592
1483
  }
1484
+ if (loading || !url) return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ViewerSkeleton, { className: "flex-1" });
1593
1485
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1594
1486
  "div",
1595
1487
  {
@@ -1600,10 +1492,8 @@ function SvgRenderedView({ source, fileName }) {
1600
1492
  {
1601
1493
  src: url,
1602
1494
  alt: fileName,
1603
- className: "max-h-full max-w-full object-contain drop-shadow-sm",
1604
- onError: onLoadError
1605
- },
1606
- retryKey
1495
+ className: "max-h-full max-w-full object-contain drop-shadow-sm"
1496
+ }
1607
1497
  )
1608
1498
  }
1609
1499
  );
@@ -1757,7 +1647,7 @@ function MediaFileViewer({
1757
1647
  }) {
1758
1648
  const kind = previewKind ?? getPreviewKind(props.fileName, props.mimeType ?? null);
1759
1649
  const viewer = getMediaViewer(kind) ?? BUILT_IN[kind];
1760
- if (viewer) return (0, import_react12.createElement)(viewer, props);
1650
+ if (viewer) return (0, import_react11.createElement)(viewer, props);
1761
1651
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(MediaViewerUnavailable, { ...props, kind });
1762
1652
  }
1763
1653
  //# sourceMappingURL=viewers.cjs.map