@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/index.cjs CHANGED
@@ -224,6 +224,9 @@ function resolveRef(ref) {
224
224
  if (!entry || entry.id !== ref || entry.gen !== generation) return null;
225
225
  return el;
226
226
  }
227
+ function clearRefs() {
228
+ registry.clear();
229
+ }
227
230
  var counter, generation, entries, registry;
228
231
  var init_refs = __esm({
229
232
  "src/dom/refs.ts"() {
@@ -237,6 +240,7 @@ var init_refs = __esm({
237
240
  // src/dom/snapshot.ts
238
241
  var snapshot_exports = {};
239
242
  __export(snapshot_exports, {
243
+ clearRefs: () => clearRefs,
240
244
  resolveRef: () => resolveRef,
241
245
  snapshot: () => snapshot,
242
246
  snapshotRegion: () => snapshotRegion
@@ -478,7 +482,6 @@ var init_snapshot = __esm({
478
482
  var context_exports = {};
479
483
  __export(context_exports, {
480
484
  artifactIdFromPath: () => artifactIdFromPath,
481
- beginAuth: () => beginAuth,
482
485
  callTool: () => callTool,
483
486
  grantsFromIframeSrcs: () => grantsFromIframeSrcs,
484
487
  isPrivate: () => isPrivate,
@@ -486,7 +489,8 @@ __export(context_exports, {
486
489
  redact: () => redact,
487
490
  sendRegion: () => sendRegion,
488
491
  sendSnapshot: () => sendSnapshot,
489
- start: () => start
492
+ start: () => start,
493
+ stop: () => stop
490
494
  });
491
495
  function redact(text) {
492
496
  let out = text;
@@ -656,8 +660,11 @@ function watchNavigation() {
656
660
  publishContext();
657
661
  publishArtifactGrants();
658
662
  };
663
+ navFire = fire;
659
664
  for (const name of ["pushState", "replaceState"]) {
660
665
  const orig = history[name];
666
+ if (name === "pushState") origPushState = orig;
667
+ else origReplaceState = orig;
661
668
  history[name] = function(...args) {
662
669
  const r = orig.apply(this, args);
663
670
  fire();
@@ -678,7 +685,7 @@ async function readFocus() {
678
685
  }
679
686
  const active = document.activeElement;
680
687
  if (active && active !== document.body && !isPrivate(active) && !active.closest("[data-mf-private]")) {
681
- const { snapshot: snapshot2 } = await Promise.resolve().then(() => (init_snapshot(), snapshot_exports));
688
+ const { snapshot: snapshot2 } = await loadSnapshotModule();
682
689
  focus.focused = {
683
690
  label: redact(describeControl(active)),
684
691
  role: active.getAttribute("role") || active.tagName.toLowerCase()
@@ -692,6 +699,11 @@ async function readFocus() {
692
699
  function describeControl(el) {
693
700
  return describe(el);
694
701
  }
702
+ async function loadSnapshotModule() {
703
+ const m = await Promise.resolve().then(() => (init_snapshot(), snapshot_exports));
704
+ snapshotModule = m;
705
+ return m;
706
+ }
695
707
  function scheduleFocus() {
696
708
  if (focusTimer) return;
697
709
  focusTimer = setTimeout(async () => {
@@ -701,7 +713,8 @@ function scheduleFocus() {
701
713
  }, 250);
702
714
  }
703
715
  async function sendSnapshot(emit) {
704
- const { snapshot: snapshot2 } = await Promise.resolve().then(() => (init_snapshot(), snapshot_exports));
716
+ if (!pageContextOn) return;
717
+ const { snapshot: snapshot2 } = await loadSnapshotModule();
705
718
  const { yaml, truncated, visibleRefs } = snapshot2();
706
719
  emit({
707
720
  type: "host.snapshot",
@@ -711,7 +724,8 @@ async function sendSnapshot(emit) {
711
724
  emit({ type: "host.focus", focus: lastFocus });
712
725
  }
713
726
  async function sendRegion(ref, emit) {
714
- const { snapshotRegion: snapshotRegion2, resolveRef: resolveRef2 } = await Promise.resolve().then(() => (init_snapshot(), snapshot_exports));
727
+ if (!pageContextOn) return;
728
+ const { snapshotRegion: snapshotRegion2, resolveRef: resolveRef2 } = await loadSnapshotModule();
715
729
  const el = resolveRef2(ref);
716
730
  if (!el) {
717
731
  emit({
@@ -732,55 +746,73 @@ async function callTool(call, emit) {
732
746
  error: "Host tools are read-only in this version; no action was taken."
733
747
  });
734
748
  }
735
- function beginAuth(config, emit) {
736
- const w = window.open(
737
- `${config.origin}/embed/authorize?k=${encodeURIComponent(
738
- config.publishableKey
739
- )}&o=${encodeURIComponent(location.origin)}`,
740
- "mf_auth",
741
- "width=460,height=640"
742
- );
743
- if (!w) return;
744
- const onMsg = (event) => {
745
- if (event.origin !== config.origin) return;
746
- const d = event.data;
747
- if (d?.type !== "mf:embed:auth") return;
748
- window.removeEventListener("message", onMsg);
749
- emit({ type: "host.auth", token: d.token, expiresAt: d.expiresAt });
750
- };
751
- window.addEventListener("message", onMsg);
749
+ function stop() {
750
+ document.removeEventListener("click", onClick, { capture: true });
751
+ document.removeEventListener("submit", onSubmit, { capture: true });
752
+ document.removeEventListener("change", onChange, { capture: true });
753
+ document.removeEventListener("selectionchange", scheduleFocus);
754
+ document.removeEventListener("focusin", scheduleFocus, { capture: true });
755
+ window.removeEventListener("scroll", scheduleFocus, { capture: true });
756
+ if (navFire) {
757
+ window.removeEventListener("popstate", navFire);
758
+ navFire = null;
759
+ }
760
+ if (origPushState) {
761
+ history.pushState = origPushState;
762
+ origPushState = null;
763
+ }
764
+ if (origReplaceState) {
765
+ history.replaceState = origReplaceState;
766
+ origReplaceState = null;
767
+ }
768
+ if (focusTimer) {
769
+ clearTimeout(focusTimer);
770
+ focusTimer = null;
771
+ }
772
+ send = null;
773
+ activity.length = 0;
774
+ lastFocus = {};
775
+ widgetOrigin = "";
776
+ snapshotModule?.clearRefs();
752
777
  }
753
- function start(emit, origin) {
778
+ function start(emit, origin, pageContext = true) {
779
+ stop();
754
780
  send = emit;
755
781
  widgetOrigin = origin || "";
756
782
  lastUrl = location.pathname;
757
- publishContext();
758
- publishArtifactGrants();
759
- watchNavigation();
760
- document.addEventListener("click", onClick, { capture: true, passive: true });
761
- document.addEventListener("submit", onSubmit, {
762
- capture: true,
763
- passive: true
764
- });
765
- document.addEventListener("change", onChange, {
766
- capture: true,
767
- passive: true
768
- });
769
- document.addEventListener("selectionchange", scheduleFocus, {
770
- passive: true
771
- });
772
- document.addEventListener("focusin", scheduleFocus, {
773
- capture: true,
774
- passive: true
775
- });
776
- window.addEventListener("scroll", scheduleFocus, {
777
- capture: true,
778
- passive: true
779
- });
783
+ pageContextOn = pageContext;
784
+ if (pageContextOn) {
785
+ publishContext();
786
+ publishArtifactGrants();
787
+ watchNavigation();
788
+ document.addEventListener("click", onClick, {
789
+ capture: true,
790
+ passive: true
791
+ });
792
+ document.addEventListener("submit", onSubmit, {
793
+ capture: true,
794
+ passive: true
795
+ });
796
+ document.addEventListener("change", onChange, {
797
+ capture: true,
798
+ passive: true
799
+ });
800
+ document.addEventListener("selectionchange", scheduleFocus, {
801
+ passive: true
802
+ });
803
+ document.addEventListener("focusin", scheduleFocus, {
804
+ capture: true,
805
+ passive: true
806
+ });
807
+ window.addEventListener("scroll", scheduleFocus, {
808
+ capture: true,
809
+ passive: true
810
+ });
811
+ }
780
812
  const theme = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
781
813
  emit({ type: "host.theme", mode: theme });
782
814
  }
783
- var widgetOrigin, MAX_ACTIVITY, send, activitySeq, activity, lastUrl, PII, FORM_CONTROLS, ACTIONABLE, lastFocus, focusTimer, snapshotSeq;
815
+ var widgetOrigin, MAX_ACTIVITY, send, activitySeq, activity, lastUrl, pageContextOn, navFire, origPushState, origReplaceState, snapshotModule, PII, FORM_CONTROLS, ACTIONABLE, lastFocus, focusTimer, snapshotSeq;
784
816
  var init_context = __esm({
785
817
  "src/context.ts"() {
786
818
  widgetOrigin = "";
@@ -789,6 +821,11 @@ var init_context = __esm({
789
821
  activitySeq = 0;
790
822
  activity = [];
791
823
  lastUrl = "";
824
+ pageContextOn = true;
825
+ navFire = null;
826
+ origPushState = null;
827
+ origReplaceState = null;
828
+ snapshotModule = null;
792
829
  PII = [
793
830
  /\b[\w.+-]+@[\w-]+\.[\w.]{2,}\b/g,
794
831
  // email
@@ -851,12 +888,16 @@ function readConfig() {
851
888
  `[matterfact] data-container="${sel}" matched nothing; falling back to the corner`
852
889
  );
853
890
  }
891
+ const pageContextAttr = el?.dataset.pageContext?.toLowerCase();
854
892
  return {
855
893
  publishableKey,
856
894
  origin: el?.dataset.origin || DEFAULT_ORIGIN,
857
895
  theme: el?.dataset.theme || "auto",
858
896
  surface: el?.dataset.surface || "",
859
- container
897
+ container,
898
+ pageContext: pageContextAttr !== "off" && pageContextAttr !== "false" && pageContextAttr !== "0"
899
+ // No `data-dev` — see the `dev` field's doc comment: the URL trigger is the
900
+ // point for the script-tag path, so there is deliberately no script-tag knob here.
860
901
  };
861
902
  }
862
903
  var POS_KEY = "mf.embed.pos";
@@ -1022,7 +1063,9 @@ var EmbedHost = class {
1022
1063
  iframe.setAttribute("allow", "microphone");
1023
1064
  iframe.src = `${this.config.origin}/embed/chat?k=${encodeURIComponent(
1024
1065
  this.config.publishableKey
1025
- )}&o=${encodeURIComponent(location.origin)}` + (this.config.surface ? `&s=${encodeURIComponent(this.config.surface)}` : "") + (devRequested() ? "&dev=1" : "") + (this.inline ? "&inline=1" : "");
1066
+ )}&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
1067
+ // programmatic `dev` (the React prop). See both doc comments above.
1068
+ (devRequested() || this.config.dev ? "&dev=1" : "") + (this.inline ? "&inline=1" : "");
1026
1069
  this.iframe = iframe;
1027
1070
  this.shadow.appendChild(iframe);
1028
1071
  window.addEventListener("message", this.onMessage);
@@ -1092,27 +1135,26 @@ var EmbedHost = class {
1092
1135
  * window.matterfact = { getEmbedAuthToken: () => getIdToken(user) };
1093
1136
  *
1094
1137
  * Read fresh at call time (not at readConfig), so a global set after the loader booted
1095
- * — e.g. once the host's auth is ready — is still picked up. Otherwise fall back to the
1096
- * hosted-login popup. `expiresAt: 0`: the widget doesn't cache it, it exchanges the
1097
- * token for a rotating embed session anyway.
1138
+ * — e.g. once the host's auth is ready — is still picked up. `expiresAt: 0`: the widget
1139
+ * doesn't cache it, it exchanges the token for a rotating embed session anyway.
1140
+ *
1141
+ * No provider ⇒ this does nothing. The loader never opens a sign-in popup — the widget
1142
+ * owns interactive sign-in (inline Firebase/email, and Microsoft's own MSAL login popup
1143
+ * for Entra, then the PKCE token exchange), so the loader must not also fire its stale
1144
+ * `/embed/authorize?k=&o=` popup, which the deployed authorize page no longer accepts.
1098
1145
  */
1099
1146
  async provideAuth() {
1100
1147
  const provider = this.config.authTokenProvider ?? globalThis.matterfact?.getEmbedAuthToken;
1101
- if (provider) {
1102
- try {
1103
- const token = await provider();
1104
- if (token) {
1105
- this.send({ type: "host.auth", token, expiresAt: 0 });
1106
- return;
1107
- }
1108
- } catch {
1109
- }
1148
+ if (!provider) return;
1149
+ try {
1150
+ const token = await provider();
1151
+ if (token) this.send({ type: "host.auth", token, expiresAt: 0 });
1152
+ } catch {
1110
1153
  }
1111
- void this.loadContext().then((m) => m.beginAuth(this.config, this.send));
1112
1154
  }
1113
1155
  loadContext() {
1114
1156
  this.context ?? (this.context = Promise.resolve().then(() => (init_context(), context_exports)).then((m) => {
1115
- m.start(this.send, this.config.origin);
1157
+ m.start(this.send, this.config.origin, this.config.pageContext);
1116
1158
  if (this.config.theme !== "auto") {
1117
1159
  this.send({ type: "host.theme", mode: this.config.theme });
1118
1160
  }
@@ -1135,6 +1177,7 @@ var EmbedHost = class {
1135
1177
  this.iframe = null;
1136
1178
  this.shadow = null;
1137
1179
  this.ready = false;
1180
+ void this.context?.then((m) => m.stop());
1138
1181
  }
1139
1182
  };
1140
1183
  function mount(config) {