@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.
@@ -2375,6 +2375,10 @@ function materialIdentity(material) {
2375
2375
  }
2376
2376
  return `payload:${id}`;
2377
2377
  }
2378
+ function snapshotContent(snapshot) {
2379
+ const { sourceId, label, payload, coverage, unsavedChanges, omissions, sections, limits } = snapshot;
2380
+ return JSON.stringify({ sourceId, label, payload, coverage, unsavedChanges, omissions, sections, limits });
2381
+ }
2378
2382
  function useFence() {
2379
2383
  const mounted = React12.useRef(true);
2380
2384
  const nextId = React12.useRef(0);
@@ -2477,7 +2481,7 @@ function ContentTransferMenuSession({ source, surface, label, projection, sectio
2477
2481
  const captured = await material.capture(signal);
2478
2482
  sourceMaterial = { id: captured.snapshot.sourceId, label: captured.snapshot.label, capture: async () => captured };
2479
2483
  } else if ("kind" in material) {
2480
- sourceMaterial = applyMenuProjection ? { id: "direct", label: sourceLabel, capture: async () => directSource(material, { label: sourceLabel, ...projection ? { projection } : {}, ...sections ? { sections } : {} }) } : material;
2484
+ sourceMaterial = { id: materialIdentity(material), label: sourceLabel, capture: async () => directSource(material, { sourceId: materialIdentity(material), label: sourceLabel, ...applyMenuProjection && projection ? { projection } : {}, ...applyMenuProjection && sections ? { sections } : {} }) };
2481
2485
  } else if (applyMenuProjection && (projection || sections)) {
2482
2486
  sourceMaterial = {
2483
2487
  id: material.id,
@@ -2494,7 +2498,7 @@ function ContentTransferMenuSession({ source, surface, label, projection, sectio
2494
2498
  } else {
2495
2499
  sourceMaterial = material;
2496
2500
  }
2497
- const raw = "kind" in sourceMaterial ? directSource(sourceMaterial) : await sourceMaterial.capture({ scope, signal });
2501
+ const raw = await sourceMaterial.capture({ scope, signal });
2498
2502
  const constrained = resolveTransferPreferences(providerPreferences, preferences, raw.snapshot.limits);
2499
2503
  const result = await capture({ id: raw.snapshot.sourceId, label: raw.snapshot.label, capture: async () => ({ ...raw, snapshot: { ...raw.snapshot, limits: constrained.limits } }) }, signal, scope);
2500
2504
  if (scope !== "all-matching" || !collectPages) return result;
@@ -2554,31 +2558,117 @@ function ContentTransferMenuSession({ source, surface, label, projection, sectio
2554
2558
  fence.finish(controller);
2555
2559
  }
2556
2560
  }, [captureOne, capturePrimary, fence, formatSources, initialScope, preferences, providerPreferences, report, transport]);
2557
- const openWorkspace = React12.useCallback(async (key = primaryWorkspaceKey, variant) => {
2558
- const variantMaterial = variant && "kind" in variant.source ? { id: `variant:${key}`, label: variant.label, capture: async () => directSource(variant.source, { label: variant.label }) } : variant?.source;
2559
- const captureWorkspace = variantMaterial ? (scope, signal) => captureOne(variantMaterial, scope, signal, true) : capturePrimary;
2560
- workspaceCaptures.current.set(key, captureWorkspace);
2561
- if (workspaces[key]) {
2562
- setWorkspaceKey(key);
2563
- setWorkspaceOpen(true);
2564
- return;
2565
- }
2566
- const { controller, id } = fence.begin();
2567
- try {
2568
- const result = await captureWorkspace(initialScope, controller.signal);
2569
- if (!fence.valid(id, controller)) return;
2570
- const resolved = resolveTransferPreferences(providerPreferences, preferences, result.snapshot.limits);
2571
- const original = createDraft(result.snapshot);
2572
- const base = { ...result, key, original, draft: original, scope: initialScope, preferences: resolved, ...result.collectionError ? { error: result.collectionError } : {} };
2573
- setWorkspaces((current) => ({ ...current, [key]: { ...base, draft: rebuild(base, initialControls(base)) } }));
2574
- setWorkspaceKey(key);
2575
- setWorkspaceOpen(true);
2576
- } catch (error) {
2577
- if (fence.valid(id, controller)) setNotice(errorMessage(error, "Could not capture content"));
2578
- } finally {
2579
- fence.finish(controller);
2580
- }
2581
- }, [captureOne, capturePrimary, fence, initialScope, preferences, primaryWorkspaceKey, providerPreferences, workspaces]);
2561
+ const openWorkspace = React12.useCallback(
2562
+ async (key = primaryWorkspaceKey, variant) => {
2563
+ const variantMaterial = variant && "kind" in variant.source ? {
2564
+ id: `variant:${key}`,
2565
+ label: variant.label,
2566
+ capture: async () => directSource(variant.source, {
2567
+ sourceId: `variant:${key}`,
2568
+ label: variant.label
2569
+ })
2570
+ } : variant?.source;
2571
+ const captureWorkspace = (scope, signal, collectPages = true) => captureOne(variantMaterial ?? primary, scope, signal, true, collectPages);
2572
+ workspaceCaptures.current.set(key, captureWorkspace);
2573
+ const existing = workspaces[key];
2574
+ const { controller, id } = fence.begin();
2575
+ if (existing) {
2576
+ try {
2577
+ const allMatching = existing.scope === "all-matching";
2578
+ const fresh = await captureWorkspace(
2579
+ existing.scope,
2580
+ controller.signal,
2581
+ !allMatching
2582
+ );
2583
+ if (!fence.valid(id, controller) || !workspaceCaptures.current.has(key))
2584
+ return;
2585
+ if (allMatching && (!existing.allMatching || !fresh.allMatching))
2586
+ throw new Error(
2587
+ "This source can no longer provide an all-matching snapshot."
2588
+ );
2589
+ const collectionIdentity = (value) => value && {
2590
+ query: value.source.query,
2591
+ projection: value.projection,
2592
+ columns: value.columns,
2593
+ limits: value.limits
2594
+ };
2595
+ const changed = allMatching ? JSON.stringify(collectionIdentity(existing.allMatching)) !== JSON.stringify(collectionIdentity(fresh.allMatching)) : snapshotContent(existing.snapshot) !== snapshotContent(fresh.snapshot);
2596
+ setWorkspaces((current) => {
2597
+ const retained = current[key];
2598
+ if (!retained) return current;
2599
+ return {
2600
+ ...current,
2601
+ [key]: {
2602
+ ...retained,
2603
+ error: fresh.collectionError,
2604
+ ...allMatching ? { sourceStale: changed } : changed ? { pending: { ...fresh, scope: retained.scope } } : { pending: void 0 }
2605
+ }
2606
+ };
2607
+ });
2608
+ setWorkspaceKey(key);
2609
+ setWorkspaceOpen(true);
2610
+ } catch (error) {
2611
+ if (fence.valid(id, controller) && workspaceCaptures.current.has(key)) {
2612
+ setWorkspaces(
2613
+ (current) => current[key] ? {
2614
+ ...current,
2615
+ [key]: {
2616
+ ...current[key],
2617
+ error: errorMessage(error, "Could not refresh source")
2618
+ }
2619
+ } : current
2620
+ );
2621
+ setWorkspaceKey(key);
2622
+ setWorkspaceOpen(true);
2623
+ }
2624
+ } finally {
2625
+ fence.finish(controller);
2626
+ }
2627
+ return;
2628
+ }
2629
+ try {
2630
+ const result = await captureWorkspace(initialScope, controller.signal);
2631
+ if (!fence.valid(id, controller)) return;
2632
+ const resolved = resolveTransferPreferences(
2633
+ providerPreferences,
2634
+ preferences,
2635
+ result.snapshot.limits
2636
+ );
2637
+ const original = createDraft(result.snapshot);
2638
+ const base = {
2639
+ ...result,
2640
+ key,
2641
+ original,
2642
+ draft: original,
2643
+ scope: initialScope,
2644
+ preferences: resolved,
2645
+ ...result.collectionError ? { error: result.collectionError } : {}
2646
+ };
2647
+ setWorkspaces((current) => ({
2648
+ ...current,
2649
+ [key]: { ...base, draft: rebuild(base, initialControls(base)) }
2650
+ }));
2651
+ setWorkspaceKey(key);
2652
+ setWorkspaceOpen(true);
2653
+ } catch (error) {
2654
+ if (fence.valid(id, controller))
2655
+ setNotice(errorMessage(error, "Could not capture content"));
2656
+ } finally {
2657
+ fence.finish(controller);
2658
+ }
2659
+ },
2660
+ [
2661
+ captureOne,
2662
+ capturePrimary,
2663
+ fence,
2664
+ initialScope,
2665
+ preferences,
2666
+ primaryWorkspaceKey,
2667
+ primary,
2668
+ providerPreferences,
2669
+ workspaces
2670
+ ]
2671
+ );
2582
2672
  React12.useImperativeHandle(controllerRef, () => ({
2583
2673
  focusTrigger: () => triggerRef.current?.focus(),
2584
2674
  prepare: async (requestedSource, options) => {
@@ -2801,7 +2891,7 @@ function PreparationWorkspace({ restoreFocus, open, state, scopes, onClose, onCh
2801
2891
  setBusy("capture");
2802
2892
  try {
2803
2893
  const result = await captureScope(scope, controller.signal);
2804
- if (fence.valid(id, controller) && latest.current.draft.revision === revision) onChange({ ...latest.current, pending: { ...result, scope }, error: result.collectionError });
2894
+ if (fence.valid(id, controller) && latest.current.draft.revision === revision) onChange({ ...latest.current, pending: { ...result, scope }, sourceStale: false, error: result.collectionError });
2805
2895
  } catch (error) {
2806
2896
  if (fence.valid(id, controller)) onChange({ ...latest.current, error: errorMessage(error, "Refresh failed") });
2807
2897
  } finally {
@@ -3044,6 +3134,7 @@ function PreparationWorkspace({ restoreFocus, open, state, scopes, onClose, onCh
3044
3134
  /* @__PURE__ */ jsx19(Button, { type: "button", variant: "outline", onClick: () => setPendingControls(null), children: "Keep edited draft" }),
3045
3135
  /* @__PURE__ */ jsx19(Button, { type: "button", onClick: applyPendingControls, children: "Rebuild from source" })
3046
3136
  ] }) : null,
3137
+ state.sourceStale ? /* @__PURE__ */ jsx19("p", { role: "alert", className: "matrx-alchemy-decision", children: "The source changed since this draft. Use Refresh source to review the latest data." }) : null,
3047
3138
  state.pending ? /* @__PURE__ */ jsxs13("div", { className: "matrx-alchemy-decision", role: "alert", children: [
3048
3139
  /* @__PURE__ */ jsx19("p", { children: "A fresh source snapshot is ready." }),
3049
3140
  /* @__PURE__ */ jsx19(Button, { type: "button", variant: "outline", onClick: () => resolveCapture("keep"), children: "Keep current snapshot and draft" }),