@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 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 |
@@ -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) => readFlag(UNREAD_STATE_KEY, storage);
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.removeItem(UNREAD_STATE_KEY);
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
- `@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{right:0;top:var(--mm-viewport-top,auto);bottom:0;width:100vw;height:var(--mm-viewport-height,100dvh);min-height:0;border:0;border-radius:0}}`,
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
- syncLauncherLabel();
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
- setHasUnread(hasUnread);
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": "0.1.0-alpha.1",
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
+ }