@ai-matrx/design-system 0.18.2 → 0.18.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,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.18.3 — 2026-09-12
4
+
5
+ Reopening an Alchemy preparation now checks the current source while retaining
6
+ manual drafts. Changed direct sources offer an explicit keep-or-regenerate
7
+ choice, all-matching sources detect identity changes without eagerly fetching
8
+ pages, and failed refreshes remain visible instead of silently reusing stale
9
+ content.
10
+
11
+ ### Consumer action
12
+
13
+ Update to 0.18.3. No host configuration changes are required.
14
+
3
15
  ## 0.18.2 — 2026-09-12
4
16
 
5
17
  Alchemy copy sessions now reset only when their explicit identity changes,
@@ -1250,6 +1250,10 @@ function materialIdentity(material) {
1250
1250
  }
1251
1251
  return `payload:${id}`;
1252
1252
  }
1253
+ function snapshotContent(snapshot) {
1254
+ const { sourceId, label, payload, coverage, unsavedChanges, omissions, sections, limits } = snapshot;
1255
+ return JSON.stringify({ sourceId, label, payload, coverage, unsavedChanges, omissions, sections, limits });
1256
+ }
1253
1257
  function useFence() {
1254
1258
  const mounted = React8.useRef(true);
1255
1259
  const nextId = React8.useRef(0);
@@ -1352,7 +1356,7 @@ function ContentTransferMenuSession({ source, surface, label, projection, sectio
1352
1356
  const captured = await material.capture(signal);
1353
1357
  sourceMaterial = { id: captured.snapshot.sourceId, label: captured.snapshot.label, capture: async () => captured };
1354
1358
  } else if ("kind" in material) {
1355
- sourceMaterial = applyMenuProjection ? { id: "direct", label: sourceLabel, capture: async () => (0, import_content_transfer2.directSource)(material, { label: sourceLabel, ...projection ? { projection } : {}, ...sections ? { sections } : {} }) } : material;
1359
+ sourceMaterial = { id: materialIdentity(material), label: sourceLabel, capture: async () => (0, import_content_transfer2.directSource)(material, { sourceId: materialIdentity(material), label: sourceLabel, ...applyMenuProjection && projection ? { projection } : {}, ...applyMenuProjection && sections ? { sections } : {} }) };
1356
1360
  } else if (applyMenuProjection && (projection || sections)) {
1357
1361
  sourceMaterial = {
1358
1362
  id: material.id,
@@ -1369,7 +1373,7 @@ function ContentTransferMenuSession({ source, surface, label, projection, sectio
1369
1373
  } else {
1370
1374
  sourceMaterial = material;
1371
1375
  }
1372
- const raw = "kind" in sourceMaterial ? (0, import_content_transfer2.directSource)(sourceMaterial) : await sourceMaterial.capture({ scope, signal });
1376
+ const raw = await sourceMaterial.capture({ scope, signal });
1373
1377
  const constrained = (0, import_content_transfer2.resolveTransferPreferences)(providerPreferences, preferences, raw.snapshot.limits);
1374
1378
  const result = await (0, import_content_transfer2.capture)({ id: raw.snapshot.sourceId, label: raw.snapshot.label, capture: async () => ({ ...raw, snapshot: { ...raw.snapshot, limits: constrained.limits } }) }, signal, scope);
1375
1379
  if (scope !== "all-matching" || !collectPages) return result;
@@ -1429,31 +1433,117 @@ function ContentTransferMenuSession({ source, surface, label, projection, sectio
1429
1433
  fence.finish(controller);
1430
1434
  }
1431
1435
  }, [captureOne, capturePrimary, fence, formatSources, initialScope, preferences, providerPreferences, report, transport]);
1432
- const openWorkspace = React8.useCallback(async (key = primaryWorkspaceKey, variant) => {
1433
- const variantMaterial = variant && "kind" in variant.source ? { id: `variant:${key}`, label: variant.label, capture: async () => (0, import_content_transfer2.directSource)(variant.source, { label: variant.label }) } : variant?.source;
1434
- const captureWorkspace = variantMaterial ? (scope, signal) => captureOne(variantMaterial, scope, signal, true) : capturePrimary;
1435
- workspaceCaptures.current.set(key, captureWorkspace);
1436
- if (workspaces[key]) {
1437
- setWorkspaceKey(key);
1438
- setWorkspaceOpen(true);
1439
- return;
1440
- }
1441
- const { controller, id } = fence.begin();
1442
- try {
1443
- const result = await captureWorkspace(initialScope, controller.signal);
1444
- if (!fence.valid(id, controller)) return;
1445
- const resolved = (0, import_content_transfer2.resolveTransferPreferences)(providerPreferences, preferences, result.snapshot.limits);
1446
- const original = (0, import_content_transfer2.createDraft)(result.snapshot);
1447
- const base = { ...result, key, original, draft: original, scope: initialScope, preferences: resolved, ...result.collectionError ? { error: result.collectionError } : {} };
1448
- setWorkspaces((current) => ({ ...current, [key]: { ...base, draft: rebuild(base, initialControls(base)) } }));
1449
- setWorkspaceKey(key);
1450
- setWorkspaceOpen(true);
1451
- } catch (error) {
1452
- if (fence.valid(id, controller)) setNotice(errorMessage(error, "Could not capture content"));
1453
- } finally {
1454
- fence.finish(controller);
1455
- }
1456
- }, [captureOne, capturePrimary, fence, initialScope, preferences, primaryWorkspaceKey, providerPreferences, workspaces]);
1436
+ const openWorkspace = React8.useCallback(
1437
+ async (key = primaryWorkspaceKey, variant) => {
1438
+ const variantMaterial = variant && "kind" in variant.source ? {
1439
+ id: `variant:${key}`,
1440
+ label: variant.label,
1441
+ capture: async () => (0, import_content_transfer2.directSource)(variant.source, {
1442
+ sourceId: `variant:${key}`,
1443
+ label: variant.label
1444
+ })
1445
+ } : variant?.source;
1446
+ const captureWorkspace = (scope, signal, collectPages = true) => captureOne(variantMaterial ?? primary, scope, signal, true, collectPages);
1447
+ workspaceCaptures.current.set(key, captureWorkspace);
1448
+ const existing = workspaces[key];
1449
+ const { controller, id } = fence.begin();
1450
+ if (existing) {
1451
+ try {
1452
+ const allMatching = existing.scope === "all-matching";
1453
+ const fresh = await captureWorkspace(
1454
+ existing.scope,
1455
+ controller.signal,
1456
+ !allMatching
1457
+ );
1458
+ if (!fence.valid(id, controller) || !workspaceCaptures.current.has(key))
1459
+ return;
1460
+ if (allMatching && (!existing.allMatching || !fresh.allMatching))
1461
+ throw new Error(
1462
+ "This source can no longer provide an all-matching snapshot."
1463
+ );
1464
+ const collectionIdentity = (value) => value && {
1465
+ query: value.source.query,
1466
+ projection: value.projection,
1467
+ columns: value.columns,
1468
+ limits: value.limits
1469
+ };
1470
+ const changed = allMatching ? JSON.stringify(collectionIdentity(existing.allMatching)) !== JSON.stringify(collectionIdentity(fresh.allMatching)) : snapshotContent(existing.snapshot) !== snapshotContent(fresh.snapshot);
1471
+ setWorkspaces((current) => {
1472
+ const retained = current[key];
1473
+ if (!retained) return current;
1474
+ return {
1475
+ ...current,
1476
+ [key]: {
1477
+ ...retained,
1478
+ error: fresh.collectionError,
1479
+ ...allMatching ? { sourceStale: changed } : changed ? { pending: { ...fresh, scope: retained.scope } } : { pending: void 0 }
1480
+ }
1481
+ };
1482
+ });
1483
+ setWorkspaceKey(key);
1484
+ setWorkspaceOpen(true);
1485
+ } catch (error) {
1486
+ if (fence.valid(id, controller) && workspaceCaptures.current.has(key)) {
1487
+ setWorkspaces(
1488
+ (current) => current[key] ? {
1489
+ ...current,
1490
+ [key]: {
1491
+ ...current[key],
1492
+ error: errorMessage(error, "Could not refresh source")
1493
+ }
1494
+ } : current
1495
+ );
1496
+ setWorkspaceKey(key);
1497
+ setWorkspaceOpen(true);
1498
+ }
1499
+ } finally {
1500
+ fence.finish(controller);
1501
+ }
1502
+ return;
1503
+ }
1504
+ try {
1505
+ const result = await captureWorkspace(initialScope, controller.signal);
1506
+ if (!fence.valid(id, controller)) return;
1507
+ const resolved = (0, import_content_transfer2.resolveTransferPreferences)(
1508
+ providerPreferences,
1509
+ preferences,
1510
+ result.snapshot.limits
1511
+ );
1512
+ const original = (0, import_content_transfer2.createDraft)(result.snapshot);
1513
+ const base = {
1514
+ ...result,
1515
+ key,
1516
+ original,
1517
+ draft: original,
1518
+ scope: initialScope,
1519
+ preferences: resolved,
1520
+ ...result.collectionError ? { error: result.collectionError } : {}
1521
+ };
1522
+ setWorkspaces((current) => ({
1523
+ ...current,
1524
+ [key]: { ...base, draft: rebuild(base, initialControls(base)) }
1525
+ }));
1526
+ setWorkspaceKey(key);
1527
+ setWorkspaceOpen(true);
1528
+ } catch (error) {
1529
+ if (fence.valid(id, controller))
1530
+ setNotice(errorMessage(error, "Could not capture content"));
1531
+ } finally {
1532
+ fence.finish(controller);
1533
+ }
1534
+ },
1535
+ [
1536
+ captureOne,
1537
+ capturePrimary,
1538
+ fence,
1539
+ initialScope,
1540
+ preferences,
1541
+ primaryWorkspaceKey,
1542
+ primary,
1543
+ providerPreferences,
1544
+ workspaces
1545
+ ]
1546
+ );
1457
1547
  React8.useImperativeHandle(controllerRef, () => ({
1458
1548
  focusTrigger: () => triggerRef.current?.focus(),
1459
1549
  prepare: async (requestedSource, options) => {
@@ -1676,7 +1766,7 @@ function PreparationWorkspace({ restoreFocus, open, state, scopes, onClose, onCh
1676
1766
  setBusy("capture");
1677
1767
  try {
1678
1768
  const result = await captureScope(scope, controller.signal);
1679
- if (fence.valid(id, controller) && latest.current.draft.revision === revision) onChange({ ...latest.current, pending: { ...result, scope }, error: result.collectionError });
1769
+ if (fence.valid(id, controller) && latest.current.draft.revision === revision) onChange({ ...latest.current, pending: { ...result, scope }, sourceStale: false, error: result.collectionError });
1680
1770
  } catch (error) {
1681
1771
  if (fence.valid(id, controller)) onChange({ ...latest.current, error: errorMessage(error, "Refresh failed") });
1682
1772
  } finally {
@@ -1919,6 +2009,7 @@ function PreparationWorkspace({ restoreFocus, open, state, scopes, onClose, onCh
1919
2009
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Button, { type: "button", variant: "outline", onClick: () => setPendingControls(null), children: "Keep edited draft" }),
1920
2010
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Button, { type: "button", onClick: applyPendingControls, children: "Rebuild from source" })
1921
2011
  ] }) : null,
2012
+ state.sourceStale ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { role: "alert", className: "matrx-alchemy-decision", children: "The source changed since this draft. Use Refresh source to review the latest data." }) : null,
1922
2013
  state.pending ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "matrx-alchemy-decision", role: "alert", children: [
1923
2014
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { children: "A fresh source snapshot is ready." }),
1924
2015
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Button, { type: "button", variant: "outline", onClick: () => resolveCapture("keep"), children: "Keep current snapshot and draft" }),