@morgan-and-morgan/intake-agent 1.0.0-alpha.15 → 1.0.0-alpha.17

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/README.md CHANGED
@@ -6,6 +6,7 @@ Load the widget with a script tag, pinning an exact version:
6
6
  <script
7
7
  src="https://cdn.jsdelivr.net/npm/@morgan-and-morgan/intake-agent@<version>/dist/mm-intake-widget.js"
8
8
  data-chat-url="https://example.com/chat"
9
+ data-prompt-delay-seconds="30"
9
10
  ></script>
10
11
  ```
11
12
 
@@ -34,9 +35,33 @@ major. Every release is tagged `intake-agent-vx.y.z`.
34
35
  | --- | --- | --- |
35
36
  | `data-chat-url` | Release environment URL | Chat iframe target |
36
37
  | `data-auto-open` | `false` | Open the chat immediately after mount |
38
+ | `data-prompt-delay-seconds` | `30` | Seconds after mount before showing the consultation modal |
39
+
40
+ Set the delay on the script tag before it loads. Non-negative numbers are
41
+ accepted, including fractions; `0` shows the modal immediately. Missing, empty,
42
+ invalid, negative, or overflowing values (above 2147483.647 seconds) use 30 seconds.
43
+ The value is read locally and adds no configuration request. Changing the
44
+ attribute after the script loads does not reschedule the modal. Existing popup
45
+ suppression and dismissal cooldowns still apply.
37
46
 
38
47
  The consultation modal reads `window.drupalSettings.mxPopup`, falling back to
39
48
  `window.mxChatbotSettings.mxPopup` when the Drupal field is absent. Only the
40
49
  boolean `false` suppresses the modal, including calls to `showPrompt()`.
41
50
  Missing settings preserve the usual display rules. Settings are read when the
42
- modal is about to appear; the launcher and chat remain available.
51
+ modal is about to appear; the launcher and chat remain available. Set suppression
52
+ before loading the embed when using a zero-second delay, so the first display
53
+ check sees the intended setting.
54
+
55
+ ## Consultation prompt design
56
+
57
+ The responsive prompt follows the [desktop and mobile Figma layouts](https://www.figma.com/design/0SxWl42gdhx08KFe2ldpd8/MM-Design-System-v4.0?node-id=21250-169311).
58
+ Portrait, avatar, status dots, and icons are exported from those layers.
59
+ The fonts are the existing website's [Kuunari Bold Compressed](https://www.forthepeople.com/themes/custom/morgan/fonts/Kuunari/kuunari-bold-compressed.woff2)
60
+ and [Nunito Sans](https://www.forthepeople.com/themes/custom/morgan/fonts/NunitoSans/NunitoSans.woff2).
61
+ On forthepeople.com, the prompt loads these fonts from the host site to respect
62
+ its font security policy. Other hosts use the bundled copies.
63
+ Keep prompt assets in `public/` and `apps/expo/public/intake-widget/` identical;
64
+ the asset tests check both the CDN and portal copies.
65
+
66
+ Retain `consultation-prompt.webp` at both asset origins for older deployed widget
67
+ versions. The redesigned prompt does not request it.
@@ -0,0 +1,3 @@
1
+ <svg width="24" height="29" viewBox="0 0 24 29" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M9.75 22.5V23.25L13.125 20.7188C13.5 20.4375 13.9688 20.25 14.4844 20.25H21C21.375 20.25 21.75 19.9219 21.75 19.5V6C21.75 5.625 21.375 5.25 21 5.25H3C2.57812 5.25 2.25 5.625 2.25 6V19.5C2.25 19.9219 2.57812 20.25 3 20.25H7.5C8.71875 20.25 9.75 21.2812 9.75 22.5ZM9.70312 26.1094L9.46875 26.25L8.67188 26.8594C8.4375 27.0469 8.15625 27.0469 7.875 26.9531C7.64062 26.8125 7.5 26.5781 7.5 26.25V25.2656V24.9844V24.9375V24.75V22.5H5.25H3C1.3125 22.5 0 21.1875 0 19.5V6C0 4.35938 1.3125 3 3 3H21C22.6406 3 24 4.35938 24 6V19.5C24 21.1875 22.6406 22.5 21 22.5H14.4844L9.75 26.0625L9.70312 26.1094ZM7.125 9.75H16.875C17.4844 9.75 18 10.2656 18 10.875C18 11.5312 17.4844 12 16.875 12H7.125C6.46875 12 6 11.5312 6 10.875C6 10.2656 6.46875 9.75 7.125 9.75ZM7.125 14.25H12.375C12.9844 14.25 13.5 14.7656 13.5 15.375C13.5 16.0312 12.9844 16.5 12.375 16.5H7.125C6.46875 16.5 6 16.0312 6 15.375C6 14.7656 6.46875 14.25 7.125 14.25Z" fill="#061E39"/>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="18" height="29" viewBox="0 0 18 29" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M15.1875 22.3125L9 16.0781L2.76562 22.3125C2.48438 22.5938 2.01562 22.5938 1.6875 22.3125C1.40625 21.9844 1.40625 21.5156 1.6875 21.2344L7.92188 15L1.73438 8.8125C1.40625 8.53125 1.40625 8.0625 1.73438 7.73438C2.01562 7.45312 2.48438 7.45312 2.76562 7.73438L9 13.9688L15.1875 7.73438C15.4688 7.45312 15.9375 7.45312 16.2656 7.73438C16.5469 8.0625 16.5469 8.53125 16.2656 8.8125L10.0312 15L16.2656 21.2344C16.5469 21.5156 16.5469 21.9844 16.2656 22.3125C15.9375 22.5938 15.4688 22.5938 15.1875 22.3125Z" fill="white"/>
3
+ </svg>
Binary file
@@ -0,0 +1,3 @@
1
+ <svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="14.6667" height="14.6667" viewBox="0 0 14.6667 14.6667" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path id="Ellipse 52" d="M13.6667 7.33333C13.6667 10.8311 10.8311 13.6667 7.33333 13.6667C3.83553 13.6667 1 10.8311 1 7.33333C1 3.83553 3.83553 1 7.33333 1C10.8311 1 13.6667 3.83553 13.6667 7.33333Z" fill="#1FC16B" stroke-width="2" stroke="#132A4E"/>
3
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg preserveAspectRatio="none" overflow="visible" style="display: block;" width="11.619" height="11.619" viewBox="0 0 11.619 11.619" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path id="Ellipse 52" d="M10.619 5.80952C10.619 8.46575 8.46575 10.619 5.80952 10.619C3.1533 10.619 1 8.46575 1 5.80952C1 3.1533 3.1533 1 5.80952 1C8.46575 1 10.619 3.1533 10.619 5.80952Z" fill="#1FC16B" stroke-width="2" stroke="#132A4E"/>
3
+ </svg>
Binary file
@@ -1,5 +1,17 @@
1
1
  (function() {
2
2
  "use strict";
3
+ const DEFAULT_PROMPT_DELAY_MS = 3e4;
4
+ const MAX_PROMPT_DELAY_MS = 2147483647;
5
+ const readPromptDelayMs = (seconds) => {
6
+ if (seconds === void 0 || seconds.trim() === "") {
7
+ return DEFAULT_PROMPT_DELAY_MS;
8
+ }
9
+ const delayMs = Number(seconds) * 1e3;
10
+ if (!Number.isFinite(delayMs) || delayMs < 0 || delayMs > MAX_PROMPT_DELAY_MS) {
11
+ return DEFAULT_PROMPT_DELAY_MS;
12
+ }
13
+ return delayMs;
14
+ };
3
15
  const readLoaderConfig = (script, baseUri) => {
4
16
  var _a;
5
17
  const scriptUrl = new URL(script.src, baseUri);
@@ -9,7 +21,8 @@
9
21
  assetBaseUrl: new URL(".", scriptUrl).href,
10
22
  autoOpen: script.dataset.autoOpen === "true",
11
23
  chatOrigin: new URL(chatUrl).origin,
12
- chatUrl
24
+ chatUrl,
25
+ promptDelayMs: readPromptDelayMs(script.dataset.promptDelaySeconds)
13
26
  };
14
27
  };
15
28
  const ATTRIBUTION_FIELD_MAX_LENGTH = {
@@ -220,6 +233,130 @@
220
233
  return false;
221
234
  }
222
235
  };
236
+ const designSystemColors = {
237
+ // Colors/Base/black
238
+ "base-black": "#000000",
239
+ // Colors/Primary/100
240
+ "primary-100": "#f3f8ff",
241
+ // Colors/Primary/800
242
+ "primary-800": "#132a4e",
243
+ // Colors/Primary/900
244
+ "primary-900": "#061e39",
245
+ // Colors/Secondary/500
246
+ "secondary-500": "#ffec00",
247
+ // Colors/Secondary/700
248
+ "secondary-700": "#e2d100"
249
+ };
250
+ const PROMPT_FONTS = [
251
+ {
252
+ family: "MMIntakeKuunari",
253
+ file: "consultation-kuunari.woff2",
254
+ hostPath: "/themes/custom/morgan/fonts/Kuunari/kuunari-bold-compressed.woff2",
255
+ weight: "700",
256
+ fallbackClass: "mm-prompt-fallback-title"
257
+ },
258
+ {
259
+ family: "MMIntakeNunitoSans",
260
+ file: "consultation-nunito-sans.woff2",
261
+ hostPath: "/themes/custom/morgan/fonts/NunitoSans/NunitoSans.woff2",
262
+ weight: "100 900",
263
+ fallbackClass: "mm-prompt-fallback-status"
264
+ }
265
+ ];
266
+ const ownedPromptFonts = /* @__PURE__ */ new WeakMap();
267
+ const updateFontFallback = async (font, target, fallbackClass) => {
268
+ var _a, _b, _c;
269
+ if (font.status !== "loaded") (_a = target.backdrop) == null ? void 0 : _a.classList.add(fallbackClass);
270
+ try {
271
+ await font.loaded;
272
+ (_b = target.backdrop) == null ? void 0 : _b.classList.remove(fallbackClass);
273
+ } catch {
274
+ (_c = target.backdrop) == null ? void 0 : _c.classList.add(fallbackClass);
275
+ }
276
+ };
277
+ const registerPromptFonts = (assetUrl, backdrop, host = globalThis.location) => {
278
+ const target = { backdrop };
279
+ const registrations = [];
280
+ const dispose = () => {
281
+ if (target.backdrop === void 0) return;
282
+ target.backdrop = void 0;
283
+ for (const registration of registrations) {
284
+ registration.users -= 1;
285
+ if (registration.users === 0) {
286
+ document.fonts.delete(registration.font);
287
+ ownedPromptFonts.delete(registration.font);
288
+ }
289
+ }
290
+ };
291
+ if (typeof FontFace === "undefined") {
292
+ backdrop.classList.add(...PROMPT_FONTS.map(({ fallbackClass }) => fallbackClass));
293
+ return dispose;
294
+ }
295
+ const useHostFonts = ["forthepeople.com", "www.forthepeople.com"].includes(host.hostname);
296
+ for (const { family, file, hostPath, weight, fallbackClass } of PROMPT_FONTS) {
297
+ let font = [...document.fonts].find((registered) => registered.family === family);
298
+ if (font === void 0) {
299
+ const url = useHostFonts ? new URL(hostPath, host.origin).href : assetUrl(file);
300
+ font = new FontFace(family, `url(${JSON.stringify(url)})`, { weight, display: "swap" });
301
+ document.fonts.add(font);
302
+ ownedPromptFonts.set(font, { font, users: 0 });
303
+ }
304
+ const registration = ownedPromptFonts.get(font);
305
+ if (registration !== void 0) {
306
+ registration.users += 1;
307
+ registrations.push(registration);
308
+ }
309
+ void updateFontFallback(font, target, fallbackClass);
310
+ }
311
+ return dispose;
312
+ };
313
+ const PROMPT_STYLES = [
314
+ `.mm-prompt-backdrop{--mm-prompt-blue:${designSystemColors["primary-800"]};--mm-prompt-dark:${designSystemColors["primary-900"]};--mm-prompt-yellow:${designSystemColors["secondary-500"]};--mm-prompt-yellow-end:${designSystemColors["secondary-700"]};--mm-prompt-text:${designSystemColors["primary-100"]};position:fixed;inset:0;z-index:2147483002;display:flex;flex-direction:column;align-items:center;padding:12px;overflow-y:auto;overscroll-behavior:contain;touch-action:pan-y pinch-zoom;background:rgba(0,0,0,.5);animation:mm-prompt-backdrop-in .25s cubic-bezier(.23,1,.32,1) both;font-family:"MMIntakeNunitoSans",var(--mm-font);font-variation-settings:"YTLC" 500,"wdth" 100;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}`,
315
+ // Auto margins center the card when it fits and keep its top reachable when it overflows.
316
+ ".mm-prompt-card{position:relative;isolation:isolate;display:flex;flex:none;flex-direction:column;margin-block:auto;width:600px;max-width:100%;padding:16px 16px 32px;border-radius:16px;background:var(--mm-prompt-blue);color:var(--mm-white);box-shadow:0 4px 5px rgba(0,0,0,.1),0 7px 10px rgba(0,0,0,.07),0 2px 16px rgba(0,0,0,.08);animation:mm-prompt-card-in .25s cubic-bezier(.23,1,.32,1) both}",
317
+ ".mm-prompt-status{position:relative;z-index:1;display:flex;align-items:center;gap:13px;align-self:flex-start;max-width:calc(100% - 52px);padding:8px 40px 8px 8px;border-radius:999px;background:var(--mm-prompt-dark);color:var(--mm-prompt-text)}",
318
+ ".mm-prompt-avatar{position:relative;flex:none;width:56px;height:56px}",
319
+ ".mm-prompt-team{display:block;width:100%;height:100%;border-radius:50%}",
320
+ // The exported indicator bounds include Figma's 2px outside stroke.
321
+ ".mm-prompt-online{position:absolute;right:-2px;top:40.17px;display:block;line-height:0}",
322
+ ".mm-prompt-online img{display:block}",
323
+ ".mm-prompt-status-text{margin:0;font-size:20px;font-weight:700;line-height:27px;white-space:nowrap}",
324
+ ".mm-prompt-hero{position:relative;display:flex;align-items:center;height:304px;margin-top:-40px;padding-left:16px}",
325
+ // The heading extends over the portrait's transparent edge.
326
+ '.mm-prompt-title{position:relative;z-index:1;flex:1;min-width:0;height:232px;display:flex;flex-direction:column;justify-content:flex-end;margin:0;font-family:"MMIntakeKuunari",Impact,sans-serif;font-size:72px;font-weight:700;line-height:64px;letter-spacing:-2px;text-transform:uppercase}',
327
+ ".mm-prompt-title-line{display:block;white-space:nowrap}",
328
+ ".mm-prompt-mobile-break{display:none}",
329
+ ".mm-prompt-portrait{position:relative;flex:none;width:270px;height:304px}",
330
+ ".mm-prompt-image{display:block;width:100%;height:100%;pointer-events:none}",
331
+ `.mm-prompt-cta{position:relative;display:flex;align-items:center;justify-content:center;gap:16px;height:56px;margin:0 16px;border-radius:8px;background:linear-gradient(to bottom,var(--mm-prompt-yellow) 41.827%,var(--mm-prompt-yellow-end));box-shadow:0 2px 4px rgba(0,0,0,.05),0 4px 3px rgba(0,0,0,.04),0 1px 2px rgba(0,0,0,.03);color:${designSystemColors["base-black"]};font-size:20px;font-weight:700;line-height:24px}`,
332
+ ".mm-prompt-cta-label{min-width:90px}",
333
+ // Preserve the Figma text frames in the SVG exports so centering retains their alignment.
334
+ ".mm-prompt-chat-icon{display:block;width:24px;height:29px}",
335
+ // Keep the existing whole-card click target and the two-control focus loop.
336
+ ".mm-prompt-open{position:absolute;inset:0;z-index:2;border:0;padding:0;border-radius:16px;background:transparent;cursor:pointer}",
337
+ ".mm-prompt-close{position:absolute;top:16px;right:16px;z-index:3;display:grid;place-items:center;width:44px;height:44px;padding:0;border:2px solid var(--mm-prompt-yellow);border-radius:50%;background:var(--mm-prompt-dark);cursor:pointer}",
338
+ ".mm-prompt-close-icon{display:block;width:18px;height:29px;pointer-events:none}",
339
+ ".mm-prompt-close:hover{background:var(--mm-prompt-blue)}",
340
+ ".mm-prompt-open:focus-visible,.mm-prompt-close:focus-visible{outline:3px solid var(--mm-prompt-yellow);outline-offset:3px}",
341
+ '@media(max-width:623px){.mm-prompt-backdrop{background:rgba(0,0,0,.7)}.mm-prompt-card{width:min(360px,calc(100vw - 24px));min-height:496px;padding:16px 12px;overflow:hidden}.mm-prompt-status{align-self:stretch;padding-right:40px}.mm-prompt-avatar{width:40px;height:40px}.mm-prompt-online{top:28.12px}.mm-prompt-status-text{min-width:0;font-size:18px;line-height:22px}.mm-prompt-hero{position:static;flex:1;min-height:332px;align-items:flex-end;height:auto;margin-top:0;padding:0}.mm-prompt-title{top:1px;flex:none;width:100%;height:auto;font-size:64px;line-height:56px;letter-spacing:-1.28px}.mm-prompt-mobile-break{display:block}.mm-prompt-portrait{position:absolute;top:93px;right:-37px;width:253px;height:285px}.mm-prompt-portrait::after{content:"";position:absolute;left:10px;right:0;bottom:0;height:60px;background:linear-gradient(to top,var(--mm-prompt-blue) 8.772%,transparent 94.737%)}.mm-prompt-cta{flex:none;height:48px;margin:24px 0 0}.mm-prompt-close{right:12px}.mm-prompt-open:focus-visible{outline-offset:-4px}}',
342
+ '.mm-prompt-fallback-title .mm-prompt-title{font-family:"MMIntakeKuunari",Arial,sans-serif;font-size:32px;letter-spacing:0}',
343
+ '.mm-prompt-fallback-status .mm-prompt-status-text{font-family:"MMIntakeNunitoSans",Arial,sans-serif;font-size:16px}',
344
+ ".mm-prompt-close-contrast{display:none}",
345
+ "@media(forced-colors:active){.mm-prompt-card,.mm-prompt-cta,.mm-prompt-close{border:1px solid ButtonText}.mm-prompt-close-icon,.mm-prompt-chat-icon{display:none}.mm-prompt-close-contrast{display:block;color:ButtonText;font:28px/1 Arial,sans-serif}.mm-prompt-open:focus-visible,.mm-prompt-close:focus-visible{outline:2px solid ButtonText;outline-offset:-4px}}"
346
+ ].join("");
347
+ const promptMarkup = (assetUrl) => [
348
+ '<div class="mm-prompt-card">',
349
+ '<div class="mm-prompt-status"><span class="mm-prompt-avatar" aria-hidden="true">',
350
+ `<img loading="lazy" class="mm-prompt-team" src="${assetUrl("consultation-team.png")}" alt="" width="56" height="56" decoding="async" />`,
351
+ `<picture class="mm-prompt-online"><source media="(max-width:623px)" srcset="${assetUrl("consultation-online-mobile.svg")}" /><img loading="lazy" src="${assetUrl("consultation-online-desktop.svg")}" alt="" /></picture>`,
352
+ '</span><p class="mm-prompt-status-text">Our team is <br class="mm-prompt-mobile-break" />available 24/7</p></div>',
353
+ '<div class="mm-prompt-hero"><h2 class="mm-prompt-title"><span class="mm-prompt-title-line">Click <br class="mm-prompt-mobile-break" />to start</span><span class="mm-prompt-title-line">your free</span><span class="mm-prompt-title-line">consultation</span></h2>',
354
+ `<div class="mm-prompt-portrait" aria-hidden="true"><img loading="lazy" class="mm-prompt-image" src="${assetUrl("consultation-john-morgan.png")}" alt="" width="270" height="304" decoding="async" /></div></div>`,
355
+ `<span class="mm-prompt-cta" aria-hidden="true"><img loading="lazy" class="mm-prompt-chat-icon" src="${assetUrl("consultation-chat.svg")}" alt="" width="24" height="29" /><span class="mm-prompt-cta-label">Chat now</span></span>`,
356
+ '<button class="mm-prompt-open" type="button" aria-label="Chat now about a free consultation"></button>',
357
+ `<button class="mm-prompt-close" type="button" aria-label="Dismiss consultation prompt"><img class="mm-prompt-close-icon" src="${assetUrl("consultation-close.svg")}" alt="" width="18" height="29" /><span class="mm-prompt-close-contrast" aria-hidden="true">×</span></button>`,
358
+ "</div>"
359
+ ].join("");
223
360
  const LUCIDE_ICON_PATHS = {
224
361
  "message-circle": '<path d="M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719"/>',
225
362
  "x": '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>',
@@ -235,7 +372,6 @@
235
372
  const FULL_PAGE_PANEL_QUERY = "(max-width:600px),(max-height:500px)";
236
373
  const lucideIcon = (name, className, strokeWidth = 2) => `<svg class="${className}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="${String(strokeWidth)}" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">${LUCIDE_ICON_PATHS[name]}</svg>`;
237
374
  const OPEN_CLOSE_ICON = lucideIcon("x", "mm-icon mm-icon-close");
238
- const PROMPT_CLOSE_ICON = lucideIcon("x", "mm-prompt-close-icon", 2.5);
239
375
  const WIDGET_STYLES = [
240
376
  ':host{all:initial;color-scheme:light;--mm-yellow:#fdeb01;--mm-yellow-hover:#f5de00;--mm-yellow-pressed:#e6cc00;--mm-blue:#115099;--mm-blue-hover:#0b3568;--mm-ink:#111;--mm-white:#fff;--mm-surface-blue:#f3f8ff;--mm-chrome:#e8eef2;--mm-line:#e2e8f0;--mm-muted:#545454;--mm-green:#0f9d58;--mm-font:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif}',
241
377
  "*,*::before,*::after{box-sizing:border-box}",
@@ -278,31 +414,19 @@
278
414
  ".mm-dot{width:8px;height:8px;border-radius:999px;background:var(--mm-muted);animation:mm-dot 1.2s ease-in-out infinite}",
279
415
  ".mm-dot:nth-child(2){animation-delay:.15s}",
280
416
  ".mm-dot:nth-child(3){animation-delay:.3s}",
281
- ".mm-prompt-backdrop{position:fixed;inset:0;z-index:2147483002;display:grid;place-items:center;padding:16px;overscroll-behavior:contain;touch-action:none;background:rgba(0,0,0,.64);animation:mm-prompt-backdrop-in .25s cubic-bezier(.23,1,.32,1) both;font-family:var(--mm-font)}",
282
- ".mm-prompt-card{position:relative;width:min(632px,calc(100vw - 32px),calc((100vh - 32px)*1.26148));aspect-ratio:632/501;animation:mm-prompt-card-in .25s cubic-bezier(.23,1,.32,1) both}",
283
- ".mm-prompt-image{display:block;width:100%;height:100%;object-fit:contain;pointer-events:none}",
284
- ".mm-prompt-open,.mm-prompt-close{position:absolute;border:0;outline:none;background:transparent;cursor:pointer}",
285
- ".mm-prompt-open{inset:0;border-radius:24px}",
286
- ".mm-prompt-close{top:4.5%;right:3.1%;z-index:1;display:grid;place-items:center;width:44px;height:44px;border-radius:999px;color:var(--mm-white)}",
287
- '.mm-prompt-close::before{content:"";position:absolute;width:28px;height:28px;border-radius:999px;background:#050505;box-shadow:0 1px 4px rgba(0,0,0,.35);transition:background-color .12s ease}',
288
- ".mm-prompt-close-icon{position:relative;display:block;width:16px;height:16px;pointer-events:none}",
289
- ".mm-prompt-close:hover::before{background:#252525}",
290
- ".mm-prompt-open:focus-visible{outline:3px solid var(--mm-yellow);outline-offset:-8px}",
291
- ".mm-prompt-close:focus-visible{outline:none}",
292
- ".mm-prompt-close:focus-visible::before{background:#050505;box-shadow:inset 0 0 0 2px var(--mm-yellow),0 1px 4px rgba(0,0,0,.35)}",
417
+ PROMPT_STYLES,
293
418
  "[hidden]{display:none!important}",
294
419
  `@media ${FULL_PAGE_PANEL_QUERY}{.mm-launcher[aria-expanded="true"]{top:calc(var(--mm-viewport-top,0px) + 10px);right:10px;bottom:auto;width:44px;height:44px}.mm-chat{left:0;right:0;top:var(--mm-viewport-top,auto);bottom:0;width:auto;height:var(--mm-viewport-height,100dvh);min-height:0;border:0;border-radius:0;box-shadow:0 calc(100lvh - 100svh) 0 var(--mm-panel-bottom,var(--mm-chrome))}}`,
295
- "@media(forced-colors:active){.mm-prompt-close:focus-visible{outline:2px solid ButtonText;outline-offset:-9px}}",
296
420
  "@media(prefers-reduced-motion:reduce){.mm-launcher,.mm-chat{animation:none;transition:none}.mm-prompt-card{animation-name:mm-prompt-backdrop-in}.mm-dot{animation:none}}"
297
421
  ].join("");
298
- const widgetMarkup = (launcherImageUrl, promptImageUrl) => [
422
+ const widgetMarkup = (assetUrl) => [
299
423
  `<style>${WIDGET_STYLES}</style>`,
300
- `<button class="mm-launcher" type="button" aria-label="Open intake chat" aria-expanded="false" draggable="false"><img class="mm-text-us" src="${launcherImageUrl}" alt="" width="38" height="147" decoding="async" />${OPEN_CLOSE_ICON}</button>`,
424
+ `<button class="mm-launcher" type="button" aria-label="Open intake chat" aria-expanded="false" draggable="false"><img class="mm-text-us" src="${assetUrl("button-text-us.png")}" alt="" width="38" height="147" decoding="async" />${OPEN_CLOSE_ICON}</button>`,
301
425
  '<section class="mm-chat" role="dialog" aria-label="Morgan and Morgan intake chat" inert hidden>',
302
426
  '<div class="mm-frame-wrap"><div class="mm-loading" role="status" aria-label="Loading secure chat"><span class="mm-dot"></span><span class="mm-dot"></span><span class="mm-dot"></span></div><iframe class="mm-frame mm-chat-frame" title="Morgan and Morgan intake chat" referrerpolicy="strict-origin-when-cross-origin"></iframe></div>',
303
427
  "</section>",
304
428
  '<section class="mm-prompt-backdrop" role="dialog" aria-modal="true" aria-label="Start your free consultation" inert hidden>',
305
- `<div class="mm-prompt-card"><img class="mm-prompt-image" src="${promptImageUrl}" alt="Our team is available 24/7. Chat with a real person. Click to start your free consultation." width="632" height="501" decoding="async" /><button class="mm-prompt-open" type="button" aria-label="Message us about a free consultation"></button><button class="mm-prompt-close" type="button" aria-label="Dismiss consultation prompt">${PROMPT_CLOSE_ICON}</button></div>`,
429
+ promptMarkup(assetUrl),
306
430
  "</section>"
307
431
  ].join("");
308
432
  const requireElement = (shadow, selector) => {
@@ -312,9 +436,11 @@
312
436
  }
313
437
  return element;
314
438
  };
315
- const renderWidgetShell = (shadow, launcherImageUrl, promptImageUrl) => {
316
- shadow.innerHTML = widgetMarkup(launcherImageUrl, promptImageUrl);
439
+ const renderWidgetShell = (shadow, assetUrl) => {
440
+ shadow.innerHTML = widgetMarkup(assetUrl);
441
+ const disposePromptFonts = registerPromptFonts(assetUrl, requireElement(shadow, ".mm-prompt-backdrop"));
317
442
  return {
443
+ disposePromptFonts,
318
444
  chatDialog: requireElement(shadow, ".mm-chat"),
319
445
  chatFrame: requireElement(shadow, ".mm-chat-frame"),
320
446
  chatLoading: requireElement(shadow, ".mm-loading"),
@@ -325,7 +451,6 @@
325
451
  };
326
452
  };
327
453
  const PANEL_ANIMATION_MS = 180;
328
- const PROMPT_DELAY_MS = 5e3;
329
454
  const PROMPT_COOLDOWN_MS = 24 * 60 * 60 * 1e3;
330
455
  const PROMPT_COOLDOWN_KEY = "mm-intake-widget-prompt-dismissed-at";
331
456
  const PROMPT_SESSION_KEY = "mm-intake-widget-prompt-dismissed";
@@ -367,14 +492,15 @@
367
492
  }
368
493
  };
369
494
  const mountIntakeWidget = (config, onDestroy) => {
495
+ var _a;
370
496
  const mount = document.createElement("div");
371
497
  mount.dataset.mmIntakeWidget = "";
372
498
  const shadow = mount.attachShadow({ mode: "open" });
373
499
  const assetUrl = (fileName) => {
374
- var _a;
375
- return new URL(fileName, (_a = config.assetBaseUrl) != null ? _a : `${config.chatOrigin}/intake-widget/`).href;
500
+ var _a2;
501
+ return new URL(fileName, (_a2 = config.assetBaseUrl) != null ? _a2 : `${config.chatOrigin}/intake-widget/`).href;
376
502
  };
377
- const elements = renderWidgetShell(shadow, assetUrl("button-text-us.png"), assetUrl("consultation-prompt.webp"));
503
+ const elements = renderWidgetShell(shadow, assetUrl);
378
504
  document.body.append(mount);
379
505
  if (typeof mount.showPopover === "function") {
380
506
  mount.setAttribute("popover", "manual");
@@ -398,8 +524,8 @@
398
524
  elements.launcher.setAttribute("aria-label", isOpen ? "Close intake chat" : "Open intake chat");
399
525
  };
400
526
  const postToChat = (message) => {
401
- var _a;
402
- (_a = elements.chatFrame.contentWindow) == null ? void 0 : _a.postMessage(message, config.chatOrigin);
527
+ var _a2;
528
+ (_a2 = elements.chatFrame.contentWindow) == null ? void 0 : _a2.postMessage(message, config.chatOrigin);
403
529
  };
404
530
  const postHostReady = () => {
405
531
  const currentUrl = globalThis.location.href;
@@ -427,10 +553,10 @@
427
553
  postToChat(createEmbedProtocolMessage(EMBED_MESSAGE_TYPE.FOCUS_COMPOSER));
428
554
  };
429
555
  const answerAuthTokenRequest = async () => {
430
- var _a;
556
+ var _a2;
431
557
  let token;
432
558
  try {
433
- token = await ((_a = config.getAuthToken) == null ? void 0 : _a.call(config));
559
+ token = await ((_a2 = config.getAuthToken) == null ? void 0 : _a2.call(config));
434
560
  } catch {
435
561
  token = void 0;
436
562
  }
@@ -462,11 +588,11 @@
462
588
  }
463
589
  };
464
590
  const showPrompt = () => {
465
- var _a, _b, _c;
591
+ var _a2, _b, _c;
466
592
  globalThis.clearTimeout(promptTimer);
467
593
  promptTimer = void 0;
468
594
  const host = globalThis;
469
- const mxPopup = (_c = (_a = host.drupalSettings) == null ? void 0 : _a.mxPopup) != null ? _c : (_b = host.mxChatbotSettings) == null ? void 0 : _b.mxPopup;
595
+ const mxPopup = (_c = (_a2 = host.drupalSettings) == null ? void 0 : _a2.mxPopup) != null ? _c : (_b = host.mxChatbotSettings) == null ? void 0 : _b.mxPopup;
470
596
  if (mxPopup === false || hasPromptCooldown() || elements.chatDialog.dataset.state === "open") {
471
597
  return;
472
598
  }
@@ -528,7 +654,7 @@
528
654
  }
529
655
  };
530
656
  const restoreHostChrome = () => {
531
- var _a, _b;
657
+ var _a2, _b;
532
658
  if (themeColorBackup === null) {
533
659
  return;
534
660
  }
@@ -539,7 +665,7 @@
539
665
  createdThemeColor = null;
540
666
  themeColorBackup = null;
541
667
  for (const [index, root] of hostRoots.entries()) {
542
- root.style.backgroundColor = (_a = hostBackgroundColorBackup == null ? void 0 : hostBackgroundColorBackup[index]) != null ? _a : "";
668
+ root.style.backgroundColor = (_a2 = hostBackgroundColorBackup == null ? void 0 : hostBackgroundColorBackup[index]) != null ? _a2 : "";
543
669
  root.style.overflow = (_b = hostOverflowBackup == null ? void 0 : hostOverflowBackup[index]) != null ? _b : "";
544
670
  }
545
671
  hostBackgroundColorBackup = null;
@@ -565,16 +691,16 @@
565
691
  syncHostChrome();
566
692
  }
567
693
  const startViewportTracking = () => {
568
- var _a, _b;
569
- (_a = globalThis.visualViewport) == null ? void 0 : _a.addEventListener("resize", syncViewport);
694
+ var _a2, _b;
695
+ (_a2 = globalThis.visualViewport) == null ? void 0 : _a2.addEventListener("resize", syncViewport);
570
696
  (_b = globalThis.visualViewport) == null ? void 0 : _b.addEventListener("scroll", syncViewport);
571
697
  fullPagePanelQuery == null ? void 0 : fullPagePanelQuery.addEventListener("change", syncHostChrome);
572
698
  syncViewport();
573
699
  syncHostChrome();
574
700
  };
575
701
  const stopViewportTracking = () => {
576
- var _a, _b;
577
- (_a = globalThis.visualViewport) == null ? void 0 : _a.removeEventListener("resize", syncViewport);
702
+ var _a2, _b;
703
+ (_a2 = globalThis.visualViewport) == null ? void 0 : _a2.removeEventListener("resize", syncViewport);
578
704
  (_b = globalThis.visualViewport) == null ? void 0 : _b.removeEventListener("scroll", syncViewport);
579
705
  fullPagePanelQuery == null ? void 0 : fullPagePanelQuery.removeEventListener("change", syncHostChrome);
580
706
  setKeyboardOpen(false);
@@ -611,7 +737,7 @@
611
737
  }
612
738
  };
613
739
  const closeChat = () => {
614
- var _a;
740
+ var _a2;
615
741
  elements.launcher.focus();
616
742
  writeOpenState(false);
617
743
  const wasOpen = elements.chatDialog.dataset.state === "open";
@@ -624,7 +750,7 @@
624
750
  if (!wasOpen) {
625
751
  return;
626
752
  }
627
- if (((_a = matchMediaQuery("(prefers-reduced-motion: reduce)")) == null ? void 0 : _a.matches) === true) {
753
+ if (((_a2 = matchMediaQuery("(prefers-reduced-motion: reduce)")) == null ? void 0 : _a2.matches) === true) {
628
754
  finishChatClose();
629
755
  return;
630
756
  }
@@ -700,6 +826,7 @@
700
826
  }
701
827
  };
702
828
  const destroy = () => {
829
+ elements.disposePromptFonts();
703
830
  stopViewportTracking();
704
831
  globalThis.clearTimeout(chatCloseTimer);
705
832
  globalThis.clearTimeout(chatReadyTimer);
@@ -740,7 +867,7 @@
740
867
  if (returnedFromSigning || readOpenState()) {
741
868
  openChat();
742
869
  } else if (!hasPromptCooldown()) {
743
- promptTimer = globalThis.setTimeout(showPrompt, PROMPT_DELAY_MS);
870
+ promptTimer = globalThis.setTimeout(showPrompt, (_a = config.promptDelayMs) != null ? _a : DEFAULT_PROMPT_DELAY_MS);
744
871
  }
745
872
  return Object.freeze({
746
873
  close: closeChat,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morgan-and-morgan/intake-agent",
3
- "version": "1.0.0-alpha.15",
3
+ "version": "1.0.0-alpha.17",
4
4
  "description": "Embeddable chat widget loader.",
5
5
  "license": "UNLICENSED",
6
6
  "files": [