@matterfact/embed 0.2.2 → 0.4.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 (42) hide show
  1. package/README.md +48 -0
  2. package/dist/{chunk-6VW4CCNF.js → chunk-523BCS2Z.js} +3 -3
  3. package/dist/{chunk-6VW4CCNF.js.map → chunk-523BCS2Z.js.map} +3 -3
  4. package/dist/{chunk-6N5OYQLF.js → chunk-SBNMM3ZV.js} +125 -31
  5. package/dist/chunk-SBNMM3ZV.js.map +1 -0
  6. package/dist/chunk-URIBHOOQ.js +2 -0
  7. package/dist/chunk-XLQ36ZMV.js +2 -0
  8. package/dist/chunk-XLQ36ZMV.js.map +7 -0
  9. package/dist/{chunk-VMYCQB4O.js → chunk-Z4WT3TIZ.js} +124 -33
  10. package/dist/chunk-Z4WT3TIZ.js.map +1 -0
  11. package/dist/{context-CWMQYYFI.js → context-432WQST4.js} +10 -4
  12. package/dist/context-KZAUOIR6.js +3 -0
  13. package/dist/{context-23EUTK33.js.map → context-KZAUOIR6.js.map} +1 -1
  14. package/dist/embed.js +1 -1
  15. package/dist/embed.js.map +3 -3
  16. package/dist/index.cjs +140 -35
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +27 -1
  19. package/dist/index.d.ts +27 -1
  20. package/dist/index.js +11 -4
  21. package/dist/index.js.map +1 -1
  22. package/dist/react.cjs +169 -39
  23. package/dist/react.cjs.map +1 -1
  24. package/dist/react.d.cts +34 -2
  25. package/dist/react.d.ts +34 -2
  26. package/dist/react.js +38 -7
  27. package/dist/react.js.map +1 -1
  28. package/dist/{snapshot-KN2KJ2PM.js → snapshot-Q3CALMHD.js} +7 -4
  29. package/dist/snapshot-Q3CALMHD.js.map +1 -0
  30. package/dist/{snapshot-Z7YUSB27.js → snapshot-SOMSG2OF.js} +6 -2
  31. package/dist/snapshot-SOMSG2OF.js.map +1 -0
  32. package/package.json +2 -2
  33. package/dist/chunk-6N5OYQLF.js.map +0 -1
  34. package/dist/chunk-MWRVWYWX.js +0 -2
  35. package/dist/chunk-MWRVWYWX.js.map +0 -7
  36. package/dist/chunk-U4IRZU6E.js +0 -2
  37. package/dist/chunk-VMYCQB4O.js.map +0 -1
  38. package/dist/context-23EUTK33.js +0 -3
  39. package/dist/snapshot-KN2KJ2PM.js.map +0 -1
  40. package/dist/snapshot-Z7YUSB27.js.map +0 -1
  41. /package/dist/{chunk-U4IRZU6E.js.map → chunk-URIBHOOQ.js.map} +0 -0
  42. /package/dist/{context-CWMQYYFI.js.map → context-432WQST4.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
@@ -494,14 +498,17 @@ var init_snapshot = __esm({
494
498
  // src/context.ts
495
499
  var context_exports = {};
496
500
  __export(context_exports, {
501
+ artifactIdFromPath: () => artifactIdFromPath,
497
502
  beginAuth: () => beginAuth,
498
503
  callTool: () => callTool,
504
+ grantsFromIframeSrcs: () => grantsFromIframeSrcs,
499
505
  isPrivate: () => isPrivate,
500
506
  readPageContext: () => readPageContext,
501
507
  redact: () => redact,
502
508
  sendRegion: () => sendRegion,
503
509
  sendSnapshot: () => sendSnapshot,
504
- start: () => start
510
+ start: () => start,
511
+ stop: () => stop
505
512
  });
506
513
  function redact(text) {
507
514
  let out = text;
@@ -517,6 +524,10 @@ function isPrivate(el) {
517
524
  }
518
525
  return false;
519
526
  }
527
+ function artifactIdFromPath(pathname) {
528
+ const m = pathname.match(/^\/(?:embed\/)?artifacts\/(.+?)\/?$/);
529
+ return m ? decodeURIComponent(m[1]) : null;
530
+ }
520
531
  function embeddedMatterfactEntities() {
521
532
  if (!widgetOrigin) return [];
522
533
  const out = [];
@@ -528,10 +539,9 @@ function embeddedMatterfactEntities() {
528
539
  continue;
529
540
  }
530
541
  if (u.origin !== widgetOrigin) continue;
531
- const m = u.pathname.match(/^\/artifacts\/(.+?)\/?$/);
532
- if (!m) continue;
533
- const id = decodeURIComponent(m[1]);
534
- if (!id || out.some((e) => e.id === id)) continue;
542
+ const id = artifactIdFromPath(u.pathname);
543
+ if (!id) continue;
544
+ if (out.some((e) => e.id === id)) continue;
535
545
  out.push({
536
546
  kind: "artifact",
537
547
  id,
@@ -575,6 +585,36 @@ function readPageContext() {
575
585
  function publishContext() {
576
586
  send?.({ type: "host.context", context: readPageContext() });
577
587
  }
588
+ function grantsFromIframeSrcs(srcs, origin) {
589
+ const out = [];
590
+ for (const raw of srcs) {
591
+ let u;
592
+ try {
593
+ u = new URL(raw || "", location.href);
594
+ } catch {
595
+ continue;
596
+ }
597
+ if (u.origin !== origin) continue;
598
+ const id = artifactIdFromPath(u.pathname);
599
+ if (!id) continue;
600
+ const owner = u.searchParams.get("owner") || "";
601
+ const token = u.searchParams.get("t") || "";
602
+ if (!owner || !token) continue;
603
+ if (out.some((g) => g.id === id)) continue;
604
+ out.push({ id, owner, token });
605
+ }
606
+ return out;
607
+ }
608
+ function readArtifactGrants() {
609
+ if (!widgetOrigin) return [];
610
+ const srcs = Array.from(document.querySelectorAll("iframe")).map(
611
+ (f) => f.getAttribute("src") || ""
612
+ );
613
+ return grantsFromIframeSrcs(srcs, widgetOrigin);
614
+ }
615
+ function publishArtifactGrants() {
616
+ send?.({ type: "host.artifactGrants", grants: readArtifactGrants() });
617
+ }
578
618
  function pushActivity(e) {
579
619
  activity.push({ ...e, seq: ++activitySeq, ts: Date.now() });
580
620
  if (activity.length > MAX_ACTIVITY) activity.shift();
@@ -636,9 +676,13 @@ function watchNavigation() {
636
676
  lastUrl = url;
637
677
  pushActivity({ type: "nav", summary: `navigated to ${url}` });
638
678
  publishContext();
679
+ publishArtifactGrants();
639
680
  };
681
+ navFire = fire;
640
682
  for (const name of ["pushState", "replaceState"]) {
641
683
  const orig = history[name];
684
+ if (name === "pushState") origPushState = orig;
685
+ else origReplaceState = orig;
642
686
  history[name] = function(...args) {
643
687
  const r = orig.apply(this, args);
644
688
  fire();
@@ -659,7 +703,7 @@ async function readFocus() {
659
703
  }
660
704
  const active = document.activeElement;
661
705
  if (active && active !== document.body && !isPrivate(active) && !active.closest("[data-mf-private]")) {
662
- const { snapshot: snapshot2 } = await Promise.resolve().then(() => (init_snapshot(), snapshot_exports));
706
+ const { snapshot: snapshot2 } = await loadSnapshotModule();
663
707
  void snapshot2;
664
708
  focus.focused = {
665
709
  label: redact(describeControl(active)),
@@ -674,6 +718,11 @@ async function readFocus() {
674
718
  function describeControl(el) {
675
719
  return describe(el);
676
720
  }
721
+ async function loadSnapshotModule() {
722
+ const m = await Promise.resolve().then(() => (init_snapshot(), snapshot_exports));
723
+ snapshotModule = m;
724
+ return m;
725
+ }
677
726
  function scheduleFocus() {
678
727
  if (focusTimer) return;
679
728
  focusTimer = setTimeout(async () => {
@@ -683,7 +732,8 @@ function scheduleFocus() {
683
732
  }, 250);
684
733
  }
685
734
  async function sendSnapshot(emit) {
686
- const { snapshot: snapshot2 } = await Promise.resolve().then(() => (init_snapshot(), snapshot_exports));
735
+ if (!pageContextOn) return;
736
+ const { snapshot: snapshot2 } = await loadSnapshotModule();
687
737
  const { yaml, truncated, visibleRefs } = snapshot2();
688
738
  emit({
689
739
  type: "host.snapshot",
@@ -693,7 +743,8 @@ async function sendSnapshot(emit) {
693
743
  emit({ type: "host.focus", focus: lastFocus });
694
744
  }
695
745
  async function sendRegion(ref, emit) {
696
- const { snapshotRegion: snapshotRegion2, resolveRef: resolveRef2 } = await Promise.resolve().then(() => (init_snapshot(), snapshot_exports));
746
+ if (!pageContextOn) return;
747
+ const { snapshotRegion: snapshotRegion2, resolveRef: resolveRef2 } = await loadSnapshotModule();
697
748
  const el = resolveRef2(ref);
698
749
  if (!el) {
699
750
  emit({
@@ -723,45 +774,86 @@ function beginAuth(config, emit) {
723
774
  "width=460,height=640"
724
775
  );
725
776
  if (!w) return;
777
+ if (pendingAuthListener) window.removeEventListener("message", pendingAuthListener);
726
778
  const onMsg = (event) => {
727
779
  if (event.origin !== config.origin) return;
728
780
  const d = event.data;
729
781
  if (d?.type !== "mf:embed:auth") return;
730
782
  window.removeEventListener("message", onMsg);
783
+ pendingAuthListener = null;
731
784
  emit({ type: "host.auth", token: d.token, expiresAt: d.expiresAt });
732
785
  };
786
+ pendingAuthListener = onMsg;
733
787
  window.addEventListener("message", onMsg);
734
788
  }
735
- function start(emit, origin) {
789
+ function stop() {
790
+ document.removeEventListener("click", onClick, { capture: true });
791
+ document.removeEventListener("submit", onSubmit, { capture: true });
792
+ document.removeEventListener("change", onChange, { capture: true });
793
+ document.removeEventListener("selectionchange", scheduleFocus);
794
+ document.removeEventListener("focusin", scheduleFocus, { capture: true });
795
+ window.removeEventListener("scroll", scheduleFocus, { capture: true });
796
+ if (navFire) {
797
+ window.removeEventListener("popstate", navFire);
798
+ navFire = null;
799
+ }
800
+ if (origPushState) {
801
+ history.pushState = origPushState;
802
+ origPushState = null;
803
+ }
804
+ if (origReplaceState) {
805
+ history.replaceState = origReplaceState;
806
+ origReplaceState = null;
807
+ }
808
+ if (pendingAuthListener) {
809
+ window.removeEventListener("message", pendingAuthListener);
810
+ pendingAuthListener = null;
811
+ }
812
+ if (focusTimer) {
813
+ clearTimeout(focusTimer);
814
+ focusTimer = null;
815
+ }
816
+ send = null;
817
+ activity.length = 0;
818
+ lastFocus = {};
819
+ widgetOrigin = "";
820
+ snapshotModule?.clearRefs();
821
+ }
822
+ function start(emit, origin, pageContext = true) {
823
+ stop();
736
824
  send = emit;
737
825
  widgetOrigin = origin || "";
738
826
  lastUrl = location.pathname;
739
- publishContext();
740
- watchNavigation();
741
- document.addEventListener("click", onClick, { capture: true, passive: true });
742
- document.addEventListener("submit", onSubmit, {
743
- capture: true,
744
- passive: true
745
- });
746
- document.addEventListener("change", onChange, {
747
- capture: true,
748
- passive: true
749
- });
750
- document.addEventListener("selectionchange", scheduleFocus, {
751
- passive: true
752
- });
753
- document.addEventListener("focusin", scheduleFocus, {
754
- capture: true,
755
- passive: true
756
- });
757
- window.addEventListener("scroll", scheduleFocus, {
758
- capture: true,
759
- passive: true
760
- });
827
+ pageContextOn = pageContext;
828
+ if (pageContextOn) {
829
+ publishContext();
830
+ publishArtifactGrants();
831
+ watchNavigation();
832
+ document.addEventListener("click", onClick, { capture: true, passive: true });
833
+ document.addEventListener("submit", onSubmit, {
834
+ capture: true,
835
+ passive: true
836
+ });
837
+ document.addEventListener("change", onChange, {
838
+ capture: true,
839
+ passive: true
840
+ });
841
+ document.addEventListener("selectionchange", scheduleFocus, {
842
+ passive: true
843
+ });
844
+ document.addEventListener("focusin", scheduleFocus, {
845
+ capture: true,
846
+ passive: true
847
+ });
848
+ window.addEventListener("scroll", scheduleFocus, {
849
+ capture: true,
850
+ passive: true
851
+ });
852
+ }
761
853
  const theme = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
762
854
  emit({ type: "host.theme", mode: theme });
763
855
  }
764
- var widgetOrigin, MAX_ACTIVITY, send, activitySeq, activity, lastUrl, PII, FORM_CONTROLS, ACTIONABLE, lastFocus, focusTimer, snapshotSeq;
856
+ var widgetOrigin, MAX_ACTIVITY, send, activitySeq, activity, lastUrl, pageContextOn, navFire, origPushState, origReplaceState, pendingAuthListener, snapshotModule, PII, FORM_CONTROLS, ACTIONABLE, lastFocus, focusTimer, snapshotSeq;
765
857
  var init_context = __esm({
766
858
  "src/context.ts"() {
767
859
  "use strict";
@@ -771,6 +863,12 @@ var init_context = __esm({
771
863
  activitySeq = 0;
772
864
  activity = [];
773
865
  lastUrl = "";
866
+ pageContextOn = true;
867
+ navFire = null;
868
+ origPushState = null;
869
+ origReplaceState = null;
870
+ pendingAuthListener = null;
871
+ snapshotModule = null;
774
872
  PII = [
775
873
  /\b[\w.+-]+@[\w-]+\.[\w.]{2,}\b/g,
776
874
  // email
@@ -798,7 +896,8 @@ var init_context = __esm({
798
896
  // src/react.tsx
799
897
  var react_exports = {};
800
898
  __export(react_exports, {
801
- MatterfactAgent: () => MatterfactAgent
899
+ MatterfactAgent: () => MatterfactAgent,
900
+ MatterfactArtifact: () => MatterfactArtifact
802
901
  });
803
902
  module.exports = __toCommonJS(react_exports);
804
903
  var import_react = require("react");
@@ -989,7 +1088,9 @@ var EmbedHost = class {
989
1088
  iframe.setAttribute("allow", "microphone");
990
1089
  iframe.src = `${this.config.origin}/embed/chat?k=${encodeURIComponent(
991
1090
  this.config.publishableKey
992
- )}&o=${encodeURIComponent(location.origin)}` + (this.config.surface ? `&s=${encodeURIComponent(this.config.surface)}` : "") + (devRequested() ? "&dev=1" : "") + (this.inline ? "&inline=1" : "");
1091
+ )}&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
1092
+ // programmatic `dev` (the React prop). See both doc comments above.
1093
+ (devRequested() || this.config.dev ? "&dev=1" : "") + (this.inline ? "&inline=1" : "");
993
1094
  this.iframe = iframe;
994
1095
  this.shadow.appendChild(iframe);
995
1096
  window.addEventListener("message", this.onMessage);
@@ -1079,7 +1180,7 @@ var EmbedHost = class {
1079
1180
  }
1080
1181
  loadContext() {
1081
1182
  this.context ?? (this.context = Promise.resolve().then(() => (init_context(), context_exports)).then((m) => {
1082
- m.start(this.send, this.config.origin);
1183
+ m.start(this.send, this.config.origin, this.config.pageContext);
1083
1184
  if (this.config.theme !== "auto") {
1084
1185
  this.send({ type: "host.theme", mode: this.config.theme });
1085
1186
  }
@@ -1102,6 +1203,7 @@ var EmbedHost = class {
1102
1203
  this.iframe = null;
1103
1204
  this.shadow = null;
1104
1205
  this.ready = false;
1206
+ void this.context?.then((m) => m.stop());
1105
1207
  }
1106
1208
  };
1107
1209
  function mount(config) {
@@ -1121,7 +1223,9 @@ function MatterfactAgent({
1121
1223
  getAuthToken,
1122
1224
  inline = false,
1123
1225
  className,
1124
- style
1226
+ style,
1227
+ pageContext,
1228
+ dev
1125
1229
  }) {
1126
1230
  const authRef = (0, import_react.useRef)(getAuthToken);
1127
1231
  authRef.current = getAuthToken;
@@ -1137,7 +1241,9 @@ function MatterfactAgent({
1137
1241
  // Always present; a null return (no getAuthToken supplied) makes the core fall
1138
1242
  // back to the popup/inline sign-in, so this is safe either way.
1139
1243
  authTokenProvider: async () => await authRef.current?.() ?? null,
1140
- container: inline ? slot.current : null
1244
+ container: inline ? slot.current : null,
1245
+ pageContext,
1246
+ dev
1141
1247
  };
1142
1248
  let host = null;
1143
1249
  try {
@@ -1146,7 +1252,7 @@ function MatterfactAgent({
1146
1252
  console.error("[matterfact] failed to mount the embed widget", e);
1147
1253
  }
1148
1254
  return () => host?.destroy();
1149
- }, [publishableKey, widgetOrigin2, surface, theme, inline]);
1255
+ }, [publishableKey, widgetOrigin2, surface, theme, inline, pageContext, dev]);
1150
1256
  if (!inline) return null;
1151
1257
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1152
1258
  "div",
@@ -1157,8 +1263,32 @@ function MatterfactAgent({
1157
1263
  }
1158
1264
  );
1159
1265
  }
1266
+ var DEFAULT_ARTIFACT_ORIGIN = "https://app.matterfact.com";
1267
+ function MatterfactArtifact({
1268
+ slug,
1269
+ owner,
1270
+ token,
1271
+ widgetOrigin: widgetOrigin2,
1272
+ theme = "auto",
1273
+ className,
1274
+ style
1275
+ }) {
1276
+ const origin = widgetOrigin2 || DEFAULT_ARTIFACT_ORIGIN;
1277
+ const src = `${origin}/embed/artifacts/${encodeURIComponent(slug)}?owner=${encodeURIComponent(owner)}&t=${encodeURIComponent(token)}&theme=${theme}`;
1278
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1279
+ "iframe",
1280
+ {
1281
+ src,
1282
+ title: `matterfact artifact ${slug}`,
1283
+ className,
1284
+ style: { width: "100%", height: 600, border: 0, ...style },
1285
+ sandbox: "allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox"
1286
+ }
1287
+ );
1288
+ }
1160
1289
  // Annotate the CommonJS export names for ESM import in node:
1161
1290
  0 && (module.exports = {
1162
- MatterfactAgent
1291
+ MatterfactAgent,
1292
+ MatterfactArtifact
1163
1293
  });
1164
1294
  //# sourceMappingURL=react.cjs.map