@eshal-bot/chat-widget 0.2.0 → 0.2.2

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.
@@ -87896,6 +87896,8 @@
87896
87896
  },
87897
87897
  children: props.title
87898
87898
  }), /*#__PURE__*/jsxRuntimeExports.jsx("div", {
87899
+ role: "img",
87900
+ "aria-label": props.title || seriesName || 'Bar chart',
87899
87901
  style: {
87900
87902
  height: chartHeight,
87901
87903
  width: '100%'
@@ -88172,6 +88174,8 @@
88172
88174
  },
88173
88175
  children: props.title
88174
88176
  }), /*#__PURE__*/jsxRuntimeExports.jsx("div", {
88177
+ role: "img",
88178
+ "aria-label": props.title || (donut ? 'Donut chart' : 'Pie chart'),
88175
88179
  style: {
88176
88180
  height: 260,
88177
88181
  width: '100%'
@@ -88289,6 +88293,8 @@
88289
88293
  },
88290
88294
  children: props.title
88291
88295
  }), /*#__PURE__*/jsxRuntimeExports.jsx("div", {
88296
+ role: "img",
88297
+ "aria-label": props.title || seriesName || (area ? 'Area chart' : 'Line chart'),
88292
88298
  style: {
88293
88299
  height: 220,
88294
88300
  width: '100%'
@@ -91194,7 +91200,46 @@
91194
91200
  };
91195
91201
  }
91196
91202
 
91197
- const WidgetCitationContext = /*#__PURE__*/React.createContext(null);
91203
+ /**
91204
+ * WidgetCitationContext - the message's `sources` array + download handler,
91205
+ * supplied by `<MessageBubble>` and consumed by every `<CitationPill>` that
91206
+ * `rehypeCitations` renders inside the ReactMarkdown output.
91207
+ *
91208
+ * EV-2904 - WHY THIS LIVES IN ITS OWN MODULE, BEHIND A GLOBAL SINGLETON
91209
+ * ---------------------------------------------------------------------
91210
+ * The vanilla/IIFE build (`chat-widget.min.js` - the `<script src=...>`
91211
+ * snippet customers actually install) ships the markdown pipeline as a
91212
+ * SEPARATE IIFE bundle, `chat-widget-markdown-chunk.min.js`, injected at
91213
+ * runtime by vanilla/lazyChunkLoader.js. Per rollup.config.js, `react` is
91214
+ * the ONLY module the two bundles share (via the `__eshalChatWidgetReact`
91215
+ * global) - every other module, including this one, is bundled a SECOND
91216
+ * time into the chunk.
91217
+ *
91218
+ * The provider (`MessageBubble.jsx`) lives in the main bundle; the consumer
91219
+ * (`CitationPill.jsx`) is pulled in by `MarkdownRenderer.jsx` and therefore
91220
+ * lives in the chunk. With a plain `createContext(null)` in each bundle,
91221
+ * those are two DIFFERENT context objects: the pill's `useContext` finds no
91222
+ * matching Provider above it, falls back to the default `null`, and
91223
+ * `CitationPill` returns `null` (its "rendered outside a chat" guard). The
91224
+ * result is that citations silently vanish on the installed widget - no
91225
+ * error, no raw `[N]` leak - while the Testing Lab and the ESM/UMD builds
91226
+ * (single module graph) render them fine.
91227
+ *
91228
+ * Stashing the context object on a `window` key makes it a true
91229
+ * cross-bundle singleton: whichever bundle evaluates first creates it, the
91230
+ * other reuses it, so provider and consumer always agree. Safe for the
91231
+ * single-graph builds too - the second import just re-reads the same
91232
+ * object, which is exactly what a plain module-level `createContext` did.
91233
+ *
91234
+ * `createContext` must come from the one shared React instance for hooks to
91235
+ * work at all; rollup.config.js already guarantees that via the
91236
+ * `__eshalChatWidgetReact` global, so the object created here is valid in
91237
+ * both bundles.
91238
+ */
91239
+ const GLOBAL_KEY = "__eshalChatWidgetCitationContext";
91240
+ const globalScope = typeof window !== "undefined" ? window : typeof globalThis !== "undefined" ? globalThis : undefined;
91241
+ const WidgetCitationContext = globalScope ? globalScope[GLOBAL_KEY] || (globalScope[GLOBAL_KEY] = /*#__PURE__*/React.createContext(null)) : /*#__PURE__*/React.createContext(null);
91242
+
91198
91243
  function decodeName(name) {
91199
91244
  if (!name) return "";
91200
91245
  try {
@@ -91291,7 +91336,27 @@
91291
91336
  setOpen(false);
91292
91337
  };
91293
91338
  const onKey = e => {
91294
- if (e.key === "Escape") setOpen(false);
91339
+ if (e.key === "Escape") {
91340
+ setOpen(false);
91341
+ return;
91342
+ }
91343
+ if (e.key === "Tab" && popoverRef.current) {
91344
+ const focusables = popoverRef.current.querySelectorAll('a[href], button:not([disabled])');
91345
+ if (focusables.length === 0) return;
91346
+ const first = focusables[0];
91347
+ const last = focusables[focusables.length - 1];
91348
+ // document.activeElement is unreliable across a shadow boundary
91349
+ // (it resolves to the shadow host, not the focused element inside
91350
+ // it) - composedPath()[0] gives the true focused node either way.
91351
+ const active = typeof e.composedPath === "function" ? e.composedPath()[0] : e.target;
91352
+ if (e.shiftKey && active === first) {
91353
+ e.preventDefault();
91354
+ last.focus();
91355
+ } else if (!e.shiftKey && active === last) {
91356
+ e.preventDefault();
91357
+ first.focus();
91358
+ }
91359
+ }
91295
91360
  };
91296
91361
  document.addEventListener("mousedown", onDocClick);
91297
91362
  document.addEventListener("keydown", onKey);
@@ -91311,6 +91376,23 @@
91311
91376
  setOpen(true);
91312
91377
  };
91313
91378
 
91379
+ // Move focus into the popover on open; restore it to the pill on close
91380
+ // so keyboard users don't lose their place when Escape or a click
91381
+ // outside dismisses it.
91382
+ const wasOpenRef = React.useRef(false);
91383
+ React.useEffect(() => {
91384
+ if (open) {
91385
+ var _popoverRef$current;
91386
+ wasOpenRef.current = true;
91387
+ const firstFocusable = (_popoverRef$current = popoverRef.current) === null || _popoverRef$current === void 0 ? void 0 : _popoverRef$current.querySelector('a[href], button:not([disabled])');
91388
+ firstFocusable === null || firstFocusable === void 0 || firstFocusable.focus();
91389
+ } else if (wasOpenRef.current) {
91390
+ var _anchorRef$current;
91391
+ wasOpenRef.current = false;
91392
+ (_anchorRef$current = anchorRef.current) === null || _anchorRef$current === void 0 || _anchorRef$current.focus();
91393
+ }
91394
+ }, [open]);
91395
+
91314
91396
  // No context (rendered outside a chat) - hide the marker rather than
91315
91397
  // leaking a raw `[N]` into the bubble.
91316
91398
  if (!ctx) {
@@ -91363,6 +91445,7 @@
91363
91445
  tabIndex: 0,
91364
91446
  dir: "ltr",
91365
91447
  title: firstLabel + (extraCount ? " (+".concat(extraCount, " more)") : ""),
91448
+ "aria-label": firstLabel + (extraCount ? " (+".concat(extraCount, " more)") : ""),
91366
91449
  onClick: onPillClick,
91367
91450
  onKeyDown: e => {
91368
91451
  if (e.key === "Enter" || e.key === " ") {