@matterfact/embed 0.3.0 → 0.5.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.
Files changed (56) hide show
  1. package/README.md +45 -5
  2. package/dist/{chunk-33LPVL2R.js → chunk-GZZYR6YF.js} +3 -3
  3. package/dist/{chunk-33LPVL2R.js.map → chunk-GZZYR6YF.js.map} +3 -3
  4. package/dist/chunk-JN3ZMONO.js +2 -0
  5. package/dist/chunk-JN3ZMONO.js.map +7 -0
  6. package/dist/{chunk-CHPADW72.js → chunk-JSH3BRYP.js} +80 -47
  7. package/dist/chunk-JSH3BRYP.js.map +1 -0
  8. package/dist/{chunk-SQJX27LT.js → chunk-NZIN3LK7.js} +79 -46
  9. package/dist/chunk-NZIN3LK7.js.map +1 -0
  10. package/dist/chunk-Z53V2P4B.js +2 -0
  11. package/dist/context-AQY2LJWK.js +3 -0
  12. package/dist/{context-BFNFFHC2.js.map → context-AQY2LJWK.js.map} +1 -1
  13. package/dist/{context-MORDASRH.js → context-UBTZEEWS.js} +6 -6
  14. package/dist/embed.js +1 -1
  15. package/dist/embed.js.map +3 -3
  16. package/dist/index.cjs +106 -63
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +23 -3
  19. package/dist/index.d.ts +23 -3
  20. package/dist/index.js +23 -17
  21. package/dist/index.js.map +1 -1
  22. package/dist/react.cjs +109 -66
  23. package/dist/react.cjs.map +1 -1
  24. package/dist/react.d.cts +14 -1
  25. package/dist/react.d.ts +14 -1
  26. package/dist/react.js +25 -19
  27. package/dist/react.js.map +1 -1
  28. package/dist/{snapshot-IWVXLE4G.js → snapshot-HYMRZTZC.js} +6 -2
  29. package/dist/snapshot-HYMRZTZC.js.map +1 -0
  30. package/dist/{snapshot-QBV7DTJP.js → snapshot-XFCV2MYR.js} +7 -4
  31. package/dist/snapshot-XFCV2MYR.js.map +1 -0
  32. package/examples/README.md +46 -0
  33. package/examples/demo-host/index.html +163 -0
  34. package/examples/demo-host/serve.mjs +62 -0
  35. package/examples/embed-demo/.env.example +24 -0
  36. package/examples/embed-demo/README.md +72 -0
  37. package/examples/embed-demo/index.html +12 -0
  38. package/examples/embed-demo/package.json +25 -0
  39. package/examples/embed-demo/src/App.tsx +189 -0
  40. package/examples/embed-demo/src/config.ts +43 -0
  41. package/examples/embed-demo/src/main.tsx +10 -0
  42. package/examples/embed-demo/src/styles.css +354 -0
  43. package/examples/embed-demo/tsconfig.json +16 -0
  44. package/examples/embed-demo/vite.config.ts +11 -0
  45. package/examples/host-panel.html +237 -0
  46. package/package.json +3 -2
  47. package/dist/chunk-CHPADW72.js.map +0 -1
  48. package/dist/chunk-F7DLOR7F.js +0 -2
  49. package/dist/chunk-F7DLOR7F.js.map +0 -7
  50. package/dist/chunk-FBIJZJGL.js +0 -2
  51. package/dist/chunk-SQJX27LT.js.map +0 -1
  52. package/dist/context-BFNFFHC2.js +0 -3
  53. package/dist/snapshot-IWVXLE4G.js.map +0 -1
  54. package/dist/snapshot-QBV7DTJP.js.map +0 -1
  55. /package/dist/{chunk-FBIJZJGL.js.map → chunk-Z53V2P4B.js.map} +0 -0
  56. /package/dist/{context-MORDASRH.js.map → context-UBTZEEWS.js.map} +0 -0
package/dist/react.cjs CHANGED
@@ -238,6 +238,9 @@ function resolveRef(ref) {
238
238
  if (!entry || entry.id !== ref || entry.gen !== generation) return null;
239
239
  return el;
240
240
  }
241
+ function clearRefs() {
242
+ registry.clear();
243
+ }
241
244
  var counter, generation, entries, registry;
242
245
  var init_refs = __esm({
243
246
  "src/dom/refs.ts"() {
@@ -252,6 +255,7 @@ var init_refs = __esm({
252
255
  // src/dom/snapshot.ts
253
256
  var snapshot_exports = {};
254
257
  __export(snapshot_exports, {
258
+ clearRefs: () => clearRefs,
255
259
  resolveRef: () => resolveRef,
256
260
  snapshot: () => snapshot,
257
261
  snapshotRegion: () => snapshotRegion
@@ -495,7 +499,6 @@ var init_snapshot = __esm({
495
499
  var context_exports = {};
496
500
  __export(context_exports, {
497
501
  artifactIdFromPath: () => artifactIdFromPath,
498
- beginAuth: () => beginAuth,
499
502
  callTool: () => callTool,
500
503
  grantsFromIframeSrcs: () => grantsFromIframeSrcs,
501
504
  isPrivate: () => isPrivate,
@@ -503,7 +506,8 @@ __export(context_exports, {
503
506
  redact: () => redact,
504
507
  sendRegion: () => sendRegion,
505
508
  sendSnapshot: () => sendSnapshot,
506
- start: () => start
509
+ start: () => start,
510
+ stop: () => stop
507
511
  });
508
512
  function redact(text) {
509
513
  let out = text;
@@ -673,8 +677,11 @@ function watchNavigation() {
673
677
  publishContext();
674
678
  publishArtifactGrants();
675
679
  };
680
+ navFire = fire;
676
681
  for (const name of ["pushState", "replaceState"]) {
677
682
  const orig = history[name];
683
+ if (name === "pushState") origPushState = orig;
684
+ else origReplaceState = orig;
678
685
  history[name] = function(...args) {
679
686
  const r = orig.apply(this, args);
680
687
  fire();
@@ -695,7 +702,7 @@ async function readFocus() {
695
702
  }
696
703
  const active = document.activeElement;
697
704
  if (active && active !== document.body && !isPrivate(active) && !active.closest("[data-mf-private]")) {
698
- const { snapshot: snapshot2 } = await Promise.resolve().then(() => (init_snapshot(), snapshot_exports));
705
+ const { snapshot: snapshot2 } = await loadSnapshotModule();
699
706
  void snapshot2;
700
707
  focus.focused = {
701
708
  label: redact(describeControl(active)),
@@ -710,6 +717,11 @@ async function readFocus() {
710
717
  function describeControl(el) {
711
718
  return describe(el);
712
719
  }
720
+ async function loadSnapshotModule() {
721
+ const m = await Promise.resolve().then(() => (init_snapshot(), snapshot_exports));
722
+ snapshotModule = m;
723
+ return m;
724
+ }
713
725
  function scheduleFocus() {
714
726
  if (focusTimer) return;
715
727
  focusTimer = setTimeout(async () => {
@@ -719,7 +731,8 @@ function scheduleFocus() {
719
731
  }, 250);
720
732
  }
721
733
  async function sendSnapshot(emit) {
722
- const { snapshot: snapshot2 } = await Promise.resolve().then(() => (init_snapshot(), snapshot_exports));
734
+ if (!pageContextOn) return;
735
+ const { snapshot: snapshot2 } = await loadSnapshotModule();
723
736
  const { yaml, truncated, visibleRefs } = snapshot2();
724
737
  emit({
725
738
  type: "host.snapshot",
@@ -729,7 +742,8 @@ async function sendSnapshot(emit) {
729
742
  emit({ type: "host.focus", focus: lastFocus });
730
743
  }
731
744
  async function sendRegion(ref, emit) {
732
- const { snapshotRegion: snapshotRegion2, resolveRef: resolveRef2 } = await Promise.resolve().then(() => (init_snapshot(), snapshot_exports));
745
+ if (!pageContextOn) return;
746
+ const { snapshotRegion: snapshotRegion2, resolveRef: resolveRef2 } = await loadSnapshotModule();
733
747
  const el = resolveRef2(ref);
734
748
  if (!el) {
735
749
  emit({
@@ -750,55 +764,73 @@ async function callTool(call, emit) {
750
764
  error: "Host tools are read-only in this version; no action was taken."
751
765
  });
752
766
  }
753
- function beginAuth(config, emit) {
754
- const w = window.open(
755
- `${config.origin}/embed/authorize?k=${encodeURIComponent(
756
- config.publishableKey
757
- )}&o=${encodeURIComponent(location.origin)}`,
758
- "mf_auth",
759
- "width=460,height=640"
760
- );
761
- if (!w) return;
762
- const onMsg = (event) => {
763
- if (event.origin !== config.origin) return;
764
- const d = event.data;
765
- if (d?.type !== "mf:embed:auth") return;
766
- window.removeEventListener("message", onMsg);
767
- emit({ type: "host.auth", token: d.token, expiresAt: d.expiresAt });
768
- };
769
- window.addEventListener("message", onMsg);
770
- }
771
- function start(emit, origin) {
767
+ function stop() {
768
+ document.removeEventListener("click", onClick, { capture: true });
769
+ document.removeEventListener("submit", onSubmit, { capture: true });
770
+ document.removeEventListener("change", onChange, { capture: true });
771
+ document.removeEventListener("selectionchange", scheduleFocus);
772
+ document.removeEventListener("focusin", scheduleFocus, { capture: true });
773
+ window.removeEventListener("scroll", scheduleFocus, { capture: true });
774
+ if (navFire) {
775
+ window.removeEventListener("popstate", navFire);
776
+ navFire = null;
777
+ }
778
+ if (origPushState) {
779
+ history.pushState = origPushState;
780
+ origPushState = null;
781
+ }
782
+ if (origReplaceState) {
783
+ history.replaceState = origReplaceState;
784
+ origReplaceState = null;
785
+ }
786
+ if (focusTimer) {
787
+ clearTimeout(focusTimer);
788
+ focusTimer = null;
789
+ }
790
+ send = null;
791
+ activity.length = 0;
792
+ lastFocus = {};
793
+ widgetOrigin = "";
794
+ snapshotModule?.clearRefs();
795
+ }
796
+ function start(emit, origin, pageContext = true) {
797
+ stop();
772
798
  send = emit;
773
799
  widgetOrigin = origin || "";
774
800
  lastUrl = location.pathname;
775
- publishContext();
776
- publishArtifactGrants();
777
- watchNavigation();
778
- document.addEventListener("click", onClick, { capture: true, passive: true });
779
- document.addEventListener("submit", onSubmit, {
780
- capture: true,
781
- passive: true
782
- });
783
- document.addEventListener("change", onChange, {
784
- capture: true,
785
- passive: true
786
- });
787
- document.addEventListener("selectionchange", scheduleFocus, {
788
- passive: true
789
- });
790
- document.addEventListener("focusin", scheduleFocus, {
791
- capture: true,
792
- passive: true
793
- });
794
- window.addEventListener("scroll", scheduleFocus, {
795
- capture: true,
796
- passive: true
797
- });
801
+ pageContextOn = pageContext;
802
+ if (pageContextOn) {
803
+ publishContext();
804
+ publishArtifactGrants();
805
+ watchNavigation();
806
+ document.addEventListener("click", onClick, {
807
+ capture: true,
808
+ passive: true
809
+ });
810
+ document.addEventListener("submit", onSubmit, {
811
+ capture: true,
812
+ passive: true
813
+ });
814
+ document.addEventListener("change", onChange, {
815
+ capture: true,
816
+ passive: true
817
+ });
818
+ document.addEventListener("selectionchange", scheduleFocus, {
819
+ passive: true
820
+ });
821
+ document.addEventListener("focusin", scheduleFocus, {
822
+ capture: true,
823
+ passive: true
824
+ });
825
+ window.addEventListener("scroll", scheduleFocus, {
826
+ capture: true,
827
+ passive: true
828
+ });
829
+ }
798
830
  const theme = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
799
831
  emit({ type: "host.theme", mode: theme });
800
832
  }
801
- var widgetOrigin, MAX_ACTIVITY, send, activitySeq, activity, lastUrl, PII, FORM_CONTROLS, ACTIONABLE, lastFocus, focusTimer, snapshotSeq;
833
+ var widgetOrigin, MAX_ACTIVITY, send, activitySeq, activity, lastUrl, pageContextOn, navFire, origPushState, origReplaceState, snapshotModule, PII, FORM_CONTROLS, ACTIONABLE, lastFocus, focusTimer, snapshotSeq;
802
834
  var init_context = __esm({
803
835
  "src/context.ts"() {
804
836
  "use strict";
@@ -808,6 +840,11 @@ var init_context = __esm({
808
840
  activitySeq = 0;
809
841
  activity = [];
810
842
  lastUrl = "";
843
+ pageContextOn = true;
844
+ navFire = null;
845
+ origPushState = null;
846
+ origReplaceState = null;
847
+ snapshotModule = null;
811
848
  PII = [
812
849
  /\b[\w.+-]+@[\w-]+\.[\w.]{2,}\b/g,
813
850
  // email
@@ -1027,7 +1064,9 @@ var EmbedHost = class {
1027
1064
  iframe.setAttribute("allow", "microphone");
1028
1065
  iframe.src = `${this.config.origin}/embed/chat?k=${encodeURIComponent(
1029
1066
  this.config.publishableKey
1030
- )}&o=${encodeURIComponent(location.origin)}` + (this.config.surface ? `&s=${encodeURIComponent(this.config.surface)}` : "") + (devRequested() ? "&dev=1" : "") + (this.inline ? "&inline=1" : "");
1067
+ )}&o=${encodeURIComponent(location.origin)}` + (this.config.surface ? `&s=${encodeURIComponent(this.config.surface)}` : "") + // Either trigger works: the URL param (no redeploy needed) OR the config's
1068
+ // programmatic `dev` (the React prop). See both doc comments above.
1069
+ (devRequested() || this.config.dev ? "&dev=1" : "") + (this.inline ? "&inline=1" : "");
1031
1070
  this.iframe = iframe;
1032
1071
  this.shadow.appendChild(iframe);
1033
1072
  window.addEventListener("message", this.onMessage);
@@ -1097,27 +1136,26 @@ var EmbedHost = class {
1097
1136
  * window.matterfact = { getEmbedAuthToken: () => getIdToken(user) };
1098
1137
  *
1099
1138
  * Read fresh at call time (not at readConfig), so a global set after the loader booted
1100
- * — e.g. once the host's auth is ready — is still picked up. Otherwise fall back to the
1101
- * hosted-login popup. `expiresAt: 0`: the widget doesn't cache it, it exchanges the
1102
- * token for a rotating embed session anyway.
1139
+ * — e.g. once the host's auth is ready — is still picked up. `expiresAt: 0`: the widget
1140
+ * doesn't cache it, it exchanges the token for a rotating embed session anyway.
1141
+ *
1142
+ * No provider ⇒ this does nothing. The loader never opens a sign-in popup — the widget
1143
+ * owns interactive sign-in (inline Firebase/email, and Microsoft's own MSAL login popup
1144
+ * for Entra, then the PKCE token exchange), so the loader must not also fire its stale
1145
+ * `/embed/authorize?k=&o=` popup, which the deployed authorize page no longer accepts.
1103
1146
  */
1104
1147
  async provideAuth() {
1105
1148
  const provider = this.config.authTokenProvider ?? globalThis.matterfact?.getEmbedAuthToken;
1106
- if (provider) {
1107
- try {
1108
- const token = await provider();
1109
- if (token) {
1110
- this.send({ type: "host.auth", token, expiresAt: 0 });
1111
- return;
1112
- }
1113
- } catch {
1114
- }
1149
+ if (!provider) return;
1150
+ try {
1151
+ const token = await provider();
1152
+ if (token) this.send({ type: "host.auth", token, expiresAt: 0 });
1153
+ } catch {
1115
1154
  }
1116
- void this.loadContext().then((m) => m.beginAuth(this.config, this.send));
1117
1155
  }
1118
1156
  loadContext() {
1119
1157
  this.context ?? (this.context = Promise.resolve().then(() => (init_context(), context_exports)).then((m) => {
1120
- m.start(this.send, this.config.origin);
1158
+ m.start(this.send, this.config.origin, this.config.pageContext);
1121
1159
  if (this.config.theme !== "auto") {
1122
1160
  this.send({ type: "host.theme", mode: this.config.theme });
1123
1161
  }
@@ -1140,6 +1178,7 @@ var EmbedHost = class {
1140
1178
  this.iframe = null;
1141
1179
  this.shadow = null;
1142
1180
  this.ready = false;
1181
+ void this.context?.then((m) => m.stop());
1143
1182
  }
1144
1183
  };
1145
1184
  function mount(config) {
@@ -1159,7 +1198,9 @@ function MatterfactAgent({
1159
1198
  getAuthToken,
1160
1199
  inline = false,
1161
1200
  className,
1162
- style
1201
+ style,
1202
+ pageContext,
1203
+ dev
1163
1204
  }) {
1164
1205
  const authRef = (0, import_react.useRef)(getAuthToken);
1165
1206
  authRef.current = getAuthToken;
@@ -1175,7 +1216,9 @@ function MatterfactAgent({
1175
1216
  // Always present; a null return (no getAuthToken supplied) makes the core fall
1176
1217
  // back to the popup/inline sign-in, so this is safe either way.
1177
1218
  authTokenProvider: async () => await authRef.current?.() ?? null,
1178
- container: inline ? slot.current : null
1219
+ container: inline ? slot.current : null,
1220
+ pageContext,
1221
+ dev
1179
1222
  };
1180
1223
  let host = null;
1181
1224
  try {
@@ -1184,7 +1227,7 @@ function MatterfactAgent({
1184
1227
  console.error("[matterfact] failed to mount the embed widget", e);
1185
1228
  }
1186
1229
  return () => host?.destroy();
1187
- }, [publishableKey, widgetOrigin2, surface, theme, inline]);
1230
+ }, [publishableKey, widgetOrigin2, surface, theme, inline, pageContext, dev]);
1188
1231
  if (!inline) return null;
1189
1232
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1190
1233
  "div",