@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/dist/react.d.cts CHANGED
@@ -446,6 +446,30 @@ interface MatterfactArtifactProps {
446
446
  className?: string;
447
447
  /** Applied to the `<iframe>` element. Merged over the default fill. */
448
448
  style?: React.CSSProperties;
449
+ /**
450
+ * Drive the artifact's declared params from your page — the company it's about, the
451
+ * time window, the peer set. View-only: a param change re-renders the artifact from
452
+ * data it already has, and never re-queries or runs the agent.
453
+ *
454
+ * Pass your whole param state; the component works out what actually changed and
455
+ * pushes only that. That matters, because a push is authoritative for every key it
456
+ * names: re-asserting a param the reader has since changed in the artifact's own
457
+ * control bar would silently overwrite their choice. Dropping a key stops driving it
458
+ * and leaves its current value alone — there is no "unset".
459
+ *
460
+ * NOTE: `MatterfactAgent`'s `setArtifactParams` steers artifacts rendered INSIDE the
461
+ * chat. This prop steers THIS artifact. They are different iframes; neither reaches
462
+ * the other.
463
+ */
464
+ params?: Record<string, unknown>;
465
+ /**
466
+ * Fired when a param changes inside the artifact — `'user'` when the reader used its
467
+ * control bar, `'context'` when a bound param followed the host page's context.
468
+ *
469
+ * Never fires for your own `params` pushes, so feeding this straight back into the
470
+ * `params` prop cannot loop.
471
+ */
472
+ onParamsChange?: (params: Record<string, unknown>, source: 'user' | 'context') => void;
449
473
  }
450
474
  /**
451
475
  * Render a matterfact artifact inline in a host page. Chrome-free, cross-origin. When a
@@ -462,7 +486,7 @@ interface MatterfactArtifactProps {
462
486
  * built at all — matching the "incomplete link" placeholder the embed artifact
463
487
  * page itself renders for a missing owner/token.
464
488
  */
465
- declare function MatterfactArtifact({ name: nameProp, slug, owner, token, widgetOrigin: widgetOriginProp, theme: themeProp, className, style, }: MatterfactArtifactProps): react_jsx_runtime.JSX.Element;
489
+ declare function MatterfactArtifact({ name: nameProp, slug, owner, token, widgetOrigin: widgetOriginProp, theme: themeProp, className, style, params, onParamsChange, }: MatterfactArtifactProps): react_jsx_runtime.JSX.Element;
466
490
  /** The two live-drivable doc params, mirroring `EmbedDocumentPage`'s `HostDocParams`. */
467
491
  type MatterfactDocParams = {
468
492
  ticker?: string;
package/dist/react.d.ts CHANGED
@@ -446,6 +446,30 @@ interface MatterfactArtifactProps {
446
446
  className?: string;
447
447
  /** Applied to the `<iframe>` element. Merged over the default fill. */
448
448
  style?: React.CSSProperties;
449
+ /**
450
+ * Drive the artifact's declared params from your page — the company it's about, the
451
+ * time window, the peer set. View-only: a param change re-renders the artifact from
452
+ * data it already has, and never re-queries or runs the agent.
453
+ *
454
+ * Pass your whole param state; the component works out what actually changed and
455
+ * pushes only that. That matters, because a push is authoritative for every key it
456
+ * names: re-asserting a param the reader has since changed in the artifact's own
457
+ * control bar would silently overwrite their choice. Dropping a key stops driving it
458
+ * and leaves its current value alone — there is no "unset".
459
+ *
460
+ * NOTE: `MatterfactAgent`'s `setArtifactParams` steers artifacts rendered INSIDE the
461
+ * chat. This prop steers THIS artifact. They are different iframes; neither reaches
462
+ * the other.
463
+ */
464
+ params?: Record<string, unknown>;
465
+ /**
466
+ * Fired when a param changes inside the artifact — `'user'` when the reader used its
467
+ * control bar, `'context'` when a bound param followed the host page's context.
468
+ *
469
+ * Never fires for your own `params` pushes, so feeding this straight back into the
470
+ * `params` prop cannot loop.
471
+ */
472
+ onParamsChange?: (params: Record<string, unknown>, source: 'user' | 'context') => void;
449
473
  }
450
474
  /**
451
475
  * Render a matterfact artifact inline in a host page. Chrome-free, cross-origin. When a
@@ -462,7 +486,7 @@ interface MatterfactArtifactProps {
462
486
  * built at all — matching the "incomplete link" placeholder the embed artifact
463
487
  * page itself renders for a missing owner/token.
464
488
  */
465
- declare function MatterfactArtifact({ name: nameProp, slug, owner, token, widgetOrigin: widgetOriginProp, theme: themeProp, className, style, }: MatterfactArtifactProps): react_jsx_runtime.JSX.Element;
489
+ declare function MatterfactArtifact({ name: nameProp, slug, owner, token, widgetOrigin: widgetOriginProp, theme: themeProp, className, style, params, onParamsChange, }: MatterfactArtifactProps): react_jsx_runtime.JSX.Element;
466
490
  /** The two live-drivable doc params, mirroring `EmbedDocumentPage`'s `HostDocParams`. */
467
491
  type MatterfactDocParams = {
468
492
  ticker?: string;
package/dist/react.js CHANGED
@@ -2,7 +2,7 @@
2
2
  "use client";
3
3
 
4
4
  // src/react.tsx
5
- import { useEffect as useEffect2, useRef } from "react";
5
+ import { useCallback, useEffect as useEffect2, useRef } from "react";
6
6
 
7
7
  // src/protocol.ts
8
8
  var PROTOCOL_VERSION = 3;
@@ -1011,6 +1011,13 @@ function MatterfactAgent({
1011
1011
  );
1012
1012
  }
1013
1013
  var DEFAULT_ARTIFACT_ORIGIN = "https://app.matterfact.com";
1014
+ function snapshot(params) {
1015
+ const out = /* @__PURE__ */ new Map();
1016
+ for (const [key, value] of Object.entries(params ?? {})) {
1017
+ out.set(key, JSON.stringify(value ?? null));
1018
+ }
1019
+ return out;
1020
+ }
1014
1021
  function MatterfactArtifact({
1015
1022
  name: nameProp,
1016
1023
  slug,
@@ -1019,7 +1026,9 @@ function MatterfactArtifact({
1019
1026
  widgetOrigin: widgetOriginProp,
1020
1027
  theme: themeProp,
1021
1028
  className,
1022
- style
1029
+ style,
1030
+ params,
1031
+ onParamsChange
1023
1032
  }) {
1024
1033
  const ctx = useMatterfactConfig();
1025
1034
  const artifactName = nameProp ?? slug;
@@ -1030,6 +1039,66 @@ function MatterfactArtifact({
1030
1039
  }
1031
1040
  const origin = widgetOriginProp ?? ctx?.widgetOrigin ?? DEFAULT_ARTIFACT_ORIGIN;
1032
1041
  const theme = themeProp ?? ctx?.theme ?? "auto";
1042
+ const frame = useRef(null);
1043
+ const changeRef = useRef(onParamsChange);
1044
+ changeRef.current = onParamsChange;
1045
+ const paramsRef = useRef(params);
1046
+ paramsRef.current = params;
1047
+ const posted = useRef(/* @__PURE__ */ new Map());
1048
+ const post = useCallback(
1049
+ (values) => {
1050
+ if (Object.keys(values).length === 0) return;
1051
+ frame.current?.contentWindow?.postMessage(
1052
+ { type: "host.artifactParams", params: values },
1053
+ origin
1054
+ );
1055
+ },
1056
+ [origin]
1057
+ );
1058
+ const postAll = useCallback(() => {
1059
+ posted.current = snapshot(paramsRef.current);
1060
+ post({ ...paramsRef.current });
1061
+ }, [post]);
1062
+ const onLoad = useCallback(() => postAll(), [postAll]);
1063
+ useEffect2(() => {
1064
+ if (typeof window === "undefined") return;
1065
+ const onMessage = (event) => {
1066
+ if (!frame.current || event.source !== frame.current.contentWindow) return;
1067
+ const data = event.data;
1068
+ if (!data || typeof data !== "object") return;
1069
+ if (data.type === "widget.artifactReady") {
1070
+ postAll();
1071
+ return;
1072
+ }
1073
+ if (data.type === "widget.artifactParams" && data.params) {
1074
+ const source = data.source === "context" ? "context" : "user";
1075
+ try {
1076
+ changeRef.current?.(data.params, source);
1077
+ } catch {
1078
+ }
1079
+ }
1080
+ };
1081
+ window.addEventListener("message", onMessage);
1082
+ return () => window.removeEventListener("message", onMessage);
1083
+ }, [postAll]);
1084
+ const paramsKey = JSON.stringify(params ?? null);
1085
+ const settled = useRef(false);
1086
+ useEffect2(() => {
1087
+ const next = snapshot(paramsRef.current);
1088
+ if (!settled.current) {
1089
+ settled.current = true;
1090
+ posted.current = next;
1091
+ return;
1092
+ }
1093
+ const changed = {};
1094
+ for (const [key, json] of next) {
1095
+ if (posted.current.get(key) !== json) {
1096
+ changed[key] = paramsRef.current[key];
1097
+ }
1098
+ }
1099
+ posted.current = next;
1100
+ post(changed);
1101
+ }, [paramsKey, post]);
1033
1102
  let src;
1034
1103
  if (token) {
1035
1104
  src = `${origin}/embed/artifacts/${encodeURIComponent(artifactName)}?owner=${encodeURIComponent(owner)}&t=${encodeURIComponent(token)}&theme=${theme}`;
@@ -1057,7 +1126,9 @@ function MatterfactArtifact({
1057
1126
  return /* @__PURE__ */ jsx2(
1058
1127
  "iframe",
1059
1128
  {
1129
+ ref: frame,
1060
1130
  src,
1131
+ onLoad,
1061
1132
  title: `matterfact artifact ${artifactName}`,
1062
1133
  className,
1063
1134
  style: { width: "100%", height: 600, border: 0, ...style },
@@ -1070,10 +1141,10 @@ function diffDocParams(params, sent) {
1070
1141
  const changed = {};
1071
1142
  for (const [key, value] of Object.entries(params)) {
1072
1143
  if (value === void 0) continue;
1073
- const snapshot = JSON.stringify(value);
1074
- if (sent.get(key) === snapshot) continue;
1144
+ const snapshot2 = JSON.stringify(value);
1145
+ if (sent.get(key) === snapshot2) continue;
1075
1146
  changed[key] = value;
1076
- sent.set(key, snapshot);
1147
+ sent.set(key, snapshot2);
1077
1148
  }
1078
1149
  return changed;
1079
1150
  }