@athenaintel/react 0.57.0 → 0.57.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.
Files changed (48) hide show
  1. package/README.md +25 -2
  2. package/dist/chat/AthenaChat.cjs +61 -3
  3. package/dist/chat/AthenaChat.cjs.map +1 -1
  4. package/dist/chat/AthenaChat.js +62 -4
  5. package/dist/chat/AthenaChat.js.map +1 -1
  6. package/dist/chat-ui/index.cjs +1 -0
  7. package/dist/chat-ui/index.cjs.map +1 -1
  8. package/dist/chat-ui/index.d.ts +1 -1
  9. package/dist/chat-ui/index.js +2 -1
  10. package/dist/chat-ui/messages/inbound-channel-message.cjs +23 -0
  11. package/dist/chat-ui/messages/inbound-channel-message.cjs.map +1 -1
  12. package/dist/chat-ui/messages/inbound-channel-message.d.ts +13 -20
  13. package/dist/chat-ui/messages/inbound-channel-message.js +24 -1
  14. package/dist/chat-ui/messages/inbound-channel-message.js.map +1 -1
  15. package/dist/chat-ui/messages/message-components.cjs +14 -1
  16. package/dist/chat-ui/messages/message-components.cjs.map +1 -1
  17. package/dist/chat-ui/messages/message-components.js +15 -2
  18. package/dist/chat-ui/messages/message-components.js.map +1 -1
  19. package/dist/index.cjs +1 -0
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.js +2 -1
  23. package/dist/provider/AthenaProvider.cjs +7 -0
  24. package/dist/provider/AthenaProvider.cjs.map +1 -1
  25. package/dist/provider/AthenaProvider.js +9 -2
  26. package/dist/provider/AthenaProvider.js.map +1 -1
  27. package/dist/react/package.json.cjs +1 -1
  28. package/dist/react/package.json.js +1 -1
  29. package/dist/runtime/statewire-run-config.cjs +15 -0
  30. package/dist/runtime/statewire-run-config.cjs.map +1 -1
  31. package/dist/runtime/statewire-run-config.d.ts +13 -0
  32. package/dist/runtime/statewire-run-config.js +15 -0
  33. package/dist/runtime/statewire-run-config.js.map +1 -1
  34. package/dist/threads/SessionList.cjs +22 -19
  35. package/dist/threads/SessionList.cjs.map +1 -1
  36. package/dist/threads/SessionList.js +22 -19
  37. package/dist/threads/SessionList.js.map +1 -1
  38. package/dist/threads/live-session-rows.cjs +207 -0
  39. package/dist/threads/live-session-rows.cjs.map +1 -0
  40. package/dist/threads/live-session-rows.d.ts +175 -0
  41. package/dist/threads/live-session-rows.js +207 -0
  42. package/dist/threads/live-session-rows.js.map +1 -0
  43. package/dist/threads/statewire-thread-list.cjs +173 -20
  44. package/dist/threads/statewire-thread-list.cjs.map +1 -1
  45. package/dist/threads/statewire-thread-list.d.ts +44 -1
  46. package/dist/threads/statewire-thread-list.js +174 -21
  47. package/dist/threads/statewire-thread-list.js.map +1 -1
  48. package/package.json +1 -1
@@ -34,7 +34,7 @@ import { TextShimmerLoader, ThreadRunningIndicator } from "./messages/running-in
34
34
  import { SignInCard, SignInConsent, SignInFallbackSection, SignInSecondaryButton, SignInSelect, SignInTextField } from "./auth/sign-in-card.js";
35
35
  import { RuntimeMessageCard } from "./messages/runtime-message.js";
36
36
  import { WakeMarkerCard, WakeMarkerMessage } from "./messages/wake-marker.js";
37
- import { InboundChannelLine, InboundChannelSubject, withInboundMessageBody } from "./messages/inbound-channel-message.js";
37
+ import { InboundChannelLine, InboundChannelSubject, useUserMessageCopy, withInboundMessageBody } from "./messages/inbound-channel-message.js";
38
38
  import { HandoffArrivalAfterMessage, HandoffArrivalDivider, HandoffArrivalScope } from "./messages/handoff-arrival-divider.js";
39
39
  import { AnswerLabel, AnswerLabelScope } from "./messages/answer-label.js";
40
40
  import { VoiceDelegationCard, VoiceDelegationMessage } from "./messages/voice-delegation-card.js";
@@ -170,6 +170,7 @@ export {
170
170
  useReadThreadReadingCursor,
171
171
  useStatewireApprovalRequest,
172
172
  useStatewireToolOutputs,
173
+ useUserMessageCopy,
173
174
  webpageMentionFingerprint,
174
175
  withInboundMessageBody
175
176
  };
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const jsxRuntime = require("react/jsx-runtime");
4
+ const react$1 = require("@assistant-ui/core/react");
4
5
  const react = require("@assistant-ui/react");
5
6
  const lucideReact = require("lucide-react");
6
7
  const require$$0 = require("react");
@@ -211,9 +212,31 @@ function withInboundMessageBody(Text) {
211
212
  wrappedTextComponents.set(Text, InboundMessageBodyText);
212
213
  return InboundMessageBodyText;
213
214
  }
215
+ function writeClipboard(text) {
216
+ if (typeof navigator === "undefined" || !navigator.clipboard) {
217
+ return Promise.reject(new Error("Clipboard API is unavailable"));
218
+ }
219
+ return navigator.clipboard.writeText(text);
220
+ }
221
+ function useUserMessageCopy() {
222
+ const custom = react.useAuiState((s) => {
223
+ var _a;
224
+ return (_a = s.message.metadata) == null ? void 0 : _a.custom;
225
+ });
226
+ const copyToClipboard = require$$0.useCallback(
227
+ (text) => {
228
+ const words = inboundChannel.inboundMessageBody({ custom, text });
229
+ const subject = inboundChannel.inboundEmailSubject(custom);
230
+ return writeClipboard(subject ? [subject, words].filter(Boolean).join("\n\n") : words);
231
+ },
232
+ [custom]
233
+ );
234
+ return react$1.useActionBarCopy({ copyToClipboard });
235
+ }
214
236
  exports.ChannelMark = ChannelMark;
215
237
  exports.InboundChannelLine = InboundChannelLine;
216
238
  exports.InboundChannelSource = InboundChannelSource;
217
239
  exports.InboundChannelSubject = InboundChannelSubject;
240
+ exports.useUserMessageCopy = useUserMessageCopy;
218
241
  exports.withInboundMessageBody = withInboundMessageBody;
219
242
  //# sourceMappingURL=inbound-channel-message.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"inbound-channel-message.cjs","sources":["../../../src/chat-ui/messages/inbound-channel-message.tsx"],"sourcesContent":["/**\n * A user turn that came in over a channel (Slack, email, a text, Microsoft\n * Teams, the Programmatic API, a voice memo), drawn as the words that were\n * sent under a line naming the channel and the sender.\n *\n * The channel's routing block (`<inbound-message-context>`: the facts the\n * agent is told, such as Slack ids, the numbers a text went between and how a\n * reply is delivered) leaves the bubble. The line above the bubble opens it\n * as a labelled list, and hovering the line shows the channel, the place and\n * the sender in full. Every host that renders the SDK's user message gets\n * this; a host with its own user message composes it from\n * `InboundChannelLine`, `InboundChannelSubject` and `withInboundMessageBody`\n * (or `UserMessageText`). Only a message with the `inbound_channel` envelope\n * is a channel message: tags someone types show as typed.\n *\n * The hover card is CSS, not a Radix tooltip, and the glyphs are icons the\n * chat already draws: a host that splits `node_modules` into startup vendor\n * chunks (the Office pane) would otherwise load a tooltip stack at startup for\n * a line it rarely shows.\n */\nimport { type TextMessagePartComponent, useAuiState } from \"@assistant-ui/react\";\nimport {\n inboundEmailSubject,\n inboundMessageBody,\n isInboundChannelMessage,\n} from \"@athenaintel/agent-runtime\";\nimport { AudioLinesIcon, ChevronDownIcon, CodeIcon, MailIcon, MessageSquareIcon } from \"lucide-react\";\nimport { type FC, useMemo, useState } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger } from \"../../ui/collapsible\";\nimport {\n type InboundChannelKind,\n type InboundChannelMessage,\n readInboundChannelMessage,\n} from \"./inbound-channel-reader\";\n\n/** Slack's four-colour mark. */\nconst SlackMark: FC<{ className?: string }> = ({ className }) => (\n <svg viewBox=\"0 0 2447.6 2452.5\" className={className} aria-hidden=\"true\" focusable=\"false\">\n <g clipRule=\"evenodd\" fillRule=\"evenodd\">\n <path\n d=\"m897.4 0c-135.3.1-244.8 109.9-244.7 245.2-.1 135.3 109.5 245.1 244.8 245.2h244.8v-245.1c.1-135.3-109.5-245.1-244.9-245.3.1 0 .1 0 0 0m0 654h-652.6c-135.3.1-244.9 109.9-244.8 245.2-.2 135.3 109.4 245.1 244.7 245.3h652.7c135.3-.1 244.9-109.9 244.8-245.2.1-135.4-109.5-245.2-244.8-245.3z\"\n fill=\"#36c5f0\"\n />\n <path\n d=\"m2447.6 899.2c.1-135.3-109.5-245.1-244.8-245.2-135.3.1-244.9 109.9-244.8 245.2v245.3h244.8c135.3-.1 244.9-109.9 244.8-245.3zm-652.7 0v-654c.1-135.2-109.4-245-244.7-245.2-135.3.1-244.9 109.9-244.8 245.2v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.3z\"\n fill=\"#2eb67d\"\n />\n <path\n d=\"m1550.1 2452.5c135.3-.1 244.9-109.9 244.8-245.2.1-135.3-109.5-245.1-244.8-245.2h-244.8v245.2c-.1 135.2 109.5 245 244.8 245.2zm0-654.1h652.7c135.3-.1 244.9-109.9 244.8-245.2.2-135.3-109.4-245.1-244.7-245.3h-652.7c-135.3.1-244.9 109.9-244.8 245.2-.1 135.4 109.4 245.2 244.7 245.3z\"\n fill=\"#ecb22e\"\n />\n <path\n d=\"m0 1553.2c-.1 135.3 109.5 245.1 244.8 245.2 135.3-.1 244.9-109.9 244.8-245.2v-245.2h-244.8c-135.3.1-244.9 109.9-244.8 245.2zm652.7 0v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.2v-653.9c.2-135.3-109.4-245.1-244.7-245.3-135.4 0-244.9 109.8-244.8 245.1 0 0 0 .1 0 0\"\n fill=\"#e01e5a\"\n />\n </g>\n </svg>\n);\n\n/** Microsoft Teams' mark: the two people and the T tile, in flat colour. */\nconst TeamsMark: FC<{ className?: string }> = ({ className }) => (\n <svg viewBox=\"0 0 2228.833 2073.333\" className={className} aria-hidden=\"true\" focusable=\"false\">\n <path\n fill=\"#5059C9\"\n d=\"M1554.637 777.5h575.713c54.391 0 98.483 44.092 98.483 98.483v524.398c0 199.901-162.051 361.952-361.952 361.952h-1.711c-199.901.028-361.975-162-362.004-361.901V828.971c0-28.427 23.044-51.471 51.471-51.471z\"\n />\n <circle fill=\"#5059C9\" cx=\"1943.75\" cy=\"440.583\" r=\"233.25\" />\n <circle fill=\"#7B83EB\" cx=\"1218.083\" cy=\"336.917\" r=\"336.917\" />\n <path\n fill=\"#7B83EB\"\n d=\"M1667.323 777.5H717.01c-53.743 1.33-96.257 45.931-95.01 99.676v598.105c-7.505 322.519 247.657 590.16 570.167 598.053 322.51-7.893 577.671-275.534 570.167-598.053V877.176c1.245-53.745-41.268-98.346-95.011-99.676z\"\n />\n <path\n fill=\"#4B53BC\"\n d=\"M95.01 466.5h950.312c52.473 0 95.01 42.538 95.01 95.01v950.312c0 52.473-42.538 95.01-95.01 95.01H95.01c-52.473 0-95.01-42.538-95.01-95.01V561.51c0-52.472 42.538-95.01 95.01-95.01z\"\n />\n <path fill=\"#FFF\" d=\"M820.211 828.193H630.241v517.297H509.211V828.193H320.123V727.844h500.088z\" />\n </svg>\n);\n\n/**\n * The channel's mark at the size `className` gives: Slack's and Teams' own\n * logos, a tinted glyph for email, texts, the API and voice memos, a speech\n * bubble for any other channel. Decorative: the line it sits on names the\n * channel.\n */\nexport const ChannelMark: FC<{ kind: InboundChannelKind; className?: string }> = ({\n kind,\n className,\n}) => {\n switch (kind) {\n case \"slack\":\n return <SlackMark className={className} />;\n case \"teams\":\n return <TeamsMark className={className} />;\n case \"email\":\n return <MailIcon className={cn(\"text-sky-600\", className)} aria-hidden=\"true\" />;\n case \"sms\":\n return <MessageSquareIcon className={cn(\"text-emerald-600\", className)} aria-hidden=\"true\" />;\n case \"api\":\n return <CodeIcon className={cn(\"text-muted-foreground\", className)} aria-hidden=\"true\" />;\n case \"voice_memo\":\n return <AudioLinesIcon className={cn(\"text-violet-600\", className)} aria-hidden=\"true\" />;\n default:\n return <MessageSquareIcon className={cn(\"text-muted-foreground\", className)} aria-hidden=\"true\" />;\n }\n};\n\n/** The first text part's words: where a channel's routing block sits. */\nfunction firstText(parts: unknown): string {\n if (!Array.isArray(parts)) return \"\";\n for (const part of parts) {\n if (\n typeof part === \"object\" &&\n part !== null &&\n (part as { type?: unknown }).type === \"text\" &&\n typeof (part as { text?: unknown }).text === \"string\"\n ) {\n return (part as { text: string }).text;\n }\n }\n return \"\";\n}\n\n/** Everything the agent read with the message, labelled; the thread a mention came in below it. */\nconst InboundChannelFacts: FC<{ message: InboundChannelMessage }> = ({ message }) => (\n <div\n className=\"flex w-full min-w-0 flex-col gap-2 rounded-xl border border-border bg-background px-3 py-2.5 text-left text-xs\"\n data-slot=\"inbound-channel-facts\"\n >\n <p className=\"text-muted-foreground\">What the agent received with this message</p>\n {message.facts.length > 0 ? (\n <dl className=\"grid grid-cols-[auto_minmax(0,1fr)] gap-x-3 gap-y-1\">\n {message.facts.map((fact, index) => (\n <div key={`${fact.key}-${index}`} className=\"contents\">\n <dt className=\"whitespace-nowrap text-muted-foreground\">{fact.label}</dt>\n <dd className=\"text-foreground [overflow-wrap:anywhere]\">{fact.value}</dd>\n </div>\n ))}\n </dl>\n ) : null}\n {message.quoted ? (\n <div className=\"flex flex-col gap-1\">\n <p className=\"text-muted-foreground\">Earlier in the thread</p>\n <p className=\"whitespace-pre-wrap border-border border-l-2 pl-2 text-foreground [overflow-wrap:anywhere]\">\n {message.quoted}\n </p>\n </div>\n ) : null}\n </div>\n);\n\n/**\n * The line above a channel message's bubble: the channel's mark, the sender's\n * name and address (an email address, a phone number), and a chevron that\n * opens what the agent received. Hovering or focusing it shows a card with\n * the channel, the place and the sender in full, while the facts are closed.\n */\nexport const InboundChannelSource: FC<{ message: InboundChannelMessage; className?: string }> = ({\n message,\n className,\n}) => {\n const [open, setOpen] = useState(false);\n const origin = message.origin;\n const name = origin.senderName;\n const handle = origin.senderHandle;\n const primary = name ?? handle ?? origin.label;\n const secondary = name && handle ? handle : null;\n const channelLine = [origin.label, origin.place].filter(Boolean).join(\" · \");\n\n return (\n <Collapsible\n open={open}\n onOpenChange={setOpen}\n className={cn(\"flex w-full min-w-0 flex-col items-end gap-1\", className)}\n data-slot=\"inbound-channel\"\n data-channel={origin.kind}\n >\n <div className=\"group/inbound-channel relative flex min-w-0 max-w-full justify-end\">\n <CollapsibleTrigger\n data-testid=\"inbound-channel-source\"\n className=\"flex min-w-0 max-w-full items-center gap-1.5 rounded-md px-1.5 py-0.5 text-muted-foreground text-xs transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <ChannelMark kind={origin.kind} className=\"size-3.5 shrink-0\" />\n <span className=\"truncate font-medium text-foreground/80\">{primary}</span>\n {secondary ? <span className=\"truncate\">{secondary}</span> : null}\n <span className=\"sr-only\">{`, via ${channelLine}. Show what the agent received`}</span>\n <ChevronDownIcon\n className={cn(\"size-3 shrink-0 transition-transform\", open && \"rotate-180\")}\n aria-hidden=\"true\"\n />\n </CollapsibleTrigger>\n {/* The hover card: what the line says, in full (the button's own text\n carries it for screen readers). Shown below the line, over the\n bubble, after a short rest, and never while the facts are open. */}\n {open ? null : (\n <div\n aria-hidden=\"true\"\n data-slot=\"inbound-channel-card\"\n className=\"pointer-events-none absolute top-full right-0 z-20 mt-1 flex w-max max-w-xs flex-col gap-0.5 rounded-md bg-foreground px-3 py-1.5 text-left text-background text-xs opacity-0 shadow-md transition-opacity delay-0 group-focus-within/inbound-channel:opacity-100 group-hover/inbound-channel:opacity-100 group-hover/inbound-channel:delay-300\"\n >\n <span className=\"flex items-center gap-1.5 font-medium\">\n <ChannelMark kind={origin.kind} className=\"size-3.5 shrink-0\" />\n {channelLine}\n </span>\n {name ? <span>{name}</span> : null}\n {handle ? <span className=\"opacity-80\">{handle}</span> : null}\n {origin.subject ? (\n <span className=\"opacity-80\">\n {`${origin.kind === \"voice_memo\" ? \"Memo\" : \"Subject\"}: ${origin.subject}`}\n </span>\n ) : null}\n </div>\n )}\n </div>\n <CollapsibleContent className=\"w-full min-w-0\">\n <InboundChannelFacts message={message} />\n </CollapsibleContent>\n </Collapsible>\n );\n};\n\n/**\n * The line above the bubble for the assistant-ui message in scope; nothing\n * for a message without the `inbound_channel` envelope. Selects the raw\n * metadata and parts (stable for a settled message) and derives in `useMemo`.\n */\nexport const InboundChannelLine: FC<{ className?: string }> = ({ className }) => {\n const custom = useAuiState((s) => s.message.metadata?.custom);\n const parts = useAuiState((s) => s.message.parts);\n const message = useMemo(\n () =>\n isInboundChannelMessage(custom)\n ? readInboundChannelMessage({ custom, text: firstText(parts) })\n : null,\n [custom, parts],\n );\n return message ? <InboundChannelSource message={message} className={className} /> : null;\n};\n\n/** An email's subject, the bubble's first line. Nothing for any other message. */\nexport const InboundChannelSubject: FC = () => {\n const subject = useAuiState((s) => inboundEmailSubject(s.message.metadata?.custom));\n if (!subject) return null;\n return (\n <div className=\"mb-1 font-semibold\" data-slot=\"inbound-channel-subject\">\n {subject}\n </div>\n );\n};\n\nconst wrappedTextComponents = new WeakMap<TextMessagePartComponent, TextMessagePartComponent>();\n\n/**\n * `Text` showing a channel message's words without the routing block the\n * agent read ahead of them. Any other text passes through unchanged, tags a\n * person typed included. Cached per component, so a host's part map keeps a\n * stable identity.\n */\nexport function withInboundMessageBody(Text: TextMessagePartComponent): TextMessagePartComponent {\n const cached = wrappedTextComponents.get(Text);\n if (cached) return cached;\n const InboundMessageBodyText: TextMessagePartComponent = (props) => {\n const custom = useAuiState((s) => s.message.metadata?.custom);\n const text = useMemo(() => inboundMessageBody({ custom, text: props.text }), [custom, props.text]);\n return <Text {...props} text={text} />;\n };\n wrappedTextComponents.set(Text, InboundMessageBodyText);\n return InboundMessageBodyText;\n}\n"],"names":["jsx","jsxs","MailIcon","cn","MessageSquareIcon","CodeIcon","AudioLinesIcon","useState","Collapsible","CollapsibleTrigger","ChevronDownIcon","CollapsibleContent","useAuiState","useMemo","isInboundChannelMessage","readInboundChannelMessage","inboundEmailSubject","inboundMessageBody"],"mappings":"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"}
1
+ {"version":3,"file":"inbound-channel-message.cjs","sources":["../../../src/chat-ui/messages/inbound-channel-message.tsx"],"sourcesContent":["/**\n * A user turn that came in over a channel (Slack, email, a text, Microsoft\n * Teams, the Programmatic API, a voice memo), drawn as the words that were\n * sent under a line naming the channel and the sender.\n *\n * The channel's routing block (`<inbound-message-context>`: the facts the\n * agent is told, such as Slack ids, the numbers a text went between and how a\n * reply is delivered) leaves the bubble. The line above the bubble opens it\n * as a labelled list, and hovering the line shows the channel, the place and\n * the sender in full. Every host that renders the SDK's user message gets\n * this; a host with its own user message composes it from\n * `InboundChannelLine`, `InboundChannelSubject` and `withInboundMessageBody`\n * (or `UserMessageText`). Only a message with the `inbound_channel` envelope\n * is a channel message: tags someone types show as typed.\n *\n * The hover card is CSS, not a Radix tooltip, and the glyphs are icons the\n * chat already draws: a host that splits `node_modules` into startup vendor\n * chunks (the Office pane) would otherwise load a tooltip stack at startup for\n * a line it rarely shows.\n */\nimport { useActionBarCopy } from \"@assistant-ui/core/react\";\nimport { type TextMessagePartComponent, useAuiState } from \"@assistant-ui/react\";\nimport {\n inboundEmailSubject,\n inboundMessageBody,\n isInboundChannelMessage,\n} from \"@athenaintel/agent-runtime\";\nimport { AudioLinesIcon, ChevronDownIcon, CodeIcon, MailIcon, MessageSquareIcon } from \"lucide-react\";\nimport { type FC, useCallback, useMemo, useState } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger } from \"../../ui/collapsible\";\nimport {\n type InboundChannelKind,\n type InboundChannelMessage,\n readInboundChannelMessage,\n} from \"./inbound-channel-reader\";\n\n/** Slack's four-colour mark. */\nconst SlackMark: FC<{ className?: string }> = ({ className }) => (\n <svg viewBox=\"0 0 2447.6 2452.5\" className={className} aria-hidden=\"true\" focusable=\"false\">\n <g clipRule=\"evenodd\" fillRule=\"evenodd\">\n <path\n d=\"m897.4 0c-135.3.1-244.8 109.9-244.7 245.2-.1 135.3 109.5 245.1 244.8 245.2h244.8v-245.1c.1-135.3-109.5-245.1-244.9-245.3.1 0 .1 0 0 0m0 654h-652.6c-135.3.1-244.9 109.9-244.8 245.2-.2 135.3 109.4 245.1 244.7 245.3h652.7c135.3-.1 244.9-109.9 244.8-245.2.1-135.4-109.5-245.2-244.8-245.3z\"\n fill=\"#36c5f0\"\n />\n <path\n d=\"m2447.6 899.2c.1-135.3-109.5-245.1-244.8-245.2-135.3.1-244.9 109.9-244.8 245.2v245.3h244.8c135.3-.1 244.9-109.9 244.8-245.3zm-652.7 0v-654c.1-135.2-109.4-245-244.7-245.2-135.3.1-244.9 109.9-244.8 245.2v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.3z\"\n fill=\"#2eb67d\"\n />\n <path\n d=\"m1550.1 2452.5c135.3-.1 244.9-109.9 244.8-245.2.1-135.3-109.5-245.1-244.8-245.2h-244.8v245.2c-.1 135.2 109.5 245 244.8 245.2zm0-654.1h652.7c135.3-.1 244.9-109.9 244.8-245.2.2-135.3-109.4-245.1-244.7-245.3h-652.7c-135.3.1-244.9 109.9-244.8 245.2-.1 135.4 109.4 245.2 244.7 245.3z\"\n fill=\"#ecb22e\"\n />\n <path\n d=\"m0 1553.2c-.1 135.3 109.5 245.1 244.8 245.2 135.3-.1 244.9-109.9 244.8-245.2v-245.2h-244.8c-135.3.1-244.9 109.9-244.8 245.2zm652.7 0v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.2v-653.9c.2-135.3-109.4-245.1-244.7-245.3-135.4 0-244.9 109.8-244.8 245.1 0 0 0 .1 0 0\"\n fill=\"#e01e5a\"\n />\n </g>\n </svg>\n);\n\n/** Microsoft Teams' mark: the two people and the T tile, in flat colour. */\nconst TeamsMark: FC<{ className?: string }> = ({ className }) => (\n <svg viewBox=\"0 0 2228.833 2073.333\" className={className} aria-hidden=\"true\" focusable=\"false\">\n <path\n fill=\"#5059C9\"\n d=\"M1554.637 777.5h575.713c54.391 0 98.483 44.092 98.483 98.483v524.398c0 199.901-162.051 361.952-361.952 361.952h-1.711c-199.901.028-361.975-162-362.004-361.901V828.971c0-28.427 23.044-51.471 51.471-51.471z\"\n />\n <circle fill=\"#5059C9\" cx=\"1943.75\" cy=\"440.583\" r=\"233.25\" />\n <circle fill=\"#7B83EB\" cx=\"1218.083\" cy=\"336.917\" r=\"336.917\" />\n <path\n fill=\"#7B83EB\"\n d=\"M1667.323 777.5H717.01c-53.743 1.33-96.257 45.931-95.01 99.676v598.105c-7.505 322.519 247.657 590.16 570.167 598.053 322.51-7.893 577.671-275.534 570.167-598.053V877.176c1.245-53.745-41.268-98.346-95.011-99.676z\"\n />\n <path\n fill=\"#4B53BC\"\n d=\"M95.01 466.5h950.312c52.473 0 95.01 42.538 95.01 95.01v950.312c0 52.473-42.538 95.01-95.01 95.01H95.01c-52.473 0-95.01-42.538-95.01-95.01V561.51c0-52.472 42.538-95.01 95.01-95.01z\"\n />\n <path fill=\"#FFF\" d=\"M820.211 828.193H630.241v517.297H509.211V828.193H320.123V727.844h500.088z\" />\n </svg>\n);\n\n/**\n * The channel's mark at the size `className` gives: Slack's and Teams' own\n * logos, a tinted glyph for email, texts, the API and voice memos, a speech\n * bubble for any other channel. Decorative: the line it sits on names the\n * channel.\n */\nexport const ChannelMark: FC<{ kind: InboundChannelKind; className?: string }> = ({\n kind,\n className,\n}) => {\n switch (kind) {\n case \"slack\":\n return <SlackMark className={className} />;\n case \"teams\":\n return <TeamsMark className={className} />;\n case \"email\":\n return <MailIcon className={cn(\"text-sky-600\", className)} aria-hidden=\"true\" />;\n case \"sms\":\n return <MessageSquareIcon className={cn(\"text-emerald-600\", className)} aria-hidden=\"true\" />;\n case \"api\":\n return <CodeIcon className={cn(\"text-muted-foreground\", className)} aria-hidden=\"true\" />;\n case \"voice_memo\":\n return <AudioLinesIcon className={cn(\"text-violet-600\", className)} aria-hidden=\"true\" />;\n default:\n return <MessageSquareIcon className={cn(\"text-muted-foreground\", className)} aria-hidden=\"true\" />;\n }\n};\n\n/** The first text part's words: where a channel's routing block sits. */\nfunction firstText(parts: unknown): string {\n if (!Array.isArray(parts)) return \"\";\n for (const part of parts) {\n if (\n typeof part === \"object\" &&\n part !== null &&\n (part as { type?: unknown }).type === \"text\" &&\n typeof (part as { text?: unknown }).text === \"string\"\n ) {\n return (part as { text: string }).text;\n }\n }\n return \"\";\n}\n\n/** Everything the agent read with the message, labelled; the thread a mention came in below it. */\nconst InboundChannelFacts: FC<{ message: InboundChannelMessage }> = ({ message }) => (\n <div\n className=\"flex w-full min-w-0 flex-col gap-2 rounded-xl border border-border bg-background px-3 py-2.5 text-left text-xs\"\n data-slot=\"inbound-channel-facts\"\n >\n <p className=\"text-muted-foreground\">What the agent received with this message</p>\n {message.facts.length > 0 ? (\n <dl className=\"grid grid-cols-[auto_minmax(0,1fr)] gap-x-3 gap-y-1\">\n {message.facts.map((fact, index) => (\n <div key={`${fact.key}-${index}`} className=\"contents\">\n <dt className=\"whitespace-nowrap text-muted-foreground\">{fact.label}</dt>\n <dd className=\"text-foreground [overflow-wrap:anywhere]\">{fact.value}</dd>\n </div>\n ))}\n </dl>\n ) : null}\n {message.quoted ? (\n <div className=\"flex flex-col gap-1\">\n <p className=\"text-muted-foreground\">Earlier in the thread</p>\n <p className=\"whitespace-pre-wrap border-border border-l-2 pl-2 text-foreground [overflow-wrap:anywhere]\">\n {message.quoted}\n </p>\n </div>\n ) : null}\n </div>\n);\n\n/**\n * The line above a channel message's bubble: the channel's mark, the sender's\n * name and address (an email address, a phone number), and a chevron that\n * opens what the agent received. Hovering or focusing it shows a card with\n * the channel, the place and the sender in full, while the facts are closed.\n */\nexport const InboundChannelSource: FC<{ message: InboundChannelMessage; className?: string }> = ({\n message,\n className,\n}) => {\n const [open, setOpen] = useState(false);\n const origin = message.origin;\n const name = origin.senderName;\n const handle = origin.senderHandle;\n const primary = name ?? handle ?? origin.label;\n const secondary = name && handle ? handle : null;\n const channelLine = [origin.label, origin.place].filter(Boolean).join(\" · \");\n\n return (\n <Collapsible\n open={open}\n onOpenChange={setOpen}\n className={cn(\"flex w-full min-w-0 flex-col items-end gap-1\", className)}\n data-slot=\"inbound-channel\"\n data-channel={origin.kind}\n >\n <div className=\"group/inbound-channel relative flex min-w-0 max-w-full justify-end\">\n <CollapsibleTrigger\n data-testid=\"inbound-channel-source\"\n className=\"flex min-w-0 max-w-full items-center gap-1.5 rounded-md px-1.5 py-0.5 text-muted-foreground text-xs transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <ChannelMark kind={origin.kind} className=\"size-3.5 shrink-0\" />\n <span className=\"truncate font-medium text-foreground/80\">{primary}</span>\n {secondary ? <span className=\"truncate\">{secondary}</span> : null}\n <span className=\"sr-only\">{`, via ${channelLine}. Show what the agent received`}</span>\n <ChevronDownIcon\n className={cn(\"size-3 shrink-0 transition-transform\", open && \"rotate-180\")}\n aria-hidden=\"true\"\n />\n </CollapsibleTrigger>\n {/* The hover card: what the line says, in full (the button's own text\n carries it for screen readers). Shown below the line, over the\n bubble, after a short rest, and never while the facts are open. */}\n {open ? null : (\n <div\n aria-hidden=\"true\"\n data-slot=\"inbound-channel-card\"\n className=\"pointer-events-none absolute top-full right-0 z-20 mt-1 flex w-max max-w-xs flex-col gap-0.5 rounded-md bg-foreground px-3 py-1.5 text-left text-background text-xs opacity-0 shadow-md transition-opacity delay-0 group-focus-within/inbound-channel:opacity-100 group-hover/inbound-channel:opacity-100 group-hover/inbound-channel:delay-300\"\n >\n <span className=\"flex items-center gap-1.5 font-medium\">\n <ChannelMark kind={origin.kind} className=\"size-3.5 shrink-0\" />\n {channelLine}\n </span>\n {name ? <span>{name}</span> : null}\n {handle ? <span className=\"opacity-80\">{handle}</span> : null}\n {origin.subject ? (\n <span className=\"opacity-80\">\n {`${origin.kind === \"voice_memo\" ? \"Memo\" : \"Subject\"}: ${origin.subject}`}\n </span>\n ) : null}\n </div>\n )}\n </div>\n <CollapsibleContent className=\"w-full min-w-0\">\n <InboundChannelFacts message={message} />\n </CollapsibleContent>\n </Collapsible>\n );\n};\n\n/**\n * The line above the bubble for the assistant-ui message in scope; nothing\n * for a message without the `inbound_channel` envelope. Selects the raw\n * metadata and parts (stable for a settled message) and derives in `useMemo`.\n */\nexport const InboundChannelLine: FC<{ className?: string }> = ({ className }) => {\n const custom = useAuiState((s) => s.message.metadata?.custom);\n const parts = useAuiState((s) => s.message.parts);\n const message = useMemo(\n () =>\n isInboundChannelMessage(custom)\n ? readInboundChannelMessage({ custom, text: firstText(parts) })\n : null,\n [custom, parts],\n );\n return message ? <InboundChannelSource message={message} className={className} /> : null;\n};\n\n/** An email's subject, the bubble's first line. Nothing for any other message. */\nexport const InboundChannelSubject: FC = () => {\n const subject = useAuiState((s) => inboundEmailSubject(s.message.metadata?.custom));\n if (!subject) return null;\n return (\n <div className=\"mb-1 font-semibold\" data-slot=\"inbound-channel-subject\">\n {subject}\n </div>\n );\n};\n\nconst wrappedTextComponents = new WeakMap<TextMessagePartComponent, TextMessagePartComponent>();\n\n/**\n * `Text` showing a channel message's words without the routing block the\n * agent read ahead of them. Any other text passes through unchanged, tags a\n * person typed included. Cached per component, so a host's part map keeps a\n * stable identity.\n */\nexport function withInboundMessageBody(Text: TextMessagePartComponent): TextMessagePartComponent {\n const cached = wrappedTextComponents.get(Text);\n if (cached) return cached;\n const InboundMessageBodyText: TextMessagePartComponent = (props) => {\n const custom = useAuiState((s) => s.message.metadata?.custom);\n const text = useMemo(() => inboundMessageBody({ custom, text: props.text }), [custom, props.text]);\n return <Text {...props} text={text} />;\n };\n wrappedTextComponents.set(Text, InboundMessageBodyText);\n return InboundMessageBodyText;\n}\n\n/** The clipboard write assistant-ui's own Copy makes. */\nfunction writeClipboard(text: string): Promise<void> {\n if (typeof navigator === \"undefined\" || !navigator.clipboard) {\n return Promise.reject(new Error(\"Clipboard API is unavailable\"));\n }\n return navigator.clipboard.writeText(text);\n}\n\n/**\n * Copy for a user bubble, which copies what the bubble shows: a channel\n * message's words without the routing block the agent read ahead of them\n * (`inboundMessageBody`), under an email's subject (`inboundEmailSubject`);\n * any other message's text as assistant-ui's own Copy does. While the message\n * is being edited it copies the draft, as that Copy does. `copy` writes the\n * clipboard and flashes `isCopied`; `disabled` says there is nothing to copy.\n */\nexport function useUserMessageCopy(): { copy: () => void; disabled: boolean; isCopied: boolean } {\n const custom = useAuiState((s) => s.message.metadata?.custom);\n const copyToClipboard = useCallback(\n (text: string) => {\n const words = inboundMessageBody({ custom, text });\n const subject = inboundEmailSubject(custom);\n return writeClipboard(subject ? [subject, words].filter(Boolean).join(\"\\n\\n\") : words);\n },\n [custom],\n );\n return useActionBarCopy({ copyToClipboard });\n}\n"],"names":["jsx","jsxs","MailIcon","cn","MessageSquareIcon","CodeIcon","AudioLinesIcon","useState","Collapsible","CollapsibleTrigger","ChevronDownIcon","CollapsibleContent","useAuiState","useMemo","isInboundChannelMessage","readInboundChannelMessage","inboundEmailSubject","inboundMessageBody","useCallback","useActionBarCopy"],"mappings":"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"}
@@ -1,23 +1,3 @@
1
- /**
2
- * A user turn that came in over a channel (Slack, email, a text, Microsoft
3
- * Teams, the Programmatic API, a voice memo), drawn as the words that were
4
- * sent under a line naming the channel and the sender.
5
- *
6
- * The channel's routing block (`<inbound-message-context>`: the facts the
7
- * agent is told, such as Slack ids, the numbers a text went between and how a
8
- * reply is delivered) leaves the bubble. The line above the bubble opens it
9
- * as a labelled list, and hovering the line shows the channel, the place and
10
- * the sender in full. Every host that renders the SDK's user message gets
11
- * this; a host with its own user message composes it from
12
- * `InboundChannelLine`, `InboundChannelSubject` and `withInboundMessageBody`
13
- * (or `UserMessageText`). Only a message with the `inbound_channel` envelope
14
- * is a channel message: tags someone types show as typed.
15
- *
16
- * The hover card is CSS, not a Radix tooltip, and the glyphs are icons the
17
- * chat already draws: a host that splits `node_modules` into startup vendor
18
- * chunks (the Office pane) would otherwise load a tooltip stack at startup for
19
- * a line it rarely shows.
20
- */
21
1
  import { type TextMessagePartComponent } from "@assistant-ui/react";
22
2
  import { type FC } from "react";
23
3
  import { type InboundChannelKind, type InboundChannelMessage } from "./inbound-channel-reader";
@@ -58,3 +38,16 @@ export declare const InboundChannelSubject: FC;
58
38
  * stable identity.
59
39
  */
60
40
  export declare function withInboundMessageBody(Text: TextMessagePartComponent): TextMessagePartComponent;
41
+ /**
42
+ * Copy for a user bubble, which copies what the bubble shows: a channel
43
+ * message's words without the routing block the agent read ahead of them
44
+ * (`inboundMessageBody`), under an email's subject (`inboundEmailSubject`);
45
+ * any other message's text as assistant-ui's own Copy does. While the message
46
+ * is being edited it copies the draft, as that Copy does. `copy` writes the
47
+ * clipboard and flashes `isCopied`; `disabled` says there is nothing to copy.
48
+ */
49
+ export declare function useUserMessageCopy(): {
50
+ copy: () => void;
51
+ disabled: boolean;
52
+ isCopied: boolean;
53
+ };
@@ -1,7 +1,8 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useActionBarCopy } from "@assistant-ui/core/react";
2
3
  import { useAuiState } from "@assistant-ui/react";
3
4
  import { ChevronDownIcon, MessageSquareIcon, AudioLinesIcon, CodeIcon, MailIcon } from "lucide-react";
4
- import { useMemo, useState } from "react";
5
+ import { useMemo, useCallback, useState } from "react";
5
6
  import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "../../ui/collapsible.js";
6
7
  import { readInboundChannelMessage } from "./inbound-channel-reader.js";
7
8
  import { isInboundChannelMessage, inboundEmailSubject, inboundMessageBody } from "../../agent-runtime/src/inbound-channel.js";
@@ -209,11 +210,33 @@ function withInboundMessageBody(Text) {
209
210
  wrappedTextComponents.set(Text, InboundMessageBodyText);
210
211
  return InboundMessageBodyText;
211
212
  }
213
+ function writeClipboard(text) {
214
+ if (typeof navigator === "undefined" || !navigator.clipboard) {
215
+ return Promise.reject(new Error("Clipboard API is unavailable"));
216
+ }
217
+ return navigator.clipboard.writeText(text);
218
+ }
219
+ function useUserMessageCopy() {
220
+ const custom = useAuiState((s) => {
221
+ var _a;
222
+ return (_a = s.message.metadata) == null ? void 0 : _a.custom;
223
+ });
224
+ const copyToClipboard = useCallback(
225
+ (text) => {
226
+ const words = inboundMessageBody({ custom, text });
227
+ const subject = inboundEmailSubject(custom);
228
+ return writeClipboard(subject ? [subject, words].filter(Boolean).join("\n\n") : words);
229
+ },
230
+ [custom]
231
+ );
232
+ return useActionBarCopy({ copyToClipboard });
233
+ }
212
234
  export {
213
235
  ChannelMark,
214
236
  InboundChannelLine,
215
237
  InboundChannelSource,
216
238
  InboundChannelSubject,
239
+ useUserMessageCopy,
217
240
  withInboundMessageBody
218
241
  };
219
242
  //# sourceMappingURL=inbound-channel-message.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"inbound-channel-message.js","sources":["../../../src/chat-ui/messages/inbound-channel-message.tsx"],"sourcesContent":["/**\n * A user turn that came in over a channel (Slack, email, a text, Microsoft\n * Teams, the Programmatic API, a voice memo), drawn as the words that were\n * sent under a line naming the channel and the sender.\n *\n * The channel's routing block (`<inbound-message-context>`: the facts the\n * agent is told, such as Slack ids, the numbers a text went between and how a\n * reply is delivered) leaves the bubble. The line above the bubble opens it\n * as a labelled list, and hovering the line shows the channel, the place and\n * the sender in full. Every host that renders the SDK's user message gets\n * this; a host with its own user message composes it from\n * `InboundChannelLine`, `InboundChannelSubject` and `withInboundMessageBody`\n * (or `UserMessageText`). Only a message with the `inbound_channel` envelope\n * is a channel message: tags someone types show as typed.\n *\n * The hover card is CSS, not a Radix tooltip, and the glyphs are icons the\n * chat already draws: a host that splits `node_modules` into startup vendor\n * chunks (the Office pane) would otherwise load a tooltip stack at startup for\n * a line it rarely shows.\n */\nimport { type TextMessagePartComponent, useAuiState } from \"@assistant-ui/react\";\nimport {\n inboundEmailSubject,\n inboundMessageBody,\n isInboundChannelMessage,\n} from \"@athenaintel/agent-runtime\";\nimport { AudioLinesIcon, ChevronDownIcon, CodeIcon, MailIcon, MessageSquareIcon } from \"lucide-react\";\nimport { type FC, useMemo, useState } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger } from \"../../ui/collapsible\";\nimport {\n type InboundChannelKind,\n type InboundChannelMessage,\n readInboundChannelMessage,\n} from \"./inbound-channel-reader\";\n\n/** Slack's four-colour mark. */\nconst SlackMark: FC<{ className?: string }> = ({ className }) => (\n <svg viewBox=\"0 0 2447.6 2452.5\" className={className} aria-hidden=\"true\" focusable=\"false\">\n <g clipRule=\"evenodd\" fillRule=\"evenodd\">\n <path\n d=\"m897.4 0c-135.3.1-244.8 109.9-244.7 245.2-.1 135.3 109.5 245.1 244.8 245.2h244.8v-245.1c.1-135.3-109.5-245.1-244.9-245.3.1 0 .1 0 0 0m0 654h-652.6c-135.3.1-244.9 109.9-244.8 245.2-.2 135.3 109.4 245.1 244.7 245.3h652.7c135.3-.1 244.9-109.9 244.8-245.2.1-135.4-109.5-245.2-244.8-245.3z\"\n fill=\"#36c5f0\"\n />\n <path\n d=\"m2447.6 899.2c.1-135.3-109.5-245.1-244.8-245.2-135.3.1-244.9 109.9-244.8 245.2v245.3h244.8c135.3-.1 244.9-109.9 244.8-245.3zm-652.7 0v-654c.1-135.2-109.4-245-244.7-245.2-135.3.1-244.9 109.9-244.8 245.2v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.3z\"\n fill=\"#2eb67d\"\n />\n <path\n d=\"m1550.1 2452.5c135.3-.1 244.9-109.9 244.8-245.2.1-135.3-109.5-245.1-244.8-245.2h-244.8v245.2c-.1 135.2 109.5 245 244.8 245.2zm0-654.1h652.7c135.3-.1 244.9-109.9 244.8-245.2.2-135.3-109.4-245.1-244.7-245.3h-652.7c-135.3.1-244.9 109.9-244.8 245.2-.1 135.4 109.4 245.2 244.7 245.3z\"\n fill=\"#ecb22e\"\n />\n <path\n d=\"m0 1553.2c-.1 135.3 109.5 245.1 244.8 245.2 135.3-.1 244.9-109.9 244.8-245.2v-245.2h-244.8c-135.3.1-244.9 109.9-244.8 245.2zm652.7 0v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.2v-653.9c.2-135.3-109.4-245.1-244.7-245.3-135.4 0-244.9 109.8-244.8 245.1 0 0 0 .1 0 0\"\n fill=\"#e01e5a\"\n />\n </g>\n </svg>\n);\n\n/** Microsoft Teams' mark: the two people and the T tile, in flat colour. */\nconst TeamsMark: FC<{ className?: string }> = ({ className }) => (\n <svg viewBox=\"0 0 2228.833 2073.333\" className={className} aria-hidden=\"true\" focusable=\"false\">\n <path\n fill=\"#5059C9\"\n d=\"M1554.637 777.5h575.713c54.391 0 98.483 44.092 98.483 98.483v524.398c0 199.901-162.051 361.952-361.952 361.952h-1.711c-199.901.028-361.975-162-362.004-361.901V828.971c0-28.427 23.044-51.471 51.471-51.471z\"\n />\n <circle fill=\"#5059C9\" cx=\"1943.75\" cy=\"440.583\" r=\"233.25\" />\n <circle fill=\"#7B83EB\" cx=\"1218.083\" cy=\"336.917\" r=\"336.917\" />\n <path\n fill=\"#7B83EB\"\n d=\"M1667.323 777.5H717.01c-53.743 1.33-96.257 45.931-95.01 99.676v598.105c-7.505 322.519 247.657 590.16 570.167 598.053 322.51-7.893 577.671-275.534 570.167-598.053V877.176c1.245-53.745-41.268-98.346-95.011-99.676z\"\n />\n <path\n fill=\"#4B53BC\"\n d=\"M95.01 466.5h950.312c52.473 0 95.01 42.538 95.01 95.01v950.312c0 52.473-42.538 95.01-95.01 95.01H95.01c-52.473 0-95.01-42.538-95.01-95.01V561.51c0-52.472 42.538-95.01 95.01-95.01z\"\n />\n <path fill=\"#FFF\" d=\"M820.211 828.193H630.241v517.297H509.211V828.193H320.123V727.844h500.088z\" />\n </svg>\n);\n\n/**\n * The channel's mark at the size `className` gives: Slack's and Teams' own\n * logos, a tinted glyph for email, texts, the API and voice memos, a speech\n * bubble for any other channel. Decorative: the line it sits on names the\n * channel.\n */\nexport const ChannelMark: FC<{ kind: InboundChannelKind; className?: string }> = ({\n kind,\n className,\n}) => {\n switch (kind) {\n case \"slack\":\n return <SlackMark className={className} />;\n case \"teams\":\n return <TeamsMark className={className} />;\n case \"email\":\n return <MailIcon className={cn(\"text-sky-600\", className)} aria-hidden=\"true\" />;\n case \"sms\":\n return <MessageSquareIcon className={cn(\"text-emerald-600\", className)} aria-hidden=\"true\" />;\n case \"api\":\n return <CodeIcon className={cn(\"text-muted-foreground\", className)} aria-hidden=\"true\" />;\n case \"voice_memo\":\n return <AudioLinesIcon className={cn(\"text-violet-600\", className)} aria-hidden=\"true\" />;\n default:\n return <MessageSquareIcon className={cn(\"text-muted-foreground\", className)} aria-hidden=\"true\" />;\n }\n};\n\n/** The first text part's words: where a channel's routing block sits. */\nfunction firstText(parts: unknown): string {\n if (!Array.isArray(parts)) return \"\";\n for (const part of parts) {\n if (\n typeof part === \"object\" &&\n part !== null &&\n (part as { type?: unknown }).type === \"text\" &&\n typeof (part as { text?: unknown }).text === \"string\"\n ) {\n return (part as { text: string }).text;\n }\n }\n return \"\";\n}\n\n/** Everything the agent read with the message, labelled; the thread a mention came in below it. */\nconst InboundChannelFacts: FC<{ message: InboundChannelMessage }> = ({ message }) => (\n <div\n className=\"flex w-full min-w-0 flex-col gap-2 rounded-xl border border-border bg-background px-3 py-2.5 text-left text-xs\"\n data-slot=\"inbound-channel-facts\"\n >\n <p className=\"text-muted-foreground\">What the agent received with this message</p>\n {message.facts.length > 0 ? (\n <dl className=\"grid grid-cols-[auto_minmax(0,1fr)] gap-x-3 gap-y-1\">\n {message.facts.map((fact, index) => (\n <div key={`${fact.key}-${index}`} className=\"contents\">\n <dt className=\"whitespace-nowrap text-muted-foreground\">{fact.label}</dt>\n <dd className=\"text-foreground [overflow-wrap:anywhere]\">{fact.value}</dd>\n </div>\n ))}\n </dl>\n ) : null}\n {message.quoted ? (\n <div className=\"flex flex-col gap-1\">\n <p className=\"text-muted-foreground\">Earlier in the thread</p>\n <p className=\"whitespace-pre-wrap border-border border-l-2 pl-2 text-foreground [overflow-wrap:anywhere]\">\n {message.quoted}\n </p>\n </div>\n ) : null}\n </div>\n);\n\n/**\n * The line above a channel message's bubble: the channel's mark, the sender's\n * name and address (an email address, a phone number), and a chevron that\n * opens what the agent received. Hovering or focusing it shows a card with\n * the channel, the place and the sender in full, while the facts are closed.\n */\nexport const InboundChannelSource: FC<{ message: InboundChannelMessage; className?: string }> = ({\n message,\n className,\n}) => {\n const [open, setOpen] = useState(false);\n const origin = message.origin;\n const name = origin.senderName;\n const handle = origin.senderHandle;\n const primary = name ?? handle ?? origin.label;\n const secondary = name && handle ? handle : null;\n const channelLine = [origin.label, origin.place].filter(Boolean).join(\" · \");\n\n return (\n <Collapsible\n open={open}\n onOpenChange={setOpen}\n className={cn(\"flex w-full min-w-0 flex-col items-end gap-1\", className)}\n data-slot=\"inbound-channel\"\n data-channel={origin.kind}\n >\n <div className=\"group/inbound-channel relative flex min-w-0 max-w-full justify-end\">\n <CollapsibleTrigger\n data-testid=\"inbound-channel-source\"\n className=\"flex min-w-0 max-w-full items-center gap-1.5 rounded-md px-1.5 py-0.5 text-muted-foreground text-xs transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <ChannelMark kind={origin.kind} className=\"size-3.5 shrink-0\" />\n <span className=\"truncate font-medium text-foreground/80\">{primary}</span>\n {secondary ? <span className=\"truncate\">{secondary}</span> : null}\n <span className=\"sr-only\">{`, via ${channelLine}. Show what the agent received`}</span>\n <ChevronDownIcon\n className={cn(\"size-3 shrink-0 transition-transform\", open && \"rotate-180\")}\n aria-hidden=\"true\"\n />\n </CollapsibleTrigger>\n {/* The hover card: what the line says, in full (the button's own text\n carries it for screen readers). Shown below the line, over the\n bubble, after a short rest, and never while the facts are open. */}\n {open ? null : (\n <div\n aria-hidden=\"true\"\n data-slot=\"inbound-channel-card\"\n className=\"pointer-events-none absolute top-full right-0 z-20 mt-1 flex w-max max-w-xs flex-col gap-0.5 rounded-md bg-foreground px-3 py-1.5 text-left text-background text-xs opacity-0 shadow-md transition-opacity delay-0 group-focus-within/inbound-channel:opacity-100 group-hover/inbound-channel:opacity-100 group-hover/inbound-channel:delay-300\"\n >\n <span className=\"flex items-center gap-1.5 font-medium\">\n <ChannelMark kind={origin.kind} className=\"size-3.5 shrink-0\" />\n {channelLine}\n </span>\n {name ? <span>{name}</span> : null}\n {handle ? <span className=\"opacity-80\">{handle}</span> : null}\n {origin.subject ? (\n <span className=\"opacity-80\">\n {`${origin.kind === \"voice_memo\" ? \"Memo\" : \"Subject\"}: ${origin.subject}`}\n </span>\n ) : null}\n </div>\n )}\n </div>\n <CollapsibleContent className=\"w-full min-w-0\">\n <InboundChannelFacts message={message} />\n </CollapsibleContent>\n </Collapsible>\n );\n};\n\n/**\n * The line above the bubble for the assistant-ui message in scope; nothing\n * for a message without the `inbound_channel` envelope. Selects the raw\n * metadata and parts (stable for a settled message) and derives in `useMemo`.\n */\nexport const InboundChannelLine: FC<{ className?: string }> = ({ className }) => {\n const custom = useAuiState((s) => s.message.metadata?.custom);\n const parts = useAuiState((s) => s.message.parts);\n const message = useMemo(\n () =>\n isInboundChannelMessage(custom)\n ? readInboundChannelMessage({ custom, text: firstText(parts) })\n : null,\n [custom, parts],\n );\n return message ? <InboundChannelSource message={message} className={className} /> : null;\n};\n\n/** An email's subject, the bubble's first line. Nothing for any other message. */\nexport const InboundChannelSubject: FC = () => {\n const subject = useAuiState((s) => inboundEmailSubject(s.message.metadata?.custom));\n if (!subject) return null;\n return (\n <div className=\"mb-1 font-semibold\" data-slot=\"inbound-channel-subject\">\n {subject}\n </div>\n );\n};\n\nconst wrappedTextComponents = new WeakMap<TextMessagePartComponent, TextMessagePartComponent>();\n\n/**\n * `Text` showing a channel message's words without the routing block the\n * agent read ahead of them. Any other text passes through unchanged, tags a\n * person typed included. Cached per component, so a host's part map keeps a\n * stable identity.\n */\nexport function withInboundMessageBody(Text: TextMessagePartComponent): TextMessagePartComponent {\n const cached = wrappedTextComponents.get(Text);\n if (cached) return cached;\n const InboundMessageBodyText: TextMessagePartComponent = (props) => {\n const custom = useAuiState((s) => s.message.metadata?.custom);\n const text = useMemo(() => inboundMessageBody({ custom, text: props.text }), [custom, props.text]);\n return <Text {...props} text={text} />;\n };\n wrappedTextComponents.set(Text, InboundMessageBodyText);\n return InboundMessageBodyText;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"inbound-channel-message.js","sources":["../../../src/chat-ui/messages/inbound-channel-message.tsx"],"sourcesContent":["/**\n * A user turn that came in over a channel (Slack, email, a text, Microsoft\n * Teams, the Programmatic API, a voice memo), drawn as the words that were\n * sent under a line naming the channel and the sender.\n *\n * The channel's routing block (`<inbound-message-context>`: the facts the\n * agent is told, such as Slack ids, the numbers a text went between and how a\n * reply is delivered) leaves the bubble. The line above the bubble opens it\n * as a labelled list, and hovering the line shows the channel, the place and\n * the sender in full. Every host that renders the SDK's user message gets\n * this; a host with its own user message composes it from\n * `InboundChannelLine`, `InboundChannelSubject` and `withInboundMessageBody`\n * (or `UserMessageText`). Only a message with the `inbound_channel` envelope\n * is a channel message: tags someone types show as typed.\n *\n * The hover card is CSS, not a Radix tooltip, and the glyphs are icons the\n * chat already draws: a host that splits `node_modules` into startup vendor\n * chunks (the Office pane) would otherwise load a tooltip stack at startup for\n * a line it rarely shows.\n */\nimport { useActionBarCopy } from \"@assistant-ui/core/react\";\nimport { type TextMessagePartComponent, useAuiState } from \"@assistant-ui/react\";\nimport {\n inboundEmailSubject,\n inboundMessageBody,\n isInboundChannelMessage,\n} from \"@athenaintel/agent-runtime\";\nimport { AudioLinesIcon, ChevronDownIcon, CodeIcon, MailIcon, MessageSquareIcon } from \"lucide-react\";\nimport { type FC, useCallback, useMemo, useState } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger } from \"../../ui/collapsible\";\nimport {\n type InboundChannelKind,\n type InboundChannelMessage,\n readInboundChannelMessage,\n} from \"./inbound-channel-reader\";\n\n/** Slack's four-colour mark. */\nconst SlackMark: FC<{ className?: string }> = ({ className }) => (\n <svg viewBox=\"0 0 2447.6 2452.5\" className={className} aria-hidden=\"true\" focusable=\"false\">\n <g clipRule=\"evenodd\" fillRule=\"evenodd\">\n <path\n d=\"m897.4 0c-135.3.1-244.8 109.9-244.7 245.2-.1 135.3 109.5 245.1 244.8 245.2h244.8v-245.1c.1-135.3-109.5-245.1-244.9-245.3.1 0 .1 0 0 0m0 654h-652.6c-135.3.1-244.9 109.9-244.8 245.2-.2 135.3 109.4 245.1 244.7 245.3h652.7c135.3-.1 244.9-109.9 244.8-245.2.1-135.4-109.5-245.2-244.8-245.3z\"\n fill=\"#36c5f0\"\n />\n <path\n d=\"m2447.6 899.2c.1-135.3-109.5-245.1-244.8-245.2-135.3.1-244.9 109.9-244.8 245.2v245.3h244.8c135.3-.1 244.9-109.9 244.8-245.3zm-652.7 0v-654c.1-135.2-109.4-245-244.7-245.2-135.3.1-244.9 109.9-244.8 245.2v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.3z\"\n fill=\"#2eb67d\"\n />\n <path\n d=\"m1550.1 2452.5c135.3-.1 244.9-109.9 244.8-245.2.1-135.3-109.5-245.1-244.8-245.2h-244.8v245.2c-.1 135.2 109.5 245 244.8 245.2zm0-654.1h652.7c135.3-.1 244.9-109.9 244.8-245.2.2-135.3-109.4-245.1-244.7-245.3h-652.7c-135.3.1-244.9 109.9-244.8 245.2-.1 135.4 109.4 245.2 244.7 245.3z\"\n fill=\"#ecb22e\"\n />\n <path\n d=\"m0 1553.2c-.1 135.3 109.5 245.1 244.8 245.2 135.3-.1 244.9-109.9 244.8-245.2v-245.2h-244.8c-135.3.1-244.9 109.9-244.8 245.2zm652.7 0v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.2v-653.9c.2-135.3-109.4-245.1-244.7-245.3-135.4 0-244.9 109.8-244.8 245.1 0 0 0 .1 0 0\"\n fill=\"#e01e5a\"\n />\n </g>\n </svg>\n);\n\n/** Microsoft Teams' mark: the two people and the T tile, in flat colour. */\nconst TeamsMark: FC<{ className?: string }> = ({ className }) => (\n <svg viewBox=\"0 0 2228.833 2073.333\" className={className} aria-hidden=\"true\" focusable=\"false\">\n <path\n fill=\"#5059C9\"\n d=\"M1554.637 777.5h575.713c54.391 0 98.483 44.092 98.483 98.483v524.398c0 199.901-162.051 361.952-361.952 361.952h-1.711c-199.901.028-361.975-162-362.004-361.901V828.971c0-28.427 23.044-51.471 51.471-51.471z\"\n />\n <circle fill=\"#5059C9\" cx=\"1943.75\" cy=\"440.583\" r=\"233.25\" />\n <circle fill=\"#7B83EB\" cx=\"1218.083\" cy=\"336.917\" r=\"336.917\" />\n <path\n fill=\"#7B83EB\"\n d=\"M1667.323 777.5H717.01c-53.743 1.33-96.257 45.931-95.01 99.676v598.105c-7.505 322.519 247.657 590.16 570.167 598.053 322.51-7.893 577.671-275.534 570.167-598.053V877.176c1.245-53.745-41.268-98.346-95.011-99.676z\"\n />\n <path\n fill=\"#4B53BC\"\n d=\"M95.01 466.5h950.312c52.473 0 95.01 42.538 95.01 95.01v950.312c0 52.473-42.538 95.01-95.01 95.01H95.01c-52.473 0-95.01-42.538-95.01-95.01V561.51c0-52.472 42.538-95.01 95.01-95.01z\"\n />\n <path fill=\"#FFF\" d=\"M820.211 828.193H630.241v517.297H509.211V828.193H320.123V727.844h500.088z\" />\n </svg>\n);\n\n/**\n * The channel's mark at the size `className` gives: Slack's and Teams' own\n * logos, a tinted glyph for email, texts, the API and voice memos, a speech\n * bubble for any other channel. Decorative: the line it sits on names the\n * channel.\n */\nexport const ChannelMark: FC<{ kind: InboundChannelKind; className?: string }> = ({\n kind,\n className,\n}) => {\n switch (kind) {\n case \"slack\":\n return <SlackMark className={className} />;\n case \"teams\":\n return <TeamsMark className={className} />;\n case \"email\":\n return <MailIcon className={cn(\"text-sky-600\", className)} aria-hidden=\"true\" />;\n case \"sms\":\n return <MessageSquareIcon className={cn(\"text-emerald-600\", className)} aria-hidden=\"true\" />;\n case \"api\":\n return <CodeIcon className={cn(\"text-muted-foreground\", className)} aria-hidden=\"true\" />;\n case \"voice_memo\":\n return <AudioLinesIcon className={cn(\"text-violet-600\", className)} aria-hidden=\"true\" />;\n default:\n return <MessageSquareIcon className={cn(\"text-muted-foreground\", className)} aria-hidden=\"true\" />;\n }\n};\n\n/** The first text part's words: where a channel's routing block sits. */\nfunction firstText(parts: unknown): string {\n if (!Array.isArray(parts)) return \"\";\n for (const part of parts) {\n if (\n typeof part === \"object\" &&\n part !== null &&\n (part as { type?: unknown }).type === \"text\" &&\n typeof (part as { text?: unknown }).text === \"string\"\n ) {\n return (part as { text: string }).text;\n }\n }\n return \"\";\n}\n\n/** Everything the agent read with the message, labelled; the thread a mention came in below it. */\nconst InboundChannelFacts: FC<{ message: InboundChannelMessage }> = ({ message }) => (\n <div\n className=\"flex w-full min-w-0 flex-col gap-2 rounded-xl border border-border bg-background px-3 py-2.5 text-left text-xs\"\n data-slot=\"inbound-channel-facts\"\n >\n <p className=\"text-muted-foreground\">What the agent received with this message</p>\n {message.facts.length > 0 ? (\n <dl className=\"grid grid-cols-[auto_minmax(0,1fr)] gap-x-3 gap-y-1\">\n {message.facts.map((fact, index) => (\n <div key={`${fact.key}-${index}`} className=\"contents\">\n <dt className=\"whitespace-nowrap text-muted-foreground\">{fact.label}</dt>\n <dd className=\"text-foreground [overflow-wrap:anywhere]\">{fact.value}</dd>\n </div>\n ))}\n </dl>\n ) : null}\n {message.quoted ? (\n <div className=\"flex flex-col gap-1\">\n <p className=\"text-muted-foreground\">Earlier in the thread</p>\n <p className=\"whitespace-pre-wrap border-border border-l-2 pl-2 text-foreground [overflow-wrap:anywhere]\">\n {message.quoted}\n </p>\n </div>\n ) : null}\n </div>\n);\n\n/**\n * The line above a channel message's bubble: the channel's mark, the sender's\n * name and address (an email address, a phone number), and a chevron that\n * opens what the agent received. Hovering or focusing it shows a card with\n * the channel, the place and the sender in full, while the facts are closed.\n */\nexport const InboundChannelSource: FC<{ message: InboundChannelMessage; className?: string }> = ({\n message,\n className,\n}) => {\n const [open, setOpen] = useState(false);\n const origin = message.origin;\n const name = origin.senderName;\n const handle = origin.senderHandle;\n const primary = name ?? handle ?? origin.label;\n const secondary = name && handle ? handle : null;\n const channelLine = [origin.label, origin.place].filter(Boolean).join(\" · \");\n\n return (\n <Collapsible\n open={open}\n onOpenChange={setOpen}\n className={cn(\"flex w-full min-w-0 flex-col items-end gap-1\", className)}\n data-slot=\"inbound-channel\"\n data-channel={origin.kind}\n >\n <div className=\"group/inbound-channel relative flex min-w-0 max-w-full justify-end\">\n <CollapsibleTrigger\n data-testid=\"inbound-channel-source\"\n className=\"flex min-w-0 max-w-full items-center gap-1.5 rounded-md px-1.5 py-0.5 text-muted-foreground text-xs transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <ChannelMark kind={origin.kind} className=\"size-3.5 shrink-0\" />\n <span className=\"truncate font-medium text-foreground/80\">{primary}</span>\n {secondary ? <span className=\"truncate\">{secondary}</span> : null}\n <span className=\"sr-only\">{`, via ${channelLine}. Show what the agent received`}</span>\n <ChevronDownIcon\n className={cn(\"size-3 shrink-0 transition-transform\", open && \"rotate-180\")}\n aria-hidden=\"true\"\n />\n </CollapsibleTrigger>\n {/* The hover card: what the line says, in full (the button's own text\n carries it for screen readers). Shown below the line, over the\n bubble, after a short rest, and never while the facts are open. */}\n {open ? null : (\n <div\n aria-hidden=\"true\"\n data-slot=\"inbound-channel-card\"\n className=\"pointer-events-none absolute top-full right-0 z-20 mt-1 flex w-max max-w-xs flex-col gap-0.5 rounded-md bg-foreground px-3 py-1.5 text-left text-background text-xs opacity-0 shadow-md transition-opacity delay-0 group-focus-within/inbound-channel:opacity-100 group-hover/inbound-channel:opacity-100 group-hover/inbound-channel:delay-300\"\n >\n <span className=\"flex items-center gap-1.5 font-medium\">\n <ChannelMark kind={origin.kind} className=\"size-3.5 shrink-0\" />\n {channelLine}\n </span>\n {name ? <span>{name}</span> : null}\n {handle ? <span className=\"opacity-80\">{handle}</span> : null}\n {origin.subject ? (\n <span className=\"opacity-80\">\n {`${origin.kind === \"voice_memo\" ? \"Memo\" : \"Subject\"}: ${origin.subject}`}\n </span>\n ) : null}\n </div>\n )}\n </div>\n <CollapsibleContent className=\"w-full min-w-0\">\n <InboundChannelFacts message={message} />\n </CollapsibleContent>\n </Collapsible>\n );\n};\n\n/**\n * The line above the bubble for the assistant-ui message in scope; nothing\n * for a message without the `inbound_channel` envelope. Selects the raw\n * metadata and parts (stable for a settled message) and derives in `useMemo`.\n */\nexport const InboundChannelLine: FC<{ className?: string }> = ({ className }) => {\n const custom = useAuiState((s) => s.message.metadata?.custom);\n const parts = useAuiState((s) => s.message.parts);\n const message = useMemo(\n () =>\n isInboundChannelMessage(custom)\n ? readInboundChannelMessage({ custom, text: firstText(parts) })\n : null,\n [custom, parts],\n );\n return message ? <InboundChannelSource message={message} className={className} /> : null;\n};\n\n/** An email's subject, the bubble's first line. Nothing for any other message. */\nexport const InboundChannelSubject: FC = () => {\n const subject = useAuiState((s) => inboundEmailSubject(s.message.metadata?.custom));\n if (!subject) return null;\n return (\n <div className=\"mb-1 font-semibold\" data-slot=\"inbound-channel-subject\">\n {subject}\n </div>\n );\n};\n\nconst wrappedTextComponents = new WeakMap<TextMessagePartComponent, TextMessagePartComponent>();\n\n/**\n * `Text` showing a channel message's words without the routing block the\n * agent read ahead of them. Any other text passes through unchanged, tags a\n * person typed included. Cached per component, so a host's part map keeps a\n * stable identity.\n */\nexport function withInboundMessageBody(Text: TextMessagePartComponent): TextMessagePartComponent {\n const cached = wrappedTextComponents.get(Text);\n if (cached) return cached;\n const InboundMessageBodyText: TextMessagePartComponent = (props) => {\n const custom = useAuiState((s) => s.message.metadata?.custom);\n const text = useMemo(() => inboundMessageBody({ custom, text: props.text }), [custom, props.text]);\n return <Text {...props} text={text} />;\n };\n wrappedTextComponents.set(Text, InboundMessageBodyText);\n return InboundMessageBodyText;\n}\n\n/** The clipboard write assistant-ui's own Copy makes. */\nfunction writeClipboard(text: string): Promise<void> {\n if (typeof navigator === \"undefined\" || !navigator.clipboard) {\n return Promise.reject(new Error(\"Clipboard API is unavailable\"));\n }\n return navigator.clipboard.writeText(text);\n}\n\n/**\n * Copy for a user bubble, which copies what the bubble shows: a channel\n * message's words without the routing block the agent read ahead of them\n * (`inboundMessageBody`), under an email's subject (`inboundEmailSubject`);\n * any other message's text as assistant-ui's own Copy does. While the message\n * is being edited it copies the draft, as that Copy does. `copy` writes the\n * clipboard and flashes `isCopied`; `disabled` says there is nothing to copy.\n */\nexport function useUserMessageCopy(): { copy: () => void; disabled: boolean; isCopied: boolean } {\n const custom = useAuiState((s) => s.message.metadata?.custom);\n const copyToClipboard = useCallback(\n (text: string) => {\n const words = inboundMessageBody({ custom, text });\n const subject = inboundEmailSubject(custom);\n return writeClipboard(subject ? [subject, words].filter(Boolean).join(\"\\n\\n\") : words);\n },\n [custom],\n );\n return useActionBarCopy({ copyToClipboard });\n}\n"],"names":[],"mappings":"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"}
@@ -100,6 +100,19 @@ const UserMessageText = ({ text }) => {
100
100
  return /* @__PURE__ */ jsxRuntime.jsx(chatMessageBody.ChatMessageBody, { text: shown, typed: true });
101
101
  };
102
102
  const USER_MESSAGE_PART_COMPONENTS = { Text: UserMessageText, Image: messageImages.MessageImagePart };
103
+ const UserCopyActionButton = () => {
104
+ const { copy, disabled, isCopied } = inboundChannelMessage.useUserMessageCopy();
105
+ return /* @__PURE__ */ jsxRuntime.jsx(
106
+ threadChrome.SmallIconButton,
107
+ {
108
+ tooltip: "Copy",
109
+ onClick: copy,
110
+ disabled,
111
+ "data-copied": isCopied ? "true" : void 0,
112
+ children: isCopied ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckIcon, { className: "size-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CopyIcon, { className: "size-3" })
113
+ }
114
+ );
115
+ };
103
116
  const UserActionBar = () => /* @__PURE__ */ jsxRuntime.jsxs(
104
117
  react.ActionBarPrimitive.Root,
105
118
  {
@@ -110,7 +123,7 @@ const UserActionBar = () => /* @__PURE__ */ jsxRuntime.jsxs(
110
123
  "group-hover/user-message:pointer-events-auto group-hover/user-message:opacity-100"
111
124
  ),
112
125
  children: [
113
- /* @__PURE__ */ jsxRuntime.jsx(CopyActionButton, {}),
126
+ /* @__PURE__ */ jsxRuntime.jsx(UserCopyActionButton, {}),
114
127
  /* @__PURE__ */ jsxRuntime.jsx(EditActionButton, {})
115
128
  ]
116
129
  }
@@ -1 +1 @@
1
- {"version":3,"file":"message-components.cjs","sources":["../../../src/chat-ui/messages/message-components.tsx"],"sourcesContent":["import {\n ActionBarPrimitive,\n AuiIf,\n BranchPickerPrimitive,\n MessagePrimitive,\n useAuiState,\n useMessagePartText,\n useSmooth,\n type ToolCallMessagePartComponent,\n} from \"@assistant-ui/react\";\nimport {\n CHAT_EDIT_UNREACHABLE_LABEL,\n DEEP_AGENT_COMPOSER_MARKDOWN_KEY,\n hideUnterminatedMarkdownLink,\n inboundMessageBody,\n isGithubWakeMessage,\n isSubscriptionWakeMessage,\n isVoiceDelegationMessage,\n readDeepAgentComposerMarkdown,\n readRuntimeMessageFromSourceMessages,\n readSoleRuntimeMessageKind,\n useChatOutboxReachable,\n useDeepAgentChatOutboxContext,\n} from \"@athenaintel/agent-runtime\";\nimport {\n ArrowUpIcon,\n CheckIcon,\n ChevronLeftIcon,\n ChevronRightIcon,\n CopyIcon,\n ListPlusIcon,\n PencilIcon,\n RefreshCwIcon,\n} from \"lucide-react\";\nimport {\n type FC,\n type ReactNode,\n useMemo,\n} from \"react\";\nimport { useAthenaRunLive } from \"../../chat/use-athena-run-live\";\nimport type { ComposerAction } from \"../composer/composer-action\";\nimport { cn } from \"../../lib/utils\";\nimport { ChatMessageBody } from \"../rich-text/chat-message-body\";\nimport { AnswerLabel } from \"./answer-label\";\nimport { InboundChannelLine, InboundChannelSubject } from \"./inbound-channel-message\";\nimport { useMessageActionsReadOnly } from \"./message-actions-config\";\nimport { MessageImageGallery, MessageImagePart } from \"./message-images\";\nimport { RuntimeMessageCard } from \"./runtime-message\";\nimport { hasVisibleContent } from \"./running-indicator\";\nimport { VoiceDelegationMessage } from \"./voice-delegation-card\";\nimport { WakeMarkerMessage } from \"./wake-marker\";\nimport { SmallIconButton } from \"./thread-chrome\";\n\nexport {\n IconButton,\n SmallIconButton,\n THREAD_HEADER_ACTIONS_CLASS,\n THREAD_HEADER_CLASS,\n THREAD_HEADER_ICON_CLASS,\n THREAD_HEADER_PILL_CLASS,\n THREAD_PANEL_CLASS,\n} from \"./thread-chrome\";\n\nexport const BranchPicker: FC = () => (\n <BranchPickerPrimitive.Root\n hideWhenSingleBranch\n className=\"inline-flex items-center gap-0.5 text-xs text-muted-foreground\"\n data-testid=\"chat-branch-picker\"\n >\n <BranchPickerPrimitive.Previous asChild>\n <SmallIconButton\n tooltip=\"Previous branch\"\n data-testid=\"chat-branch-previous\"\n >\n <ChevronLeftIcon className=\"size-3\" />\n </SmallIconButton>\n </BranchPickerPrimitive.Previous>\n <span className=\"font-medium\" data-testid=\"chat-branch-count\">\n <BranchPickerPrimitive.Number /> / <BranchPickerPrimitive.Count />\n </span>\n <BranchPickerPrimitive.Next asChild>\n <SmallIconButton tooltip=\"Next branch\" data-testid=\"chat-branch-next\">\n <ChevronRightIcon className=\"size-3\" />\n </SmallIconButton>\n </BranchPickerPrimitive.Next>\n </BranchPickerPrimitive.Root>\n);\n\nconst CopyActionButton: FC = () => (\n <ActionBarPrimitive.Copy asChild>\n <SmallIconButton tooltip=\"Copy\">\n <AuiIf condition={(state) => state.message.isCopied}>\n <CheckIcon className=\"size-3\" />\n </AuiIf>\n <AuiIf condition={(state) => !state.message.isCopied}>\n <CopyIcon className=\"size-3\" />\n </AuiIf>\n </SmallIconButton>\n </ActionBarPrimitive.Copy>\n);\n\n/**\n * The edit primitive does not check the runtime's edit capability itself —\n * clicking it on a runtime without one throws inside onPress, where no error\n * boundary can catch it — so the button must gate on the capability and, to\n * match the rewind semantics of `run/edit`, stay hidden while a run is live.\n * The read-only context covers threads the capability flags cannot: a host's\n * static `edit: true` still applies to fenced legacy conversations. While the\n * host's chat outbox says Athena can't be reached it is not offered: shown\n * disabled, its tooltip says why, and it comes back once Athena answers.\n */\nconst EditActionButton: FC = () => {\n const allowEdit = useAuiState((s) => s.thread.capabilities.edit);\n const isRunning = useAthenaRunLive();\n const readOnly = useMessageActionsReadOnly();\n const reachable = useChatOutboxReachable(useDeepAgentChatOutboxContext());\n if (!allowEdit || isRunning || readOnly) return null;\n if (!reachable) {\n return (\n <SmallIconButton\n tooltip={CHAT_EDIT_UNREACHABLE_LABEL}\n aria-disabled\n data-testid=\"chat-message-edit-unreachable\"\n className=\"cursor-not-allowed opacity-50 hover:bg-transparent hover:text-muted-foreground\"\n >\n <PencilIcon className=\"size-3\" />\n </SmallIconButton>\n );\n }\n return (\n <ActionBarPrimitive.Edit asChild>\n <SmallIconButton tooltip=\"Edit\" data-testid=\"chat-message-edit\">\n <PencilIcon className=\"size-3\" />\n </SmallIconButton>\n </ActionBarPrimitive.Edit>\n );\n};\n\n/** Regenerate dispatches `run/reload`, so it hides on read-only threads too. */\nconst RegenerateActionButton: FC = () => {\n const readOnly = useMessageActionsReadOnly();\n if (readOnly) return null;\n return (\n <ActionBarPrimitive.Reload asChild>\n <SmallIconButton tooltip=\"Regenerate\">\n <RefreshCwIcon className=\"size-3\" />\n </SmallIconButton>\n </ActionBarPrimitive.Reload>\n );\n};\n\n/**\n * Reveal-on-demand treatment for action bars: invisible and click-through\n * until the surrounding named group is hovered or a control inside receives\n * keyboard focus. The `focus-within` escape hatch is what keeps the hidden\n * controls reachable for keyboard users.\n */\nexport const ACTION_BAR_REVEAL_CLASS =\n \"pointer-events-none opacity-0 transition-opacity focus-within:pointer-events-auto focus-within:opacity-100\";\n\n/** Shared chat body; the reveal holds incomplete links until their URL is complete. */\nexport const MarkdownText: FC = () => {\n const revealed = useSmooth(useMessagePartText(), true);\n const isRunning = revealed.status.type === \"running\";\n const text = isRunning ? hideUnterminatedMarkdownLink(revealed.text) : revealed.text;\n return <ChatMessageBody text={text} isRunning={isRunning} />;\n};\n\n/**\n * A user message's text, shown as it was typed.\n *\n * A composer sends plain text exactly as typed, because the model reads it\n * literally, so the text alone no longer says which characters are\n * formatting: read as Markdown, `List<T>` loses `<T>`, `obj.__init__` shows a\n * bold \"init\" and `a\\\\b` one backslash. A composer send therefore carries the\n * draft it was serialized from (`metadata.composer_markdown`, only when the\n * two differ), and this renders that draft when it is a draft of the\n * message's own text (`readDeepAgentComposerMarkdown`), formatting and\n * mention chips included. Any other text — no draft, or one of other words —\n * renders with the typed reading (`ChatMessageBody typed`), which leaves raw\n * HTML and underscore emphasis as the characters they were typed with, so\n * `List<T>`, `obj.__init__` and `a\\b` show as typed there too.\n *\n * A message that came in over a channel (Slack, email, a text, Teams) starts\n * with the routing block the agent read (`<inbound-message-context>`); it\n * shows without it, as the words that were sent (`inboundMessageBody`), and\n * the line above the bubble opens the block (`InboundChannelSource`). Only a\n * message with the channel's `inbound_channel` envelope is one: tags a person\n * typed at the start of a chat message show as typed.\n *\n * Copy copies the message text, which is the text as sent, and Edit opens it\n * in a plain-text box, so neither reads the draft.\n */\nexport const UserMessageText: FC<{ text: string }> = ({ text }) => {\n const custom = useAuiState((s) => s.message.metadata?.custom);\n const shown = useMemo(() => {\n const body = inboundMessageBody({ custom, text });\n return (\n readDeepAgentComposerMarkdown({\n metadata: { [DEEP_AGENT_COMPOSER_MARKDOWN_KEY]: custom?.[DEEP_AGENT_COMPOSER_MARKDOWN_KEY] },\n text: body,\n }) ?? body\n );\n }, [custom, text]);\n return <ChatMessageBody text={shown} typed />;\n};\n\n/** A user message's parts: its text as typed, and its pictures, each opening the viewer. */\nconst USER_MESSAGE_PART_COMPONENTS = { Text: UserMessageText, Image: MessageImagePart };\n\nconst UserActionBar: FC = () => (\n <ActionBarPrimitive.Root\n autohide=\"never\"\n className={cn(\n \"flex min-h-6 items-center gap-0.5\",\n ACTION_BAR_REVEAL_CLASS,\n \"group-hover/user-message:pointer-events-auto group-hover/user-message:opacity-100\",\n )}\n >\n <CopyActionButton />\n <EditActionButton />\n </ActionBarPrimitive.Root>\n);\n\nexport const UserMessage: FC = () => {\n // A GitHub wake or a subscription's delivery is an event, not words the\n // user typed: it renders as the compact wake marker instead of a bubble\n // quoting the injected text.\n const isWake = useAuiState(\n (s) =>\n isGithubWakeMessage(s.message.metadata?.custom) ||\n isSubscriptionWakeMessage(s.message.metadata?.custom),\n );\n // A voice delegation's anchor is the ask the live voice model handed to the\n // thread, not words the user typed: it renders as the shared voice-task\n // card on every host instead of a second user bubble under the spoken turn.\n const isVoiceDelegation = useAuiState((s) =>\n isVoiceDelegationMessage({ messageId: s.message.id, custom: s.message.metadata?.custom }),\n );\n if (isWake) {\n return (\n <MessagePrimitive.Root data-message-role=\"user\" className=\"mb-3\">\n <WakeMarkerMessage />\n </MessagePrimitive.Root>\n );\n }\n if (isVoiceDelegation) {\n return (\n <MessagePrimitive.Root data-message-role=\"user\" className=\"mb-3\">\n <VoiceDelegationMessage />\n </MessagePrimitive.Root>\n );\n }\n return (\n <MessagePrimitive.Root\n data-message-role=\"user\"\n className=\"group/user-message mb-3 flex justify-end\"\n >\n <div className=\"flex min-w-0 max-w-[85%] flex-col items-end gap-1\">\n {/* A message that came in over a channel: the line naming the channel\n and the sender, above the words that were sent. */}\n <InboundChannelLine />\n <div className=\"min-w-0 max-w-full rounded-2xl rounded-br-md bg-muted px-3.5 py-2 text-sm text-foreground\">\n <InboundChannelSubject />\n <MessageImageGallery>\n <MessagePrimitive.Content components={USER_MESSAGE_PART_COMPONENTS} />\n </MessageImageGallery>\n </div>\n <div className=\"flex min-h-6 items-center gap-1\">\n <BranchPicker />\n <UserActionBar />\n </div>\n </div>\n </MessagePrimitive.Root>\n );\n};\n\nexport interface CreateAssistantMessageOptions {\n /** Host-registered card for tools without a dedicated UI. */\n toolFallback: ToolCallMessagePartComponent;\n /** Extra action-bar controls appended after copy/regenerate. */\n extraActions?: ReactNode;\n /**\n * Renders the message body. Hosts that group content into activity\n * timelines supply their own; the default renders markdown and tool cards.\n */\n content?: FC;\n /**\n * Inline affordance rendered inside a runtime-ending card (e.g. a Continue\n * button). Hosts without one leave it unset.\n */\n runtimeMessageAction?: ReactNode;\n}\n\n/**\n * Builds the assistant bubble. Call this once per host (memoized on its\n * inputs) so the returned component identity stays stable across renders.\n */\nexport function createAssistantMessage({\n toolFallback,\n extraActions,\n content: Content,\n runtimeMessageAction,\n}: CreateAssistantMessageOptions): FC {\n const AssistantActionBar: FC = () => {\n // The latest reply keeps its actions visible; older replies reveal them\n // on hover so a long transcript is not a wall of icon rows.\n const isLast = useAuiState((s) => s.message.isLast);\n return (\n <ActionBarPrimitive.Root\n autohide=\"never\"\n className={cn(\n \"mt-1 flex min-h-6 items-center gap-0.5\",\n !isLast && [\n ACTION_BAR_REVEAL_CLASS,\n \"group-hover/assistant-message:pointer-events-auto group-hover/assistant-message:opacity-100\",\n ],\n )}\n >\n <CopyActionButton />\n <RegenerateActionButton />\n {extraActions}\n </ActionBarPrimitive.Root>\n );\n };\n\n // A run-lifecycle marker or handoff summary is a system event, not\n // assistant speech. When it owns the whole row, render the status card in\n // place of the bubble; when it is chunked in with the assistant's own work\n // (or is a handoff whose text IS the closing summary), keep the body and\n // frame it with the card above.\n const RuntimeEnding: FC<{ placement: \"row\" | \"banner\" }> = ({ placement }) => {\n // Select the message (a stable store reference) and derive from it: the\n // reader allocates a fresh object per call, and `useAuiState` compares\n // snapshots with `Object.is`, so selecting the reader's result directly\n // would re-render on every store update and trip React's update-depth\n // guard.\n const message = useAuiState((s) => s.message);\n const runtime = useMemo(() => readRuntimeMessageFromSourceMessages(message), [message]);\n if (!runtime || runtime.silent) return null;\n if (placement === \"row\" && !runtime.isSoleSourceMessage) return null;\n if (placement === \"banner\" && runtime.isSoleSourceMessage && runtime.kind !== \"handoff\") {\n return null;\n }\n return <RuntimeMessageCard message={runtime} action={runtimeMessageAction} />;\n };\n\n const AssistantMessage: FC = () => {\n // An assistant row exists before its first token streams; showing\n // copy/regenerate against nothing looks broken, so wait for content.\n const showActions = useAuiState((state) =>\n hasVisibleContent(state.message.content),\n );\n // Primitive selector: the reader builds a fresh object per call.\n const soleRuntimeKind = useAuiState((s) => readSoleRuntimeMessageKind(s.message));\n const soleRuntimeSilent = useAuiState(\n (s) => readRuntimeMessageFromSourceMessages(s.message)?.silent === true,\n );\n\n if (soleRuntimeKind && soleRuntimeKind !== \"handoff\") {\n if (soleRuntimeSilent) return null;\n return (\n <MessagePrimitive.Root\n data-message-role=\"assistant\"\n className=\"group group/assistant-message mb-3\"\n >\n <div className=\"max-w-[95%]\">\n <RuntimeEnding placement=\"row\" />\n </div>\n </MessagePrimitive.Root>\n );\n }\n\n return (\n <MessagePrimitive.Root\n data-message-role=\"assistant\"\n className=\"group group/assistant-message mb-3\"\n >\n <div className=\"max-w-[95%]\">\n <AnswerLabel className=\"mb-1\" />\n <RuntimeEnding placement=\"banner\" />\n {Content ? (\n <Content />\n ) : (\n <MessagePrimitive.Content\n components={{\n Text: MarkdownText,\n tools: { Fallback: toolFallback },\n }}\n />\n )}\n {showActions ? (\n <div className=\"flex min-h-6 items-center gap-1\">\n <BranchPicker />\n <AssistantActionBar />\n </div>\n ) : null}\n </div>\n </MessagePrimitive.Root>\n );\n };\n\n return AssistantMessage;\n}\n\n/** Composer chrome: the dock around the composer, its surface and its rows. */\nexport const COMPOSER_DOCK_CLASS = \"bg-background px-2.5 pb-2 pt-1.5\";\nexport const COMPOSER_SURFACE_CLASS =\n \"relative flex flex-col gap-1 rounded-xl border border-neutral-200 bg-white shadow-[0_2px_10px_rgba(54,45,33,0.08)]\";\nexport const COMPOSER_EDITOR_ROW_CLASS = \"px-2.5 pt-1\";\nexport const COMPOSER_TOOLBAR_CLASS =\n \"flex items-center gap-1 border-t border-neutral-100 px-2 py-1.5\";\n/**\n * Paper composer chrome (`ChatComposerDock appearance=\"paper\"`): a card-toned\n * surface with a larger radius, a focus-within ring, a drag-over tint keyed\n * off `data-drag-active`, and a bottom rail — the chrome the SDK's\n * `AthenaChat` thread renders. Hosts on the default chrome above are\n * untouched. The dock carries no padding of its own; the paper host's footer\n * spaces it. The radius and border colour also bind to the `--composer-radius`\n * / `--composer-border` theme variables in `styles.css`.\n */\nexport const COMPOSER_PAPER_DOCK_CLASS = \"aui-composer-dock\";\nexport const COMPOSER_PAPER_SURFACE_CLASS =\n \"aui-composer-root relative flex w-full flex-col rounded-[1.375rem] border border-border bg-card p-2.5 text-card-foreground shadow-[0_1px_2px_rgba(0,0,0,0.04)] outline-none transition-shadow duration-150 focus-within:shadow-[0_8px_24px_-14px_rgba(0,0,0,0.25)] focus-within:ring-2 focus-within:ring-ring/40 data-[drag-active=true]:bg-primary/5 data-[drag-active=true]:ring-2 data-[drag-active=true]:ring-primary/40\";\nexport const COMPOSER_PAPER_EDITOR_ROW_CLASS = \"px-0.5\";\nexport const COMPOSER_PAPER_TOOLBAR_CLASS =\n \"aui-composer-action-wrapper relative flex items-center justify-between gap-1 pt-1\";\nexport const THREAD_SCROLL_TO_BOTTOM_CLASS =\n \"inline-flex items-center justify-center rounded-full border bg-background/80 p-1 shadow-sm backdrop-blur transition-opacity hover:bg-accent disabled:invisible\";\n\nconst ComposerStopIcon: FC = () => (\n <svg\n aria-hidden=\"true\"\n className=\"size-4\"\n viewBox=\"0 0 16 16\"\n fill=\"currentColor\"\n >\n <rect width=\"10\" height=\"10\" x=\"3\" y=\"3\" rx=\"2\" />\n </svg>\n);\n\nexport interface ComposerStopButtonProps {\n onStop: () => void;\n disabled?: boolean;\n title?: string;\n}\n\nexport interface ComposerSendButtonProps {\n action: Extract<ComposerAction, \"send\" | \"queue\">;\n onSend: () => void;\n disabled?: boolean;\n title?: string;\n}\n\n/** Stop control shown while a run is in flight and there is nothing queued. */\nexport const ComposerStopButton: FC<ComposerStopButtonProps> = ({\n onStop,\n disabled = false,\n title = \"Stop generation\",\n}) => (\n <button\n type=\"button\"\n onClick={onStop}\n disabled={disabled}\n title={title}\n className={cn(\n \"inline-flex size-8 shrink-0 items-center justify-center rounded-lg bg-foreground text-background shadow-sm transition-colors hover:bg-foreground/80\",\n disabled && \"cursor-not-allowed opacity-60\",\n )}\n >\n <ComposerStopIcon />\n </button>\n);\n/** Send control, which becomes a queue control while a run is in flight. */\nexport const ComposerSendButton: FC<ComposerSendButtonProps> = ({\n action,\n onSend,\n disabled = false,\n title,\n}) => (\n <button\n type=\"button\"\n onClick={onSend}\n disabled={disabled}\n title={title ?? (action === \"queue\" ? \"Queue message\" : \"Send\")}\n className={cn(\n \"inline-flex size-8 shrink-0 items-center justify-center rounded-full shadow-sm transition-colors\",\n disabled\n ? \"cursor-not-allowed bg-muted text-muted-foreground opacity-60\"\n : \"bg-foreground text-background hover:bg-foreground/80\",\n )}\n >\n {action === \"queue\" ? (\n <ListPlusIcon className=\"size-4\" />\n ) : (\n <ArrowUpIcon className=\"size-4\" />\n )}\n </button>\n);\n"],"names":["jsxs","BranchPickerPrimitive","jsx","SmallIconButton","ChevronLeftIcon","ChevronRightIcon","ActionBarPrimitive","AuiIf","CheckIcon","CopyIcon","useAuiState","useAthenaRunLive","useMessageActionsReadOnly","useChatOutboxReachable","useDeepAgentChatOutboxContext","CHAT_EDIT_UNREACHABLE_LABEL","PencilIcon","RefreshCwIcon","useSmooth","useMessagePartText","hideUnterminatedMarkdownLink","ChatMessageBody","useMemo","inboundMessageBody","readDeepAgentComposerMarkdown","DEEP_AGENT_COMPOSER_MARKDOWN_KEY","MessageImagePart","cn","isGithubWakeMessage","isSubscriptionWakeMessage","isVoiceDelegationMessage","MessagePrimitive","WakeMarkerMessage","VoiceDelegationMessage","InboundChannelLine","InboundChannelSubject","MessageImageGallery","readRuntimeMessageFromSourceMessages","RuntimeMessageCard","hasVisibleContent","readSoleRuntimeMessageKind","AnswerLabel","ListPlusIcon","ArrowUpIcon"],"mappings":"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"}
1
+ {"version":3,"file":"message-components.cjs","sources":["../../../src/chat-ui/messages/message-components.tsx"],"sourcesContent":["import {\n ActionBarPrimitive,\n AuiIf,\n BranchPickerPrimitive,\n MessagePrimitive,\n useAuiState,\n useMessagePartText,\n useSmooth,\n type ToolCallMessagePartComponent,\n} from \"@assistant-ui/react\";\nimport {\n CHAT_EDIT_UNREACHABLE_LABEL,\n DEEP_AGENT_COMPOSER_MARKDOWN_KEY,\n hideUnterminatedMarkdownLink,\n inboundMessageBody,\n isGithubWakeMessage,\n isSubscriptionWakeMessage,\n isVoiceDelegationMessage,\n readDeepAgentComposerMarkdown,\n readRuntimeMessageFromSourceMessages,\n readSoleRuntimeMessageKind,\n useChatOutboxReachable,\n useDeepAgentChatOutboxContext,\n} from \"@athenaintel/agent-runtime\";\nimport {\n ArrowUpIcon,\n CheckIcon,\n ChevronLeftIcon,\n ChevronRightIcon,\n CopyIcon,\n ListPlusIcon,\n PencilIcon,\n RefreshCwIcon,\n} from \"lucide-react\";\nimport {\n type FC,\n type ReactNode,\n useMemo,\n} from \"react\";\nimport { useAthenaRunLive } from \"../../chat/use-athena-run-live\";\nimport type { ComposerAction } from \"../composer/composer-action\";\nimport { cn } from \"../../lib/utils\";\nimport { ChatMessageBody } from \"../rich-text/chat-message-body\";\nimport { AnswerLabel } from \"./answer-label\";\nimport { InboundChannelLine, InboundChannelSubject, useUserMessageCopy } from \"./inbound-channel-message\";\nimport { useMessageActionsReadOnly } from \"./message-actions-config\";\nimport { MessageImageGallery, MessageImagePart } from \"./message-images\";\nimport { RuntimeMessageCard } from \"./runtime-message\";\nimport { hasVisibleContent } from \"./running-indicator\";\nimport { VoiceDelegationMessage } from \"./voice-delegation-card\";\nimport { WakeMarkerMessage } from \"./wake-marker\";\nimport { SmallIconButton } from \"./thread-chrome\";\n\nexport {\n IconButton,\n SmallIconButton,\n THREAD_HEADER_ACTIONS_CLASS,\n THREAD_HEADER_CLASS,\n THREAD_HEADER_ICON_CLASS,\n THREAD_HEADER_PILL_CLASS,\n THREAD_PANEL_CLASS,\n} from \"./thread-chrome\";\n\nexport const BranchPicker: FC = () => (\n <BranchPickerPrimitive.Root\n hideWhenSingleBranch\n className=\"inline-flex items-center gap-0.5 text-xs text-muted-foreground\"\n data-testid=\"chat-branch-picker\"\n >\n <BranchPickerPrimitive.Previous asChild>\n <SmallIconButton\n tooltip=\"Previous branch\"\n data-testid=\"chat-branch-previous\"\n >\n <ChevronLeftIcon className=\"size-3\" />\n </SmallIconButton>\n </BranchPickerPrimitive.Previous>\n <span className=\"font-medium\" data-testid=\"chat-branch-count\">\n <BranchPickerPrimitive.Number /> / <BranchPickerPrimitive.Count />\n </span>\n <BranchPickerPrimitive.Next asChild>\n <SmallIconButton tooltip=\"Next branch\" data-testid=\"chat-branch-next\">\n <ChevronRightIcon className=\"size-3\" />\n </SmallIconButton>\n </BranchPickerPrimitive.Next>\n </BranchPickerPrimitive.Root>\n);\n\nconst CopyActionButton: FC = () => (\n <ActionBarPrimitive.Copy asChild>\n <SmallIconButton tooltip=\"Copy\">\n <AuiIf condition={(state) => state.message.isCopied}>\n <CheckIcon className=\"size-3\" />\n </AuiIf>\n <AuiIf condition={(state) => !state.message.isCopied}>\n <CopyIcon className=\"size-3\" />\n </AuiIf>\n </SmallIconButton>\n </ActionBarPrimitive.Copy>\n);\n\n/**\n * The edit primitive does not check the runtime's edit capability itself —\n * clicking it on a runtime without one throws inside onPress, where no error\n * boundary can catch it — so the button must gate on the capability and, to\n * match the rewind semantics of `run/edit`, stay hidden while a run is live.\n * The read-only context covers threads the capability flags cannot: a host's\n * static `edit: true` still applies to fenced legacy conversations. While the\n * host's chat outbox says Athena can't be reached it is not offered: shown\n * disabled, its tooltip says why, and it comes back once Athena answers.\n */\nconst EditActionButton: FC = () => {\n const allowEdit = useAuiState((s) => s.thread.capabilities.edit);\n const isRunning = useAthenaRunLive();\n const readOnly = useMessageActionsReadOnly();\n const reachable = useChatOutboxReachable(useDeepAgentChatOutboxContext());\n if (!allowEdit || isRunning || readOnly) return null;\n if (!reachable) {\n return (\n <SmallIconButton\n tooltip={CHAT_EDIT_UNREACHABLE_LABEL}\n aria-disabled\n data-testid=\"chat-message-edit-unreachable\"\n className=\"cursor-not-allowed opacity-50 hover:bg-transparent hover:text-muted-foreground\"\n >\n <PencilIcon className=\"size-3\" />\n </SmallIconButton>\n );\n }\n return (\n <ActionBarPrimitive.Edit asChild>\n <SmallIconButton tooltip=\"Edit\" data-testid=\"chat-message-edit\">\n <PencilIcon className=\"size-3\" />\n </SmallIconButton>\n </ActionBarPrimitive.Edit>\n );\n};\n\n/** Regenerate dispatches `run/reload`, so it hides on read-only threads too. */\nconst RegenerateActionButton: FC = () => {\n const readOnly = useMessageActionsReadOnly();\n if (readOnly) return null;\n return (\n <ActionBarPrimitive.Reload asChild>\n <SmallIconButton tooltip=\"Regenerate\">\n <RefreshCwIcon className=\"size-3\" />\n </SmallIconButton>\n </ActionBarPrimitive.Reload>\n );\n};\n\n/**\n * Reveal-on-demand treatment for action bars: invisible and click-through\n * until the surrounding named group is hovered or a control inside receives\n * keyboard focus. The `focus-within` escape hatch is what keeps the hidden\n * controls reachable for keyboard users.\n */\nexport const ACTION_BAR_REVEAL_CLASS =\n \"pointer-events-none opacity-0 transition-opacity focus-within:pointer-events-auto focus-within:opacity-100\";\n\n/** Shared chat body; the reveal holds incomplete links until their URL is complete. */\nexport const MarkdownText: FC = () => {\n const revealed = useSmooth(useMessagePartText(), true);\n const isRunning = revealed.status.type === \"running\";\n const text = isRunning ? hideUnterminatedMarkdownLink(revealed.text) : revealed.text;\n return <ChatMessageBody text={text} isRunning={isRunning} />;\n};\n\n/**\n * A user message's text, shown as it was typed.\n *\n * A composer sends plain text exactly as typed, because the model reads it\n * literally, so the text alone no longer says which characters are\n * formatting: read as Markdown, `List<T>` loses `<T>`, `obj.__init__` shows a\n * bold \"init\" and `a\\\\b` one backslash. A composer send therefore carries the\n * draft it was serialized from (`metadata.composer_markdown`, only when the\n * two differ), and this renders that draft when it is a draft of the\n * message's own text (`readDeepAgentComposerMarkdown`), formatting and\n * mention chips included. Any other text — no draft, or one of other words —\n * renders with the typed reading (`ChatMessageBody typed`), which leaves raw\n * HTML and underscore emphasis as the characters they were typed with, so\n * `List<T>`, `obj.__init__` and `a\\b` show as typed there too.\n *\n * A message that came in over a channel (Slack, email, a text, Teams) starts\n * with the routing block the agent read (`<inbound-message-context>`); it\n * shows without it, as the words that were sent (`inboundMessageBody`), and\n * the line above the bubble opens the block (`InboundChannelSource`). Only a\n * message with the channel's `inbound_channel` envelope is one: tags a person\n * typed at the start of a chat message show as typed.\n *\n * Copy copies the message text, which is the text as sent, and Edit opens it\n * in a plain-text box, so neither reads the draft.\n */\nexport const UserMessageText: FC<{ text: string }> = ({ text }) => {\n const custom = useAuiState((s) => s.message.metadata?.custom);\n const shown = useMemo(() => {\n const body = inboundMessageBody({ custom, text });\n return (\n readDeepAgentComposerMarkdown({\n metadata: { [DEEP_AGENT_COMPOSER_MARKDOWN_KEY]: custom?.[DEEP_AGENT_COMPOSER_MARKDOWN_KEY] },\n text: body,\n }) ?? body\n );\n }, [custom, text]);\n return <ChatMessageBody text={shown} typed />;\n};\n\n/** A user message's parts: its text as typed, and its pictures, each opening the viewer. */\nconst USER_MESSAGE_PART_COMPONENTS = { Text: UserMessageText, Image: MessageImagePart };\n\n/** Copy on a user bubble copies what the bubble shows (`useUserMessageCopy`). */\nconst UserCopyActionButton: FC = () => {\n const { copy, disabled, isCopied } = useUserMessageCopy();\n return (\n <SmallIconButton\n tooltip=\"Copy\"\n onClick={copy}\n disabled={disabled}\n data-copied={isCopied ? \"true\" : undefined}\n >\n {isCopied ? <CheckIcon className=\"size-3\" /> : <CopyIcon className=\"size-3\" />}\n </SmallIconButton>\n );\n};\n\nconst UserActionBar: FC = () => (\n <ActionBarPrimitive.Root\n autohide=\"never\"\n className={cn(\n \"flex min-h-6 items-center gap-0.5\",\n ACTION_BAR_REVEAL_CLASS,\n \"group-hover/user-message:pointer-events-auto group-hover/user-message:opacity-100\",\n )}\n >\n <UserCopyActionButton />\n <EditActionButton />\n </ActionBarPrimitive.Root>\n);\n\nexport const UserMessage: FC = () => {\n // A GitHub wake or a subscription's delivery is an event, not words the\n // user typed: it renders as the compact wake marker instead of a bubble\n // quoting the injected text.\n const isWake = useAuiState(\n (s) =>\n isGithubWakeMessage(s.message.metadata?.custom) ||\n isSubscriptionWakeMessage(s.message.metadata?.custom),\n );\n // A voice delegation's anchor is the ask the live voice model handed to the\n // thread, not words the user typed: it renders as the shared voice-task\n // card on every host instead of a second user bubble under the spoken turn.\n const isVoiceDelegation = useAuiState((s) =>\n isVoiceDelegationMessage({ messageId: s.message.id, custom: s.message.metadata?.custom }),\n );\n if (isWake) {\n return (\n <MessagePrimitive.Root data-message-role=\"user\" className=\"mb-3\">\n <WakeMarkerMessage />\n </MessagePrimitive.Root>\n );\n }\n if (isVoiceDelegation) {\n return (\n <MessagePrimitive.Root data-message-role=\"user\" className=\"mb-3\">\n <VoiceDelegationMessage />\n </MessagePrimitive.Root>\n );\n }\n return (\n <MessagePrimitive.Root\n data-message-role=\"user\"\n className=\"group/user-message mb-3 flex justify-end\"\n >\n <div className=\"flex min-w-0 max-w-[85%] flex-col items-end gap-1\">\n {/* A message that came in over a channel: the line naming the channel\n and the sender, above the words that were sent. */}\n <InboundChannelLine />\n <div className=\"min-w-0 max-w-full rounded-2xl rounded-br-md bg-muted px-3.5 py-2 text-sm text-foreground\">\n <InboundChannelSubject />\n <MessageImageGallery>\n <MessagePrimitive.Content components={USER_MESSAGE_PART_COMPONENTS} />\n </MessageImageGallery>\n </div>\n <div className=\"flex min-h-6 items-center gap-1\">\n <BranchPicker />\n <UserActionBar />\n </div>\n </div>\n </MessagePrimitive.Root>\n );\n};\n\nexport interface CreateAssistantMessageOptions {\n /** Host-registered card for tools without a dedicated UI. */\n toolFallback: ToolCallMessagePartComponent;\n /** Extra action-bar controls appended after copy/regenerate. */\n extraActions?: ReactNode;\n /**\n * Renders the message body. Hosts that group content into activity\n * timelines supply their own; the default renders markdown and tool cards.\n */\n content?: FC;\n /**\n * Inline affordance rendered inside a runtime-ending card (e.g. a Continue\n * button). Hosts without one leave it unset.\n */\n runtimeMessageAction?: ReactNode;\n}\n\n/**\n * Builds the assistant bubble. Call this once per host (memoized on its\n * inputs) so the returned component identity stays stable across renders.\n */\nexport function createAssistantMessage({\n toolFallback,\n extraActions,\n content: Content,\n runtimeMessageAction,\n}: CreateAssistantMessageOptions): FC {\n const AssistantActionBar: FC = () => {\n // The latest reply keeps its actions visible; older replies reveal them\n // on hover so a long transcript is not a wall of icon rows.\n const isLast = useAuiState((s) => s.message.isLast);\n return (\n <ActionBarPrimitive.Root\n autohide=\"never\"\n className={cn(\n \"mt-1 flex min-h-6 items-center gap-0.5\",\n !isLast && [\n ACTION_BAR_REVEAL_CLASS,\n \"group-hover/assistant-message:pointer-events-auto group-hover/assistant-message:opacity-100\",\n ],\n )}\n >\n <CopyActionButton />\n <RegenerateActionButton />\n {extraActions}\n </ActionBarPrimitive.Root>\n );\n };\n\n // A run-lifecycle marker or handoff summary is a system event, not\n // assistant speech. When it owns the whole row, render the status card in\n // place of the bubble; when it is chunked in with the assistant's own work\n // (or is a handoff whose text IS the closing summary), keep the body and\n // frame it with the card above.\n const RuntimeEnding: FC<{ placement: \"row\" | \"banner\" }> = ({ placement }) => {\n // Select the message (a stable store reference) and derive from it: the\n // reader allocates a fresh object per call, and `useAuiState` compares\n // snapshots with `Object.is`, so selecting the reader's result directly\n // would re-render on every store update and trip React's update-depth\n // guard.\n const message = useAuiState((s) => s.message);\n const runtime = useMemo(() => readRuntimeMessageFromSourceMessages(message), [message]);\n if (!runtime || runtime.silent) return null;\n if (placement === \"row\" && !runtime.isSoleSourceMessage) return null;\n if (placement === \"banner\" && runtime.isSoleSourceMessage && runtime.kind !== \"handoff\") {\n return null;\n }\n return <RuntimeMessageCard message={runtime} action={runtimeMessageAction} />;\n };\n\n const AssistantMessage: FC = () => {\n // An assistant row exists before its first token streams; showing\n // copy/regenerate against nothing looks broken, so wait for content.\n const showActions = useAuiState((state) =>\n hasVisibleContent(state.message.content),\n );\n // Primitive selector: the reader builds a fresh object per call.\n const soleRuntimeKind = useAuiState((s) => readSoleRuntimeMessageKind(s.message));\n const soleRuntimeSilent = useAuiState(\n (s) => readRuntimeMessageFromSourceMessages(s.message)?.silent === true,\n );\n\n if (soleRuntimeKind && soleRuntimeKind !== \"handoff\") {\n if (soleRuntimeSilent) return null;\n return (\n <MessagePrimitive.Root\n data-message-role=\"assistant\"\n className=\"group group/assistant-message mb-3\"\n >\n <div className=\"max-w-[95%]\">\n <RuntimeEnding placement=\"row\" />\n </div>\n </MessagePrimitive.Root>\n );\n }\n\n return (\n <MessagePrimitive.Root\n data-message-role=\"assistant\"\n className=\"group group/assistant-message mb-3\"\n >\n <div className=\"max-w-[95%]\">\n <AnswerLabel className=\"mb-1\" />\n <RuntimeEnding placement=\"banner\" />\n {Content ? (\n <Content />\n ) : (\n <MessagePrimitive.Content\n components={{\n Text: MarkdownText,\n tools: { Fallback: toolFallback },\n }}\n />\n )}\n {showActions ? (\n <div className=\"flex min-h-6 items-center gap-1\">\n <BranchPicker />\n <AssistantActionBar />\n </div>\n ) : null}\n </div>\n </MessagePrimitive.Root>\n );\n };\n\n return AssistantMessage;\n}\n\n/** Composer chrome: the dock around the composer, its surface and its rows. */\nexport const COMPOSER_DOCK_CLASS = \"bg-background px-2.5 pb-2 pt-1.5\";\nexport const COMPOSER_SURFACE_CLASS =\n \"relative flex flex-col gap-1 rounded-xl border border-neutral-200 bg-white shadow-[0_2px_10px_rgba(54,45,33,0.08)]\";\nexport const COMPOSER_EDITOR_ROW_CLASS = \"px-2.5 pt-1\";\nexport const COMPOSER_TOOLBAR_CLASS =\n \"flex items-center gap-1 border-t border-neutral-100 px-2 py-1.5\";\n/**\n * Paper composer chrome (`ChatComposerDock appearance=\"paper\"`): a card-toned\n * surface with a larger radius, a focus-within ring, a drag-over tint keyed\n * off `data-drag-active`, and a bottom rail — the chrome the SDK's\n * `AthenaChat` thread renders. Hosts on the default chrome above are\n * untouched. The dock carries no padding of its own; the paper host's footer\n * spaces it. The radius and border colour also bind to the `--composer-radius`\n * / `--composer-border` theme variables in `styles.css`.\n */\nexport const COMPOSER_PAPER_DOCK_CLASS = \"aui-composer-dock\";\nexport const COMPOSER_PAPER_SURFACE_CLASS =\n \"aui-composer-root relative flex w-full flex-col rounded-[1.375rem] border border-border bg-card p-2.5 text-card-foreground shadow-[0_1px_2px_rgba(0,0,0,0.04)] outline-none transition-shadow duration-150 focus-within:shadow-[0_8px_24px_-14px_rgba(0,0,0,0.25)] focus-within:ring-2 focus-within:ring-ring/40 data-[drag-active=true]:bg-primary/5 data-[drag-active=true]:ring-2 data-[drag-active=true]:ring-primary/40\";\nexport const COMPOSER_PAPER_EDITOR_ROW_CLASS = \"px-0.5\";\nexport const COMPOSER_PAPER_TOOLBAR_CLASS =\n \"aui-composer-action-wrapper relative flex items-center justify-between gap-1 pt-1\";\nexport const THREAD_SCROLL_TO_BOTTOM_CLASS =\n \"inline-flex items-center justify-center rounded-full border bg-background/80 p-1 shadow-sm backdrop-blur transition-opacity hover:bg-accent disabled:invisible\";\n\nconst ComposerStopIcon: FC = () => (\n <svg\n aria-hidden=\"true\"\n className=\"size-4\"\n viewBox=\"0 0 16 16\"\n fill=\"currentColor\"\n >\n <rect width=\"10\" height=\"10\" x=\"3\" y=\"3\" rx=\"2\" />\n </svg>\n);\n\nexport interface ComposerStopButtonProps {\n onStop: () => void;\n disabled?: boolean;\n title?: string;\n}\n\nexport interface ComposerSendButtonProps {\n action: Extract<ComposerAction, \"send\" | \"queue\">;\n onSend: () => void;\n disabled?: boolean;\n title?: string;\n}\n\n/** Stop control shown while a run is in flight and there is nothing queued. */\nexport const ComposerStopButton: FC<ComposerStopButtonProps> = ({\n onStop,\n disabled = false,\n title = \"Stop generation\",\n}) => (\n <button\n type=\"button\"\n onClick={onStop}\n disabled={disabled}\n title={title}\n className={cn(\n \"inline-flex size-8 shrink-0 items-center justify-center rounded-lg bg-foreground text-background shadow-sm transition-colors hover:bg-foreground/80\",\n disabled && \"cursor-not-allowed opacity-60\",\n )}\n >\n <ComposerStopIcon />\n </button>\n);\n/** Send control, which becomes a queue control while a run is in flight. */\nexport const ComposerSendButton: FC<ComposerSendButtonProps> = ({\n action,\n onSend,\n disabled = false,\n title,\n}) => (\n <button\n type=\"button\"\n onClick={onSend}\n disabled={disabled}\n title={title ?? (action === \"queue\" ? \"Queue message\" : \"Send\")}\n className={cn(\n \"inline-flex size-8 shrink-0 items-center justify-center rounded-full shadow-sm transition-colors\",\n disabled\n ? \"cursor-not-allowed bg-muted text-muted-foreground opacity-60\"\n : \"bg-foreground text-background hover:bg-foreground/80\",\n )}\n >\n {action === \"queue\" ? (\n <ListPlusIcon className=\"size-4\" />\n ) : (\n <ArrowUpIcon className=\"size-4\" />\n )}\n </button>\n);\n"],"names":["jsxs","BranchPickerPrimitive","jsx","SmallIconButton","ChevronLeftIcon","ChevronRightIcon","ActionBarPrimitive","AuiIf","CheckIcon","CopyIcon","useAuiState","useAthenaRunLive","useMessageActionsReadOnly","useChatOutboxReachable","useDeepAgentChatOutboxContext","CHAT_EDIT_UNREACHABLE_LABEL","PencilIcon","RefreshCwIcon","useSmooth","useMessagePartText","hideUnterminatedMarkdownLink","ChatMessageBody","useMemo","inboundMessageBody","readDeepAgentComposerMarkdown","DEEP_AGENT_COMPOSER_MARKDOWN_KEY","MessageImagePart","useUserMessageCopy","cn","isGithubWakeMessage","isSubscriptionWakeMessage","isVoiceDelegationMessage","MessagePrimitive","WakeMarkerMessage","VoiceDelegationMessage","InboundChannelLine","InboundChannelSubject","MessageImageGallery","readRuntimeMessageFromSourceMessages","RuntimeMessageCard","hasVisibleContent","readSoleRuntimeMessageKind","AnswerLabel","ListPlusIcon","ArrowUpIcon"],"mappings":"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"}
@@ -5,7 +5,7 @@ import { useMemo } from "react";
5
5
  import { useAthenaRunLive } from "../../chat/use-athena-run-live.js";
6
6
  import { ChatMessageBody } from "../rich-text/chat-message-body.js";
7
7
  import { AnswerLabel } from "./answer-label.js";
8
- import { InboundChannelLine, InboundChannelSubject } from "./inbound-channel-message.js";
8
+ import { InboundChannelLine, InboundChannelSubject, useUserMessageCopy } from "./inbound-channel-message.js";
9
9
  import { useMessageActionsReadOnly } from "./message-actions-config.js";
10
10
  import { MessageImageGallery, MessageImagePart } from "./message-images.js";
11
11
  import { RuntimeMessageCard } from "./runtime-message.js";
@@ -99,6 +99,19 @@ const UserMessageText = ({ text }) => {
99
99
  return /* @__PURE__ */ jsx(ChatMessageBody, { text: shown, typed: true });
100
100
  };
101
101
  const USER_MESSAGE_PART_COMPONENTS = { Text: UserMessageText, Image: MessageImagePart };
102
+ const UserCopyActionButton = () => {
103
+ const { copy, disabled, isCopied } = useUserMessageCopy();
104
+ return /* @__PURE__ */ jsx(
105
+ SmallIconButton,
106
+ {
107
+ tooltip: "Copy",
108
+ onClick: copy,
109
+ disabled,
110
+ "data-copied": isCopied ? "true" : void 0,
111
+ children: isCopied ? /* @__PURE__ */ jsx(CheckIcon, { className: "size-3" }) : /* @__PURE__ */ jsx(CopyIcon, { className: "size-3" })
112
+ }
113
+ );
114
+ };
102
115
  const UserActionBar = () => /* @__PURE__ */ jsxs(
103
116
  ActionBarPrimitive.Root,
104
117
  {
@@ -109,7 +122,7 @@ const UserActionBar = () => /* @__PURE__ */ jsxs(
109
122
  "group-hover/user-message:pointer-events-auto group-hover/user-message:opacity-100"
110
123
  ),
111
124
  children: [
112
- /* @__PURE__ */ jsx(CopyActionButton, {}),
125
+ /* @__PURE__ */ jsx(UserCopyActionButton, {}),
113
126
  /* @__PURE__ */ jsx(EditActionButton, {})
114
127
  ]
115
128
  }