@matterfact/embed 0.15.0 → 0.16.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/README.md CHANGED
@@ -61,6 +61,39 @@ about the data behind it — no wiring, and the token never reaches the model.
61
61
  An artifact can declare typed params — a company, a time window, a peer set. Readers change
62
62
  them in a control bar on the artifact itself; your page can drive them too.
63
63
 
64
+ There are two artifacts you might be driving, and they're **different iframes** — one
65
+ doesn't reach the other:
66
+
67
+ | The artifact… | Drive it with |
68
+ |---|---|
69
+ | on your page, via `<MatterfactArtifact>` | the `params` prop |
70
+ | inside the chat widget | `host.setArtifactParams()` |
71
+
72
+ ### An artifact on your page
73
+
74
+ ```tsx
75
+ <MatterfactArtifact
76
+ slug="tsla-liquidity"
77
+ owner="you@firm.com"
78
+ token="…"
79
+ params={{ ticker, window: '5Y' }}
80
+ onParamsChange={(params, source) => setTicker(params.ticker)}
81
+ />
82
+ ```
83
+
84
+ Pass your whole param state — the component works out what actually changed and pushes only
85
+ that. Safe to set before the artifact has loaded; it's delivered as soon as the frame is
86
+ ready. Changing a param never reloads the artifact.
87
+
88
+ Dropping a key stops driving it and leaves its current value alone; there is no "unset".
89
+ Re-adding a key you'd dropped re-asserts it, which is how you force a value back after a
90
+ reader has changed it.
91
+
92
+ `onParamsChange` fires for reader- and page-context-driven changes, never for your own
93
+ pushes — so feeding it straight back into `params` can't loop.
94
+
95
+ ### An artifact inside the chat
96
+
64
97
  ```js
65
98
  const host = mount({ …config });
66
99
 
@@ -69,8 +102,8 @@ host.setArtifactParams({ ticker: 'NVDA' }); // or window.matterfact.setArtifac
69
102
 
70
103
  Safe to call before the widget finishes loading — it's queued and delivered on ready.
71
104
 
72
- **A push is authoritative for every key it names.** Send only the params you're actually
73
- changing:
105
+ **A push is authoritative for every key it names.** Unlike the `params` prop, this is raw:
106
+ send only the params you're actually changing.
74
107
 
75
108
  ```js
76
109
  host.setArtifactParams({ window: '5Y' }); // ✅ changes the window
@@ -90,6 +123,8 @@ onEvent(e) {
90
123
  }
91
124
  ```
92
125
 
126
+ ### Either way
127
+
93
128
  A param can also follow your page automatically: if it declares a binding and you publish
94
129
  page context (`window.matterfact.context` or `getPageContext`), it tracks the entity you're
95
130
  showing with no glue code. A reader's own pick takes over from there, until they choose to
package/dist/react.cjs CHANGED
@@ -1362,8 +1362,8 @@ async function readFocus() {
1362
1362
  }
1363
1363
  const active = document.activeElement;
1364
1364
  if (active && active !== document.body && !isPrivate(active) && !active.closest("[data-mf-private]")) {
1365
- const { snapshot: snapshot2 } = await loadSnapshotModule();
1366
- void snapshot2;
1365
+ const { snapshot: snapshot3 } = await loadSnapshotModule();
1366
+ void snapshot3;
1367
1367
  focus.focused = {
1368
1368
  label: redact(describeControl(active)),
1369
1369
  role: active.getAttribute("role") || active.tagName.toLowerCase()
@@ -1406,9 +1406,9 @@ async function sendSnapshot(emit) {
1406
1406
  emit({ type: "host.focus", focus: lastFocus });
1407
1407
  return;
1408
1408
  }
1409
- const { snapshot: snapshot2 } = await loadSnapshotModule();
1409
+ const { snapshot: snapshot3 } = await loadSnapshotModule();
1410
1410
  if (!allowsAuto(effectivePageContextMode())) return;
1411
- const { yaml, truncated, visibleRefs } = snapshot2();
1411
+ const { yaml, truncated, visibleRefs } = snapshot3();
1412
1412
  emit({
1413
1413
  type: "host.snapshot",
1414
1414
  snapshot: { yaml: redact(yaml), seq: ++snapshotSeq, truncated }
@@ -2624,6 +2624,13 @@ function MatterfactAgent({
2624
2624
  );
2625
2625
  }
2626
2626
  var DEFAULT_ARTIFACT_ORIGIN = "https://app.matterfact.com";
2627
+ function snapshot2(params) {
2628
+ const out = /* @__PURE__ */ new Map();
2629
+ for (const [key, value] of Object.entries(params ?? {})) {
2630
+ out.set(key, JSON.stringify(value ?? null));
2631
+ }
2632
+ return out;
2633
+ }
2627
2634
  function MatterfactArtifact({
2628
2635
  name: nameProp,
2629
2636
  slug,
@@ -2632,7 +2639,9 @@ function MatterfactArtifact({
2632
2639
  widgetOrigin: widgetOriginProp,
2633
2640
  theme: themeProp,
2634
2641
  className: className2,
2635
- style
2642
+ style,
2643
+ params,
2644
+ onParamsChange
2636
2645
  }) {
2637
2646
  const ctx = useMatterfactConfig();
2638
2647
  const artifactName = nameProp ?? slug;
@@ -2643,6 +2652,66 @@ function MatterfactArtifact({
2643
2652
  }
2644
2653
  const origin = widgetOriginProp ?? ctx?.widgetOrigin ?? DEFAULT_ARTIFACT_ORIGIN;
2645
2654
  const theme = themeProp ?? ctx?.theme ?? "auto";
2655
+ const frame = (0, import_react2.useRef)(null);
2656
+ const changeRef = (0, import_react2.useRef)(onParamsChange);
2657
+ changeRef.current = onParamsChange;
2658
+ const paramsRef = (0, import_react2.useRef)(params);
2659
+ paramsRef.current = params;
2660
+ const posted = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
2661
+ const post = (0, import_react2.useCallback)(
2662
+ (values) => {
2663
+ if (Object.keys(values).length === 0) return;
2664
+ frame.current?.contentWindow?.postMessage(
2665
+ { type: "host.artifactParams", params: values },
2666
+ origin
2667
+ );
2668
+ },
2669
+ [origin]
2670
+ );
2671
+ const postAll = (0, import_react2.useCallback)(() => {
2672
+ posted.current = snapshot2(paramsRef.current);
2673
+ post({ ...paramsRef.current });
2674
+ }, [post]);
2675
+ const onLoad = (0, import_react2.useCallback)(() => postAll(), [postAll]);
2676
+ (0, import_react2.useEffect)(() => {
2677
+ if (typeof window === "undefined") return;
2678
+ const onMessage = (event) => {
2679
+ if (!frame.current || event.source !== frame.current.contentWindow) return;
2680
+ const data = event.data;
2681
+ if (!data || typeof data !== "object") return;
2682
+ if (data.type === "widget.artifactReady") {
2683
+ postAll();
2684
+ return;
2685
+ }
2686
+ if (data.type === "widget.artifactParams" && data.params) {
2687
+ const source = data.source === "context" ? "context" : "user";
2688
+ try {
2689
+ changeRef.current?.(data.params, source);
2690
+ } catch {
2691
+ }
2692
+ }
2693
+ };
2694
+ window.addEventListener("message", onMessage);
2695
+ return () => window.removeEventListener("message", onMessage);
2696
+ }, [postAll]);
2697
+ const paramsKey = JSON.stringify(params ?? null);
2698
+ const settled = (0, import_react2.useRef)(false);
2699
+ (0, import_react2.useEffect)(() => {
2700
+ const next = snapshot2(paramsRef.current);
2701
+ if (!settled.current) {
2702
+ settled.current = true;
2703
+ posted.current = next;
2704
+ return;
2705
+ }
2706
+ const changed = {};
2707
+ for (const [key, json] of next) {
2708
+ if (posted.current.get(key) !== json) {
2709
+ changed[key] = paramsRef.current[key];
2710
+ }
2711
+ }
2712
+ posted.current = next;
2713
+ post(changed);
2714
+ }, [paramsKey, post]);
2646
2715
  let src;
2647
2716
  if (token) {
2648
2717
  src = `${origin}/embed/artifacts/${encodeURIComponent(artifactName)}?owner=${encodeURIComponent(owner)}&t=${encodeURIComponent(token)}&theme=${theme}`;
@@ -2670,7 +2739,9 @@ function MatterfactArtifact({
2670
2739
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2671
2740
  "iframe",
2672
2741
  {
2742
+ ref: frame,
2673
2743
  src,
2744
+ onLoad,
2674
2745
  title: `matterfact artifact ${artifactName}`,
2675
2746
  className: className2,
2676
2747
  style: { width: "100%", height: 600, border: 0, ...style },
@@ -2683,10 +2754,10 @@ function diffDocParams(params, sent) {
2683
2754
  const changed = {};
2684
2755
  for (const [key, value] of Object.entries(params)) {
2685
2756
  if (value === void 0) continue;
2686
- const snapshot2 = JSON.stringify(value);
2687
- if (sent.get(key) === snapshot2) continue;
2757
+ const snapshot3 = JSON.stringify(value);
2758
+ if (sent.get(key) === snapshot3) continue;
2688
2759
  changed[key] = value;
2689
- sent.set(key, snapshot2);
2760
+ sent.set(key, snapshot3);
2690
2761
  }
2691
2762
  return changed;
2692
2763
  }