@morgan-and-morgan/intake-agent 0.1.0-alpha.1 → 1.0.0-alpha.1
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 +15 -0
- package/dist/mm-intake-widget.js +39 -12
- package/package.json +12 -11
package/README.md
CHANGED
|
@@ -9,6 +9,21 @@ Load the widget with a script tag, pinning an exact version:
|
|
|
9
9
|
></script>
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
+
## Versions
|
|
13
|
+
|
|
14
|
+
semantic-release publishes on every merge that touches this package and picks
|
|
15
|
+
the version from the commit messages, so the `version` in `package.json` is a
|
|
16
|
+
placeholder, don't bump it by hand.
|
|
17
|
+
|
|
18
|
+
| Branch | Version | dist-tag |
|
|
19
|
+
| --- | --- | --- |
|
|
20
|
+
| `develop` | `x.y.z-alpha.N` | `alpha` |
|
|
21
|
+
| `release/**` | `x.y.z-beta.N` | `beta` |
|
|
22
|
+
| `main` | `x.y.z` | `latest` |
|
|
23
|
+
|
|
24
|
+
`fix:` bumps the patch, `feat:` the minor, a `BREAKING CHANGE:` footer the
|
|
25
|
+
major. Every release is tagged `intake-agent-vx.y.z`.
|
|
26
|
+
|
|
12
27
|
## Attributes
|
|
13
28
|
|
|
14
29
|
| Attribute | Default | Purpose |
|
package/dist/mm-intake-widget.js
CHANGED
|
@@ -28,17 +28,14 @@
|
|
|
28
28
|
};
|
|
29
29
|
const readOpenState = (storage = sessionStorage) => readFlag(OPEN_STATE_KEY, storage);
|
|
30
30
|
const writeOpenState = (isOpen, storage = sessionStorage) => writeFlag(OPEN_STATE_KEY, isOpen, storage);
|
|
31
|
-
const readUnreadState = (storage = sessionStorage) =>
|
|
32
|
-
const writeUnreadState = (hasUnread, storage = sessionStorage) => {
|
|
33
|
-
if (hasUnread) {
|
|
34
|
-
writeFlag(UNREAD_STATE_KEY, true, storage);
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
31
|
+
const readUnreadState = (storage = sessionStorage) => {
|
|
37
32
|
try {
|
|
38
|
-
storage.
|
|
33
|
+
return storage.getItem(UNREAD_STATE_KEY) !== "false";
|
|
39
34
|
} catch {
|
|
35
|
+
return false;
|
|
40
36
|
}
|
|
41
37
|
};
|
|
38
|
+
const writeUnreadState = (hasUnread, storage = sessionStorage) => writeFlag(UNREAD_STATE_KEY, hasUnread, storage);
|
|
42
39
|
const EMBED_PROTOCOL_VERSION = 1;
|
|
43
40
|
const EMBED_MESSAGE_TYPE = Object.freeze({
|
|
44
41
|
HOST_READY: "mm:intake:host-ready",
|
|
@@ -148,6 +145,9 @@
|
|
|
148
145
|
"@keyframes mm-dot{0%,100%{opacity:.4;transform:scale(1)}50%{opacity:1;transform:scale(1.2)}}",
|
|
149
146
|
// The launcher stacks above the chat panel: it is the only close affordance
|
|
150
147
|
// (the panel is chrome-free), so the full-screen mobile panel must not cover it.
|
|
148
|
+
// Against the host page the near-max z-index is only the fallback for
|
|
149
|
+
// browsers without the Popover API — everywhere else the mount rides the top
|
|
150
|
+
// layer (widget.ts) and outranks any host z-index.
|
|
151
151
|
".mm-launcher{position:fixed;right:24px;bottom:24px;z-index:2147483001;display:grid;width:64px;height:64px;place-items:center;border:2px solid var(--mm-white);border-radius:999px;outline:none;background:var(--mm-yellow);color:var(--mm-ink);box-shadow:0 10px 28px rgba(17,17,17,.24);cursor:pointer;user-select:none;-webkit-user-select:none;-webkit-user-drag:none;animation:mm-launcher-in .25s ease-out;transition:background-color .15s ease,transform .15s ease,box-shadow .15s ease}",
|
|
152
152
|
".mm-launcher *{user-select:none;-webkit-user-select:none;-webkit-user-drag:none}",
|
|
153
153
|
".mm-launcher:hover{background:var(--mm-yellow-hover);box-shadow:0 12px 32px rgba(17,17,17,.28);transform:translateY(-1px)}",
|
|
@@ -200,7 +200,18 @@
|
|
|
200
200
|
// mount (custom properties reach into the shadow tree) to lift the launcher
|
|
201
201
|
// clear of it. Only the closed launcher: open, it moves to the top corner and
|
|
202
202
|
// tracks the viewport instead.
|
|
203
|
-
|
|
203
|
+
// `left:0;right:0` rather than `width:100vw`: `100vw` counts the classic
|
|
204
|
+
// scrollbar the host page still has behind us, so a right-anchored panel that
|
|
205
|
+
// wide starts a scrollbar's width off the left edge and clips its own header.
|
|
206
|
+
//
|
|
207
|
+
// The shadow fills the strip below the panel. Safari's bottom bar is
|
|
208
|
+
// translucent once it collapses — scrolled down, or any time the keyboard is
|
|
209
|
+
// up — and `visualViewport.height` stops short of it, so the host page shows
|
|
210
|
+
// through underneath the chat. `lvh - svh` is exactly that bar's height, and
|
|
211
|
+
// it is 0 wherever the browser has no collapsing bar, so this costs nothing
|
|
212
|
+
// there. A shadow rather than extra height: the trust line and composer must
|
|
213
|
+
// stay above the bar, not slide under it.
|
|
214
|
+
`@media ${FULL_PAGE_PANEL_QUERY}{.mm-launcher{right:16px;bottom:var(--mm-launcher-bottom,16px)}.mm-launcher[aria-expanded="true"]{top:calc(var(--mm-viewport-top,0px) + 10px);right:10px;bottom:auto;width:44px;height:44px;box-shadow:0 4px 14px rgba(17,17,17,.2)}.mm-launcher[aria-expanded="true"] .mm-icon{width:22px;height:22px}.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))}}`,
|
|
204
215
|
"@media(prefers-reduced-motion:reduce){.mm-launcher,.mm-chat{animation:none;transition:none}.mm-badge::after,.mm-dot{animation:none}}"
|
|
205
216
|
].join("");
|
|
206
217
|
const WIDGET_MARKUP = [
|
|
@@ -253,6 +264,14 @@
|
|
|
253
264
|
const shadow = mount.attachShadow({ mode: "open" });
|
|
254
265
|
const elements = renderWidgetShell(shadow);
|
|
255
266
|
document.body.append(mount);
|
|
267
|
+
if (typeof mount.showPopover === "function") {
|
|
268
|
+
mount.setAttribute("popover", "manual");
|
|
269
|
+
mount.style.cssText = "position:fixed;inset:0 auto auto 0;width:0;height:0;margin:0;border:0;padding:0;overflow:visible;background:transparent";
|
|
270
|
+
try {
|
|
271
|
+
mount.showPopover();
|
|
272
|
+
} catch {
|
|
273
|
+
}
|
|
274
|
+
}
|
|
256
275
|
const completedSigningEnvelopeId = readCompletedSigningEnvelopeId();
|
|
257
276
|
let chatHasLoaded = false;
|
|
258
277
|
let chatCloseTimer;
|
|
@@ -260,11 +279,14 @@
|
|
|
260
279
|
let isKeyboardOpen = false;
|
|
261
280
|
let hasReleasedKeyboardEarly = false;
|
|
262
281
|
let hasUnread = readUnreadState();
|
|
282
|
+
const loadUnreadIndicator = () => {
|
|
283
|
+
elements.unreadBadge.hidden = !hasUnread;
|
|
284
|
+
syncLauncherLabel();
|
|
285
|
+
};
|
|
263
286
|
const setHasUnread = (next) => {
|
|
264
287
|
hasUnread = next;
|
|
265
|
-
elements.unreadBadge.hidden = !next;
|
|
266
288
|
writeUnreadState(next);
|
|
267
|
-
|
|
289
|
+
loadUnreadIndicator();
|
|
268
290
|
};
|
|
269
291
|
const syncLauncherLabel = () => {
|
|
270
292
|
const isOpen = elements.chatDialog.dataset.state === "open";
|
|
@@ -338,9 +360,11 @@
|
|
|
338
360
|
let createdThemeColor = null;
|
|
339
361
|
const hostRoots = [document.documentElement, document.body];
|
|
340
362
|
let hostBackgroundColorBackup = null;
|
|
363
|
+
let hostOverflowBackup = null;
|
|
341
364
|
const borrowHostChrome = () => {
|
|
342
365
|
if (themeColorBackup === null) {
|
|
343
366
|
hostBackgroundColorBackup = hostRoots.map((root) => root.style.backgroundColor);
|
|
367
|
+
hostOverflowBackup = hostRoots.map((root) => root.style.overflow);
|
|
344
368
|
const metas = [...document.head.querySelectorAll('meta[name="theme-color"]')];
|
|
345
369
|
if (metas.length === 0) {
|
|
346
370
|
createdThemeColor = document.createElement("meta");
|
|
@@ -352,13 +376,14 @@
|
|
|
352
376
|
}
|
|
353
377
|
for (const root of hostRoots) {
|
|
354
378
|
root.style.setProperty("background-color", CHAT_CHROME_COLOR, "important");
|
|
379
|
+
root.style.setProperty("overflow", "hidden", "important");
|
|
355
380
|
}
|
|
356
381
|
for (const { meta } of themeColorBackup) {
|
|
357
382
|
meta.content = CHAT_CHROME_COLOR;
|
|
358
383
|
}
|
|
359
384
|
};
|
|
360
385
|
const restoreHostChrome = () => {
|
|
361
|
-
var _a2;
|
|
386
|
+
var _a2, _b;
|
|
362
387
|
if (themeColorBackup === null) {
|
|
363
388
|
return;
|
|
364
389
|
}
|
|
@@ -370,8 +395,10 @@
|
|
|
370
395
|
themeColorBackup = null;
|
|
371
396
|
for (const [index, root] of hostRoots.entries()) {
|
|
372
397
|
root.style.backgroundColor = (_a2 = hostBackgroundColorBackup == null ? void 0 : hostBackgroundColorBackup[index]) != null ? _a2 : "";
|
|
398
|
+
root.style.overflow = (_b = hostOverflowBackup == null ? void 0 : hostOverflowBackup[index]) != null ? _b : "";
|
|
373
399
|
}
|
|
374
400
|
hostBackgroundColorBackup = null;
|
|
401
|
+
hostOverflowBackup = null;
|
|
375
402
|
};
|
|
376
403
|
const syncHostChrome = () => {
|
|
377
404
|
if (elements.chatDialog.dataset.state !== "open") {
|
|
@@ -535,7 +562,7 @@
|
|
|
535
562
|
});
|
|
536
563
|
globalThis.addEventListener("message", handleMessage);
|
|
537
564
|
document.addEventListener("keydown", handleKeydown);
|
|
538
|
-
|
|
565
|
+
loadUnreadIndicator();
|
|
539
566
|
if (completedSigningEnvelopeId !== null || readOpenState()) {
|
|
540
567
|
openChat();
|
|
541
568
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morgan-and-morgan/intake-agent",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0-alpha.1",
|
|
4
4
|
"description": "Embeddable chat widget loader.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"files": [
|
|
@@ -18,20 +18,21 @@
|
|
|
18
18
|
"publishConfig": {
|
|
19
19
|
"access": "public"
|
|
20
20
|
},
|
|
21
|
-
"devDependencies": {
|
|
22
|
-
"eslint": "^9.39.1",
|
|
23
|
-
"lucide-react-native": "^0.562.0",
|
|
24
|
-
"typescript": "~5.9.3",
|
|
25
|
-
"vite": "^7.3.6",
|
|
26
|
-
"@mmcp/eslint-config": "0.1.0",
|
|
27
|
-
"@mmcp/tsconfig": "0.1.0"
|
|
28
|
-
},
|
|
29
21
|
"scripts": {
|
|
30
22
|
"build": "pnpm run build:bundle",
|
|
31
23
|
"build:bundle": "node scripts/generate-icons.mjs && vite build",
|
|
32
24
|
"generate:icons": "node scripts/generate-icons.mjs",
|
|
33
25
|
"typecheck": "tsc --noEmit",
|
|
34
26
|
"lint": "eslint \"src/**/*.ts\" --quiet",
|
|
35
|
-
"lint:fix": "eslint \"src/**/*.ts\" --quiet --fix"
|
|
27
|
+
"lint:fix": "eslint \"src/**/*.ts\" --quiet --fix",
|
|
28
|
+
"prepublishOnly": "pnpm run build:bundle"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@mmcp/eslint-config": "workspace:*",
|
|
32
|
+
"@mmcp/tsconfig": "workspace:*",
|
|
33
|
+
"eslint": "^9.39.1",
|
|
34
|
+
"lucide-react-native": "^0.562.0",
|
|
35
|
+
"typescript": "~5.9.3",
|
|
36
|
+
"vite": "^7.3.6"
|
|
36
37
|
}
|
|
37
|
-
}
|
|
38
|
+
}
|