@morgan-and-morgan/intake-agent 1.0.0-alpha.16 → 1.0.0-alpha.18
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 +17 -4
- package/dist/consultation-john-morgan.webp +0 -0
- package/dist/consultation-team.webp +0 -0
- package/dist/mm-intake-widget.js +47 -34
- package/package.json +2 -2
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,17 +35,28 @@ 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.
|
|
43
54
|
|
|
44
55
|
## Consultation prompt design
|
|
45
56
|
|
|
46
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).
|
|
47
|
-
|
|
58
|
+
The portrait and avatar use cropped, optimized original photos; CSS applies the Figma framing.
|
|
59
|
+
Status dots and icons are exported from their layers.
|
|
48
60
|
The fonts are the existing website's [Kuunari Bold Compressed](https://www.forthepeople.com/themes/custom/morgan/fonts/Kuunari/kuunari-bold-compressed.woff2)
|
|
49
61
|
and [Nunito Sans](https://www.forthepeople.com/themes/custom/morgan/fonts/NunitoSans/NunitoSans.woff2).
|
|
50
62
|
On forthepeople.com, the prompt loads these fonts from the host site to respect
|
|
@@ -52,5 +64,6 @@ its font security policy. Other hosts use the bundled copies.
|
|
|
52
64
|
Keep prompt assets in `public/` and `apps/expo/public/intake-widget/` identical;
|
|
53
65
|
the asset tests check both the CDN and portal copies.
|
|
54
66
|
|
|
55
|
-
Retain `consultation-prompt.webp`
|
|
56
|
-
|
|
67
|
+
Retain `consultation-prompt.webp`, `consultation-team.png`, and
|
|
68
|
+
`consultation-john-morgan.png` at both asset origins for older deployed widget
|
|
69
|
+
versions. The current prompt requests the separate WebP photos instead.
|
|
Binary file
|
|
Binary file
|
package/dist/mm-intake-widget.js
CHANGED
|
@@ -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 = {
|
|
@@ -298,12 +311,16 @@
|
|
|
298
311
|
return dispose;
|
|
299
312
|
};
|
|
300
313
|
const PROMPT_STYLES = [
|
|
314
|
+
// Initial dialog focus announces the popup without drawing a ring around its backdrop.
|
|
315
|
+
".mm-prompt-backdrop:focus{outline:none}",
|
|
301
316
|
`.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}`,
|
|
302
317
|
// Auto margins center the card when it fits and keep its top reachable when it overflows.
|
|
303
318
|
".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}",
|
|
304
319
|
".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)}",
|
|
305
320
|
".mm-prompt-avatar{position:relative;flex:none;width:56px;height:56px}",
|
|
306
|
-
|
|
321
|
+
// Crop the original at (310, 234, 454, 454), then preserve Figma's avatar framing in CSS.
|
|
322
|
+
".mm-prompt-team-crop{position:absolute;inset:0;overflow:hidden;border-radius:50%;transform:scaleX(-1)}",
|
|
323
|
+
".mm-prompt-team{position:absolute;display:block;left:-3.26787%;top:-0.29881%;width:103.65294%;height:103.65298%;max-width:none;transform:rotate(-1.62035deg);transform-origin:0 0}",
|
|
307
324
|
// The exported indicator bounds include Figma's 2px outside stroke.
|
|
308
325
|
".mm-prompt-online{position:absolute;right:-2px;top:40.17px;display:block;line-height:0}",
|
|
309
326
|
".mm-prompt-online img{display:block}",
|
|
@@ -313,8 +330,9 @@
|
|
|
313
330
|
'.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}',
|
|
314
331
|
".mm-prompt-title-line{display:block;white-space:nowrap}",
|
|
315
332
|
".mm-prompt-mobile-break{display:none}",
|
|
316
|
-
|
|
317
|
-
".mm-prompt-
|
|
333
|
+
// Figma node 21250:169707 crops the transparent source without baking in the card background.
|
|
334
|
+
".mm-prompt-portrait{position:relative;flex:none;width:270px;height:304px;overflow:hidden}",
|
|
335
|
+
".mm-prompt-image{position:absolute;display:block;left:-40.22%;top:-3.95%;width:181.72%;height:107.91%;max-width:none;pointer-events:none}",
|
|
318
336
|
`.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}`,
|
|
319
337
|
".mm-prompt-cta-label{min-width:90px}",
|
|
320
338
|
// Preserve the Figma text frames in the SVG exports so centering retains their alignment.
|
|
@@ -334,11 +352,11 @@
|
|
|
334
352
|
const promptMarkup = (assetUrl) => [
|
|
335
353
|
'<div class="mm-prompt-card">',
|
|
336
354
|
'<div class="mm-prompt-status"><span class="mm-prompt-avatar" aria-hidden="true">',
|
|
337
|
-
`<img loading="lazy" class="mm-prompt-team" src="${assetUrl("consultation-team.
|
|
355
|
+
`<span class="mm-prompt-team-crop"><img loading="lazy" class="mm-prompt-team" src="${assetUrl("consultation-team.webp")}" alt="" width="360" height="360" decoding="async" /></span>`,
|
|
338
356
|
`<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>`,
|
|
339
357
|
'</span><p class="mm-prompt-status-text">Our team is <br class="mm-prompt-mobile-break" />available 24/7</p></div>',
|
|
340
358
|
'<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>',
|
|
341
|
-
`<div class="mm-prompt-portrait" aria-hidden="true"><img loading="lazy" class="mm-prompt-image" src="${assetUrl("consultation-john-morgan.
|
|
359
|
+
`<div class="mm-prompt-portrait" aria-hidden="true"><img loading="lazy" class="mm-prompt-image" src="${assetUrl("consultation-john-morgan.webp")}" alt="" width="1500" height="1000" decoding="async" /></div></div>`,
|
|
342
360
|
`<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>`,
|
|
343
361
|
'<button class="mm-prompt-open" type="button" aria-label="Chat now about a free consultation"></button>',
|
|
344
362
|
`<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>`,
|
|
@@ -412,7 +430,7 @@
|
|
|
412
430
|
'<section class="mm-chat" role="dialog" aria-label="Morgan and Morgan intake chat" inert hidden>',
|
|
413
431
|
'<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>',
|
|
414
432
|
"</section>",
|
|
415
|
-
'<section class="mm-prompt-backdrop" role="dialog" aria-modal="true" aria-label="Start your free consultation" inert hidden>',
|
|
433
|
+
'<section class="mm-prompt-backdrop" role="dialog" aria-modal="true" aria-label="Start your free consultation" tabindex="-1" inert hidden>',
|
|
416
434
|
promptMarkup(assetUrl),
|
|
417
435
|
"</section>"
|
|
418
436
|
].join("");
|
|
@@ -438,7 +456,6 @@
|
|
|
438
456
|
};
|
|
439
457
|
};
|
|
440
458
|
const PANEL_ANIMATION_MS = 180;
|
|
441
|
-
const PROMPT_DELAY_MS = 5e3;
|
|
442
459
|
const PROMPT_COOLDOWN_MS = 24 * 60 * 60 * 1e3;
|
|
443
460
|
const PROMPT_COOLDOWN_KEY = "mm-intake-widget-prompt-dismissed-at";
|
|
444
461
|
const PROMPT_SESSION_KEY = "mm-intake-widget-prompt-dismissed";
|
|
@@ -480,12 +497,13 @@
|
|
|
480
497
|
}
|
|
481
498
|
};
|
|
482
499
|
const mountIntakeWidget = (config, onDestroy) => {
|
|
500
|
+
var _a;
|
|
483
501
|
const mount = document.createElement("div");
|
|
484
502
|
mount.dataset.mmIntakeWidget = "";
|
|
485
503
|
const shadow = mount.attachShadow({ mode: "open" });
|
|
486
504
|
const assetUrl = (fileName) => {
|
|
487
|
-
var
|
|
488
|
-
return new URL(fileName, (
|
|
505
|
+
var _a2;
|
|
506
|
+
return new URL(fileName, (_a2 = config.assetBaseUrl) != null ? _a2 : `${config.chatOrigin}/intake-widget/`).href;
|
|
489
507
|
};
|
|
490
508
|
const elements = renderWidgetShell(shadow, assetUrl);
|
|
491
509
|
document.body.append(mount);
|
|
@@ -511,8 +529,8 @@
|
|
|
511
529
|
elements.launcher.setAttribute("aria-label", isOpen ? "Close intake chat" : "Open intake chat");
|
|
512
530
|
};
|
|
513
531
|
const postToChat = (message) => {
|
|
514
|
-
var
|
|
515
|
-
(
|
|
532
|
+
var _a2;
|
|
533
|
+
(_a2 = elements.chatFrame.contentWindow) == null ? void 0 : _a2.postMessage(message, config.chatOrigin);
|
|
516
534
|
};
|
|
517
535
|
const postHostReady = () => {
|
|
518
536
|
const currentUrl = globalThis.location.href;
|
|
@@ -540,10 +558,10 @@
|
|
|
540
558
|
postToChat(createEmbedProtocolMessage(EMBED_MESSAGE_TYPE.FOCUS_COMPOSER));
|
|
541
559
|
};
|
|
542
560
|
const answerAuthTokenRequest = async () => {
|
|
543
|
-
var
|
|
561
|
+
var _a2;
|
|
544
562
|
let token;
|
|
545
563
|
try {
|
|
546
|
-
token = await ((
|
|
564
|
+
token = await ((_a2 = config.getAuthToken) == null ? void 0 : _a2.call(config));
|
|
547
565
|
} catch {
|
|
548
566
|
token = void 0;
|
|
549
567
|
}
|
|
@@ -575,18 +593,18 @@
|
|
|
575
593
|
}
|
|
576
594
|
};
|
|
577
595
|
const showPrompt = () => {
|
|
578
|
-
var
|
|
596
|
+
var _a2, _b, _c;
|
|
579
597
|
globalThis.clearTimeout(promptTimer);
|
|
580
598
|
promptTimer = void 0;
|
|
581
599
|
const host = globalThis;
|
|
582
|
-
const mxPopup = (_c = (
|
|
600
|
+
const mxPopup = (_c = (_a2 = host.drupalSettings) == null ? void 0 : _a2.mxPopup) != null ? _c : (_b = host.mxChatbotSettings) == null ? void 0 : _b.mxPopup;
|
|
583
601
|
if (mxPopup === false || hasPromptCooldown() || elements.chatDialog.dataset.state === "open") {
|
|
584
602
|
return;
|
|
585
603
|
}
|
|
586
604
|
promptReturnFocus = document.activeElement instanceof HTMLElement && document.activeElement !== mount ? document.activeElement : null;
|
|
587
605
|
elements.promptBackdrop.hidden = false;
|
|
588
606
|
elements.promptBackdrop.removeAttribute("inert");
|
|
589
|
-
elements.
|
|
607
|
+
elements.promptBackdrop.focus({ preventScroll: true });
|
|
590
608
|
};
|
|
591
609
|
const syncViewport = () => {
|
|
592
610
|
const viewport = globalThis.visualViewport;
|
|
@@ -641,7 +659,7 @@
|
|
|
641
659
|
}
|
|
642
660
|
};
|
|
643
661
|
const restoreHostChrome = () => {
|
|
644
|
-
var
|
|
662
|
+
var _a2, _b;
|
|
645
663
|
if (themeColorBackup === null) {
|
|
646
664
|
return;
|
|
647
665
|
}
|
|
@@ -652,7 +670,7 @@
|
|
|
652
670
|
createdThemeColor = null;
|
|
653
671
|
themeColorBackup = null;
|
|
654
672
|
for (const [index, root] of hostRoots.entries()) {
|
|
655
|
-
root.style.backgroundColor = (
|
|
673
|
+
root.style.backgroundColor = (_a2 = hostBackgroundColorBackup == null ? void 0 : hostBackgroundColorBackup[index]) != null ? _a2 : "";
|
|
656
674
|
root.style.overflow = (_b = hostOverflowBackup == null ? void 0 : hostOverflowBackup[index]) != null ? _b : "";
|
|
657
675
|
}
|
|
658
676
|
hostBackgroundColorBackup = null;
|
|
@@ -678,16 +696,16 @@
|
|
|
678
696
|
syncHostChrome();
|
|
679
697
|
}
|
|
680
698
|
const startViewportTracking = () => {
|
|
681
|
-
var
|
|
682
|
-
(
|
|
699
|
+
var _a2, _b;
|
|
700
|
+
(_a2 = globalThis.visualViewport) == null ? void 0 : _a2.addEventListener("resize", syncViewport);
|
|
683
701
|
(_b = globalThis.visualViewport) == null ? void 0 : _b.addEventListener("scroll", syncViewport);
|
|
684
702
|
fullPagePanelQuery == null ? void 0 : fullPagePanelQuery.addEventListener("change", syncHostChrome);
|
|
685
703
|
syncViewport();
|
|
686
704
|
syncHostChrome();
|
|
687
705
|
};
|
|
688
706
|
const stopViewportTracking = () => {
|
|
689
|
-
var
|
|
690
|
-
(
|
|
707
|
+
var _a2, _b;
|
|
708
|
+
(_a2 = globalThis.visualViewport) == null ? void 0 : _a2.removeEventListener("resize", syncViewport);
|
|
691
709
|
(_b = globalThis.visualViewport) == null ? void 0 : _b.removeEventListener("scroll", syncViewport);
|
|
692
710
|
fullPagePanelQuery == null ? void 0 : fullPagePanelQuery.removeEventListener("change", syncHostChrome);
|
|
693
711
|
setKeyboardOpen(false);
|
|
@@ -724,7 +742,7 @@
|
|
|
724
742
|
}
|
|
725
743
|
};
|
|
726
744
|
const closeChat = () => {
|
|
727
|
-
var
|
|
745
|
+
var _a2;
|
|
728
746
|
elements.launcher.focus();
|
|
729
747
|
writeOpenState(false);
|
|
730
748
|
const wasOpen = elements.chatDialog.dataset.state === "open";
|
|
@@ -737,7 +755,7 @@
|
|
|
737
755
|
if (!wasOpen) {
|
|
738
756
|
return;
|
|
739
757
|
}
|
|
740
|
-
if (((
|
|
758
|
+
if (((_a2 = matchMediaQuery("(prefers-reduced-motion: reduce)")) == null ? void 0 : _a2.matches) === true) {
|
|
741
759
|
finishChatClose();
|
|
742
760
|
return;
|
|
743
761
|
}
|
|
@@ -791,14 +809,9 @@
|
|
|
791
809
|
};
|
|
792
810
|
const handleKeydown = (event) => {
|
|
793
811
|
if (event.key === "Tab" && elements.promptBackdrop.hidden === false) {
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
elements.promptOpen.focus();
|
|
798
|
-
} else if (event.shiftKey && activeElement === elements.promptOpen) {
|
|
799
|
-
event.preventDefault();
|
|
800
|
-
elements.promptClose.focus();
|
|
801
|
-
}
|
|
812
|
+
event.preventDefault();
|
|
813
|
+
const next = shadow.activeElement === elements.promptClose ? elements.promptOpen : elements.promptClose;
|
|
814
|
+
next.focus();
|
|
802
815
|
return;
|
|
803
816
|
}
|
|
804
817
|
if (event.key !== "Escape") {
|
|
@@ -854,7 +867,7 @@
|
|
|
854
867
|
if (returnedFromSigning || readOpenState()) {
|
|
855
868
|
openChat();
|
|
856
869
|
} else if (!hasPromptCooldown()) {
|
|
857
|
-
promptTimer = globalThis.setTimeout(showPrompt,
|
|
870
|
+
promptTimer = globalThis.setTimeout(showPrompt, (_a = config.promptDelayMs) != null ? _a : DEFAULT_PROMPT_DELAY_MS);
|
|
858
871
|
}
|
|
859
872
|
return Object.freeze({
|
|
860
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.
|
|
3
|
+
"version": "1.0.0-alpha.18",
|
|
4
4
|
"description": "Embeddable chat widget loader.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"files": [
|
|
@@ -34,6 +34,6 @@
|
|
|
34
34
|
"@typescript/native-preview": "catalog:",
|
|
35
35
|
"lucide-react-native": "^0.562.0",
|
|
36
36
|
"typescript": "~5.9.3",
|
|
37
|
-
"vite": "^7.3.
|
|
37
|
+
"vite": "^7.3.7"
|
|
38
38
|
}
|
|
39
39
|
}
|