@matterfact/embed 0.13.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.js CHANGED
@@ -2,7 +2,7 @@
2
2
  "use client";
3
3
 
4
4
  // src/react.tsx
5
- import { useEffect, 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;
@@ -806,9 +806,63 @@ function mount(config) {
806
806
  return host;
807
807
  }
808
808
 
809
- // src/react.tsx
809
+ // src/react-context.tsx
810
+ import { createContext, useContext, useEffect, useMemo } from "react";
810
811
  import { jsx } from "react/jsx-runtime";
811
812
  var DEFAULT_ORIGIN = "https://app.matterfact.com";
813
+ var MatterfactAuthContext = createContext(null);
814
+ function useMatterfactConfig() {
815
+ return useContext(MatterfactAuthContext);
816
+ }
817
+ function MatterfactAuthProvider({
818
+ publishableKey,
819
+ widgetOrigin,
820
+ getAuthToken,
821
+ theme,
822
+ onEvent,
823
+ children
824
+ }) {
825
+ const resolvedWidgetOrigin = widgetOrigin || DEFAULT_ORIGIN;
826
+ const value = useMemo(
827
+ () => ({
828
+ publishableKey,
829
+ widgetOrigin: resolvedWidgetOrigin,
830
+ getAuthToken,
831
+ theme: theme || "auto",
832
+ onEvent
833
+ }),
834
+ [publishableKey, resolvedWidgetOrigin, getAuthToken, theme, onEvent]
835
+ );
836
+ useEffect(() => {
837
+ if (typeof window === "undefined" || !getAuthToken) return;
838
+ let inflight = null;
839
+ const onMessage = (e) => {
840
+ if (e.origin !== resolvedWidgetOrigin) return;
841
+ if (!e.data || e.data.type !== "mf.hostAuth.request") return;
842
+ const source = e.source;
843
+ if (!source) return;
844
+ try {
845
+ inflight = inflight ?? Promise.resolve(getAuthToken()).catch(() => null);
846
+ } catch {
847
+ return;
848
+ }
849
+ const p = inflight;
850
+ void p.then((token) => {
851
+ if (p === inflight) inflight = null;
852
+ if (typeof token === "string" && token) {
853
+ source.postMessage({ type: "mf.hostAuth.grant", token }, resolvedWidgetOrigin);
854
+ }
855
+ });
856
+ };
857
+ window.addEventListener("message", onMessage);
858
+ return () => window.removeEventListener("message", onMessage);
859
+ }, [getAuthToken, resolvedWidgetOrigin]);
860
+ return /* @__PURE__ */ jsx(MatterfactAuthContext.Provider, { value, children });
861
+ }
862
+
863
+ // src/react.tsx
864
+ import { jsx as jsx2 } from "react/jsx-runtime";
865
+ var DEFAULT_ORIGIN2 = "https://app.matterfact.com";
812
866
  function writeActionsGlobal(actions) {
813
867
  if (typeof window === "undefined") return;
814
868
  const w = window;
@@ -852,11 +906,11 @@ function toolDescriptorKey(artifacts, resolve, tools) {
852
906
  });
853
907
  }
854
908
  function MatterfactAgent({
855
- publishableKey,
856
- widgetOrigin,
909
+ publishableKey: publishableKeyProp,
910
+ widgetOrigin: widgetOriginProp,
857
911
  surface = "",
858
- theme = "auto",
859
- getAuthToken,
912
+ theme: themeProp,
913
+ getAuthToken: getAuthTokenProp,
860
914
  getPageContext,
861
915
  inline = false,
862
916
  className,
@@ -871,6 +925,16 @@ function MatterfactAgent({
871
925
  onToolEvent,
872
926
  onEvent
873
927
  }) {
928
+ const ctx = useMatterfactConfig();
929
+ const publishableKey = publishableKeyProp ?? ctx?.publishableKey;
930
+ if (!publishableKey) {
931
+ throw new Error(
932
+ "MatterfactAgent requires publishableKey (as a prop or via MatterfactAuthProvider)"
933
+ );
934
+ }
935
+ const widgetOrigin = widgetOriginProp ?? ctx?.widgetOrigin ?? DEFAULT_ORIGIN2;
936
+ const getAuthToken = getAuthTokenProp ?? ctx?.getAuthToken;
937
+ const theme = themeProp ?? ctx?.theme ?? "auto";
874
938
  const authRef = useRef(getAuthToken);
875
939
  authRef.current = getAuthToken;
876
940
  const pageContextRef = useRef(getPageContext);
@@ -880,21 +944,22 @@ function MatterfactAgent({
880
944
  const actionsKey = JSON.stringify(actions ?? null);
881
945
  const slot = useRef(null);
882
946
  const sitemapKey = JSON.stringify(sitemap ?? null);
883
- useEffect(() => {
947
+ useEffect2(() => {
884
948
  writeSitemapGlobal(sitemap);
885
949
  }, [sitemapKey]);
886
- useEffect(() => {
950
+ useEffect2(() => {
887
951
  writeToolGlobals(artifacts, resolve, tools, onToolEvent, onEvent);
888
952
  });
889
953
  const toolsKey = toolDescriptorKey(artifacts, resolve, tools);
890
- useEffect(() => {
954
+ useEffect2(() => {
891
955
  if (typeof window === "undefined") return;
892
956
  if (inline && !slot.current) return;
893
957
  writeActionsGlobal(actionsRef.current);
894
958
  writeToolGlobals(artifacts, resolve, tools, onToolEvent, onEvent);
895
959
  const config = {
896
960
  publishableKey,
897
- origin: widgetOrigin || DEFAULT_ORIGIN,
961
+ // Already fully resolved above (prop ?? context ?? DEFAULT_ORIGIN).
962
+ origin: widgetOrigin,
898
963
  theme,
899
964
  surface,
900
965
  // Always present; a null return (no getAuthToken supplied) makes the core fall
@@ -936,7 +1001,7 @@ function MatterfactAgent({
936
1001
  // eslint-disable-next-line react-hooks/exhaustive-deps
937
1002
  ]);
938
1003
  if (!inline) return null;
939
- return /* @__PURE__ */ jsx(
1004
+ return /* @__PURE__ */ jsx2(
940
1005
  "div",
941
1006
  {
942
1007
  ref: slot,
@@ -946,30 +1011,210 @@ function MatterfactAgent({
946
1011
  );
947
1012
  }
948
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
+ }
949
1021
  function MatterfactArtifact({
1022
+ name: nameProp,
950
1023
  slug,
951
1024
  owner,
952
1025
  token,
953
- widgetOrigin,
954
- theme = "auto",
1026
+ widgetOrigin: widgetOriginProp,
1027
+ theme: themeProp,
955
1028
  className,
956
- style
1029
+ style,
1030
+ params,
1031
+ onParamsChange
957
1032
  }) {
958
- const origin = widgetOrigin || DEFAULT_ARTIFACT_ORIGIN;
959
- const src = `${origin}/embed/artifacts/${encodeURIComponent(slug)}?owner=${encodeURIComponent(owner)}&t=${encodeURIComponent(token)}&theme=${theme}`;
960
- return /* @__PURE__ */ jsx(
1033
+ const ctx = useMatterfactConfig();
1034
+ const artifactName = nameProp ?? slug;
1035
+ if (!artifactName) {
1036
+ throw new Error(
1037
+ "MatterfactArtifact requires name (or its deprecated alias, slug)"
1038
+ );
1039
+ }
1040
+ const origin = widgetOriginProp ?? ctx?.widgetOrigin ?? DEFAULT_ARTIFACT_ORIGIN;
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]);
1102
+ let src;
1103
+ if (token) {
1104
+ src = `${origin}/embed/artifacts/${encodeURIComponent(artifactName)}?owner=${encodeURIComponent(owner)}&t=${encodeURIComponent(token)}&theme=${theme}`;
1105
+ } else if (ctx) {
1106
+ const hostOrigin = typeof window === "undefined" ? "" : window.location.origin;
1107
+ src = `${origin}/embed/artifacts/${encodeURIComponent(artifactName)}?owner=${encodeURIComponent(owner)}&k=${encodeURIComponent(ctx.publishableKey)}&o=${encodeURIComponent(hostOrigin)}&theme=${theme}`;
1108
+ } else {
1109
+ return /* @__PURE__ */ jsx2(
1110
+ "div",
1111
+ {
1112
+ className,
1113
+ style: {
1114
+ width: "100%",
1115
+ height: 600,
1116
+ display: "flex",
1117
+ alignItems: "center",
1118
+ justifyContent: "center",
1119
+ textAlign: "center",
1120
+ ...style
1121
+ },
1122
+ children: /* @__PURE__ */ jsx2("p", { children: "This embedded artifact link is incomplete." })
1123
+ }
1124
+ );
1125
+ }
1126
+ return /* @__PURE__ */ jsx2(
961
1127
  "iframe",
962
1128
  {
1129
+ ref: frame,
963
1130
  src,
964
- title: `matterfact artifact ${slug}`,
1131
+ onLoad,
1132
+ title: `matterfact artifact ${artifactName}`,
965
1133
  className,
966
1134
  style: { width: "100%", height: 600, border: 0, ...style },
967
1135
  sandbox: "allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads"
968
1136
  }
969
1137
  );
970
1138
  }
1139
+ var DEFAULT_DOC_ORIGIN = "https://app.matterfact.com";
1140
+ function diffDocParams(params, sent) {
1141
+ const changed = {};
1142
+ for (const [key, value] of Object.entries(params)) {
1143
+ if (value === void 0) continue;
1144
+ const snapshot2 = JSON.stringify(value);
1145
+ if (sent.get(key) === snapshot2) continue;
1146
+ changed[key] = value;
1147
+ sent.set(key, snapshot2);
1148
+ }
1149
+ return changed;
1150
+ }
1151
+ function MatterfactDoc({
1152
+ docKey,
1153
+ ticker = "",
1154
+ version = "latest",
1155
+ theme: themeProp,
1156
+ params,
1157
+ className,
1158
+ style
1159
+ }) {
1160
+ const ctx = useMatterfactConfig();
1161
+ const iframeRef = useRef(null);
1162
+ const sentParamsRef = useRef(/* @__PURE__ */ new Map());
1163
+ const origin = ctx?.widgetOrigin ?? DEFAULT_DOC_ORIGIN;
1164
+ const paramsKey = JSON.stringify(params ?? null);
1165
+ const pushParams = (force) => {
1166
+ if (!params) return;
1167
+ const win = iframeRef.current?.contentWindow;
1168
+ if (!win) return;
1169
+ if (force) sentParamsRef.current.clear();
1170
+ const changed = diffDocParams(params, sentParamsRef.current);
1171
+ if (Object.keys(changed).length === 0) return;
1172
+ win.postMessage({ type: "host.docParams", params: changed }, origin);
1173
+ };
1174
+ useEffect2(() => {
1175
+ pushParams(false);
1176
+ }, [paramsKey]);
1177
+ if (!docKey) {
1178
+ throw new Error("MatterfactDoc requires docKey");
1179
+ }
1180
+ if (!ctx || !ctx.publishableKey) {
1181
+ return /* @__PURE__ */ jsx2(
1182
+ "div",
1183
+ {
1184
+ className,
1185
+ style: {
1186
+ width: "100%",
1187
+ height: 600,
1188
+ display: "flex",
1189
+ alignItems: "center",
1190
+ justifyContent: "center",
1191
+ textAlign: "center",
1192
+ ...style
1193
+ },
1194
+ children: /* @__PURE__ */ jsx2("p", { children: "This embedded document link is incomplete." })
1195
+ }
1196
+ );
1197
+ }
1198
+ const theme = themeProp ?? ctx.theme ?? "auto";
1199
+ const hostOrigin = typeof window === "undefined" ? "" : window.location.origin;
1200
+ const src = `${origin}/embed/document?k=${encodeURIComponent(ctx.publishableKey)}&o=${encodeURIComponent(hostOrigin)}&key=${encodeURIComponent(docKey)}&ticker=${encodeURIComponent(ticker)}&version=${encodeURIComponent(version)}&theme=${theme}`;
1201
+ return /* @__PURE__ */ jsx2(
1202
+ "iframe",
1203
+ {
1204
+ ref: iframeRef,
1205
+ src,
1206
+ title: `matterfact document ${docKey}`,
1207
+ className,
1208
+ style: { width: "100%", height: 600, border: 0, ...style },
1209
+ sandbox: "allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads",
1210
+ onLoad: () => pushParams(true)
1211
+ }
1212
+ );
1213
+ }
971
1214
  export {
972
1215
  MatterfactAgent,
973
- MatterfactArtifact
1216
+ MatterfactArtifact,
1217
+ MatterfactAuthProvider,
1218
+ MatterfactDoc
974
1219
  };
975
1220
  //# sourceMappingURL=react.js.map