@cossistant/react 0.0.31 → 0.0.32

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 (54) hide show
  1. package/hooks/private/use-grouped-messages.d.ts.map +1 -1
  2. package/hooks/private/use-grouped-messages.js +7 -2
  3. package/hooks/private/use-grouped-messages.js.map +1 -1
  4. package/hooks/use-send-message.js +1 -1
  5. package/hooks/use-send-message.js.map +1 -1
  6. package/package.json +4 -4
  7. package/packages/types/src/api/conversation.d.ts +16 -12
  8. package/packages/types/src/api/conversation.d.ts.map +1 -1
  9. package/packages/types/src/api/timeline-item.d.ts +11 -9
  10. package/packages/types/src/api/timeline-item.d.ts.map +1 -1
  11. package/packages/types/src/realtime-events.d.ts +34 -13
  12. package/packages/types/src/realtime-events.d.ts.map +1 -1
  13. package/packages/types/src/schemas.d.ts +4 -3
  14. package/packages/types/src/schemas.d.ts.map +1 -1
  15. package/primitives/avatar/image.d.ts +1 -1
  16. package/primitives/timeline-item.d.ts +2 -2
  17. package/primitives/timeline-item.d.ts.map +1 -1
  18. package/primitives/timeline-item.js +8 -2
  19. package/primitives/timeline-item.js.map +1 -1
  20. package/provider.d.ts.map +1 -1
  21. package/provider.js +6 -3
  22. package/provider.js.map +1 -1
  23. package/support/components/avatar-stack.js +1 -1
  24. package/support/components/avatar-stack.js.map +1 -1
  25. package/support/components/avatar.d.ts +1 -2
  26. package/support/components/avatar.d.ts.map +1 -1
  27. package/support/components/avatar.js +9 -7
  28. package/support/components/avatar.js.map +1 -1
  29. package/support/components/conversation-button-link.js +2 -1
  30. package/support/components/conversation-button-link.js.map +1 -1
  31. package/support/components/conversation-event.js +1 -1
  32. package/support/components/conversation-event.js.map +1 -1
  33. package/support/components/conversation-resolved-feedback.d.ts +1 -1
  34. package/support/components/conversation-resolved-feedback.d.ts.map +1 -1
  35. package/support/components/conversation-resolved-feedback.js +56 -13
  36. package/support/components/conversation-resolved-feedback.js.map +1 -1
  37. package/support/components/typing-indicator.d.ts.map +1 -1
  38. package/support/components/typing-indicator.js +15 -7
  39. package/support/components/typing-indicator.js.map +1 -1
  40. package/support/pages/conversation-history.js +1 -1
  41. package/support/pages/conversation.js +4 -2
  42. package/support/pages/conversation.js.map +1 -1
  43. package/support/pages/home.js +1 -1
  44. package/support/text/locales/en.js +3 -0
  45. package/support/text/locales/en.js.map +1 -1
  46. package/support/text/locales/es.js +3 -0
  47. package/support/text/locales/es.js.map +1 -1
  48. package/support/text/locales/fr.js +3 -0
  49. package/support/text/locales/fr.js.map +1 -1
  50. package/support/text/locales/keys.d.ts +9 -0
  51. package/support/text/locales/keys.d.ts.map +1 -1
  52. package/support/text/locales/keys.js +3 -0
  53. package/support/text/locales/keys.js.map +1 -1
  54. package/utils/use-render-element.d.ts.map +1 -1
@@ -4,8 +4,8 @@ import { AvatarFallback } from "../../primitives/avatar/fallback.js";
4
4
  import { AvatarImage } from "../../primitives/avatar/image.js";
5
5
  import { CossistantLogo } from "./cossistant-branding.js";
6
6
  import { OnlineIndicator, getAgentStatus } from "./online-indicator.js";
7
- import { jsx, jsxs } from "react/jsx-runtime";
8
7
  import { Facehash } from "facehash";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
9
 
10
10
  //#region src/support/components/avatar.tsx
11
11
  /**
@@ -20,18 +20,20 @@ const DEFAULT_AVATAR_COLORS = [
20
20
  ];
21
21
  /**
22
22
  * Renders a squared avatar with graceful fallbacks using Facehash when no
23
- * image is available. Features squircle corners when supported by the browser
24
- * and a subtle ring border.
23
+ * image is available. Features rounded corners and a subtle ring border.
25
24
  *
26
25
  * For AI agents without an image, displays the Cossistant logo without
27
26
  * a background.
28
27
  */
29
28
  function Avatar({ className, image, name, isAI = false, showBackground = true, colorClasses = DEFAULT_AVATAR_COLORS, lastSeenAt, indicatorSize = 6 }) {
30
29
  const agentStatus = isAI ? "offline" : getAgentStatus(lastSeenAt);
31
- if (isAI && !image) return /* @__PURE__ */ jsx("div", {
32
- className: cn("flex items-center justify-center rounded bg-co-background-200 ring-1 ring-co-border/30 dark:bg-co-background-500", className),
33
- children: /* @__PURE__ */ jsx(CossistantLogo, { className: "h-1/2 w-1/2" })
34
- });
30
+ if (isAI && !image) {
31
+ if (showBackground) return /* @__PURE__ */ jsx("div", {
32
+ className: cn("flex items-center justify-center rounded bg-co-background-200 ring-1 ring-co-border/30 dark:bg-co-background-500", className),
33
+ children: /* @__PURE__ */ jsx(CossistantLogo, { className: "h-1/2 w-1/2" })
34
+ });
35
+ return /* @__PURE__ */ jsx(CossistantLogo, { className: cn("h-full w-full", className) });
36
+ }
35
37
  if (isAI && image) return /* @__PURE__ */ jsxs(Avatar$1, {
36
38
  className: cn("flex size-9 items-center justify-center overflow-clip rounded bg-co-background-200 ring-1 ring-co-border/30 dark:bg-co-background-500", className),
37
39
  children: [/* @__PURE__ */ jsx(AvatarImage, {
@@ -1 +1 @@
1
- {"version":3,"file":"avatar.js","names":["AvatarPrimitive"],"sources":["../../../src/support/components/avatar.tsx"],"sourcesContent":["import { Facehash } from \"facehash\";\nimport type { ReactElement } from \"react\";\n\nimport {\n\tAvatarFallback,\n\tAvatarImage,\n\tAvatar as AvatarPrimitive,\n} from \"../../primitives/avatar\";\nimport { cn } from \"../utils\";\nimport { CossistantLogo } from \"./cossistant-branding\";\nimport { getAgentStatus, OnlineIndicator } from \"./online-indicator\";\n\n/**\n * Default Cossistant theme colors for avatar fallbacks.\n * These use the Tailwind classes defined in support.css.\n */\nconst DEFAULT_AVATAR_COLORS = [\n\t\"bg-co-pink\",\n\t\"bg-co-blue\",\n\t\"bg-co-yellow\",\n\t\"bg-co-orange\",\n];\n\ntype AvatarProps = {\n\tclassName?: string;\n\timage?: string | null;\n\tname: string;\n\t/** Whether this avatar is for an AI agent */\n\tisAI?: boolean;\n\t/** Whether to show the background circle (default: true) */\n\tshowBackground?: boolean;\n\t/**\n\t * Tailwind class array for Facehash background colors.\n\t * Defaults to Cossistant theme colors (pink, blue, yellow, orange).\n\t * @example [\"bg-pink-500\", \"bg-blue-500\", \"bg-green-500\"]\n\t */\n\tcolorClasses?: string[];\n\t/**\n\t * Last seen timestamp for the agent. When provided, shows a status indicator:\n\t * - Green (online): seen within last 15 minutes\n\t * - Orange (away): seen within last hour\n\t * Only shown for non-AI agents.\n\t */\n\tlastSeenAt?: string | null;\n\t/**\n\t * Size of the online indicator in pixels.\n\t * Defaults to 6px.\n\t */\n\tindicatorSize?: number;\n};\n\n/**\n * Renders a squared avatar with graceful fallbacks using Facehash when no\n * image is available. Features squircle corners when supported by the browser\n * and a subtle ring border.\n *\n * For AI agents without an image, displays the Cossistant logo without\n * a background.\n */\nexport function Avatar({\n\tclassName,\n\timage,\n\tname,\n\tisAI = false,\n\tshowBackground = true,\n\tcolorClasses = DEFAULT_AVATAR_COLORS,\n\tlastSeenAt,\n\tindicatorSize = 6,\n}: AvatarProps): ReactElement {\n\tconst agentStatus = isAI ? \"offline\" : getAgentStatus(lastSeenAt);\n\n\t// AI agent without image: show logo in avatar box (only in avatar-stack context)\n\t// or at full size when used standalone\n\tif (isAI && !image) {\n\t\treturn (\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"flex items-center justify-center rounded bg-co-background-200 ring-1 ring-co-border/30 dark:bg-co-background-500\",\n\t\t\t\t\tclassName\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t<CossistantLogo className=\"h-1/2 w-1/2\" />\n\t\t\t</div>\n\t\t);\n\t}\n\n\t// AI agent with image: show image in a square\n\tif (isAI && image) {\n\t\treturn (\n\t\t\t<AvatarPrimitive\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"flex size-9 items-center justify-center overflow-clip rounded bg-co-background-200 ring-1 ring-co-border/30 dark:bg-co-background-500\",\n\t\t\t\t\tclassName\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t<AvatarImage alt={name} src={image} />\n\t\t\t\t<AvatarFallback className=\"size-full\">\n\t\t\t\t\t<Facehash\n\t\t\t\t\t\tclassName=\"size-full\"\n\t\t\t\t\t\tcolorClasses={colorClasses}\n\t\t\t\t\t\tinteractive={false}\n\t\t\t\t\t\tname={name}\n\t\t\t\t\t\tshowInitial={false}\n\t\t\t\t\t\tsize=\"100%\"\n\t\t\t\t\t/>\n\t\t\t\t</AvatarFallback>\n\t\t\t</AvatarPrimitive>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className={cn(\"relative\", className)}>\n\t\t\t<AvatarPrimitive\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"flex size-full items-center justify-center overflow-clip rounded bg-co-background-200 ring-1 ring-co-border/30 dark:bg-co-background-500\"\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{image && <AvatarImage alt={name} src={image} />}\n\t\t\t\t<AvatarFallback className=\"size-full\">\n\t\t\t\t\t<Facehash\n\t\t\t\t\t\tclassName=\"size-full\"\n\t\t\t\t\t\tcolorClasses={colorClasses}\n\t\t\t\t\t\tinteractive={false}\n\t\t\t\t\t\tname={name}\n\t\t\t\t\t\tshowInitial={false}\n\t\t\t\t\t\tsize=\"100%\"\n\t\t\t\t\t/>\n\t\t\t\t</AvatarFallback>\n\t\t\t</AvatarPrimitive>\n\t\t\t<OnlineIndicator\n\t\t\t\tclassName=\"-bottom-0.5 -right-0.5 z-10\"\n\t\t\t\tsize={indicatorSize}\n\t\t\t\tstatus={agentStatus}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n"],"mappings":";;;;;;;;;;;;;;AAgBA,MAAM,wBAAwB;CAC7B;CACA;CACA;CACA;CACA;;;;;;;;;AAsCD,SAAgB,OAAO,EACtB,WACA,OACA,MACA,OAAO,OACP,iBAAiB,MACjB,eAAe,uBACf,YACA,gBAAgB,KACa;CAC7B,MAAM,cAAc,OAAO,YAAY,eAAe,WAAW;AAIjE,KAAI,QAAQ,CAAC,MACZ,QACC,oBAAC;EACA,WAAW,GACV,oHACA,UACA;YAED,oBAAC,kBAAe,WAAU,gBAAgB;GACrC;AAKR,KAAI,QAAQ,MACX,QACC,qBAACA;EACA,WAAW,GACV,yIACA,UACA;aAED,oBAAC;GAAY,KAAK;GAAM,KAAK;IAAS,EACtC,oBAAC;GAAe,WAAU;aACzB,oBAAC;IACA,WAAU;IACI;IACd,aAAa;IACP;IACN,aAAa;IACb,MAAK;KACJ;IACc;GACA;AAIpB,QACC,qBAAC;EAAI,WAAW,GAAG,YAAY,UAAU;aACxC,qBAACA;GACA,WAAW,GACV,2IACA;cAEA,SAAS,oBAAC;IAAY,KAAK;IAAM,KAAK;KAAS,EAChD,oBAAC;IAAe,WAAU;cACzB,oBAAC;KACA,WAAU;KACI;KACd,aAAa;KACP;KACN,aAAa;KACb,MAAK;MACJ;KACc;IACA,EAClB,oBAAC;GACA,WAAU;GACV,MAAM;GACN,QAAQ;IACP;GACG"}
1
+ {"version":3,"file":"avatar.js","names":["AvatarPrimitive"],"sources":["../../../src/support/components/avatar.tsx"],"sourcesContent":["import { Facehash } from \"facehash\";\nimport type { ReactElement } from \"react\";\n\nimport {\n\tAvatarFallback,\n\tAvatarImage,\n\tAvatar as AvatarPrimitive,\n} from \"../../primitives/avatar\";\nimport { cn } from \"../utils\";\nimport { CossistantLogo } from \"./cossistant-branding\";\nimport { getAgentStatus, OnlineIndicator } from \"./online-indicator\";\n\n/**\n * Default Cossistant theme colors for avatar fallbacks.\n * These use the Tailwind classes defined in support.css.\n */\nconst DEFAULT_AVATAR_COLORS = [\n\t\"bg-co-pink\",\n\t\"bg-co-blue\",\n\t\"bg-co-yellow\",\n\t\"bg-co-orange\",\n];\n\ntype AvatarProps = {\n\tclassName?: string;\n\timage?: string | null;\n\tname: string;\n\t/** Whether this avatar is for an AI agent */\n\tisAI?: boolean;\n\t/** Whether to show the background circle (default: true) */\n\tshowBackground?: boolean;\n\t/**\n\t * Tailwind class array for Facehash background colors.\n\t * Defaults to Cossistant theme colors (pink, blue, yellow, orange).\n\t * @example [\"bg-pink-500\", \"bg-blue-500\", \"bg-green-500\"]\n\t */\n\tcolorClasses?: string[];\n\t/**\n\t * Last seen timestamp for the agent. When provided, shows a status indicator:\n\t * - Green (online): seen within last 15 minutes\n\t * - Orange (away): seen within last hour\n\t * Only shown for non-AI agents.\n\t */\n\tlastSeenAt?: string | null;\n\t/**\n\t * Size of the online indicator in pixels.\n\t * Defaults to 6px.\n\t */\n\tindicatorSize?: number;\n};\n\n/**\n * Renders a squared avatar with graceful fallbacks using Facehash when no\n * image is available. Features rounded corners and a subtle ring border.\n *\n * For AI agents without an image, displays the Cossistant logo without\n * a background.\n */\nexport function Avatar({\n\tclassName,\n\timage,\n\tname,\n\tisAI = false,\n\tshowBackground = true,\n\tcolorClasses = DEFAULT_AVATAR_COLORS,\n\tlastSeenAt,\n\tindicatorSize = 6,\n}: AvatarProps): ReactElement {\n\tconst agentStatus = isAI ? \"offline\" : getAgentStatus(lastSeenAt);\n\n\t// AI agent without image: show just the logo (no avatar wrapper)\n\t// Unless showBackground is true (e.g. in avatar stack), then wrap in a box\n\tif (isAI && !image) {\n\t\tif (showBackground) {\n\t\t\treturn (\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"flex items-center justify-center rounded bg-co-background-200 ring-1 ring-co-border/30 dark:bg-co-background-500\",\n\t\t\t\t\t\tclassName\n\t\t\t\t\t)}\n\t\t\t\t>\n\t\t\t\t\t<CossistantLogo className=\"h-1/2 w-1/2\" />\n\t\t\t\t</div>\n\t\t\t);\n\t\t}\n\t\treturn <CossistantLogo className={cn(\"h-full w-full\", className)} />;\n\t}\n\n\t// AI agent with image: show image in a square\n\tif (isAI && image) {\n\t\treturn (\n\t\t\t<AvatarPrimitive\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"flex size-9 items-center justify-center overflow-clip rounded bg-co-background-200 ring-1 ring-co-border/30 dark:bg-co-background-500\",\n\t\t\t\t\tclassName\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t<AvatarImage alt={name} src={image} />\n\t\t\t\t<AvatarFallback className=\"size-full\">\n\t\t\t\t\t<Facehash\n\t\t\t\t\t\tclassName=\"size-full\"\n\t\t\t\t\t\tcolorClasses={colorClasses}\n\t\t\t\t\t\tinteractive={false}\n\t\t\t\t\t\tname={name}\n\t\t\t\t\t\tshowInitial={false}\n\t\t\t\t\t\tsize=\"100%\"\n\t\t\t\t\t/>\n\t\t\t\t</AvatarFallback>\n\t\t\t</AvatarPrimitive>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className={cn(\"relative\", className)}>\n\t\t\t<AvatarPrimitive\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"flex size-full items-center justify-center overflow-clip rounded bg-co-background-200 ring-1 ring-co-border/30 dark:bg-co-background-500\"\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{image && <AvatarImage alt={name} src={image} />}\n\t\t\t\t<AvatarFallback className=\"size-full\">\n\t\t\t\t\t<Facehash\n\t\t\t\t\t\tclassName=\"size-full\"\n\t\t\t\t\t\tcolorClasses={colorClasses}\n\t\t\t\t\t\tinteractive={false}\n\t\t\t\t\t\tname={name}\n\t\t\t\t\t\tshowInitial={false}\n\t\t\t\t\t\tsize=\"100%\"\n\t\t\t\t\t/>\n\t\t\t\t</AvatarFallback>\n\t\t\t</AvatarPrimitive>\n\t\t\t<OnlineIndicator\n\t\t\t\tclassName=\"-bottom-0.5 -right-0.5 z-10\"\n\t\t\t\tsize={indicatorSize}\n\t\t\t\tstatus={agentStatus}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n"],"mappings":";;;;;;;;;;;;;;AAgBA,MAAM,wBAAwB;CAC7B;CACA;CACA;CACA;CACA;;;;;;;;AAqCD,SAAgB,OAAO,EACtB,WACA,OACA,MACA,OAAO,OACP,iBAAiB,MACjB,eAAe,uBACf,YACA,gBAAgB,KACa;CAC7B,MAAM,cAAc,OAAO,YAAY,eAAe,WAAW;AAIjE,KAAI,QAAQ,CAAC,OAAO;AACnB,MAAI,eACH,QACC,oBAAC;GACA,WAAW,GACV,oHACA,UACA;aAED,oBAAC,kBAAe,WAAU,gBAAgB;IACrC;AAGR,SAAO,oBAAC,kBAAe,WAAW,GAAG,iBAAiB,UAAU,GAAI;;AAIrE,KAAI,QAAQ,MACX,QACC,qBAACA;EACA,WAAW,GACV,yIACA,UACA;aAED,oBAAC;GAAY,KAAK;GAAM,KAAK;IAAS,EACtC,oBAAC;GAAe,WAAU;aACzB,oBAAC;IACA,WAAU;IACI;IACd,aAAa;IACP;IACN,aAAa;IACb,MAAK;KACJ;IACc;GACA;AAIpB,QACC,qBAAC;EAAI,WAAW,GAAG,YAAY,UAAU;aACxC,qBAACA;GACA,WAAW,GACV,2IACA;cAEA,SAAS,oBAAC;IAAY,KAAK;IAAM,KAAK;KAAS,EAChD,oBAAC;IAAe,WAAU;cACzB,oBAAC;KACA,WAAU;KACI;KACd,aAAa;KACP;KACN,aAAa;KACb,MAAK;MACJ;KACc;IACA,EAClB,oBAAC;GACA,WAAU;GACV,MAAM;GACN,QAAQ;IACP;GACG"}
@@ -45,7 +45,8 @@ function ConversationButtonLink({ conversation, onClick, className }) {
45
45
  image: assignedAgent?.image,
46
46
  isAI: assignedAgent?.type === "ai",
47
47
  lastSeenAt: assignedAgent?.lastSeenAt,
48
- name: assignedAgent?.name ?? text("common.fallbacks.supportTeam")
48
+ name: assignedAgent?.name ?? text("common.fallbacks.supportTeam"),
49
+ showBackground: true
49
50
  }),
50
51
  /* @__PURE__ */ jsxs("div", {
51
52
  className: "mr-6 ml-1 flex min-w-0 flex-1 flex-col gap-0.5",
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-button-link.js","names":["STATUS_BADGE_CLASSNAMES: Record<ConversationStatus, string>","STATUS_TEXT_KEYS: Record<ConversationStatus, SupportTextKey>","Icon"],"sources":["../../../src/support/components/conversation-button-link.tsx"],"sourcesContent":["import { type Conversation, ConversationStatus } from \"@cossistant/types\";\nimport type React from \"react\";\nimport { useConversationPreview } from \"../../hooks/use-conversation-preview\";\nimport { type SupportTextKey, useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\nimport { Avatar } from \"./avatar\";\nimport { coButtonVariants } from \"./button\";\nimport Icon from \"./icons\";\nimport { BouncingDots } from \"./typing-indicator\";\n\nexport type ConversationButtonLinkProps = {\n\tconversation: Conversation;\n\tonClick?: () => void;\n\tclassName?: string;\n};\n\nconst STATUS_BADGE_CLASSNAMES: Record<ConversationStatus, string> = {\n\t[ConversationStatus.OPEN]: \"bg-co-success/20 text-co-success-foreground\",\n\t[ConversationStatus.RESOLVED]: \"bg-co-neutral/20 text-co-neutral-foreground\",\n\t[ConversationStatus.SPAM]: \"bg-co-warning/20 text-co-warning-foreground\",\n};\n\nconst DEFAULT_STATUS_BADGE_CLASSNAME =\n\t\"bg-co-neutral/20 text-co-neutral-foreground\";\n\nconst STATUS_TEXT_KEYS: Record<ConversationStatus, SupportTextKey> = {\n\t[ConversationStatus.OPEN]: \"component.conversationButtonLink.status.open\",\n\t[ConversationStatus.RESOLVED]:\n\t\t\"component.conversationButtonLink.status.resolved\",\n\t[ConversationStatus.SPAM]: \"component.conversationButtonLink.status.spam\",\n};\n\n/**\n * Renders a navigable preview card for a conversation including assigned agent\n * details, last message snippets and typing indicators.\n */\nexport function ConversationButtonLink({\n\tconversation,\n\tonClick,\n\tclassName,\n}: ConversationButtonLinkProps): React.ReactElement | null {\n\tconst preview = useConversationPreview({ conversation });\n\tconst text = useSupportText();\n\tconst { lastMessage, assignedAgent, typing } = preview;\n\n\tconst statusBadgeClassName = conversation.deletedAt\n\t\t? STATUS_BADGE_CLASSNAMES[ConversationStatus.RESOLVED]\n\t\t: (STATUS_BADGE_CLASSNAMES[conversation.status] ??\n\t\t\tDEFAULT_STATUS_BADGE_CLASSNAME);\n\n\tconst statusTextKey = conversation.deletedAt\n\t\t? STATUS_TEXT_KEYS[ConversationStatus.RESOLVED]\n\t\t: STATUS_TEXT_KEYS[conversation.status];\n\n\tconst statusText = conversation.deletedAt\n\t\t? text(\"component.conversationButtonLink.status.closed\")\n\t\t: statusTextKey\n\t\t\t? text(statusTextKey)\n\t\t\t: text(\"common.fallbacks.unknown\");\n\n\t// Show the actual title if it exists, otherwise use the preview title (which may fallback to message)\n\tconst displayTitle = conversation.title || preview.title;\n\n\t// Show the truncated message content as secondary text\n\tconst messagePreview = lastMessage?.content || null;\n\n\treturn (\n\t\t<button\n\t\t\tclassName={cn(\n\t\t\t\tcoButtonVariants({\n\t\t\t\t\tvariant: \"secondary\",\n\t\t\t\t\tsize: \"large\",\n\t\t\t\t}),\n\t\t\t\t\"group/btn relative gap-2 border-0 border-co-border/50 border-b pr-3 pl-3 text-left transition-colors first-of-type:rounded-t last-of-type:rounded-b last-of-type:border-b-0 has-[>svg]:pl-3\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\tonClick={onClick}\n\t\t\ttype=\"button\"\n\t\t>\n\t\t\t<Avatar\n\t\t\t\tclassName=\"size-8 flex-shrink-0\"\n\t\t\t\timage={assignedAgent?.image}\n\t\t\t\tisAI={assignedAgent?.type === \"ai\"}\n\t\t\t\tlastSeenAt={assignedAgent?.lastSeenAt}\n\t\t\t\tname={assignedAgent?.name ?? text(\"common.fallbacks.supportTeam\")}\n\t\t\t/>\n\n\t\t\t<div className=\"mr-6 ml-1 flex min-w-0 flex-1 flex-col gap-0.5\">\n\t\t\t\t<div className=\"flex max-w-[90%] items-center justify-between gap-2\">\n\t\t\t\t\t<h3 className=\"truncate font-medium text-co-primary text-sm\">\n\t\t\t\t\t\t{displayTitle}\n\t\t\t\t\t</h3>\n\t\t\t\t</div>\n\t\t\t\t{typing.isTyping ? (\n\t\t\t\t\t<BouncingDots />\n\t\t\t\t) : messagePreview ? (\n\t\t\t\t\t<p className=\"truncate text-co-primary/60 text-sm\">\n\t\t\t\t\t\t{messagePreview}\n\t\t\t\t\t</p>\n\t\t\t\t) : null}\n\t\t\t</div>\n\n\t\t\t{/* <div\n className={cn(\n \"mr-6 inline-flex items-center rounded px-2 py-0.5 font-medium text-[9px] uppercase\",\n statusBadgeClassName,\n )}\n >\n {statusText}\n </div> */}\n\n\t\t\t<Icon\n\t\t\t\tclassName=\"-translate-y-1/2 absolute top-1/2 right-4 size-3 text-co-primary/60 transition-transform duration-200 group-hover/btn:translate-x-0.5 group-hover/btn:text-co-primary\"\n\t\t\t\tname=\"arrow-right\"\n\t\t\t\tvariant=\"default\"\n\t\t\t/>\n\t\t</button>\n\t);\n}\n"],"mappings":";;;;;;;;;;;AAgBA,MAAMA,0BAA8D;EAClE,mBAAmB,OAAO;EAC1B,mBAAmB,WAAW;EAC9B,mBAAmB,OAAO;CAC3B;AAKD,MAAMC,mBAA+D;EACnE,mBAAmB,OAAO;EAC1B,mBAAmB,WACnB;EACA,mBAAmB,OAAO;CAC3B;;;;;AAMD,SAAgB,uBAAuB,EACtC,cACA,SACA,aAC0D;CAC1D,MAAM,UAAU,uBAAuB,EAAE,cAAc,CAAC;CACxD,MAAM,OAAO,gBAAgB;CAC7B,MAAM,EAAE,aAAa,eAAe,WAAW;AAElB,cAAa,YACvC,wBAAwB,mBAAmB,YAC1C,wBAAwB,aAAa;CAGzC,MAAM,gBAAgB,aAAa,YAChC,iBAAiB,mBAAmB,YACpC,iBAAiB,aAAa;AAEd,cAAa,YAC7B,KAAK,iDAAiD,GACtD,gBACC,KAAK,cAAc,GACnB,KAAK,2BAA2B;CAGpC,MAAM,eAAe,aAAa,SAAS,QAAQ;CAGnD,MAAM,iBAAiB,aAAa,WAAW;AAE/C,QACC,qBAAC;EACA,WAAW,GACV,iBAAiB;GAChB,SAAS;GACT,MAAM;GACN,CAAC,EACF,+LACA,UACA;EACQ;EACT,MAAK;;GAEL,oBAAC;IACA,WAAU;IACV,OAAO,eAAe;IACtB,MAAM,eAAe,SAAS;IAC9B,YAAY,eAAe;IAC3B,MAAM,eAAe,QAAQ,KAAK,+BAA+B;KAChE;GAEF,qBAAC;IAAI,WAAU;eACd,oBAAC;KAAI,WAAU;eACd,oBAAC;MAAG,WAAU;gBACZ;OACG;MACA,EACL,OAAO,WACP,oBAAC,iBAAe,GACb,iBACH,oBAAC;KAAE,WAAU;eACX;MACE,GACD;KACC;GAWN,oBAACC;IACA,WAAU;IACV,MAAK;IACL,SAAQ;KACP;;GACM"}
1
+ {"version":3,"file":"conversation-button-link.js","names":["STATUS_BADGE_CLASSNAMES: Record<ConversationStatus, string>","STATUS_TEXT_KEYS: Record<ConversationStatus, SupportTextKey>","Icon"],"sources":["../../../src/support/components/conversation-button-link.tsx"],"sourcesContent":["import { type Conversation, ConversationStatus } from \"@cossistant/types\";\nimport type React from \"react\";\nimport { useConversationPreview } from \"../../hooks/use-conversation-preview\";\nimport { type SupportTextKey, useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\nimport { Avatar } from \"./avatar\";\nimport { coButtonVariants } from \"./button\";\nimport Icon from \"./icons\";\nimport { BouncingDots } from \"./typing-indicator\";\n\nexport type ConversationButtonLinkProps = {\n\tconversation: Conversation;\n\tonClick?: () => void;\n\tclassName?: string;\n};\n\nconst STATUS_BADGE_CLASSNAMES: Record<ConversationStatus, string> = {\n\t[ConversationStatus.OPEN]: \"bg-co-success/20 text-co-success-foreground\",\n\t[ConversationStatus.RESOLVED]: \"bg-co-neutral/20 text-co-neutral-foreground\",\n\t[ConversationStatus.SPAM]: \"bg-co-warning/20 text-co-warning-foreground\",\n};\n\nconst DEFAULT_STATUS_BADGE_CLASSNAME =\n\t\"bg-co-neutral/20 text-co-neutral-foreground\";\n\nconst STATUS_TEXT_KEYS: Record<ConversationStatus, SupportTextKey> = {\n\t[ConversationStatus.OPEN]: \"component.conversationButtonLink.status.open\",\n\t[ConversationStatus.RESOLVED]:\n\t\t\"component.conversationButtonLink.status.resolved\",\n\t[ConversationStatus.SPAM]: \"component.conversationButtonLink.status.spam\",\n};\n\n/**\n * Renders a navigable preview card for a conversation including assigned agent\n * details, last message snippets and typing indicators.\n */\nexport function ConversationButtonLink({\n\tconversation,\n\tonClick,\n\tclassName,\n}: ConversationButtonLinkProps): React.ReactElement | null {\n\tconst preview = useConversationPreview({ conversation });\n\tconst text = useSupportText();\n\tconst { lastMessage, assignedAgent, typing } = preview;\n\n\tconst statusBadgeClassName = conversation.deletedAt\n\t\t? STATUS_BADGE_CLASSNAMES[ConversationStatus.RESOLVED]\n\t\t: (STATUS_BADGE_CLASSNAMES[conversation.status] ??\n\t\t\tDEFAULT_STATUS_BADGE_CLASSNAME);\n\n\tconst statusTextKey = conversation.deletedAt\n\t\t? STATUS_TEXT_KEYS[ConversationStatus.RESOLVED]\n\t\t: STATUS_TEXT_KEYS[conversation.status];\n\n\tconst statusText = conversation.deletedAt\n\t\t? text(\"component.conversationButtonLink.status.closed\")\n\t\t: statusTextKey\n\t\t\t? text(statusTextKey)\n\t\t\t: text(\"common.fallbacks.unknown\");\n\n\t// Show the actual title if it exists, otherwise use the preview title (which may fallback to message)\n\tconst displayTitle = conversation.title || preview.title;\n\n\t// Show the truncated message content as secondary text\n\tconst messagePreview = lastMessage?.content || null;\n\n\treturn (\n\t\t<button\n\t\t\tclassName={cn(\n\t\t\t\tcoButtonVariants({\n\t\t\t\t\tvariant: \"secondary\",\n\t\t\t\t\tsize: \"large\",\n\t\t\t\t}),\n\t\t\t\t\"group/btn relative gap-2 border-0 border-co-border/50 border-b pr-3 pl-3 text-left transition-colors first-of-type:rounded-t last-of-type:rounded-b last-of-type:border-b-0 has-[>svg]:pl-3\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\tonClick={onClick}\n\t\t\ttype=\"button\"\n\t\t>\n\t\t\t<Avatar\n\t\t\t\tclassName=\"size-8 flex-shrink-0\"\n\t\t\t\timage={assignedAgent?.image}\n\t\t\t\tisAI={assignedAgent?.type === \"ai\"}\n\t\t\t\tlastSeenAt={assignedAgent?.lastSeenAt}\n\t\t\t\tname={assignedAgent?.name ?? text(\"common.fallbacks.supportTeam\")}\n\t\t\t\tshowBackground\n\t\t\t/>\n\n\t\t\t<div className=\"mr-6 ml-1 flex min-w-0 flex-1 flex-col gap-0.5\">\n\t\t\t\t<div className=\"flex max-w-[90%] items-center justify-between gap-2\">\n\t\t\t\t\t<h3 className=\"truncate font-medium text-co-primary text-sm\">\n\t\t\t\t\t\t{displayTitle}\n\t\t\t\t\t</h3>\n\t\t\t\t</div>\n\t\t\t\t{typing.isTyping ? (\n\t\t\t\t\t<BouncingDots />\n\t\t\t\t) : messagePreview ? (\n\t\t\t\t\t<p className=\"truncate text-co-primary/60 text-sm\">\n\t\t\t\t\t\t{messagePreview}\n\t\t\t\t\t</p>\n\t\t\t\t) : null}\n\t\t\t</div>\n\n\t\t\t{/* <div\n className={cn(\n \"mr-6 inline-flex items-center rounded px-2 py-0.5 font-medium text-[9px] uppercase\",\n statusBadgeClassName,\n )}\n >\n {statusText}\n </div> */}\n\n\t\t\t<Icon\n\t\t\t\tclassName=\"-translate-y-1/2 absolute top-1/2 right-4 size-3 text-co-primary/60 transition-transform duration-200 group-hover/btn:translate-x-0.5 group-hover/btn:text-co-primary\"\n\t\t\t\tname=\"arrow-right\"\n\t\t\t\tvariant=\"default\"\n\t\t\t/>\n\t\t</button>\n\t);\n}\n"],"mappings":";;;;;;;;;;;AAgBA,MAAMA,0BAA8D;EAClE,mBAAmB,OAAO;EAC1B,mBAAmB,WAAW;EAC9B,mBAAmB,OAAO;CAC3B;AAKD,MAAMC,mBAA+D;EACnE,mBAAmB,OAAO;EAC1B,mBAAmB,WACnB;EACA,mBAAmB,OAAO;CAC3B;;;;;AAMD,SAAgB,uBAAuB,EACtC,cACA,SACA,aAC0D;CAC1D,MAAM,UAAU,uBAAuB,EAAE,cAAc,CAAC;CACxD,MAAM,OAAO,gBAAgB;CAC7B,MAAM,EAAE,aAAa,eAAe,WAAW;AAElB,cAAa,YACvC,wBAAwB,mBAAmB,YAC1C,wBAAwB,aAAa;CAGzC,MAAM,gBAAgB,aAAa,YAChC,iBAAiB,mBAAmB,YACpC,iBAAiB,aAAa;AAEd,cAAa,YAC7B,KAAK,iDAAiD,GACtD,gBACC,KAAK,cAAc,GACnB,KAAK,2BAA2B;CAGpC,MAAM,eAAe,aAAa,SAAS,QAAQ;CAGnD,MAAM,iBAAiB,aAAa,WAAW;AAE/C,QACC,qBAAC;EACA,WAAW,GACV,iBAAiB;GAChB,SAAS;GACT,MAAM;GACN,CAAC,EACF,+LACA,UACA;EACQ;EACT,MAAK;;GAEL,oBAAC;IACA,WAAU;IACV,OAAO,eAAe;IACtB,MAAM,eAAe,SAAS;IAC9B,YAAY,eAAe;IAC3B,MAAM,eAAe,QAAQ,KAAK,+BAA+B;IACjE;KACC;GAEF,qBAAC;IAAI,WAAU;eACd,oBAAC;KAAI,WAAU;eACd,oBAAC;MAAG,WAAU;gBACZ;OACG;MACA,EACL,OAAO,WACP,oBAAC,iBAAe,GACb,iBACH,oBAAC;KAAE,WAAU;eACX;MACE,GACD;KACC;GAWN,oBAACC;IACA,WAAU;IACV,MAAK;IACL,SAAQ;KACP;;GACM"}
@@ -55,7 +55,7 @@ const ConversationEvent = ({ event, availableAIAgents, createdAt, availableHuman
55
55
  name: aiAgent?.name || text("common.fallbacks.cossistant"),
56
56
  showBackground: !!aiAgent?.image
57
57
  }) : /* @__PURE__ */ jsx(Avatar, {
58
- className: "size-5 flex-shrink-0 overflow-clip rounded-lg",
58
+ className: "size-5 flex-shrink-0",
59
59
  image: humanAgent?.image,
60
60
  name: humanAgent?.name || text("common.fallbacks.someone")
61
61
  })
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-event.js","names":["ConversationEvent: React.FC<ConversationEventProps>"],"sources":["../../../src/support/components/conversation-event.tsx"],"sourcesContent":["import type {\n\tAvailableAIAgent,\n\tAvailableHumanAgent,\n\tTimelinePartEvent,\n} from \"@cossistant/types\";\nimport { motion } from \"motion/react\";\nimport type React from \"react\";\nimport { useSupportText } from \"../text\";\nimport { Avatar } from \"./avatar\";\n\nexport type ConversationEventProps = {\n\tevent: TimelinePartEvent;\n\tavailableAIAgents: AvailableAIAgent[];\n\tavailableHumanAgents: AvailableHumanAgent[];\n\tcreatedAt?: string;\n};\n\nexport const ConversationEvent: React.FC<ConversationEventProps> = ({\n\tevent,\n\tavailableAIAgents,\n\tcreatedAt,\n\tavailableHumanAgents,\n}) => {\n\tconst text = useSupportText();\n\tconst isAI = event.actorAiAgentId !== null;\n\tconst humanAgent = availableHumanAgents.find(\n\t\t(agent) => agent.id === event.actorUserId\n\t);\n\tconst aiAgent = availableAIAgents.find(\n\t\t(agent) => agent.id === event.actorAiAgentId\n\t);\n\n\t// Get the actor name\n\tconst actorName = isAI\n\t\t? aiAgent?.name || text(\"common.fallbacks.cossistant\")\n\t\t: humanAgent?.name || text(\"common.fallbacks.someone\");\n\n\t// Convert event type to plain English\n\tconst getEventText = () => {\n\t\tswitch (event.eventType) {\n\t\t\tcase \"assigned\":\n\t\t\t\treturn text(\"component.conversationEvent.assigned\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"unassigned\":\n\t\t\t\treturn text(\"component.conversationEvent.unassigned\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_requested\":\n\t\t\t\treturn text(\"component.conversationEvent.participantRequested\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_joined\":\n\t\t\t\treturn text(\"component.conversationEvent.participantJoined\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_left\":\n\t\t\t\treturn text(\"component.conversationEvent.participantLeft\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"status_changed\":\n\t\t\t\treturn text(\"component.conversationEvent.statusChanged\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"priority_changed\":\n\t\t\t\treturn text(\"component.conversationEvent.priorityChanged\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"tag_added\":\n\t\t\t\treturn text(\"component.conversationEvent.tagAdded\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"tag_removed\":\n\t\t\t\treturn text(\"component.conversationEvent.tagRemoved\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"resolved\":\n\t\t\t\treturn text(\"component.conversationEvent.resolved\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"reopened\":\n\t\t\t\treturn text(\"component.conversationEvent.reopened\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"visitor_blocked\":\n\t\t\t\treturn text(\"component.conversationEvent.visitorBlocked\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"visitor_unblocked\":\n\t\t\t\treturn text(\"component.conversationEvent.visitorUnblocked\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"visitor_identified\":\n\t\t\t\treturn text(\"component.conversationEvent.visitorIdentified\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tdefault:\n\t\t\t\treturn text(\"component.conversationEvent.default\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t}\n\t};\n\n\treturn (\n\t\t<motion.div\n\t\t\tanimate={{ opacity: 1, scale: 1 }}\n\t\t\tclassName=\"flex items-center justify-center pt-4 pb-8\"\n\t\t\tinitial={{ opacity: 0, scale: 0.95 }}\n\t\t\ttransition={{ duration: 0.3, ease: \"easeOut\" }}\n\t\t>\n\t\t\t<div className=\"flex items-center gap-2 text-co-muted-foreground text-xs\">\n\t\t\t\t<div className=\"flex flex-col justify-end\">\n\t\t\t\t\t{isAI ? (\n\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\tclassName=\"size-5 flex-shrink-0\"\n\t\t\t\t\t\t\timage={aiAgent?.image}\n\t\t\t\t\t\t\tisAI\n\t\t\t\t\t\t\tname={aiAgent?.name || text(\"common.fallbacks.cossistant\")}\n\t\t\t\t\t\t\tshowBackground={!!aiAgent?.image}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\tclassName=\"size-5 flex-shrink-0 overflow-clip rounded-lg\"\n\t\t\t\t\t\t\timage={humanAgent?.image}\n\t\t\t\t\t\t\tname={humanAgent?.name || text(\"common.fallbacks.someone\")}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t<span className=\"px-2\">{getEventText()}</span>\n\t\t\t\t{createdAt && (\n\t\t\t\t\t<time className=\"text-[10px]\">\n\t\t\t\t\t\t{new Date(createdAt).toLocaleTimeString([], {\n\t\t\t\t\t\t\thour: \"2-digit\",\n\t\t\t\t\t\t\tminute: \"2-digit\",\n\t\t\t\t\t\t})}\n\t\t\t\t\t</time>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</motion.div>\n\t);\n};\n\nConversationEvent.displayName = \"ConversationEvent\";\n"],"mappings":";;;;;;AAiBA,MAAaA,qBAAuD,EACnE,OACA,mBACA,WACA,2BACK;CACL,MAAM,OAAO,gBAAgB;CAC7B,MAAM,OAAO,MAAM,mBAAmB;CACtC,MAAM,aAAa,qBAAqB,MACtC,UAAU,MAAM,OAAO,MAAM,YAC9B;CACD,MAAM,UAAU,kBAAkB,MAChC,UAAU,MAAM,OAAO,MAAM,eAC9B;CAGD,MAAM,YAAY,OACf,SAAS,QAAQ,KAAK,8BAA8B,GACpD,YAAY,QAAQ,KAAK,2BAA2B;CAGvD,MAAM,qBAAqB;AAC1B,UAAQ,MAAM,WAAd;GACC,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,aACJ,QAAO,KAAK,0CAA0C,EACrD,WACA,CAAC;GACH,KAAK,wBACJ,QAAO,KAAK,oDAAoD,EAC/D,WACA,CAAC;GACH,KAAK,qBACJ,QAAO,KAAK,iDAAiD,EAC5D,WACA,CAAC;GACH,KAAK,mBACJ,QAAO,KAAK,+CAA+C,EAC1D,WACA,CAAC;GACH,KAAK,iBACJ,QAAO,KAAK,6CAA6C,EACxD,WACA,CAAC;GACH,KAAK,mBACJ,QAAO,KAAK,+CAA+C,EAC1D,WACA,CAAC;GACH,KAAK,YACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,cACJ,QAAO,KAAK,0CAA0C,EACrD,WACA,CAAC;GACH,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,kBACJ,QAAO,KAAK,8CAA8C,EACzD,WACA,CAAC;GACH,KAAK,oBACJ,QAAO,KAAK,gDAAgD,EAC3D,WACA,CAAC;GACH,KAAK,qBACJ,QAAO,KAAK,iDAAiD,EAC5D,WACA,CAAC;GACH,QACC,QAAO,KAAK,uCAAuC,EAClD,WACA,CAAC;;;AAIL,QACC,oBAAC,OAAO;EACP,SAAS;GAAE,SAAS;GAAG,OAAO;GAAG;EACjC,WAAU;EACV,SAAS;GAAE,SAAS;GAAG,OAAO;GAAM;EACpC,YAAY;GAAE,UAAU;GAAK,MAAM;GAAW;YAE9C,qBAAC;GAAI,WAAU;;IACd,oBAAC;KAAI,WAAU;eACb,OACA,oBAAC;MACA,WAAU;MACV,OAAO,SAAS;MAChB;MACA,MAAM,SAAS,QAAQ,KAAK,8BAA8B;MAC1D,gBAAgB,CAAC,CAAC,SAAS;OAC1B,GAEF,oBAAC;MACA,WAAU;MACV,OAAO,YAAY;MACnB,MAAM,YAAY,QAAQ,KAAK,2BAA2B;OACzD;MAEE;IACN,oBAAC;KAAK,WAAU;eAAQ,cAAc;MAAQ;IAC7C,aACA,oBAAC;KAAK,WAAU;eACd,IAAI,KAAK,UAAU,CAAC,mBAAmB,EAAE,EAAE;MAC3C,MAAM;MACN,QAAQ;MACR,CAAC;MACI;;IAEH;GACM;;AAIf,kBAAkB,cAAc"}
1
+ {"version":3,"file":"conversation-event.js","names":["ConversationEvent: React.FC<ConversationEventProps>"],"sources":["../../../src/support/components/conversation-event.tsx"],"sourcesContent":["import type {\n\tAvailableAIAgent,\n\tAvailableHumanAgent,\n\tTimelinePartEvent,\n} from \"@cossistant/types\";\nimport { motion } from \"motion/react\";\nimport type React from \"react\";\nimport { useSupportText } from \"../text\";\nimport { Avatar } from \"./avatar\";\n\nexport type ConversationEventProps = {\n\tevent: TimelinePartEvent;\n\tavailableAIAgents: AvailableAIAgent[];\n\tavailableHumanAgents: AvailableHumanAgent[];\n\tcreatedAt?: string;\n};\n\nexport const ConversationEvent: React.FC<ConversationEventProps> = ({\n\tevent,\n\tavailableAIAgents,\n\tcreatedAt,\n\tavailableHumanAgents,\n}) => {\n\tconst text = useSupportText();\n\tconst isAI = event.actorAiAgentId !== null;\n\tconst humanAgent = availableHumanAgents.find(\n\t\t(agent) => agent.id === event.actorUserId\n\t);\n\tconst aiAgent = availableAIAgents.find(\n\t\t(agent) => agent.id === event.actorAiAgentId\n\t);\n\n\t// Get the actor name\n\tconst actorName = isAI\n\t\t? aiAgent?.name || text(\"common.fallbacks.cossistant\")\n\t\t: humanAgent?.name || text(\"common.fallbacks.someone\");\n\n\t// Convert event type to plain English\n\tconst getEventText = () => {\n\t\tswitch (event.eventType) {\n\t\t\tcase \"assigned\":\n\t\t\t\treturn text(\"component.conversationEvent.assigned\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"unassigned\":\n\t\t\t\treturn text(\"component.conversationEvent.unassigned\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_requested\":\n\t\t\t\treturn text(\"component.conversationEvent.participantRequested\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_joined\":\n\t\t\t\treturn text(\"component.conversationEvent.participantJoined\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"participant_left\":\n\t\t\t\treturn text(\"component.conversationEvent.participantLeft\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"status_changed\":\n\t\t\t\treturn text(\"component.conversationEvent.statusChanged\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"priority_changed\":\n\t\t\t\treturn text(\"component.conversationEvent.priorityChanged\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"tag_added\":\n\t\t\t\treturn text(\"component.conversationEvent.tagAdded\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"tag_removed\":\n\t\t\t\treturn text(\"component.conversationEvent.tagRemoved\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"resolved\":\n\t\t\t\treturn text(\"component.conversationEvent.resolved\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"reopened\":\n\t\t\t\treturn text(\"component.conversationEvent.reopened\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"visitor_blocked\":\n\t\t\t\treturn text(\"component.conversationEvent.visitorBlocked\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"visitor_unblocked\":\n\t\t\t\treturn text(\"component.conversationEvent.visitorUnblocked\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tcase \"visitor_identified\":\n\t\t\t\treturn text(\"component.conversationEvent.visitorIdentified\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t\tdefault:\n\t\t\t\treturn text(\"component.conversationEvent.default\", {\n\t\t\t\t\tactorName,\n\t\t\t\t});\n\t\t}\n\t};\n\n\treturn (\n\t\t<motion.div\n\t\t\tanimate={{ opacity: 1, scale: 1 }}\n\t\t\tclassName=\"flex items-center justify-center pt-4 pb-8\"\n\t\t\tinitial={{ opacity: 0, scale: 0.95 }}\n\t\t\ttransition={{ duration: 0.3, ease: \"easeOut\" }}\n\t\t>\n\t\t\t<div className=\"flex items-center gap-2 text-co-muted-foreground text-xs\">\n\t\t\t\t<div className=\"flex flex-col justify-end\">\n\t\t\t\t\t{isAI ? (\n\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\tclassName=\"size-5 flex-shrink-0\"\n\t\t\t\t\t\t\timage={aiAgent?.image}\n\t\t\t\t\t\t\tisAI\n\t\t\t\t\t\t\tname={aiAgent?.name || text(\"common.fallbacks.cossistant\")}\n\t\t\t\t\t\t\tshowBackground={!!aiAgent?.image}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\tclassName=\"size-5 flex-shrink-0\"\n\t\t\t\t\t\t\timage={humanAgent?.image}\n\t\t\t\t\t\t\tname={humanAgent?.name || text(\"common.fallbacks.someone\")}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t<span className=\"px-2\">{getEventText()}</span>\n\t\t\t\t{createdAt && (\n\t\t\t\t\t<time className=\"text-[10px]\">\n\t\t\t\t\t\t{new Date(createdAt).toLocaleTimeString([], {\n\t\t\t\t\t\t\thour: \"2-digit\",\n\t\t\t\t\t\t\tminute: \"2-digit\",\n\t\t\t\t\t\t})}\n\t\t\t\t\t</time>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</motion.div>\n\t);\n};\n\nConversationEvent.displayName = \"ConversationEvent\";\n"],"mappings":";;;;;;AAiBA,MAAaA,qBAAuD,EACnE,OACA,mBACA,WACA,2BACK;CACL,MAAM,OAAO,gBAAgB;CAC7B,MAAM,OAAO,MAAM,mBAAmB;CACtC,MAAM,aAAa,qBAAqB,MACtC,UAAU,MAAM,OAAO,MAAM,YAC9B;CACD,MAAM,UAAU,kBAAkB,MAChC,UAAU,MAAM,OAAO,MAAM,eAC9B;CAGD,MAAM,YAAY,OACf,SAAS,QAAQ,KAAK,8BAA8B,GACpD,YAAY,QAAQ,KAAK,2BAA2B;CAGvD,MAAM,qBAAqB;AAC1B,UAAQ,MAAM,WAAd;GACC,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,aACJ,QAAO,KAAK,0CAA0C,EACrD,WACA,CAAC;GACH,KAAK,wBACJ,QAAO,KAAK,oDAAoD,EAC/D,WACA,CAAC;GACH,KAAK,qBACJ,QAAO,KAAK,iDAAiD,EAC5D,WACA,CAAC;GACH,KAAK,mBACJ,QAAO,KAAK,+CAA+C,EAC1D,WACA,CAAC;GACH,KAAK,iBACJ,QAAO,KAAK,6CAA6C,EACxD,WACA,CAAC;GACH,KAAK,mBACJ,QAAO,KAAK,+CAA+C,EAC1D,WACA,CAAC;GACH,KAAK,YACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,cACJ,QAAO,KAAK,0CAA0C,EACrD,WACA,CAAC;GACH,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,WACJ,QAAO,KAAK,wCAAwC,EACnD,WACA,CAAC;GACH,KAAK,kBACJ,QAAO,KAAK,8CAA8C,EACzD,WACA,CAAC;GACH,KAAK,oBACJ,QAAO,KAAK,gDAAgD,EAC3D,WACA,CAAC;GACH,KAAK,qBACJ,QAAO,KAAK,iDAAiD,EAC5D,WACA,CAAC;GACH,QACC,QAAO,KAAK,uCAAuC,EAClD,WACA,CAAC;;;AAIL,QACC,oBAAC,OAAO;EACP,SAAS;GAAE,SAAS;GAAG,OAAO;GAAG;EACjC,WAAU;EACV,SAAS;GAAE,SAAS;GAAG,OAAO;GAAM;EACpC,YAAY;GAAE,UAAU;GAAK,MAAM;GAAW;YAE9C,qBAAC;GAAI,WAAU;;IACd,oBAAC;KAAI,WAAU;eACb,OACA,oBAAC;MACA,WAAU;MACV,OAAO,SAAS;MAChB;MACA,MAAM,SAAS,QAAQ,KAAK,8BAA8B;MAC1D,gBAAgB,CAAC,CAAC,SAAS;OAC1B,GAEF,oBAAC;MACA,WAAU;MACV,OAAO,YAAY;MACnB,MAAM,YAAY,QAAQ,KAAK,2BAA2B;OACzD;MAEE;IACN,oBAAC;KAAK,WAAU;eAAQ,cAAc;MAAQ;IAC7C,aACA,oBAAC;KAAK,WAAU;eACd,IAAI,KAAK,UAAU,CAAC,mBAAmB,EAAE,EAAE;MAC3C,MAAM;MACN,QAAQ;MACR,CAAC;MACI;;IAEH;GACM;;AAIf,kBAAkB,cAAc"}
@@ -5,7 +5,7 @@ import * as react_jsx_runtime0 from "react/jsx-runtime";
5
5
  type ConversationResolvedFeedbackProps = {
6
6
  status: ConversationStatus | null;
7
7
  rating: number | null;
8
- onRate?: (rating: number) => void;
8
+ onRate?: (rating: number, comment?: string) => void;
9
9
  isSubmitting?: boolean;
10
10
  className?: string;
11
11
  };
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-resolved-feedback.d.ts","names":[],"sources":["../../../src/support/components/conversation-resolved-feedback.tsx"],"sourcesContent":[],"mappings":";;;;KAKK,iCAAA;UACI;;EADJ,MAAA,CAAA,EAAA,CAAA,MAAA,EAAA,MAAA,EAAA,GAAA,IAAA;EAUW,YAAA,CAAA,EAAA,OAAA;EACf,SAAA,CAAA,EAAA,MAAA;CACA;AACA,iBAHe,4BAAA,CAGf;EAAA,MAAA;EAAA,MAAA;EAAA,MAAA;EAAA,YAAA;EAAA;AAAA,CAAA,EAGE,iCAHF,CAAA,EAGmC,kBAAA,CAAA,GAAA,CAAA,OAHnC"}
1
+ {"version":3,"file":"conversation-resolved-feedback.d.ts","names":[],"sources":["../../../src/support/components/conversation-resolved-feedback.tsx"],"sourcesContent":[],"mappings":";;;;KAMK,iCAAA;UACI;;EADJ,MAAA,CAAA,EAAA,CAAA,MAAA,EAAA,MAAA,EAAA,OAAiC,CAAjC,EAAiC,MAAA,EAAA,GAAA,IAC7B;EASO,YAAA,CAAA,EAAA,OAAA;EACf,SAAA,CAAA,EAAA,MAAA;CACA;AACA,iBAHe,4BAAA,CAGf;EAAA,MAAA;EAAA,MAAA;EAAA,MAAA;EAAA,YAAA;EAAA;AAAA,CAAA,EAGE,iCAHF,CAAA,EAGmC,kBAAA,CAAA,GAAA,CAAA,OAHnC"}
@@ -1,6 +1,7 @@
1
1
  import { cn } from "../utils/index.js";
2
2
  import icons_default from "./icons.js";
3
3
  import { Text, useSupportText } from "../text/index.js";
4
+ import { useEffect, useRef, useState } from "react";
4
5
  import { ConversationStatus } from "@cossistant/types";
5
6
  import { jsx, jsxs } from "react/jsx-runtime";
6
7
 
@@ -10,42 +11,84 @@ function ConversationResolvedFeedback({ status, rating, onRate, isSubmitting = f
10
11
  const text = useSupportText();
11
12
  const isResolved = status === ConversationStatus.RESOLVED;
12
13
  const isRated = rating != null;
13
- const isInteractive = Boolean(onRate) && !isSubmitting && !isRated;
14
- if (!isResolved) return /* @__PURE__ */ jsx("div", {
15
- className: cn("m-4 flex items-center justify-center text-balance px-4 pb-6 text-center font-medium text-co-muted-foreground text-sm", className),
16
- children: /* @__PURE__ */ jsx(Text, {
17
- as: "p",
18
- textKey: "component.conversationPage.closedMessage"
19
- })
20
- });
14
+ const [selectedRating, setSelectedRating] = useState(null);
15
+ const [hoveredRating, setHoveredRating] = useState(null);
16
+ const [comment, setComment] = useState("");
17
+ const [hasSubmitted, setHasSubmitted] = useState(false);
18
+ const textareaRef = useRef(null);
19
+ const displayRating = hoveredRating ?? (isRated ? rating : selectedRating);
20
+ const showCommentField = selectedRating != null && !isRated && !hasSubmitted;
21
+ const isInteractive = Boolean(onRate) && !isSubmitting && !isRated && !hasSubmitted;
22
+ useEffect(() => {
23
+ if (showCommentField) textareaRef.current?.focus();
24
+ }, [showCommentField]);
25
+ const handleRatingSelect = (value) => {
26
+ if (!isInteractive) return;
27
+ setSelectedRating(value);
28
+ };
29
+ const handleSubmit = () => {
30
+ if (!(selectedRating && onRate)) return;
31
+ setHasSubmitted(true);
32
+ onRate(selectedRating, comment.trim() || void 0);
33
+ };
34
+ if (!isResolved) {
35
+ const closedTextKey = status === ConversationStatus.SPAM ? "component.conversationPage.spamMessage" : "component.conversationPage.closedMessage";
36
+ return /* @__PURE__ */ jsx("div", {
37
+ className: cn("m-4 flex items-center justify-center text-balance px-4 pb-6 text-center font-medium text-co-muted-foreground text-sm", className),
38
+ children: /* @__PURE__ */ jsx(Text, {
39
+ as: "p",
40
+ textKey: closedTextKey
41
+ })
42
+ });
43
+ }
21
44
  return /* @__PURE__ */ jsxs("div", {
22
45
  className: cn("m-4 rounded-md border border-co-border/60 bg-co-background-100 px-4 py-3 text-center text-sm shadow-sm", className),
23
46
  children: [
24
47
  /* @__PURE__ */ jsx(Text, {
25
48
  as: "p",
26
49
  className: "font-medium text-co-foreground",
27
- textKey: isRated ? "component.conversationPage.ratingThanks" : "component.conversationPage.ratingPrompt"
50
+ textKey: isRated || hasSubmitted ? "component.conversationPage.ratingThanks" : "component.conversationPage.ratingPrompt"
28
51
  }),
29
52
  /* @__PURE__ */ jsx("div", {
30
53
  className: "mt-2 flex items-center justify-center gap-1",
31
54
  children: Array.from({ length: STAR_COUNT }).map((_, index) => {
32
55
  const value = index + 1;
33
- const isFilled = rating ? value <= rating : false;
56
+ const isFilled = displayRating ? value <= displayRating : false;
34
57
  return /* @__PURE__ */ jsx("button", {
35
58
  "aria-label": text("component.conversationPage.ratingLabel", { rating: value }),
36
59
  className: cn("group inline-flex h-8 w-8 items-center justify-center rounded-md transition-colors", isInteractive ? "hover:bg-co-muted" : "cursor-default opacity-70"),
37
60
  disabled: !isInteractive,
38
- onClick: () => onRate?.(value),
61
+ onClick: () => handleRatingSelect(value),
62
+ onMouseEnter: () => isInteractive && setHoveredRating(value),
63
+ onMouseLeave: () => isInteractive && setHoveredRating(null),
39
64
  type: "button",
40
65
  children: /* @__PURE__ */ jsx(icons_default, {
41
- className: "h-4 w-4 text-co-primary",
66
+ className: cn("h-4 w-4 transition-transform", isFilled ? "text-co-primary" : "text-co-muted-foreground/40", isInteractive && hoveredRating && value <= hoveredRating && "scale-110"),
42
67
  name: "star",
43
68
  variant: isFilled ? "filled" : "default"
44
69
  })
45
70
  }, value);
46
71
  })
47
72
  }),
48
- /* @__PURE__ */ jsx(Text, {
73
+ showCommentField && /* @__PURE__ */ jsxs("div", {
74
+ className: "mt-3 space-y-2",
75
+ children: [/* @__PURE__ */ jsx("textarea", {
76
+ className: "w-full resize-none rounded-md border border-co-border bg-co-background px-3 py-2 text-co-foreground text-sm placeholder:text-co-muted-foreground focus:border-co-primary focus:outline-none focus:ring-1 focus:ring-co-primary",
77
+ disabled: isSubmitting,
78
+ onChange: (e) => setComment(e.target.value),
79
+ placeholder: text("component.conversationPage.commentPlaceholder"),
80
+ ref: textareaRef,
81
+ rows: 3,
82
+ value: comment
83
+ }), /* @__PURE__ */ jsx("button", {
84
+ className: cn("w-full rounded-md bg-co-primary px-4 py-2 font-medium text-co-primary-foreground text-sm transition-colors", isSubmitting ? "cursor-not-allowed opacity-50" : "hover:bg-co-primary/90"),
85
+ disabled: isSubmitting,
86
+ onClick: handleSubmit,
87
+ type: "button",
88
+ children: text("component.conversationPage.submitFeedback")
89
+ })]
90
+ }),
91
+ (isRated || hasSubmitted || !showCommentField) && /* @__PURE__ */ jsx(Text, {
49
92
  as: "p",
50
93
  className: "mt-2 text-co-muted-foreground text-xs",
51
94
  textKey: "component.conversationPage.closedMessage"
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-resolved-feedback.js","names":["Icon"],"sources":["../../../src/support/components/conversation-resolved-feedback.tsx"],"sourcesContent":["import { ConversationStatus } from \"@cossistant/types\";\nimport { Text, useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\n\ntype ConversationResolvedFeedbackProps = {\n\tstatus: ConversationStatus | null;\n\trating: number | null;\n\tonRate?: (rating: number) => void;\n\tisSubmitting?: boolean;\n\tclassName?: string;\n};\n\nconst STAR_COUNT = 5;\n\nexport function ConversationResolvedFeedback({\n\tstatus,\n\trating,\n\tonRate,\n\tisSubmitting = false,\n\tclassName,\n}: ConversationResolvedFeedbackProps) {\n\tconst text = useSupportText();\n\tconst isResolved = status === ConversationStatus.RESOLVED;\n\tconst isRated = rating != null;\n\tconst isInteractive = Boolean(onRate) && !isSubmitting && !isRated;\n\n\tif (!isResolved) {\n\t\treturn (\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"m-4 flex items-center justify-center text-balance px-4 pb-6 text-center font-medium text-co-muted-foreground text-sm\",\n\t\t\t\t\tclassName\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t<Text as=\"p\" textKey=\"component.conversationPage.closedMessage\" />\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"m-4 rounded-md border border-co-border/60 bg-co-background-100 px-4 py-3 text-center text-sm shadow-sm\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t>\n\t\t\t<Text\n\t\t\t\tas=\"p\"\n\t\t\t\tclassName=\"font-medium text-co-foreground\"\n\t\t\t\ttextKey={\n\t\t\t\t\tisRated\n\t\t\t\t\t\t? \"component.conversationPage.ratingThanks\"\n\t\t\t\t\t\t: \"component.conversationPage.ratingPrompt\"\n\t\t\t\t}\n\t\t\t/>\n\t\t\t<div className=\"mt-2 flex items-center justify-center gap-1\">\n\t\t\t\t{Array.from({ length: STAR_COUNT }).map((_, index) => {\n\t\t\t\t\tconst value = index + 1;\n\t\t\t\t\tconst isFilled = rating ? value <= rating : false;\n\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\taria-label={text(\"component.conversationPage.ratingLabel\", {\n\t\t\t\t\t\t\t\trating: value,\n\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"group inline-flex h-8 w-8 items-center justify-center rounded-md transition-colors\",\n\t\t\t\t\t\t\t\tisInteractive\n\t\t\t\t\t\t\t\t\t? \"hover:bg-co-muted\"\n\t\t\t\t\t\t\t\t\t: \"cursor-default opacity-70\"\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\tdisabled={!isInteractive}\n\t\t\t\t\t\t\tkey={value}\n\t\t\t\t\t\t\tonClick={() => onRate?.(value)}\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Icon\n\t\t\t\t\t\t\t\tclassName=\"h-4 w-4 text-co-primary\"\n\t\t\t\t\t\t\t\tname=\"star\"\n\t\t\t\t\t\t\t\tvariant={isFilled ? \"filled\" : \"default\"}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</div>\n\t\t\t<Text\n\t\t\t\tas=\"p\"\n\t\t\t\tclassName=\"mt-2 text-co-muted-foreground text-xs\"\n\t\t\t\ttextKey=\"component.conversationPage.closedMessage\"\n\t\t\t/>\n\t\t</div>\n\t);\n}\n"],"mappings":";;;;;;;AAaA,MAAM,aAAa;AAEnB,SAAgB,6BAA6B,EAC5C,QACA,QACA,QACA,eAAe,OACf,aACqC;CACrC,MAAM,OAAO,gBAAgB;CAC7B,MAAM,aAAa,WAAW,mBAAmB;CACjD,MAAM,UAAU,UAAU;CAC1B,MAAM,gBAAgB,QAAQ,OAAO,IAAI,CAAC,gBAAgB,CAAC;AAE3D,KAAI,CAAC,WACJ,QACC,oBAAC;EACA,WAAW,GACV,wHACA,UACA;YAED,oBAAC;GAAK,IAAG;GAAI,SAAQ;IAA6C;GAC7D;AAIR,QACC,qBAAC;EACA,WAAW,GACV,0GACA,UACA;;GAED,oBAAC;IACA,IAAG;IACH,WAAU;IACV,SACC,UACG,4CACA;KAEH;GACF,oBAAC;IAAI,WAAU;cACb,MAAM,KAAK,EAAE,QAAQ,YAAY,CAAC,CAAC,KAAK,GAAG,UAAU;KACrD,MAAM,QAAQ,QAAQ;KACtB,MAAM,WAAW,SAAS,SAAS,SAAS;AAE5C,YACC,oBAAC;MACA,cAAY,KAAK,0CAA0C,EAC1D,QAAQ,OACR,CAAC;MACF,WAAW,GACV,sFACA,gBACG,sBACA,4BACH;MACD,UAAU,CAAC;MAEX,eAAe,SAAS,MAAM;MAC9B,MAAK;gBAEL,oBAACA;OACA,WAAU;OACV,MAAK;OACL,SAAS,WAAW,WAAW;QAC9B;QARG,MASG;MAET;KACG;GACN,oBAAC;IACA,IAAG;IACH,WAAU;IACV,SAAQ;KACP;;GACG"}
1
+ {"version":3,"file":"conversation-resolved-feedback.js","names":["Icon"],"sources":["../../../src/support/components/conversation-resolved-feedback.tsx"],"sourcesContent":["import { ConversationStatus } from \"@cossistant/types\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { Text, useSupportText } from \"../text\";\nimport { cn } from \"../utils\";\nimport Icon from \"./icons\";\n\ntype ConversationResolvedFeedbackProps = {\n\tstatus: ConversationStatus | null;\n\trating: number | null;\n\tonRate?: (rating: number, comment?: string) => void;\n\tisSubmitting?: boolean;\n\tclassName?: string;\n};\n\nconst STAR_COUNT = 5;\n\nexport function ConversationResolvedFeedback({\n\tstatus,\n\trating,\n\tonRate,\n\tisSubmitting = false,\n\tclassName,\n}: ConversationResolvedFeedbackProps) {\n\tconst text = useSupportText();\n\tconst isResolved = status === ConversationStatus.RESOLVED;\n\tconst isRated = rating != null;\n\n\t// Local state for the rating flow\n\tconst [selectedRating, setSelectedRating] = useState<number | null>(null);\n\tconst [hoveredRating, setHoveredRating] = useState<number | null>(null);\n\tconst [comment, setComment] = useState(\"\");\n\tconst [hasSubmitted, setHasSubmitted] = useState(false);\n\tconst textareaRef = useRef<HTMLTextAreaElement>(null);\n\n\t// Show the rating that was submitted if available, otherwise use local selection\n\tconst displayRating = hoveredRating ?? (isRated ? rating : selectedRating);\n\tconst showCommentField = selectedRating != null && !isRated && !hasSubmitted;\n\tconst isInteractive =\n\t\tBoolean(onRate) && !isSubmitting && !isRated && !hasSubmitted;\n\n\tuseEffect(() => {\n\t\tif (showCommentField) {\n\t\t\ttextareaRef.current?.focus();\n\t\t}\n\t}, [showCommentField]);\n\n\tconst handleRatingSelect = (value: number) => {\n\t\tif (!isInteractive) {\n\t\t\treturn;\n\t\t}\n\t\tsetSelectedRating(value);\n\t};\n\n\tconst handleSubmit = () => {\n\t\tif (!(selectedRating && onRate)) {\n\t\t\treturn;\n\t\t}\n\t\tsetHasSubmitted(true);\n\t\tonRate(selectedRating, comment.trim() || undefined);\n\t};\n\n\tif (!isResolved) {\n\t\tconst closedTextKey =\n\t\t\tstatus === ConversationStatus.SPAM\n\t\t\t\t? \"component.conversationPage.spamMessage\"\n\t\t\t\t: \"component.conversationPage.closedMessage\";\n\n\t\treturn (\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"m-4 flex items-center justify-center text-balance px-4 pb-6 text-center font-medium text-co-muted-foreground text-sm\",\n\t\t\t\t\tclassName\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t<Text as=\"p\" textKey={closedTextKey} />\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"m-4 rounded-md border border-co-border/60 bg-co-background-100 px-4 py-3 text-center text-sm shadow-sm\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t>\n\t\t\t<Text\n\t\t\t\tas=\"p\"\n\t\t\t\tclassName=\"font-medium text-co-foreground\"\n\t\t\t\ttextKey={\n\t\t\t\t\tisRated || hasSubmitted\n\t\t\t\t\t\t? \"component.conversationPage.ratingThanks\"\n\t\t\t\t\t\t: \"component.conversationPage.ratingPrompt\"\n\t\t\t\t}\n\t\t\t/>\n\t\t\t<div className=\"mt-2 flex items-center justify-center gap-1\">\n\t\t\t\t{Array.from({ length: STAR_COUNT }).map((_, index) => {\n\t\t\t\t\tconst value = index + 1;\n\t\t\t\t\tconst isFilled = displayRating ? value <= displayRating : false;\n\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\taria-label={text(\"component.conversationPage.ratingLabel\", {\n\t\t\t\t\t\t\t\trating: value,\n\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"group inline-flex h-8 w-8 items-center justify-center rounded-md transition-colors\",\n\t\t\t\t\t\t\t\tisInteractive\n\t\t\t\t\t\t\t\t\t? \"hover:bg-co-muted\"\n\t\t\t\t\t\t\t\t\t: \"cursor-default opacity-70\"\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\tdisabled={!isInteractive}\n\t\t\t\t\t\t\tkey={value}\n\t\t\t\t\t\t\tonClick={() => handleRatingSelect(value)}\n\t\t\t\t\t\t\tonMouseEnter={() => isInteractive && setHoveredRating(value)}\n\t\t\t\t\t\t\tonMouseLeave={() => isInteractive && setHoveredRating(null)}\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Icon\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\"h-4 w-4 transition-transform\",\n\t\t\t\t\t\t\t\t\tisFilled ? \"text-co-primary\" : \"text-co-muted-foreground/40\",\n\t\t\t\t\t\t\t\t\tisInteractive &&\n\t\t\t\t\t\t\t\t\t\thoveredRating &&\n\t\t\t\t\t\t\t\t\t\tvalue <= hoveredRating &&\n\t\t\t\t\t\t\t\t\t\t\"scale-110\"\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\tname=\"star\"\n\t\t\t\t\t\t\t\tvariant={isFilled ? \"filled\" : \"default\"}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</div>\n\n\t\t\t{showCommentField && (\n\t\t\t\t<div className=\"mt-3 space-y-2\">\n\t\t\t\t\t<textarea\n\t\t\t\t\t\tclassName=\"w-full resize-none rounded-md border border-co-border bg-co-background px-3 py-2 text-co-foreground text-sm placeholder:text-co-muted-foreground focus:border-co-primary focus:outline-none focus:ring-1 focus:ring-co-primary\"\n\t\t\t\t\t\tdisabled={isSubmitting}\n\t\t\t\t\t\tonChange={(e) => setComment(e.target.value)}\n\t\t\t\t\t\tplaceholder={text(\"component.conversationPage.commentPlaceholder\")}\n\t\t\t\t\t\tref={textareaRef}\n\t\t\t\t\t\trows={3}\n\t\t\t\t\t\tvalue={comment}\n\t\t\t\t\t/>\n\t\t\t\t\t<button\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"w-full rounded-md bg-co-primary px-4 py-2 font-medium text-co-primary-foreground text-sm transition-colors\",\n\t\t\t\t\t\t\tisSubmitting\n\t\t\t\t\t\t\t\t? \"cursor-not-allowed opacity-50\"\n\t\t\t\t\t\t\t\t: \"hover:bg-co-primary/90\"\n\t\t\t\t\t\t)}\n\t\t\t\t\t\tdisabled={isSubmitting}\n\t\t\t\t\t\tonClick={handleSubmit}\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{text(\"component.conversationPage.submitFeedback\")}\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{(isRated || hasSubmitted || !showCommentField) && (\n\t\t\t\t<Text\n\t\t\t\t\tas=\"p\"\n\t\t\t\t\tclassName=\"mt-2 text-co-muted-foreground text-xs\"\n\t\t\t\t\ttextKey=\"component.conversationPage.closedMessage\"\n\t\t\t\t/>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n"],"mappings":";;;;;;;;AAcA,MAAM,aAAa;AAEnB,SAAgB,6BAA6B,EAC5C,QACA,QACA,QACA,eAAe,OACf,aACqC;CACrC,MAAM,OAAO,gBAAgB;CAC7B,MAAM,aAAa,WAAW,mBAAmB;CACjD,MAAM,UAAU,UAAU;CAG1B,MAAM,CAAC,gBAAgB,qBAAqB,SAAwB,KAAK;CACzE,MAAM,CAAC,eAAe,oBAAoB,SAAwB,KAAK;CACvE,MAAM,CAAC,SAAS,cAAc,SAAS,GAAG;CAC1C,MAAM,CAAC,cAAc,mBAAmB,SAAS,MAAM;CACvD,MAAM,cAAc,OAA4B,KAAK;CAGrD,MAAM,gBAAgB,kBAAkB,UAAU,SAAS;CAC3D,MAAM,mBAAmB,kBAAkB,QAAQ,CAAC,WAAW,CAAC;CAChE,MAAM,gBACL,QAAQ,OAAO,IAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC;AAElD,iBAAgB;AACf,MAAI,iBACH,aAAY,SAAS,OAAO;IAE3B,CAAC,iBAAiB,CAAC;CAEtB,MAAM,sBAAsB,UAAkB;AAC7C,MAAI,CAAC,cACJ;AAED,oBAAkB,MAAM;;CAGzB,MAAM,qBAAqB;AAC1B,MAAI,EAAE,kBAAkB,QACvB;AAED,kBAAgB,KAAK;AACrB,SAAO,gBAAgB,QAAQ,MAAM,IAAI,OAAU;;AAGpD,KAAI,CAAC,YAAY;EAChB,MAAM,gBACL,WAAW,mBAAmB,OAC3B,2CACA;AAEJ,SACC,oBAAC;GACA,WAAW,GACV,wHACA,UACA;aAED,oBAAC;IAAK,IAAG;IAAI,SAAS;KAAiB;IAClC;;AAIR,QACC,qBAAC;EACA,WAAW,GACV,0GACA,UACA;;GAED,oBAAC;IACA,IAAG;IACH,WAAU;IACV,SACC,WAAW,eACR,4CACA;KAEH;GACF,oBAAC;IAAI,WAAU;cACb,MAAM,KAAK,EAAE,QAAQ,YAAY,CAAC,CAAC,KAAK,GAAG,UAAU;KACrD,MAAM,QAAQ,QAAQ;KACtB,MAAM,WAAW,gBAAgB,SAAS,gBAAgB;AAE1D,YACC,oBAAC;MACA,cAAY,KAAK,0CAA0C,EAC1D,QAAQ,OACR,CAAC;MACF,WAAW,GACV,sFACA,gBACG,sBACA,4BACH;MACD,UAAU,CAAC;MAEX,eAAe,mBAAmB,MAAM;MACxC,oBAAoB,iBAAiB,iBAAiB,MAAM;MAC5D,oBAAoB,iBAAiB,iBAAiB,KAAK;MAC3D,MAAK;gBAEL,oBAACA;OACA,WAAW,GACV,gCACA,WAAW,oBAAoB,+BAC/B,iBACC,iBACA,SAAS,iBACT,YACD;OACD,MAAK;OACL,SAAS,WAAW,WAAW;QAC9B;QAjBG,MAkBG;MAET;KACG;GAEL,oBACA,qBAAC;IAAI,WAAU;eACd,oBAAC;KACA,WAAU;KACV,UAAU;KACV,WAAW,MAAM,WAAW,EAAE,OAAO,MAAM;KAC3C,aAAa,KAAK,gDAAgD;KAClE,KAAK;KACL,MAAM;KACN,OAAO;MACN,EACF,oBAAC;KACA,WAAW,GACV,8GACA,eACG,kCACA,yBACH;KACD,UAAU;KACV,SAAS;KACT,MAAK;eAEJ,KAAK,4CAA4C;MAC1C;KACJ;IAGL,WAAW,gBAAgB,CAAC,qBAC7B,oBAAC;IACA,IAAG;IACH,WAAU;IACV,SAAQ;KACP;;GAEE"}
@@ -1 +1 @@
1
- {"version":3,"file":"typing-indicator.d.ts","names":[],"sources":["../../../src/support/components/typing-indicator.tsx"],"sourcesContent":[],"mappings":";;;;KAKY,qBAAA;KAEA,iBAAA;EAFA,EAAA,EAAA,MAAA;EAEA,IAAA,EAEL,qBAFsB;AAK7B,CAAA;AAAwD,KAA5C,oBAAA,GAAuB,OAAA,CAAM,cAAe,CAAA,cAAA,CAAA,GAAA;EAArB,YAAM,EAC1B,iBAD0B,EAAA;EAC1B,iBAAA,CAAA,EACM,gBADN,EAAA;EACM,oBAAA,CAAA,EACG,mBADH,EAAA;EACG,WAAA,CAAA,EAAA,OAAA;CAAmB;AAI9B,cAAA,YAAgB,EAAA,CAAA;EAAA;CAIzB,EAAM;EAuBG,SAAA,CAAA,EAAA,MAwDZ;CAxD2B,EAAA,GAvBxB,OAAA,CAAM,YAuBkB;AAAA,cAAf,eAAe,EAAA,OAAA,CAAA,yBAAA,CAAA,OAAA,CAAA,cAAA,CAAA,cAAA,CAAA,GAAA;EAjCb,YAAA,EAAA,iBAAA,EAAA;EACM,iBAAA,CAAA,EAAA,gBAAA,EAAA;EACG,oBAAA,CAAA,EAAA,mBAAA,EAAA"}
1
+ {"version":3,"file":"typing-indicator.d.ts","names":[],"sources":["../../../src/support/components/typing-indicator.tsx"],"sourcesContent":[],"mappings":";;;;KAKY,qBAAA;KAEA,iBAAA;EAFA,EAAA,EAAA,MAAA;EAEA,IAAA,EAEL,qBAFsB;AAK7B,CAAA;AAAwD,KAA5C,oBAAA,GAAuB,OAAA,CAAM,cAAe,CAAA,cAAA,CAAA,GAAA;EAArB,YAAM,EAC1B,iBAD0B,EAAA;EAC1B,iBAAA,CAAA,EACM,gBADN,EAAA;EACM,oBAAA,CAAA,EACG,mBADH,EAAA;EACG,WAAA,CAAA,EAAA,OAAA;CAAmB;AAI9B,cAAA,YAAgB,EAAA,CAAA;EAAA;CAIzB,EAAM;EAuBG,SAAA,CAAA,EAAA,MAsEZ;CAtE2B,EAAA,GAvBxB,OAAA,CAAM,YAuBkB;AAAA,cAAf,eAAe,EAAA,OAAA,CAAA,yBAAA,CAAA,OAAA,CAAA,cAAA,CAAA,cAAA,CAAA,GAAA;EAjCb,YAAA,EAAA,iBAAA,EAAA;EACM,iBAAA,CAAA,EAAA,gBAAA,EAAA;EACG,oBAAA,CAAA,EAAA,mBAAA,EAAA"}
@@ -1,5 +1,5 @@
1
1
  import { cn } from "../utils/index.js";
2
- import { AvatarStack } from "./avatar-stack.js";
2
+ import { Avatar } from "./avatar.js";
3
3
  import * as React$1 from "react";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
 
@@ -22,12 +22,20 @@ const TypingIndicator = React$1.forwardRef(({ participants, availableAIAgents =
22
22
  className: cn("flex items-center gap-6", className),
23
23
  ref,
24
24
  ...props,
25
- children: [withAvatars && /* @__PURE__ */ jsx(AvatarStack, {
26
- aiAgents: typingAIAgents,
27
- hideDefaultAIAgent: typingAIAgents.length === 0,
28
- humanAgents: typingHumanAgents,
29
- size: 24,
30
- spacing: 16
25
+ children: [withAvatars && /* @__PURE__ */ jsxs("div", {
26
+ className: "flex items-center",
27
+ children: [typingAIAgents.map((agent) => /* @__PURE__ */ jsx(Avatar, {
28
+ className: "size-6",
29
+ image: agent.image,
30
+ isAI: true,
31
+ name: agent.name,
32
+ showBackground: !!agent.image
33
+ }, agent.id)), typingHumanAgents.map((agent) => /* @__PURE__ */ jsx(Avatar, {
34
+ className: "size-6",
35
+ image: agent.image,
36
+ lastSeenAt: agent.lastSeenAt,
37
+ name: agent.name
38
+ }, agent.id))]
31
39
  }), /* @__PURE__ */ jsx(BouncingDots, {})]
32
40
  });
33
41
  });
@@ -1 +1 @@
1
- {"version":3,"file":"typing-indicator.js","names":["React"],"sources":["../../../src/support/components/typing-indicator.tsx"],"sourcesContent":["import type { AvailableAIAgent, AvailableHumanAgent } from \"@cossistant/types\";\nimport * as React from \"react\";\nimport { cn } from \"../utils\";\nimport { AvatarStack } from \"./avatar-stack\";\n\nexport type TypingParticipantType = \"visitor\" | \"team_member\" | \"ai\";\n\nexport type TypingParticipant = {\n\tid: string;\n\ttype: TypingParticipantType;\n};\n\nexport type TypingIndicatorProps = React.HTMLAttributes<HTMLDivElement> & {\n\tparticipants: TypingParticipant[];\n\tavailableAIAgents?: AvailableAIAgent[];\n\tavailableHumanAgents?: AvailableHumanAgent[];\n\twithAvatars?: boolean;\n};\n\nexport const BouncingDots = ({\n\tclassName,\n}: {\n\tclassName?: string;\n}): React.ReactElement => (\n\t<div className=\"flex gap-1\">\n\t\t<span\n\t\t\tclassName={cn(\n\t\t\t\t\"dot-bounce-1 size-1 rounded-full bg-co-primary\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t/>\n\t\t<span\n\t\t\tclassName={cn(\n\t\t\t\t\"dot-bounce-2 size-1 rounded-full bg-co-primary\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t/>\n\t\t<span\n\t\t\tclassName={cn(\n\t\t\t\t\"dot-bounce-3 size-1 rounded-full bg-co-primary\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t/>\n\t</div>\n);\n\nexport const TypingIndicator = React.forwardRef<\n\tHTMLDivElement,\n\tTypingIndicatorProps\n>(\n\t(\n\t\t{\n\t\t\tparticipants,\n\t\t\tavailableAIAgents = [],\n\t\t\tavailableHumanAgents = [],\n\t\t\twithAvatars = true,\n\t\t\tclassName,\n\t\t\t...props\n\t\t},\n\t\tref\n\t) => {\n\t\tif (!participants || participants.length === 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Separate AI and human participants\n\t\tconst humanParticipantIds = participants\n\t\t\t.filter((p) => p.type === \"team_member\")\n\t\t\t.map((p) => p.id);\n\n\t\tconst aiParticipantIds = participants\n\t\t\t.filter((p) => p.type === \"ai\")\n\t\t\t.map((p) => p.id);\n\n\t\t// Get matching agents\n\t\tconst typingHumanAgents = availableHumanAgents.filter((agent) =>\n\t\t\thumanParticipantIds.includes(agent.id)\n\t\t);\n\n\t\tconst typingAIAgents = availableAIAgents.filter((agent) =>\n\t\t\taiParticipantIds.includes(agent.id)\n\t\t);\n\n\t\treturn (\n\t\t\t<div\n\t\t\t\tclassName={cn(\"flex items-center gap-6\", className)}\n\t\t\t\tref={ref}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t{withAvatars && (\n\t\t\t\t\t<AvatarStack\n\t\t\t\t\t\taiAgents={typingAIAgents}\n\t\t\t\t\t\thideDefaultAIAgent={typingAIAgents.length === 0}\n\t\t\t\t\t\thumanAgents={typingHumanAgents}\n\t\t\t\t\t\tsize={24}\n\t\t\t\t\t\tspacing={16}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t\t<BouncingDots />\n\t\t\t</div>\n\t\t);\n\t}\n);\n\nTypingIndicator.displayName = \"TypingIndicator\";\n"],"mappings":";;;;;;AAmBA,MAAa,gBAAgB,EAC5B,gBAIA,qBAAC;CAAI,WAAU;;EACd,oBAAC,UACA,WAAW,GACV,kDACA,UACA,GACA;EACF,oBAAC,UACA,WAAW,GACV,kDACA,UACA,GACA;EACF,oBAAC,UACA,WAAW,GACV,kDACA,UACA,GACA;;EACG;AAGP,MAAa,kBAAkBA,QAAM,YAKnC,EACC,cACA,oBAAoB,EAAE,EACtB,uBAAuB,EAAE,EACzB,cAAc,MACd,WACA,GAAG,SAEJ,QACI;AACJ,KAAI,CAAC,gBAAgB,aAAa,WAAW,EAC5C,QAAO;CAIR,MAAM,sBAAsB,aAC1B,QAAQ,MAAM,EAAE,SAAS,cAAc,CACvC,KAAK,MAAM,EAAE,GAAG;CAElB,MAAM,mBAAmB,aACvB,QAAQ,MAAM,EAAE,SAAS,KAAK,CAC9B,KAAK,MAAM,EAAE,GAAG;CAGlB,MAAM,oBAAoB,qBAAqB,QAAQ,UACtD,oBAAoB,SAAS,MAAM,GAAG,CACtC;CAED,MAAM,iBAAiB,kBAAkB,QAAQ,UAChD,iBAAiB,SAAS,MAAM,GAAG,CACnC;AAED,QACC,qBAAC;EACA,WAAW,GAAG,2BAA2B,UAAU;EAC9C;EACL,GAAI;aAEH,eACA,oBAAC;GACA,UAAU;GACV,oBAAoB,eAAe,WAAW;GAC9C,aAAa;GACb,MAAM;GACN,SAAS;IACR,EAEH,oBAAC,iBAAe;GACX;EAGR;AAED,gBAAgB,cAAc"}
1
+ {"version":3,"file":"typing-indicator.js","names":["React"],"sources":["../../../src/support/components/typing-indicator.tsx"],"sourcesContent":["import type { AvailableAIAgent, AvailableHumanAgent } from \"@cossistant/types\";\nimport * as React from \"react\";\nimport { cn } from \"../utils\";\nimport { Avatar } from \"./avatar\";\n\nexport type TypingParticipantType = \"visitor\" | \"team_member\" | \"ai\";\n\nexport type TypingParticipant = {\n\tid: string;\n\ttype: TypingParticipantType;\n};\n\nexport type TypingIndicatorProps = React.HTMLAttributes<HTMLDivElement> & {\n\tparticipants: TypingParticipant[];\n\tavailableAIAgents?: AvailableAIAgent[];\n\tavailableHumanAgents?: AvailableHumanAgent[];\n\twithAvatars?: boolean;\n};\n\nexport const BouncingDots = ({\n\tclassName,\n}: {\n\tclassName?: string;\n}): React.ReactElement => (\n\t<div className=\"flex gap-1\">\n\t\t<span\n\t\t\tclassName={cn(\n\t\t\t\t\"dot-bounce-1 size-1 rounded-full bg-co-primary\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t/>\n\t\t<span\n\t\t\tclassName={cn(\n\t\t\t\t\"dot-bounce-2 size-1 rounded-full bg-co-primary\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t/>\n\t\t<span\n\t\t\tclassName={cn(\n\t\t\t\t\"dot-bounce-3 size-1 rounded-full bg-co-primary\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t/>\n\t</div>\n);\n\nexport const TypingIndicator = React.forwardRef<\n\tHTMLDivElement,\n\tTypingIndicatorProps\n>(\n\t(\n\t\t{\n\t\t\tparticipants,\n\t\t\tavailableAIAgents = [],\n\t\t\tavailableHumanAgents = [],\n\t\t\twithAvatars = true,\n\t\t\tclassName,\n\t\t\t...props\n\t\t},\n\t\tref\n\t) => {\n\t\tif (!participants || participants.length === 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Separate AI and human participants\n\t\tconst humanParticipantIds = participants\n\t\t\t.filter((p) => p.type === \"team_member\")\n\t\t\t.map((p) => p.id);\n\n\t\tconst aiParticipantIds = participants\n\t\t\t.filter((p) => p.type === \"ai\")\n\t\t\t.map((p) => p.id);\n\n\t\t// Get matching agents\n\t\tconst typingHumanAgents = availableHumanAgents.filter((agent) =>\n\t\t\thumanParticipantIds.includes(agent.id)\n\t\t);\n\n\t\tconst typingAIAgents = availableAIAgents.filter((agent) =>\n\t\t\taiParticipantIds.includes(agent.id)\n\t\t);\n\n\t\treturn (\n\t\t\t<div\n\t\t\t\tclassName={cn(\"flex items-center gap-6\", className)}\n\t\t\t\tref={ref}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t{withAvatars && (\n\t\t\t\t\t<div className=\"flex items-center\">\n\t\t\t\t\t\t{typingAIAgents.map((agent) => (\n\t\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\t\tclassName=\"size-6\"\n\t\t\t\t\t\t\t\timage={agent.image}\n\t\t\t\t\t\t\t\tisAI\n\t\t\t\t\t\t\t\tkey={agent.id}\n\t\t\t\t\t\t\t\tname={agent.name}\n\t\t\t\t\t\t\t\tshowBackground={!!agent.image}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t))}\n\t\t\t\t\t\t{typingHumanAgents.map((agent) => (\n\t\t\t\t\t\t\t<Avatar\n\t\t\t\t\t\t\t\tclassName=\"size-6\"\n\t\t\t\t\t\t\t\timage={agent.image}\n\t\t\t\t\t\t\t\tkey={agent.id}\n\t\t\t\t\t\t\t\tlastSeenAt={agent.lastSeenAt}\n\t\t\t\t\t\t\t\tname={agent.name}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t\t<BouncingDots />\n\t\t\t</div>\n\t\t);\n\t}\n);\n\nTypingIndicator.displayName = \"TypingIndicator\";\n"],"mappings":";;;;;;AAmBA,MAAa,gBAAgB,EAC5B,gBAIA,qBAAC;CAAI,WAAU;;EACd,oBAAC,UACA,WAAW,GACV,kDACA,UACA,GACA;EACF,oBAAC,UACA,WAAW,GACV,kDACA,UACA,GACA;EACF,oBAAC,UACA,WAAW,GACV,kDACA,UACA,GACA;;EACG;AAGP,MAAa,kBAAkBA,QAAM,YAKnC,EACC,cACA,oBAAoB,EAAE,EACtB,uBAAuB,EAAE,EACzB,cAAc,MACd,WACA,GAAG,SAEJ,QACI;AACJ,KAAI,CAAC,gBAAgB,aAAa,WAAW,EAC5C,QAAO;CAIR,MAAM,sBAAsB,aAC1B,QAAQ,MAAM,EAAE,SAAS,cAAc,CACvC,KAAK,MAAM,EAAE,GAAG;CAElB,MAAM,mBAAmB,aACvB,QAAQ,MAAM,EAAE,SAAS,KAAK,CAC9B,KAAK,MAAM,EAAE,GAAG;CAGlB,MAAM,oBAAoB,qBAAqB,QAAQ,UACtD,oBAAoB,SAAS,MAAM,GAAG,CACtC;CAED,MAAM,iBAAiB,kBAAkB,QAAQ,UAChD,iBAAiB,SAAS,MAAM,GAAG,CACnC;AAED,QACC,qBAAC;EACA,WAAW,GAAG,2BAA2B,UAAU;EAC9C;EACL,GAAI;aAEH,eACA,qBAAC;GAAI,WAAU;cACb,eAAe,KAAK,UACpB,oBAAC;IACA,WAAU;IACV,OAAO,MAAM;IACb;IAEA,MAAM,MAAM;IACZ,gBAAgB,CAAC,CAAC,MAAM;MAFnB,MAAM,GAGV,CACD,EACD,kBAAkB,KAAK,UACvB,oBAAC;IACA,WAAU;IACV,OAAO,MAAM;IAEb,YAAY,MAAM;IAClB,MAAM,MAAM;MAFP,MAAM,GAGV,CACD;IACG,EAEP,oBAAC,iBAAe;GACX;EAGR;AAED,gBAAgB,cAAc"}
@@ -1,10 +1,10 @@
1
- import { AvatarStack } from "../components/avatar-stack.js";
2
1
  import { useSupportNavigation } from "../store/support-store.js";
3
2
  import { CoButton } from "../components/button.js";
4
3
  import icons_default from "../components/icons.js";
5
4
  import { PENDING_CONVERSATION_ID } from "../../utils/id.js";
6
5
  import { Header } from "../components/header.js";
7
6
  import { Text, useSupportText } from "../text/index.js";
7
+ import { AvatarStack } from "../components/avatar-stack.js";
8
8
  import { Watermark } from "../components/watermark.js";
9
9
  import { useConversationHistoryPage } from "../../hooks/use-conversation-history-page.js";
10
10
  import { ConversationButtonLink } from "../components/conversation-button-link.js";
@@ -1,10 +1,10 @@
1
1
  import { useStoreSelector } from "../../hooks/private/store/use-store-selector.js";
2
- import { AvatarStack } from "../components/avatar-stack.js";
3
2
  import { useSupportConfig, useSupportNavigation } from "../store/support-store.js";
4
3
  import { useNewMessageSound } from "../../hooks/use-new-message-sound.js";
5
4
  import { Header } from "../components/header.js";
6
5
  import { Text, useSupportText } from "../text/index.js";
7
6
  import { useConversationPage } from "../../hooks/use-conversation-page.js";
7
+ import { AvatarStack } from "../components/avatar-stack.js";
8
8
  import { ConversationResolvedFeedback } from "../components/conversation-resolved-feedback.js";
9
9
  import { ConversationTimelineList } from "../components/conversation-timeline.js";
10
10
  import { MultimodalInput } from "../components/multimodal-input.js";
@@ -50,7 +50,7 @@ const ConversationPage = ({ params, conversationId: legacyConversationId, initia
50
50
  setPendingRating(null);
51
51
  setIsSubmittingRating(false);
52
52
  }, [activeConversation?.id]);
53
- const handleRateConversation = async (value) => {
53
+ const handleRateConversation = async (value, comment) => {
54
54
  if (!(client && activeConversation)) return;
55
55
  if (activeConversation.status !== ConversationStatus.RESOLVED || activeConversation.visitorRating) return;
56
56
  if (isSubmittingRating) return;
@@ -60,6 +60,7 @@ const ConversationPage = ({ params, conversationId: legacyConversationId, initia
60
60
  await client.submitConversationRating({
61
61
  conversationId: activeConversation.id,
62
62
  rating: value,
63
+ comment,
63
64
  visitorId: visitor?.id ?? void 0
64
65
  });
65
66
  } catch (error) {
@@ -105,6 +106,7 @@ const ConversationPage = ({ params, conversationId: legacyConversationId, initia
105
106
  }), /* @__PURE__ */ jsx(AvatarStack, {
106
107
  aiAgents: availableAIAgents,
107
108
  gapWidth: 2,
109
+ hideDefaultAIAgent: false,
108
110
  humanAgents: availableHumanAgents,
109
111
  size: 32,
110
112
  spacing: 28
@@ -1 +1 @@
1
- {"version":3,"file":"conversation.js","names":["ConversationPage: ConversationPageComponent"],"sources":["../../../src/support/pages/conversation.tsx"],"sourcesContent":["import { ConversationStatus } from \"@cossistant/types\";\nimport type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport { type ReactElement, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useStoreSelector } from \"../../hooks/private/store/use-store-selector\";\nimport { useConversationPage } from \"../../hooks/use-conversation-page\";\nimport { useNewMessageSound } from \"../../hooks/use-new-message-sound\";\nimport { useSupport } from \"../../provider\";\nimport { AvatarStack } from \"../components/avatar-stack\";\nimport { ConversationResolvedFeedback } from \"../components/conversation-resolved-feedback\";\nimport { ConversationTimelineList } from \"../components/conversation-timeline\";\nimport { Header } from \"../components/header\";\nimport { MultimodalInput } from \"../components/multimodal-input\";\nimport { IdentificationTimelineTool } from \"../components/timeline-identification-tool\";\nimport { useSupportConfig, useSupportNavigation } from \"../store\";\nimport { Text, useSupportText } from \"../text\";\n\ntype ConversationPageProps = {\n\t/**\n\t * Page params object (for compatibility with Page component)\n\t */\n\tparams?: {\n\t\t/**\n\t\t * The conversation ID to display (can be PENDING_CONVERSATION_ID or a real ID).\n\t\t */\n\t\tconversationId: string;\n\n\t\t/**\n\t\t * Optional initial message to send when opening the conversation.\n\t\t */\n\t\tinitialMessage?: string;\n\n\t\t/**\n\t\t * Optional timeline items to display (for optimistic updates or initial state).\n\t\t */\n\t\titems?: TimelineItem[];\n\t};\n\n\t// Legacy direct props support (deprecated but maintained for backward compatibility)\n\tconversationId?: string;\n\tinitialMessage?: string;\n\titems?: TimelineItem[];\n};\n\n/**\n * Conversation page with message timeline and input composer.\n */\ntype ConversationPageComponent = (props: ConversationPageProps) => ReactElement;\n\nexport const ConversationPage: ConversationPageComponent = ({\n\tparams,\n\tconversationId: legacyConversationId,\n\tinitialMessage: legacyInitialMessage,\n\titems: legacyItems,\n}: ConversationPageProps) => {\n\t// Support both params object (new) and direct props (legacy)\n\tconst initialConversationId =\n\t\tparams?.conversationId ?? legacyConversationId ?? \"\";\n\tconst initialMessage = params?.initialMessage ?? legacyInitialMessage;\n\tconst passedItems = params?.items ?? legacyItems ?? [];\n\tconst { website, availableAIAgents, availableHumanAgents, visitor, client } =\n\t\tuseSupport();\n\tconst { navigate, replace, goBack, canGoBack } = useSupportNavigation();\n\tconst { isOpen } = useSupportConfig();\n\tconst text = useSupportText();\n\tconst playNewMessageSound = useNewMessageSound({\n\t\tvolume: 0.7,\n\t\tplaybackRate: 1.0,\n\t});\n\tconst previousItemsRef = useRef<TimelineItem[]>([]);\n\tconst [pendingRating, setPendingRating] = useState<number | null>(null);\n\tconst [isSubmittingRating, setIsSubmittingRating] = useState(false);\n\n\tconst timelineTools = useMemo(\n\t\t() => ({\n\t\t\tidentification: { component: IdentificationTimelineTool },\n\t\t}),\n\t\t[]\n\t);\n\n\t// Main conversation hook - handles all logic\n\tconst conversation = useConversationPage({\n\t\tconversationId: initialConversationId,\n\t\titems: passedItems,\n\t\tinitialMessage,\n\t\tautoSeenEnabled: isOpen,\n\t\tonConversationIdChange: (newConversationId) => {\n\t\t\t// Update navigation when conversation is created\n\t\t\treplace({\n\t\t\t\tpage: \"CONVERSATION\",\n\t\t\t\tparams: { conversationId: newConversationId },\n\t\t\t});\n\t\t},\n\t});\n\n\t// Get conversation from store (no API call) to check status\n\tconst activeConversation = useStoreSelector(\n\t\tclient?.conversationsStore ?? null,\n\t\t(state) =>\n\t\t\tconversation.isPending || !state\n\t\t\t\t? null\n\t\t\t\t: state.byId[conversation.conversationId]\n\t);\n\n\tconst isConversationClosed = Boolean(\n\t\tactiveConversation &&\n\t\t\t(activeConversation.status === ConversationStatus.RESOLVED ||\n\t\t\t\tactiveConversation.status === ConversationStatus.SPAM ||\n\t\t\t\tactiveConversation.deletedAt)\n\t);\n\tconst resolvedRating =\n\t\tactiveConversation?.visitorRating ?? pendingRating ?? null;\n\n\tuseEffect(() => {\n\t\tsetPendingRating(null);\n\t\tsetIsSubmittingRating(false);\n\t}, [activeConversation?.id]);\n\n\tconst handleRateConversation = async (value: number) => {\n\t\tif (!(client && activeConversation)) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (\n\t\t\tactiveConversation.status !== ConversationStatus.RESOLVED ||\n\t\t\tactiveConversation.visitorRating\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (isSubmittingRating) {\n\t\t\treturn;\n\t\t}\n\n\t\tsetPendingRating(value);\n\t\tsetIsSubmittingRating(true);\n\n\t\ttry {\n\t\t\tawait client.submitConversationRating({\n\t\t\t\tconversationId: activeConversation.id,\n\t\t\t\trating: value,\n\t\t\t\tvisitorId: visitor?.id ?? undefined,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[support] Failed to submit rating\", error);\n\t\t\tsetPendingRating(null);\n\t\t} finally {\n\t\t\tsetIsSubmittingRating(false);\n\t\t}\n\t};\n\n\tconst handleGoBack = () => {\n\t\tif (canGoBack) {\n\t\t\tgoBack();\n\t\t} else {\n\t\t\tnavigate({ page: \"HOME\" });\n\t\t}\n\t};\n\n\t// Play sound when new messages arrive from agents (not visitor)\n\tuseEffect(() => {\n\t\tconst currentItems = conversation.items;\n\t\tconst previousItems = previousItemsRef.current;\n\n\t\t// Check if there are new items\n\t\tif (currentItems.length > previousItems.length) {\n\t\t\t// Find the new items\n\t\t\tconst newItems = currentItems.slice(previousItems.length);\n\n\t\t\t// Play sound only if new message is from agent (not visitor)\n\t\t\tfor (const item of newItems) {\n\t\t\t\tif (item.type === \"message\" && !item.visitorId) {\n\t\t\t\t\tplayNewMessageSound();\n\t\t\t\t\tbreak; // Only play once per batch\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Update the ref\n\t\tpreviousItemsRef.current = currentItems;\n\t}, [conversation.items, playNewMessageSound]);\n\n\treturn (\n\t\t<div className=\"flex h-full flex-col gap-0 overflow-hidden\">\n\t\t\t<Header onGoBack={handleGoBack}>\n\t\t\t\t<div className=\"flex w-full items-center justify-between gap-2 py-3\">\n\t\t\t\t\t<div className=\"flex flex-col\">\n\t\t\t\t\t\t<p className=\"font-medium text-co-primary text-sm\">\n\t\t\t\t\t\t\t{website?.name}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<Text\n\t\t\t\t\t\t\tas=\"p\"\n\t\t\t\t\t\t\tclassName=\"text-co-muted-foreground text-sm\"\n\t\t\t\t\t\t\ttextKey=\"common.labels.supportOnline\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t\t<AvatarStack\n\t\t\t\t\t\taiAgents={availableAIAgents}\n\t\t\t\t\t\tgapWidth={2}\n\t\t\t\t\t\thumanAgents={availableHumanAgents}\n\t\t\t\t\t\tsize={32}\n\t\t\t\t\t\tspacing={28}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t</Header>\n\n\t\t\t<ConversationTimelineList\n\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\tclassName=\"min-h-0 flex-1 px-4\"\n\t\t\t\tconversationId={conversation.conversationId}\n\t\t\t\tcurrentVisitorId={visitor?.id}\n\t\t\t\titems={conversation.items}\n\t\t\t\ttools={timelineTools}\n\t\t\t/>\n\n\t\t\t{isConversationClosed ? (\n\t\t\t\t<ConversationResolvedFeedback\n\t\t\t\t\tisSubmitting={isSubmittingRating}\n\t\t\t\t\tonRate={handleRateConversation}\n\t\t\t\t\trating={resolvedRating}\n\t\t\t\t\tstatus={activeConversation?.status ?? null}\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<div className=\"flex-shrink-0 p-1\">\n\t\t\t\t\t<MultimodalInput\n\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\tconversation.composer.isSubmitting ||\n\t\t\t\t\t\t\tconversation.composer.isUploading\n\t\t\t\t\t\t}\n\t\t\t\t\t\terror={conversation.error}\n\t\t\t\t\t\tfiles={conversation.composer.files}\n\t\t\t\t\t\tisSubmitting={conversation.composer.isSubmitting}\n\t\t\t\t\t\tisUploading={conversation.composer.isUploading}\n\t\t\t\t\t\tonChange={conversation.composer.setMessage}\n\t\t\t\t\t\tonFileSelect={conversation.composer.addFiles}\n\t\t\t\t\t\tonRemoveFile={conversation.composer.removeFile}\n\t\t\t\t\t\tonSubmit={conversation.composer.submit}\n\t\t\t\t\t\tplaceholder={text(\"component.multimodalInput.placeholder\")}\n\t\t\t\t\t\tvalue={conversation.composer.message}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"conversation.js","names":["ConversationPage: ConversationPageComponent"],"sources":["../../../src/support/pages/conversation.tsx"],"sourcesContent":["import { ConversationStatus } from \"@cossistant/types\";\nimport type { TimelineItem } from \"@cossistant/types/api/timeline-item\";\nimport { type ReactElement, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useStoreSelector } from \"../../hooks/private/store/use-store-selector\";\nimport { useConversationPage } from \"../../hooks/use-conversation-page\";\nimport { useNewMessageSound } from \"../../hooks/use-new-message-sound\";\nimport { useSupport } from \"../../provider\";\nimport { AvatarStack } from \"../components/avatar-stack\";\nimport { ConversationResolvedFeedback } from \"../components/conversation-resolved-feedback\";\nimport { ConversationTimelineList } from \"../components/conversation-timeline\";\nimport { Header } from \"../components/header\";\nimport { MultimodalInput } from \"../components/multimodal-input\";\nimport { IdentificationTimelineTool } from \"../components/timeline-identification-tool\";\nimport { useSupportConfig, useSupportNavigation } from \"../store\";\nimport { Text, useSupportText } from \"../text\";\n\ntype ConversationPageProps = {\n\t/**\n\t * Page params object (for compatibility with Page component)\n\t */\n\tparams?: {\n\t\t/**\n\t\t * The conversation ID to display (can be PENDING_CONVERSATION_ID or a real ID).\n\t\t */\n\t\tconversationId: string;\n\n\t\t/**\n\t\t * Optional initial message to send when opening the conversation.\n\t\t */\n\t\tinitialMessage?: string;\n\n\t\t/**\n\t\t * Optional timeline items to display (for optimistic updates or initial state).\n\t\t */\n\t\titems?: TimelineItem[];\n\t};\n\n\t// Legacy direct props support (deprecated but maintained for backward compatibility)\n\tconversationId?: string;\n\tinitialMessage?: string;\n\titems?: TimelineItem[];\n};\n\n/**\n * Conversation page with message timeline and input composer.\n */\ntype ConversationPageComponent = (props: ConversationPageProps) => ReactElement;\n\nexport const ConversationPage: ConversationPageComponent = ({\n\tparams,\n\tconversationId: legacyConversationId,\n\tinitialMessage: legacyInitialMessage,\n\titems: legacyItems,\n}: ConversationPageProps) => {\n\t// Support both params object (new) and direct props (legacy)\n\tconst initialConversationId =\n\t\tparams?.conversationId ?? legacyConversationId ?? \"\";\n\tconst initialMessage = params?.initialMessage ?? legacyInitialMessage;\n\tconst passedItems = params?.items ?? legacyItems ?? [];\n\tconst { website, availableAIAgents, availableHumanAgents, visitor, client } =\n\t\tuseSupport();\n\tconst { navigate, replace, goBack, canGoBack } = useSupportNavigation();\n\tconst { isOpen } = useSupportConfig();\n\tconst text = useSupportText();\n\tconst playNewMessageSound = useNewMessageSound({\n\t\tvolume: 0.7,\n\t\tplaybackRate: 1.0,\n\t});\n\tconst previousItemsRef = useRef<TimelineItem[]>([]);\n\tconst [pendingRating, setPendingRating] = useState<number | null>(null);\n\tconst [isSubmittingRating, setIsSubmittingRating] = useState(false);\n\n\tconst timelineTools = useMemo(\n\t\t() => ({\n\t\t\tidentification: { component: IdentificationTimelineTool },\n\t\t}),\n\t\t[]\n\t);\n\n\t// Main conversation hook - handles all logic\n\tconst conversation = useConversationPage({\n\t\tconversationId: initialConversationId,\n\t\titems: passedItems,\n\t\tinitialMessage,\n\t\tautoSeenEnabled: isOpen,\n\t\tonConversationIdChange: (newConversationId) => {\n\t\t\t// Update navigation when conversation is created\n\t\t\treplace({\n\t\t\t\tpage: \"CONVERSATION\",\n\t\t\t\tparams: { conversationId: newConversationId },\n\t\t\t});\n\t\t},\n\t});\n\n\t// Get conversation from store (no API call) to check status\n\tconst activeConversation = useStoreSelector(\n\t\tclient?.conversationsStore ?? null,\n\t\t(state) =>\n\t\t\tconversation.isPending || !state\n\t\t\t\t? null\n\t\t\t\t: state.byId[conversation.conversationId]\n\t);\n\n\tconst isConversationClosed = Boolean(\n\t\tactiveConversation &&\n\t\t\t(activeConversation.status === ConversationStatus.RESOLVED ||\n\t\t\t\tactiveConversation.status === ConversationStatus.SPAM ||\n\t\t\t\tactiveConversation.deletedAt)\n\t);\n\tconst resolvedRating =\n\t\tactiveConversation?.visitorRating ?? pendingRating ?? null;\n\n\tuseEffect(() => {\n\t\tsetPendingRating(null);\n\t\tsetIsSubmittingRating(false);\n\t}, [activeConversation?.id]);\n\n\tconst handleRateConversation = async (value: number, comment?: string) => {\n\t\tif (!(client && activeConversation)) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (\n\t\t\tactiveConversation.status !== ConversationStatus.RESOLVED ||\n\t\t\tactiveConversation.visitorRating\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (isSubmittingRating) {\n\t\t\treturn;\n\t\t}\n\n\t\tsetPendingRating(value);\n\t\tsetIsSubmittingRating(true);\n\n\t\ttry {\n\t\t\tawait client.submitConversationRating({\n\t\t\t\tconversationId: activeConversation.id,\n\t\t\t\trating: value,\n\t\t\t\tcomment,\n\t\t\t\tvisitorId: visitor?.id ?? undefined,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[support] Failed to submit rating\", error);\n\t\t\tsetPendingRating(null);\n\t\t} finally {\n\t\t\tsetIsSubmittingRating(false);\n\t\t}\n\t};\n\n\tconst handleGoBack = () => {\n\t\tif (canGoBack) {\n\t\t\tgoBack();\n\t\t} else {\n\t\t\tnavigate({ page: \"HOME\" });\n\t\t}\n\t};\n\n\t// Play sound when new messages arrive from agents (not visitor)\n\tuseEffect(() => {\n\t\tconst currentItems = conversation.items;\n\t\tconst previousItems = previousItemsRef.current;\n\n\t\t// Check if there are new items\n\t\tif (currentItems.length > previousItems.length) {\n\t\t\t// Find the new items\n\t\t\tconst newItems = currentItems.slice(previousItems.length);\n\n\t\t\t// Play sound only if new message is from agent (not visitor)\n\t\t\tfor (const item of newItems) {\n\t\t\t\tif (item.type === \"message\" && !item.visitorId) {\n\t\t\t\t\tplayNewMessageSound();\n\t\t\t\t\tbreak; // Only play once per batch\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Update the ref\n\t\tpreviousItemsRef.current = currentItems;\n\t}, [conversation.items, playNewMessageSound]);\n\n\treturn (\n\t\t<div className=\"flex h-full flex-col gap-0 overflow-hidden\">\n\t\t\t<Header onGoBack={handleGoBack}>\n\t\t\t\t<div className=\"flex w-full items-center justify-between gap-2 py-3\">\n\t\t\t\t\t<div className=\"flex flex-col\">\n\t\t\t\t\t\t<p className=\"font-medium text-co-primary text-sm\">\n\t\t\t\t\t\t\t{website?.name}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<Text\n\t\t\t\t\t\t\tas=\"p\"\n\t\t\t\t\t\t\tclassName=\"text-co-muted-foreground text-sm\"\n\t\t\t\t\t\t\ttextKey=\"common.labels.supportOnline\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t\t<AvatarStack\n\t\t\t\t\t\taiAgents={availableAIAgents}\n\t\t\t\t\t\tgapWidth={2}\n\t\t\t\t\t\thideDefaultAIAgent={false}\n\t\t\t\t\t\thumanAgents={availableHumanAgents}\n\t\t\t\t\t\tsize={32}\n\t\t\t\t\t\tspacing={28}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t</Header>\n\n\t\t\t<ConversationTimelineList\n\t\t\t\tavailableAIAgents={availableAIAgents}\n\t\t\t\tavailableHumanAgents={availableHumanAgents}\n\t\t\t\tclassName=\"min-h-0 flex-1 px-4\"\n\t\t\t\tconversationId={conversation.conversationId}\n\t\t\t\tcurrentVisitorId={visitor?.id}\n\t\t\t\titems={conversation.items}\n\t\t\t\ttools={timelineTools}\n\t\t\t/>\n\n\t\t\t{isConversationClosed ? (\n\t\t\t\t<ConversationResolvedFeedback\n\t\t\t\t\tisSubmitting={isSubmittingRating}\n\t\t\t\t\tonRate={handleRateConversation}\n\t\t\t\t\trating={resolvedRating}\n\t\t\t\t\tstatus={activeConversation?.status ?? null}\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<div className=\"flex-shrink-0 p-1\">\n\t\t\t\t\t<MultimodalInput\n\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\tconversation.composer.isSubmitting ||\n\t\t\t\t\t\t\tconversation.composer.isUploading\n\t\t\t\t\t\t}\n\t\t\t\t\t\terror={conversation.error}\n\t\t\t\t\t\tfiles={conversation.composer.files}\n\t\t\t\t\t\tisSubmitting={conversation.composer.isSubmitting}\n\t\t\t\t\t\tisUploading={conversation.composer.isUploading}\n\t\t\t\t\t\tonChange={conversation.composer.setMessage}\n\t\t\t\t\t\tonFileSelect={conversation.composer.addFiles}\n\t\t\t\t\t\tonRemoveFile={conversation.composer.removeFile}\n\t\t\t\t\t\tonSubmit={conversation.composer.submit}\n\t\t\t\t\t\tplaceholder={text(\"component.multimodalInput.placeholder\")}\n\t\t\t\t\t\tvalue={conversation.composer.message}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n};\n"],"mappings":"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"}
@@ -1,10 +1,10 @@
1
- import { AvatarStack } from "../components/avatar-stack.js";
2
1
  import { useSupportNavigation } from "../store/support-store.js";
3
2
  import { CoButton } from "../components/button.js";
4
3
  import icons_default from "../components/icons.js";
5
4
  import { PENDING_CONVERSATION_ID } from "../../utils/id.js";
6
5
  import { Header } from "../components/header.js";
7
6
  import { Text, useSupportText } from "../text/index.js";
7
+ import { AvatarStack } from "../components/avatar-stack.js";
8
8
  import { Watermark } from "../components/watermark.js";
9
9
  import { ConversationButtonLink } from "../components/conversation-button-link.js";
10
10
  import { useHomePage } from "../../hooks/use-home-page.js";
@@ -70,6 +70,7 @@ const en = {
70
70
  "component.identificationTool.inputLabel": "Email address",
71
71
  "component.identificationTool.eventLog": "Visitor confirmed their email address",
72
72
  "component.conversationPage.closedMessage": "This conversation is closed, start a new one to talk with us",
73
+ "component.conversationPage.spamMessage": "This conversation was marked as spam.",
73
74
  "component.conversationPage.ratingPrompt": "How did we do?",
74
75
  "component.conversationPage.ratingThanks": "Thanks for your feedback!",
75
76
  "component.conversationPage.ratingLabel": ({ variables, utils }) => {
@@ -79,6 +80,8 @@ const en = {
79
80
  });
80
81
  return `Rate ${variables.rating} ${noun}`;
81
82
  },
83
+ "component.conversationPage.commentPlaceholder": "Tell us more about your experience (optional)",
84
+ "component.conversationPage.submitFeedback": "Submit feedback",
82
85
  "component.multimodalInput.placeholder": "Type your message...",
83
86
  "component.multimodalInput.remove": ({ variables }) => `Remove ${variables.fileName}`,
84
87
  "component.navigation.articles": "Articles",
@@ -1 +1 @@
1
- {"version":3,"file":"en.js","names":["en: SupportLocaleMessages","phrases: Record<typeof period.token, string>"],"sources":["../../../../src/support/text/locales/en.tsx"],"sourcesContent":["import type { SupportLocaleMessages } from \"./keys\";\n\nconst en: SupportLocaleMessages = {\n\t\"common.actions.askQuestion\": \"Ask us a question\",\n\t\"common.actions.attachFiles\": \"Attach files\",\n\t\"common.actions.removeFile\": ({ variables }) =>\n\t\t`Remove ${variables.fileName}`,\n\t\"common.brand.watermark\": \"We run on\",\n\t\"common.fallbacks.aiAssistant\": \"AI assistant\",\n\t\"common.fallbacks.cossistant\": \"Cossistant\",\n\t\"common.fallbacks.someone\": \"Someone\",\n\t\"common.fallbacks.supportTeam\": \"Support\",\n\t\"common.fallbacks.unknown\": \"Unknown\",\n\t\"common.fallbacks.you\": \"You\",\n\t\"common.labels.aiAgentIndicator\": \"AI agent\",\n\t\"common.labels.supportOnline\": \"Support online\",\n\t\"page.conversationHistory.showMore\": ({ variables, utils }) =>\n\t\t`+${utils.formatNumber(variables.count)} more`,\n\t\"page.conversationHistory.title\": \"Conversation history\",\n\t\"page.home.greeting\": ({ variables, context, utils }) => {\n\t\tconst period = utils.timeOfDay();\n\t\tconst phrases: Record<typeof period.token, string> = {\n\t\t\tmorning: \"Morning\",\n\t\t\tafternoon: \"Afternoon\",\n\t\t\tevening: \"Evening\",\n\t\t};\n\t\tconst visitorName =\n\t\t\tvariables?.visitorName || context.visitor?.contact?.name;\n\t\treturn `${phrases[period.token]}${visitorName ? ` ${visitorName}` : \"\"}, how can we help?`;\n\t},\n\t\"page.home.history.more\": ({ variables, utils }) => {\n\t\tconst count = variables.count;\n\t\tconst noun = utils.pluralize(count, {\n\t\t\tone: \"conversation\",\n\t\t\tother: \"conversations\",\n\t\t});\n\t\treturn `+ ${utils.formatNumber(count)} more ${noun}`;\n\t},\n\t\"page.home.tagline\": ({ variables, context, utils }) => {\n\t\tconst websiteName = variables?.websiteName || context.website?.name || \"\";\n\t\tconst formatted = websiteName\n\t\t\t? `${utils.titleCase(websiteName)} support`\n\t\t\t: \"Support\";\n\t\treturn formatted;\n\t},\n\t\"component.conversationButtonLink.fallbackTitle\": \"Untitled conversation\",\n\t\"component.conversationButtonLink.lastMessage.agent\": ({ variables }) =>\n\t\t`${variables.name} - ${variables.time}`,\n\t\"component.conversationButtonLink.lastMessage.visitor\": ({ variables }) =>\n\t\t`You - ${variables.time}`,\n\t\"component.conversationButtonLink.typing\": ({ variables }) =>\n\t\t`${variables.name} is typing...`,\n\t\"component.conversationButtonLink.status.open\": \"Open\",\n\t\"component.conversationButtonLink.status.resolved\": \"Resolved\",\n\t\"component.conversationButtonLink.status.spam\": \"Spam\",\n\t\"component.conversationButtonLink.status.closed\": \"closed\",\n\t\"component.conversationEvent.assigned\": ({ variables }) =>\n\t\t`${variables.actorName} assigned the conversation`,\n\t\"component.conversationEvent.unassigned\": ({ variables }) =>\n\t\t`${variables.actorName} unassigned the conversation`,\n\t\"component.conversationEvent.default\": ({ variables }) =>\n\t\t`${variables.actorName} performed an action`,\n\t\"component.conversationEvent.participantJoined\": ({ variables }) =>\n\t\t`${variables.actorName} joined the conversation`,\n\t\"component.conversationEvent.participantLeft\": ({ variables }) =>\n\t\t`${variables.actorName} left the conversation`,\n\t\"component.conversationEvent.participantRequested\": ({ variables }) =>\n\t\t`${variables.actorName} requested a team member to join`,\n\t\"component.conversationEvent.priorityChanged\": ({ variables }) =>\n\t\t`${variables.actorName} changed the priority`,\n\t\"component.conversationEvent.reopened\": ({ variables }) =>\n\t\t`${variables.actorName} reopened the conversation`,\n\t\"component.conversationEvent.resolved\": ({ variables }) =>\n\t\t`${variables.actorName} resolved the conversation`,\n\t\"component.conversationEvent.statusChanged\": ({ variables }) =>\n\t\t`${variables.actorName} changed the status`,\n\t\"component.conversationEvent.tagAdded\": ({ variables }) =>\n\t\t`${variables.actorName} added a tag`,\n\t\"component.conversationEvent.tagRemoved\": ({ variables }) =>\n\t\t`${variables.actorName} removed a tag`,\n\t\"component.conversationEvent.visitorBlocked\": ({ variables }) =>\n\t\t`${variables.actorName} blocked the visitor`,\n\t\"component.conversationEvent.visitorUnblocked\": ({ variables }) =>\n\t\t`${variables.actorName} unblocked the visitor`,\n\t\"component.conversationEvent.visitorIdentified\": () =>\n\t\t\"Contact details confirmed\",\n\t\"component.identificationTool.title\": \"Let us keep in touch\",\n\t\"component.identificationTool.description\":\n\t\t\"Leave your email so we can follow up on this conversation.\",\n\t\"component.identificationTool.cta\": \"Share email\",\n\t\"component.identificationTool.loading\": \"Saving...\",\n\t\"component.identificationTool.success\":\n\t\t\"Thanks! We'll reach out to you at this email if we need to.\",\n\t\"component.identificationTool.error\":\n\t\t\"We couldn't save your email. Please try again.\",\n\t\"component.identificationTool.validation\":\n\t\t\"Enter an email address to continue.\",\n\t\"component.identificationTool.inputPlaceholder\": \"you@example.com\",\n\t\"component.identificationTool.inputLabel\": \"Email address\",\n\t\"component.identificationTool.eventLog\":\n\t\t\"Visitor confirmed their email address\",\n\t\"component.conversationPage.closedMessage\":\n\t\t\"This conversation is closed, start a new one to talk with us\",\n\t\"component.conversationPage.ratingPrompt\": \"How did we do?\",\n\t\"component.conversationPage.ratingThanks\": \"Thanks for your feedback!\",\n\t\"component.conversationPage.ratingLabel\": ({ variables, utils }) => {\n\t\tconst noun = utils.pluralize(variables.rating, {\n\t\t\tone: \"star\",\n\t\t\tother: \"stars\",\n\t\t});\n\t\treturn `Rate ${variables.rating} ${noun}`;\n\t},\n\t\"component.multimodalInput.placeholder\": \"Type your message...\",\n\t\"component.multimodalInput.remove\": ({ variables }) =>\n\t\t`Remove ${variables.fileName}`,\n\t\"component.navigation.articles\": \"Articles\",\n\t\"component.navigation.home\": \"Home\",\n\t\"component.message.timestamp.aiIndicator\": \"• AI agent\",\n};\n\nexport default en;\n"],"mappings":";AAEA,MAAMA,KAA4B;CACjC,8BAA8B;CAC9B,8BAA8B;CAC9B,8BAA8B,EAAE,gBAC/B,UAAU,UAAU;CACrB,0BAA0B;CAC1B,gCAAgC;CAChC,+BAA+B;CAC/B,4BAA4B;CAC5B,gCAAgC;CAChC,4BAA4B;CAC5B,wBAAwB;CACxB,kCAAkC;CAClC,+BAA+B;CAC/B,sCAAsC,EAAE,WAAW,YAClD,IAAI,MAAM,aAAa,UAAU,MAAM,CAAC;CACzC,kCAAkC;CAClC,uBAAuB,EAAE,WAAW,SAAS,YAAY;EACxD,MAAM,SAAS,MAAM,WAAW;EAChC,MAAMC,UAA+C;GACpD,SAAS;GACT,WAAW;GACX,SAAS;GACT;EACD,MAAM,cACL,WAAW,eAAe,QAAQ,SAAS,SAAS;AACrD,SAAO,GAAG,QAAQ,OAAO,SAAS,cAAc,IAAI,gBAAgB,GAAG;;CAExE,2BAA2B,EAAE,WAAW,YAAY;EACnD,MAAM,QAAQ,UAAU;EACxB,MAAM,OAAO,MAAM,UAAU,OAAO;GACnC,KAAK;GACL,OAAO;GACP,CAAC;AACF,SAAO,KAAK,MAAM,aAAa,MAAM,CAAC,QAAQ;;CAE/C,sBAAsB,EAAE,WAAW,SAAS,YAAY;EACvD,MAAM,cAAc,WAAW,eAAe,QAAQ,SAAS,QAAQ;AAIvE,SAHkB,cACf,GAAG,MAAM,UAAU,YAAY,CAAC,YAChC;;CAGJ,kDAAkD;CAClD,uDAAuD,EAAE,gBACxD,GAAG,UAAU,KAAK,KAAK,UAAU;CAClC,yDAAyD,EAAE,gBAC1D,SAAS,UAAU;CACpB,4CAA4C,EAAE,gBAC7C,GAAG,UAAU,KAAK;CACnB,gDAAgD;CAChD,oDAAoD;CACpD,gDAAgD;CAChD,kDAAkD;CAClD,yCAAyC,EAAE,gBAC1C,GAAG,UAAU,UAAU;CACxB,2CAA2C,EAAE,gBAC5C,GAAG,UAAU,UAAU;CACxB,wCAAwC,EAAE,gBACzC,GAAG,UAAU,UAAU;CACxB,kDAAkD,EAAE,gBACnD,GAAG,UAAU,UAAU;CACxB,gDAAgD,EAAE,gBACjD,GAAG,UAAU,UAAU;CACxB,qDAAqD,EAAE,gBACtD,GAAG,UAAU,UAAU;CACxB,gDAAgD,EAAE,gBACjD,GAAG,UAAU,UAAU;CACxB,yCAAyC,EAAE,gBAC1C,GAAG,UAAU,UAAU;CACxB,yCAAyC,EAAE,gBAC1C,GAAG,UAAU,UAAU;CACxB,8CAA8C,EAAE,gBAC/C,GAAG,UAAU,UAAU;CACxB,yCAAyC,EAAE,gBAC1C,GAAG,UAAU,UAAU;CACxB,2CAA2C,EAAE,gBAC5C,GAAG,UAAU,UAAU;CACxB,+CAA+C,EAAE,gBAChD,GAAG,UAAU,UAAU;CACxB,iDAAiD,EAAE,gBAClD,GAAG,UAAU,UAAU;CACxB,uDACC;CACD,sCAAsC;CACtC,4CACC;CACD,oCAAoC;CACpC,wCAAwC;CACxC,wCACC;CACD,sCACC;CACD,2CACC;CACD,iDAAiD;CACjD,2CAA2C;CAC3C,yCACC;CACD,4CACC;CACD,2CAA2C;CAC3C,2CAA2C;CAC3C,2CAA2C,EAAE,WAAW,YAAY;EACnE,MAAM,OAAO,MAAM,UAAU,UAAU,QAAQ;GAC9C,KAAK;GACL,OAAO;GACP,CAAC;AACF,SAAO,QAAQ,UAAU,OAAO,GAAG;;CAEpC,yCAAyC;CACzC,qCAAqC,EAAE,gBACtC,UAAU,UAAU;CACrB,iCAAiC;CACjC,6BAA6B;CAC7B,2CAA2C;CAC3C;AAED,iBAAe"}
1
+ {"version":3,"file":"en.js","names":["en: SupportLocaleMessages","phrases: Record<typeof period.token, string>"],"sources":["../../../../src/support/text/locales/en.tsx"],"sourcesContent":["import type { SupportLocaleMessages } from \"./keys\";\n\nconst en: SupportLocaleMessages = {\n\t\"common.actions.askQuestion\": \"Ask us a question\",\n\t\"common.actions.attachFiles\": \"Attach files\",\n\t\"common.actions.removeFile\": ({ variables }) =>\n\t\t`Remove ${variables.fileName}`,\n\t\"common.brand.watermark\": \"We run on\",\n\t\"common.fallbacks.aiAssistant\": \"AI assistant\",\n\t\"common.fallbacks.cossistant\": \"Cossistant\",\n\t\"common.fallbacks.someone\": \"Someone\",\n\t\"common.fallbacks.supportTeam\": \"Support\",\n\t\"common.fallbacks.unknown\": \"Unknown\",\n\t\"common.fallbacks.you\": \"You\",\n\t\"common.labels.aiAgentIndicator\": \"AI agent\",\n\t\"common.labels.supportOnline\": \"Support online\",\n\t\"page.conversationHistory.showMore\": ({ variables, utils }) =>\n\t\t`+${utils.formatNumber(variables.count)} more`,\n\t\"page.conversationHistory.title\": \"Conversation history\",\n\t\"page.home.greeting\": ({ variables, context, utils }) => {\n\t\tconst period = utils.timeOfDay();\n\t\tconst phrases: Record<typeof period.token, string> = {\n\t\t\tmorning: \"Morning\",\n\t\t\tafternoon: \"Afternoon\",\n\t\t\tevening: \"Evening\",\n\t\t};\n\t\tconst visitorName =\n\t\t\tvariables?.visitorName || context.visitor?.contact?.name;\n\t\treturn `${phrases[period.token]}${visitorName ? ` ${visitorName}` : \"\"}, how can we help?`;\n\t},\n\t\"page.home.history.more\": ({ variables, utils }) => {\n\t\tconst count = variables.count;\n\t\tconst noun = utils.pluralize(count, {\n\t\t\tone: \"conversation\",\n\t\t\tother: \"conversations\",\n\t\t});\n\t\treturn `+ ${utils.formatNumber(count)} more ${noun}`;\n\t},\n\t\"page.home.tagline\": ({ variables, context, utils }) => {\n\t\tconst websiteName = variables?.websiteName || context.website?.name || \"\";\n\t\tconst formatted = websiteName\n\t\t\t? `${utils.titleCase(websiteName)} support`\n\t\t\t: \"Support\";\n\t\treturn formatted;\n\t},\n\t\"component.conversationButtonLink.fallbackTitle\": \"Untitled conversation\",\n\t\"component.conversationButtonLink.lastMessage.agent\": ({ variables }) =>\n\t\t`${variables.name} - ${variables.time}`,\n\t\"component.conversationButtonLink.lastMessage.visitor\": ({ variables }) =>\n\t\t`You - ${variables.time}`,\n\t\"component.conversationButtonLink.typing\": ({ variables }) =>\n\t\t`${variables.name} is typing...`,\n\t\"component.conversationButtonLink.status.open\": \"Open\",\n\t\"component.conversationButtonLink.status.resolved\": \"Resolved\",\n\t\"component.conversationButtonLink.status.spam\": \"Spam\",\n\t\"component.conversationButtonLink.status.closed\": \"closed\",\n\t\"component.conversationEvent.assigned\": ({ variables }) =>\n\t\t`${variables.actorName} assigned the conversation`,\n\t\"component.conversationEvent.unassigned\": ({ variables }) =>\n\t\t`${variables.actorName} unassigned the conversation`,\n\t\"component.conversationEvent.default\": ({ variables }) =>\n\t\t`${variables.actorName} performed an action`,\n\t\"component.conversationEvent.participantJoined\": ({ variables }) =>\n\t\t`${variables.actorName} joined the conversation`,\n\t\"component.conversationEvent.participantLeft\": ({ variables }) =>\n\t\t`${variables.actorName} left the conversation`,\n\t\"component.conversationEvent.participantRequested\": ({ variables }) =>\n\t\t`${variables.actorName} requested a team member to join`,\n\t\"component.conversationEvent.priorityChanged\": ({ variables }) =>\n\t\t`${variables.actorName} changed the priority`,\n\t\"component.conversationEvent.reopened\": ({ variables }) =>\n\t\t`${variables.actorName} reopened the conversation`,\n\t\"component.conversationEvent.resolved\": ({ variables }) =>\n\t\t`${variables.actorName} resolved the conversation`,\n\t\"component.conversationEvent.statusChanged\": ({ variables }) =>\n\t\t`${variables.actorName} changed the status`,\n\t\"component.conversationEvent.tagAdded\": ({ variables }) =>\n\t\t`${variables.actorName} added a tag`,\n\t\"component.conversationEvent.tagRemoved\": ({ variables }) =>\n\t\t`${variables.actorName} removed a tag`,\n\t\"component.conversationEvent.visitorBlocked\": ({ variables }) =>\n\t\t`${variables.actorName} blocked the visitor`,\n\t\"component.conversationEvent.visitorUnblocked\": ({ variables }) =>\n\t\t`${variables.actorName} unblocked the visitor`,\n\t\"component.conversationEvent.visitorIdentified\": () =>\n\t\t\"Contact details confirmed\",\n\t\"component.identificationTool.title\": \"Let us keep in touch\",\n\t\"component.identificationTool.description\":\n\t\t\"Leave your email so we can follow up on this conversation.\",\n\t\"component.identificationTool.cta\": \"Share email\",\n\t\"component.identificationTool.loading\": \"Saving...\",\n\t\"component.identificationTool.success\":\n\t\t\"Thanks! We'll reach out to you at this email if we need to.\",\n\t\"component.identificationTool.error\":\n\t\t\"We couldn't save your email. Please try again.\",\n\t\"component.identificationTool.validation\":\n\t\t\"Enter an email address to continue.\",\n\t\"component.identificationTool.inputPlaceholder\": \"you@example.com\",\n\t\"component.identificationTool.inputLabel\": \"Email address\",\n\t\"component.identificationTool.eventLog\":\n\t\t\"Visitor confirmed their email address\",\n\t\"component.conversationPage.closedMessage\":\n\t\t\"This conversation is closed, start a new one to talk with us\",\n\t\"component.conversationPage.spamMessage\":\n\t\t\"This conversation was marked as spam.\",\n\t\"component.conversationPage.ratingPrompt\": \"How did we do?\",\n\t\"component.conversationPage.ratingThanks\": \"Thanks for your feedback!\",\n\t\"component.conversationPage.ratingLabel\": ({ variables, utils }) => {\n\t\tconst noun = utils.pluralize(variables.rating, {\n\t\t\tone: \"star\",\n\t\t\tother: \"stars\",\n\t\t});\n\t\treturn `Rate ${variables.rating} ${noun}`;\n\t},\n\t\"component.conversationPage.commentPlaceholder\":\n\t\t\"Tell us more about your experience (optional)\",\n\t\"component.conversationPage.submitFeedback\": \"Submit feedback\",\n\t\"component.multimodalInput.placeholder\": \"Type your message...\",\n\t\"component.multimodalInput.remove\": ({ variables }) =>\n\t\t`Remove ${variables.fileName}`,\n\t\"component.navigation.articles\": \"Articles\",\n\t\"component.navigation.home\": \"Home\",\n\t\"component.message.timestamp.aiIndicator\": \"• AI agent\",\n};\n\nexport default en;\n"],"mappings":";AAEA,MAAMA,KAA4B;CACjC,8BAA8B;CAC9B,8BAA8B;CAC9B,8BAA8B,EAAE,gBAC/B,UAAU,UAAU;CACrB,0BAA0B;CAC1B,gCAAgC;CAChC,+BAA+B;CAC/B,4BAA4B;CAC5B,gCAAgC;CAChC,4BAA4B;CAC5B,wBAAwB;CACxB,kCAAkC;CAClC,+BAA+B;CAC/B,sCAAsC,EAAE,WAAW,YAClD,IAAI,MAAM,aAAa,UAAU,MAAM,CAAC;CACzC,kCAAkC;CAClC,uBAAuB,EAAE,WAAW,SAAS,YAAY;EACxD,MAAM,SAAS,MAAM,WAAW;EAChC,MAAMC,UAA+C;GACpD,SAAS;GACT,WAAW;GACX,SAAS;GACT;EACD,MAAM,cACL,WAAW,eAAe,QAAQ,SAAS,SAAS;AACrD,SAAO,GAAG,QAAQ,OAAO,SAAS,cAAc,IAAI,gBAAgB,GAAG;;CAExE,2BAA2B,EAAE,WAAW,YAAY;EACnD,MAAM,QAAQ,UAAU;EACxB,MAAM,OAAO,MAAM,UAAU,OAAO;GACnC,KAAK;GACL,OAAO;GACP,CAAC;AACF,SAAO,KAAK,MAAM,aAAa,MAAM,CAAC,QAAQ;;CAE/C,sBAAsB,EAAE,WAAW,SAAS,YAAY;EACvD,MAAM,cAAc,WAAW,eAAe,QAAQ,SAAS,QAAQ;AAIvE,SAHkB,cACf,GAAG,MAAM,UAAU,YAAY,CAAC,YAChC;;CAGJ,kDAAkD;CAClD,uDAAuD,EAAE,gBACxD,GAAG,UAAU,KAAK,KAAK,UAAU;CAClC,yDAAyD,EAAE,gBAC1D,SAAS,UAAU;CACpB,4CAA4C,EAAE,gBAC7C,GAAG,UAAU,KAAK;CACnB,gDAAgD;CAChD,oDAAoD;CACpD,gDAAgD;CAChD,kDAAkD;CAClD,yCAAyC,EAAE,gBAC1C,GAAG,UAAU,UAAU;CACxB,2CAA2C,EAAE,gBAC5C,GAAG,UAAU,UAAU;CACxB,wCAAwC,EAAE,gBACzC,GAAG,UAAU,UAAU;CACxB,kDAAkD,EAAE,gBACnD,GAAG,UAAU,UAAU;CACxB,gDAAgD,EAAE,gBACjD,GAAG,UAAU,UAAU;CACxB,qDAAqD,EAAE,gBACtD,GAAG,UAAU,UAAU;CACxB,gDAAgD,EAAE,gBACjD,GAAG,UAAU,UAAU;CACxB,yCAAyC,EAAE,gBAC1C,GAAG,UAAU,UAAU;CACxB,yCAAyC,EAAE,gBAC1C,GAAG,UAAU,UAAU;CACxB,8CAA8C,EAAE,gBAC/C,GAAG,UAAU,UAAU;CACxB,yCAAyC,EAAE,gBAC1C,GAAG,UAAU,UAAU;CACxB,2CAA2C,EAAE,gBAC5C,GAAG,UAAU,UAAU;CACxB,+CAA+C,EAAE,gBAChD,GAAG,UAAU,UAAU;CACxB,iDAAiD,EAAE,gBAClD,GAAG,UAAU,UAAU;CACxB,uDACC;CACD,sCAAsC;CACtC,4CACC;CACD,oCAAoC;CACpC,wCAAwC;CACxC,wCACC;CACD,sCACC;CACD,2CACC;CACD,iDAAiD;CACjD,2CAA2C;CAC3C,yCACC;CACD,4CACC;CACD,0CACC;CACD,2CAA2C;CAC3C,2CAA2C;CAC3C,2CAA2C,EAAE,WAAW,YAAY;EACnE,MAAM,OAAO,MAAM,UAAU,UAAU,QAAQ;GAC9C,KAAK;GACL,OAAO;GACP,CAAC;AACF,SAAO,QAAQ,UAAU,OAAO,GAAG;;CAEpC,iDACC;CACD,6CAA6C;CAC7C,yCAAyC;CACzC,qCAAqC,EAAE,gBACtC,UAAU,UAAU;CACrB,iCAAiC;CACjC,6BAA6B;CAC7B,2CAA2C;CAC3C;AAED,iBAAe"}
@@ -69,6 +69,7 @@ const es = {
69
69
  "component.identificationTool.inputLabel": "Correo electrónico",
70
70
  "component.identificationTool.eventLog": "El visitante confirmó su correo electrónico",
71
71
  "component.conversationPage.closedMessage": "Esta conversación está cerrada, inicia una nueva para hablar con nosotros",
72
+ "component.conversationPage.spamMessage": "Esta conversación fue marcada como spam.",
72
73
  "component.conversationPage.ratingPrompt": "¿Cómo lo hicimos?",
73
74
  "component.conversationPage.ratingThanks": "¡Gracias por tu comentario!",
74
75
  "component.conversationPage.ratingLabel": ({ variables, utils }) => {
@@ -78,6 +79,8 @@ const es = {
78
79
  });
79
80
  return `Calificar con ${variables.rating} ${noun}`;
80
81
  },
82
+ "component.conversationPage.commentPlaceholder": "Cuéntanos más sobre tu experiencia (opcional)",
83
+ "component.conversationPage.submitFeedback": "Enviar comentario",
81
84
  "component.multimodalInput.placeholder": "Escribe tu mensaje...",
82
85
  "component.multimodalInput.remove": ({ variables }) => `Eliminar ${variables.fileName}`,
83
86
  "component.navigation.articles": "Artículos",