@matterfact/embed 0.15.0 → 0.17.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/index.d.cts CHANGED
@@ -18,7 +18,7 @@
18
18
  * shapes are right, "deeper integration" is a transport swap (postMessage ->
19
19
  * direct call), not a rewrite. So model the DOMAIN here, not the plumbing.
20
20
  */
21
- declare const PROTOCOL_VERSION = 3;
21
+ declare const PROTOCOL_VERSION = 4;
22
22
  /** Every message is namespaced so we never collide with the host page's own postMessage traffic. */
23
23
  declare const CHANNEL = "mf-embed";
24
24
  /**
@@ -346,6 +346,26 @@ type HostToWidget = {
346
346
  type: 'host.theme';
347
347
  mode: 'light' | 'dark';
348
348
  }
349
+ /**
350
+ * The share-link template the integration configured on the loader
351
+ * (`shareDeeplinkFormat`), e.g. `https://portal.acme.com/share/{share_token}`. Sent
352
+ * at mount; the widget mints "Share chat" links in this format. Absent config ⇒
353
+ * never sent, and the widget falls back to its own share route.
354
+ */
355
+ | {
356
+ type: 'host.deeplinkFormat';
357
+ format: string;
358
+ }
359
+ /**
360
+ * The host page's URL matches that same template, and this is the share token it
361
+ * carries — or `null` the moment the user navigates off it. Only the loader can see
362
+ * the host URL, so only the loader can report this; the widget opens (or closes) the
363
+ * shared thread on the back of it.
364
+ */
365
+ | {
366
+ type: 'host.openShare';
367
+ token: string | null;
368
+ }
349
369
  /**
350
370
  * Where the widget currently IS. Sent on mount and after anything that moves it
351
371
  * (drag, snap, mode change, viewport resize).
@@ -669,6 +689,14 @@ interface LoaderConfig {
669
689
  * the URL param still works untouched; `mount()` ORs the two together.
670
690
  */
671
691
  dev?: boolean;
692
+ /**
693
+ * Host-site share-link template. The widget mints "Share chat" links in this format,
694
+ * and this loader watches the host URL for a match so a visitor landing on one opens
695
+ * that conversation. Exactly one `{share_token}`, as a whole path segment or whole
696
+ * query value, e.g. `https://portal.acme.com/assistant?mf_share={share_token}`.
697
+ * Absent ⇒ no watch is installed and the widget keeps its own share route.
698
+ */
699
+ shareDeeplinkFormat?: string;
672
700
  }
673
701
  declare function readConfig(): LoaderConfig | null;
674
702
  declare class EmbedHost {
@@ -706,6 +734,12 @@ declare class EmbedHost {
706
734
  private dragLeftBand;
707
735
  /** Loaded on first open. Holds everything that touches the customer's DOM. */
708
736
  private context;
737
+ /** Uninstall for the share-deeplink URL watch. No initializer on purpose: an `= null`
738
+ * emits a constructor assignment into the size-budgeted stub for no behaviour. */
739
+ private deeplinkStop?;
740
+ /** Set once `destroy()` has run; falsy (never assigned) otherwise — no initializer,
741
+ * same reasoning as `deeplinkStop` above. */
742
+ private destroyed?;
709
743
  /** The host element; kept so `destroy()` can remove it (React lifecycle). */
710
744
  private hostEl;
711
745
  /** Rendering into the host's own element: they own the box, the chrome and visibility. */
package/dist/index.d.ts CHANGED
@@ -18,7 +18,7 @@
18
18
  * shapes are right, "deeper integration" is a transport swap (postMessage ->
19
19
  * direct call), not a rewrite. So model the DOMAIN here, not the plumbing.
20
20
  */
21
- declare const PROTOCOL_VERSION = 3;
21
+ declare const PROTOCOL_VERSION = 4;
22
22
  /** Every message is namespaced so we never collide with the host page's own postMessage traffic. */
23
23
  declare const CHANNEL = "mf-embed";
24
24
  /**
@@ -346,6 +346,26 @@ type HostToWidget = {
346
346
  type: 'host.theme';
347
347
  mode: 'light' | 'dark';
348
348
  }
349
+ /**
350
+ * The share-link template the integration configured on the loader
351
+ * (`shareDeeplinkFormat`), e.g. `https://portal.acme.com/share/{share_token}`. Sent
352
+ * at mount; the widget mints "Share chat" links in this format. Absent config ⇒
353
+ * never sent, and the widget falls back to its own share route.
354
+ */
355
+ | {
356
+ type: 'host.deeplinkFormat';
357
+ format: string;
358
+ }
359
+ /**
360
+ * The host page's URL matches that same template, and this is the share token it
361
+ * carries — or `null` the moment the user navigates off it. Only the loader can see
362
+ * the host URL, so only the loader can report this; the widget opens (or closes) the
363
+ * shared thread on the back of it.
364
+ */
365
+ | {
366
+ type: 'host.openShare';
367
+ token: string | null;
368
+ }
349
369
  /**
350
370
  * Where the widget currently IS. Sent on mount and after anything that moves it
351
371
  * (drag, snap, mode change, viewport resize).
@@ -669,6 +689,14 @@ interface LoaderConfig {
669
689
  * the URL param still works untouched; `mount()` ORs the two together.
670
690
  */
671
691
  dev?: boolean;
692
+ /**
693
+ * Host-site share-link template. The widget mints "Share chat" links in this format,
694
+ * and this loader watches the host URL for a match so a visitor landing on one opens
695
+ * that conversation. Exactly one `{share_token}`, as a whole path segment or whole
696
+ * query value, e.g. `https://portal.acme.com/assistant?mf_share={share_token}`.
697
+ * Absent ⇒ no watch is installed and the widget keeps its own share route.
698
+ */
699
+ shareDeeplinkFormat?: string;
672
700
  }
673
701
  declare function readConfig(): LoaderConfig | null;
674
702
  declare class EmbedHost {
@@ -706,6 +734,12 @@ declare class EmbedHost {
706
734
  private dragLeftBand;
707
735
  /** Loaded on first open. Holds everything that touches the customer's DOM. */
708
736
  private context;
737
+ /** Uninstall for the share-deeplink URL watch. No initializer on purpose: an `= null`
738
+ * emits a constructor assignment into the size-budgeted stub for no behaviour. */
739
+ private deeplinkStop?;
740
+ /** Set once `destroy()` has run; falsy (never assigned) otherwise — no initializer,
741
+ * same reasoning as `deeplinkStop` above. */
742
+ private destroyed?;
709
743
  /** The host element; kept so `destroy()` can remove it (React lifecycle). */
710
744
  private hostEl;
711
745
  /** Rendering into the host's own element: they own the box, the chrome and visibility. */
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/protocol.ts
2
- var PROTOCOL_VERSION = 3;
2
+ var PROTOCOL_VERSION = 4;
3
3
  var CHANNEL = "mf-embed";
4
4
  function envelope(payload, id) {
5
5
  return {
@@ -222,6 +222,7 @@ function dockBox(g, vw, _vh) {
222
222
 
223
223
  // src/loader.ts
224
224
  var DEFAULT_ORIGIN = "https://app.matterfact.com";
225
+ var EMBED_VERSION = "0.17.0";
225
226
  function devRequested() {
226
227
  try {
227
228
  if (new URLSearchParams(location.search).get("mfdev") === "1") return true;
@@ -266,7 +267,10 @@ function readConfig() {
266
267
  theme: el?.dataset.theme || "auto",
267
268
  surface: el?.dataset.surface || "",
268
269
  container,
269
- pageContext
270
+ pageContext,
271
+ // `data-share-deeplink-format="https://…/{share_token}"` — the script-tag twin of
272
+ // the React `shareDeeplinkFormat` prop.
273
+ shareDeeplinkFormat: el?.dataset.shareDeeplinkFormat
270
274
  // No `data-dev` — see the `dev` field's doc comment: the URL trigger is the
271
275
  // point for the script-tag path, so there is deliberately no script-tag knob here.
272
276
  // No `data-actions` either: the action policy lives entirely in the lazy chunk,
@@ -347,6 +351,8 @@ var EmbedHost = class {
347
351
  this.inline = !!config.container;
348
352
  }
349
353
  mount() {
354
+ const w = window;
355
+ (w.matterfact ?? (w.matterfact = {})).embedVersion = EMBED_VERSION;
350
356
  const host = document.createElement("div");
351
357
  this.hostEl = host;
352
358
  host.id = "matterfact-embed";
@@ -390,7 +396,7 @@ var EmbedHost = class {
390
396
  "sandbox",
391
397
  "allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads"
392
398
  );
393
- iframe.setAttribute("allow", "microphone; clipboard-write");
399
+ iframe.setAttribute("allow", "microphone; clipboard-write; web-share");
394
400
  iframe.src = `${this.config.origin}/embed/chat?k=${encodeURIComponent(
395
401
  this.config.publishableKey
396
402
  )}&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
@@ -403,6 +409,17 @@ var EmbedHost = class {
403
409
  window.addEventListener("resize", this.onViewportResize);
404
410
  this.place();
405
411
  }
412
+ const fmt = this.config.shareDeeplinkFormat;
413
+ if (fmt) {
414
+ this.send({ type: "host.deeplinkFormat", format: fmt });
415
+ void import('./deeplink-QW3VRPOY.js').then((m) => {
416
+ if (this.destroyed) return;
417
+ this.deeplinkStop = m.installDeeplinkWatch(fmt, (token) => {
418
+ this.send({ type: "host.openShare", token });
419
+ });
420
+ }).catch(() => {
421
+ });
422
+ }
406
423
  }
407
424
  /**
408
425
  * Test seam. The shadow root is CLOSED, so a test cannot reach the iframe to forge a
@@ -825,6 +842,9 @@ var EmbedHost = class {
825
842
  this.iframe = null;
826
843
  this.shadow = null;
827
844
  this.ready = false;
845
+ this.destroyed = true;
846
+ this.deeplinkStop?.();
847
+ this.deeplinkStop = null;
828
848
  void this.context?.then((m) => m.stop());
829
849
  }
830
850
  };