@eshal-bot/chat-widget 0.2.40 → 0.2.41

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.
@@ -1 +1 @@
1
- export{S as default}from"./chat-widget-index-BubaEkmK.esm.js";import"react";import"react-dom/client";import"react/jsx-runtime";import"react-dom";
1
+ export{S as default}from"./chat-widget-index-i7Tp1iNw.esm.js";import"react";import"react-dom/client";import"react/jsx-runtime";import"react-dom";
@@ -16497,6 +16497,49 @@
16497
16497
  return typeof u === 'string' && /^https?:\/\//i.test(u) ? u : undefined;
16498
16498
  }
16499
16499
 
16500
+ /**
16501
+ * What a Copy button puts on the clipboard (EV-4817, extended to every chat
16502
+ * surface): the words of a message without its card specs.
16503
+ *
16504
+ * A reply can carry ```eshal-ui``` blocks (buttons, forms, cards) that the
16505
+ * chat renders as controls. Copying the raw message put that JSON on the
16506
+ * clipboard. Same fences and tidy-up as the backend's
16507
+ * channels/utils/emailFormat.ts stripA2uiFences, so both sides agree on what
16508
+ * a fence is. Shared by the frontend (Inbox, Testing lab), the widget and
16509
+ * the agent-sdk.
16510
+ *
16511
+ * Plain JavaScript (types in clipboardText.d.ts), conservative syntax: the
16512
+ * widget's Rollup/Babel build compiles neither TypeScript nor this file.
16513
+ */
16514
+
16515
+ const A2UI_FENCE_TAGS = /(?:eshal-ui|eshal-chart|chart)\b/;
16516
+ function a2uiFenceRe() {
16517
+ return /```[ \t]*(?:eshal-ui|eshal-chart|chart)\b[ \t]*\r?\n([\s\S]*?)```/g;
16518
+ }
16519
+ function stripA2uiFences(text) {
16520
+ if (!text || !A2UI_FENCE_TAGS.test(text)) return text;
16521
+ return text.replace(a2uiFenceRe(), '').replace(/\n{3,}/g, '\n\n').trim();
16522
+ }
16523
+
16524
+ /**
16525
+ * The whole-chat "Copy Chat" text: one "Role: words" stanza per message,
16526
+ * blank line between. A message that is only a card has no words, so it is
16527
+ * left out like any other empty message.
16528
+ */
16529
+ function formatChatForClipboard(messages, assistantName) {
16530
+ if (!Array.isArray(messages)) return '';
16531
+ const label = assistantName || 'Assistant';
16532
+ const lines = [];
16533
+ for (let i = 0; i < messages.length; i++) {
16534
+ const msg = messages[i];
16535
+ if (!msg || typeof msg.content !== 'string') continue;
16536
+ const words = stripA2uiFences(msg.content);
16537
+ if (!words || words.trim() === '') continue;
16538
+ lines.push((msg.role === 'user' ? 'You' : label) + ': ' + words);
16539
+ }
16540
+ return lines.join('\n\n');
16541
+ }
16542
+
16500
16543
  /**
16501
16544
  * WidgetCitationContext - the message's `sources` array + download handler,
16502
16545
  * supplied by `<MessageBubble>` and consumed by every `<CitationPill>` that
@@ -17255,6 +17298,9 @@
17255
17298
  // `stripCitationMarkers` call and the separate `<MessageSources>`
17256
17299
  // footer were removed in the same refactor.
17257
17300
  const displayContent = rawContent;
17301
+ // Copy puts the words on the clipboard, not the ```eshal-ui``` card JSON;
17302
+ // a card-only reply has no words, so it gets no Copy button.
17303
+ const copyText = reactExports.useMemo(() => stripA2uiFences(displayContent), [displayContent]);
17258
17304
  const isTypingMessage = message.isProcessing && (!displayContent || displayContent.length === 0);
17259
17305
  // EV-2351: direction must follow THIS message's own text, not the agent's
17260
17306
  // configured default language. The blanket branding `isRtl` forced English
@@ -17476,8 +17522,8 @@
17476
17522
  className: `text-xs leading-none ${isDark ? "text-gray-400" : "text-gray-500"}`,
17477
17523
  dir: "auto",
17478
17524
  children: formatTime(message.timestamp)
17479
- }), !isTypingMessage && !message.isProcessing && displayContent && /*#__PURE__*/jsxRuntimeExports.jsx(CopyMessageButton, {
17480
- content: displayContent,
17525
+ }), !isTypingMessage && !message.isProcessing && copyText && /*#__PURE__*/jsxRuntimeExports.jsx(CopyMessageButton, {
17526
+ content: copyText,
17481
17527
  isDark: isDark,
17482
17528
  t: t
17483
17529
  }), allowFeedback !== false && onFeedback && !isTypingMessage && !message.isProcessing && displayContent && message.type !== 'welcome' && !message.isWelcome && /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
@@ -23239,7 +23285,7 @@
23239
23285
  * value by its own test (that constant sat at 0.1.113 while this package was
23240
23286
  * at 0.2.2 precisely because nothing enforced it - EV-2431).
23241
23287
  */
23242
- const WIDGET_VERSION = '0.2.40';
23288
+ const WIDGET_VERSION = '0.2.41';
23243
23289
 
23244
23290
  /**
23245
23291
  * API utility functions
@@ -23761,10 +23807,8 @@
23761
23807
 
23762
23808
  // Copy chat handler - copy all messages to clipboard
23763
23809
  const handleCopyChat = reactExports.useCallback(() => {
23764
- const text = messages.filter(msg => msg.content && msg.content.trim() !== "").map(msg => {
23765
- const role = msg.role === "user" ? "You" : widgetConfig?.conciergeName || "Assistant";
23766
- return `${role}: ${msg.content}`;
23767
- }).join("\n\n");
23810
+ // Words only: ```eshal-ui``` card JSON stays out of the transcript.
23811
+ const text = formatChatForClipboard(messages, widgetConfig?.conciergeName);
23768
23812
  if (text) {
23769
23813
  navigator.clipboard.writeText(text).catch(() => {});
23770
23814
  }