@neuronection/assistant-ui 0.23.0 → 0.24.0

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 (155) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/README.md +9 -0
  3. package/dist/about.js +1 -1
  4. package/dist/ai-magic-fill.js +1 -1
  5. package/dist/chat-branch-tree.d.ts +45 -0
  6. package/dist/chat-branch-tree.js +3 -0
  7. package/dist/chat-branch-tree.js.map +1 -0
  8. package/dist/chat-composer.d.ts +50 -0
  9. package/dist/chat-composer.js +3 -0
  10. package/dist/chat-composer.js.map +1 -0
  11. package/dist/chat-core.d.ts +154 -0
  12. package/dist/chat-core.js +4 -0
  13. package/dist/chat-core.js.map +1 -0
  14. package/dist/chat-drawer.d.ts +32 -0
  15. package/dist/chat-drawer.js +3 -0
  16. package/dist/chat-drawer.js.map +1 -0
  17. package/dist/chat-launcher.d.ts +28 -0
  18. package/dist/chat-launcher.js +3 -0
  19. package/dist/chat-launcher.js.map +1 -0
  20. package/dist/chat-markdown.d.ts +64 -0
  21. package/dist/chat-markdown.js +3 -0
  22. package/dist/chat-markdown.js.map +1 -0
  23. package/dist/chat-message.d.ts +108 -0
  24. package/dist/chat-message.js +3 -0
  25. package/dist/chat-message.js.map +1 -0
  26. package/dist/chat-panel.d.ts +28 -0
  27. package/dist/chat-panel.js +3 -0
  28. package/dist/chat-panel.js.map +1 -0
  29. package/dist/chat-reasoning.d.ts +27 -0
  30. package/dist/chat-reasoning.js +3 -0
  31. package/dist/chat-reasoning.js.map +1 -0
  32. package/dist/chat-session-list.d.ts +51 -0
  33. package/dist/chat-session-list.js +3 -0
  34. package/dist/chat-session-list.js.map +1 -0
  35. package/dist/chat-tool-card.d.ts +37 -0
  36. package/dist/chat-tool-card.js +3 -0
  37. package/dist/chat-tool-card.js.map +1 -0
  38. package/dist/chat-transcript.d.ts +33 -0
  39. package/dist/chat-transcript.js +3 -0
  40. package/dist/chat-transcript.js.map +1 -0
  41. package/dist/{chunk-CZ2BL2ZL.js → chunk-3JJ2WCPN.js} +2 -2
  42. package/dist/{chunk-CZ2BL2ZL.js.map → chunk-3JJ2WCPN.js.map} +1 -1
  43. package/dist/chunk-5IOLDMDY.js +3 -0
  44. package/dist/chunk-5IOLDMDY.js.map +1 -0
  45. package/dist/{chunk-EODV6JK7.js → chunk-5QTHDB3D.js} +3 -3
  46. package/dist/{chunk-EODV6JK7.js.map → chunk-5QTHDB3D.js.map} +1 -1
  47. package/dist/chunk-6E6RRLKU.js +3 -0
  48. package/dist/chunk-6E6RRLKU.js.map +1 -0
  49. package/dist/chunk-6GNLY2EB.js +272 -0
  50. package/dist/chunk-6GNLY2EB.js.map +1 -0
  51. package/dist/chunk-743FJDRA.js +184 -0
  52. package/dist/chunk-743FJDRA.js.map +1 -0
  53. package/dist/chunk-BSIWLVDX.js +217 -0
  54. package/dist/chunk-BSIWLVDX.js.map +1 -0
  55. package/dist/chunk-BVJT67B5.js +134 -0
  56. package/dist/chunk-BVJT67B5.js.map +1 -0
  57. package/dist/chunk-DZBTJZZV.js +201 -0
  58. package/dist/chunk-DZBTJZZV.js.map +1 -0
  59. package/dist/chunk-HQIYSW2D.js +131 -0
  60. package/dist/chunk-HQIYSW2D.js.map +1 -0
  61. package/dist/chunk-JPDD5AFN.js +119 -0
  62. package/dist/chunk-JPDD5AFN.js.map +1 -0
  63. package/dist/chunk-LSCWBNG3.js +3 -0
  64. package/dist/chunk-LSCWBNG3.js.map +1 -0
  65. package/dist/chunk-NKYPNEOX.js +198 -0
  66. package/dist/chunk-NKYPNEOX.js.map +1 -0
  67. package/dist/{chunk-INMAUGBK.js → chunk-OH7AJ3WY.js} +3 -3
  68. package/dist/{chunk-INMAUGBK.js.map → chunk-OH7AJ3WY.js.map} +1 -1
  69. package/dist/chunk-PAQ6YQ7Y.js +3 -0
  70. package/dist/chunk-PAQ6YQ7Y.js.map +1 -0
  71. package/dist/chunk-QVDCFZSE.js +72 -0
  72. package/dist/chunk-QVDCFZSE.js.map +1 -0
  73. package/dist/chunk-RPLEI6LQ.js +102 -0
  74. package/dist/chunk-RPLEI6LQ.js.map +1 -0
  75. package/dist/chunk-S62VJVWB.js +36 -0
  76. package/dist/chunk-S62VJVWB.js.map +1 -0
  77. package/dist/chunk-X3C2FBMR.js +141 -0
  78. package/dist/chunk-X3C2FBMR.js.map +1 -0
  79. package/dist/{chunk-7YLR4AGA.js → chunk-XZWLTTHA.js} +2 -2
  80. package/dist/{chunk-7YLR4AGA.js.map → chunk-XZWLTTHA.js.map} +1 -1
  81. package/dist/chunk-Z4IBQ53C.js +75 -0
  82. package/dist/chunk-Z4IBQ53C.js.map +1 -0
  83. package/dist/{chunk-7KD4MG75.js → chunk-ZK2V2LPA.js} +3 -3
  84. package/dist/chunk-ZK2V2LPA.js.map +1 -0
  85. package/dist/copy-button.js +1 -1
  86. package/dist/fonts/KaTeX_AMS-Regular.ttf +0 -0
  87. package/dist/fonts/KaTeX_AMS-Regular.woff +0 -0
  88. package/dist/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  89. package/dist/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  90. package/dist/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  91. package/dist/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  92. package/dist/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  93. package/dist/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  94. package/dist/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  95. package/dist/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  96. package/dist/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  97. package/dist/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  98. package/dist/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  99. package/dist/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  100. package/dist/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  101. package/dist/fonts/KaTeX_Main-Bold.ttf +0 -0
  102. package/dist/fonts/KaTeX_Main-Bold.woff +0 -0
  103. package/dist/fonts/KaTeX_Main-Bold.woff2 +0 -0
  104. package/dist/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  105. package/dist/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  106. package/dist/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  107. package/dist/fonts/KaTeX_Main-Italic.ttf +0 -0
  108. package/dist/fonts/KaTeX_Main-Italic.woff +0 -0
  109. package/dist/fonts/KaTeX_Main-Italic.woff2 +0 -0
  110. package/dist/fonts/KaTeX_Main-Regular.ttf +0 -0
  111. package/dist/fonts/KaTeX_Main-Regular.woff +0 -0
  112. package/dist/fonts/KaTeX_Main-Regular.woff2 +0 -0
  113. package/dist/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  114. package/dist/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  115. package/dist/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  116. package/dist/fonts/KaTeX_Math-Italic.ttf +0 -0
  117. package/dist/fonts/KaTeX_Math-Italic.woff +0 -0
  118. package/dist/fonts/KaTeX_Math-Italic.woff2 +0 -0
  119. package/dist/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  120. package/dist/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  121. package/dist/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  122. package/dist/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  123. package/dist/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  124. package/dist/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  125. package/dist/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  126. package/dist/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  127. package/dist/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  128. package/dist/fonts/KaTeX_Script-Regular.ttf +0 -0
  129. package/dist/fonts/KaTeX_Script-Regular.woff +0 -0
  130. package/dist/fonts/KaTeX_Script-Regular.woff2 +0 -0
  131. package/dist/fonts/KaTeX_Size1-Regular.ttf +0 -0
  132. package/dist/fonts/KaTeX_Size1-Regular.woff +0 -0
  133. package/dist/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  134. package/dist/fonts/KaTeX_Size2-Regular.ttf +0 -0
  135. package/dist/fonts/KaTeX_Size2-Regular.woff +0 -0
  136. package/dist/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  137. package/dist/fonts/KaTeX_Size3-Regular.ttf +0 -0
  138. package/dist/fonts/KaTeX_Size3-Regular.woff +0 -0
  139. package/dist/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  140. package/dist/fonts/KaTeX_Size4-Regular.ttf +0 -0
  141. package/dist/fonts/KaTeX_Size4-Regular.woff +0 -0
  142. package/dist/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  143. package/dist/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  144. package/dist/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  145. package/dist/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  146. package/dist/index.d.ts +15 -0
  147. package/dist/index.js +39 -26
  148. package/dist/index.js.map +1 -1
  149. package/dist/portal.js +1 -1
  150. package/dist/settings-shell.js +1 -1
  151. package/dist/styles.css +1 -1
  152. package/dist/tree-YaIz73vo.d.ts +54 -0
  153. package/dist/types-DZwzmyKE.d.ts +44 -0
  154. package/package.json +56 -2
  155. package/dist/chunk-7KD4MG75.js.map +0 -1
@@ -0,0 +1,184 @@
1
+ import { cn } from './chunk-BLJWR4ZV.js';
2
+ import * as React from 'react';
3
+ import { Square, ArrowUp } from 'lucide-react';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
+
6
+ function isIMEComposition(event) {
7
+ return event.nativeEvent.isComposing || event.keyCode === 229;
8
+ }
9
+ var ChatComposer = React.forwardRef(
10
+ function ChatComposer2({
11
+ value,
12
+ onValueChange,
13
+ onSubmit,
14
+ sending = false,
15
+ onStop,
16
+ disabled = false,
17
+ placeholder,
18
+ ariaLabel,
19
+ maxRows = 8,
20
+ toolbarStart,
21
+ toolbarEnd,
22
+ attachments,
23
+ suggestions,
24
+ onAttachFiles,
25
+ labels,
26
+ icons,
27
+ textareaRef,
28
+ className
29
+ }, ref) {
30
+ const internalTextareaRef = React.useRef(null);
31
+ const [dragging, setDragging] = React.useState(false);
32
+ const dragDepth = React.useRef(0);
33
+ const setTextarea = (element) => {
34
+ internalTextareaRef.current = element;
35
+ if (typeof textareaRef === "function") {
36
+ textareaRef(element);
37
+ } else if (textareaRef && typeof textareaRef === "object") {
38
+ textareaRef.current = element;
39
+ }
40
+ };
41
+ React.useLayoutEffect(() => {
42
+ const element = internalTextareaRef.current;
43
+ if (element === null) {
44
+ return;
45
+ }
46
+ element.style.height = "auto";
47
+ const cap = maxRows * 22;
48
+ element.style.height = element.scrollHeight > 0 ? `${Math.min(element.scrollHeight, cap)}px` : "auto";
49
+ element.style.overflowY = element.scrollHeight > cap ? "auto" : "hidden";
50
+ }, [value, maxRows]);
51
+ const submit = () => {
52
+ if (disabled || sending) {
53
+ return;
54
+ }
55
+ onSubmit();
56
+ };
57
+ const onKeyDown = (event) => {
58
+ if (event.key !== "Enter" || event.shiftKey) {
59
+ return;
60
+ }
61
+ if (isIMEComposition(event)) {
62
+ return;
63
+ }
64
+ event.preventDefault();
65
+ submit();
66
+ };
67
+ const emitFiles = (fileList) => {
68
+ const files = fileList ? Array.from(fileList) : [];
69
+ if (files.length > 0) {
70
+ onAttachFiles?.(files);
71
+ }
72
+ };
73
+ const dragCounterHandlers = {
74
+ onDragEnter: (event) => {
75
+ if (onAttachFiles === void 0 || !event.dataTransfer.types.includes("Files")) {
76
+ return;
77
+ }
78
+ event.preventDefault();
79
+ dragDepth.current += 1;
80
+ setDragging(true);
81
+ },
82
+ onDragOver: (event) => {
83
+ if (onAttachFiles === void 0 || !event.dataTransfer.types.includes("Files")) {
84
+ return;
85
+ }
86
+ event.preventDefault();
87
+ },
88
+ onDragLeave: () => {
89
+ if (dragDepth.current > 0) {
90
+ dragDepth.current -= 1;
91
+ if (dragDepth.current === 0) {
92
+ setDragging(false);
93
+ }
94
+ }
95
+ },
96
+ onDrop: (event) => {
97
+ if (onAttachFiles === void 0) {
98
+ return;
99
+ }
100
+ event.preventDefault();
101
+ dragDepth.current = 0;
102
+ setDragging(false);
103
+ emitFiles(event.dataTransfer.files);
104
+ }
105
+ };
106
+ const SendIcon = icons?.send ?? ArrowUp;
107
+ const StopIcon = icons?.stop ?? Square;
108
+ const showStop = sending && onStop !== void 0;
109
+ return /* @__PURE__ */ jsxs(
110
+ "form",
111
+ {
112
+ ref,
113
+ "data-as": "chat-composer",
114
+ "data-sending": sending,
115
+ onSubmit: (event) => {
116
+ event.preventDefault();
117
+ submit();
118
+ },
119
+ className: cn("flex w-full flex-col gap-2", className),
120
+ ...dragCounterHandlers,
121
+ children: [
122
+ suggestions ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: suggestions }) : null,
123
+ attachments ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: attachments }) : null,
124
+ /* @__PURE__ */ jsxs(
125
+ "div",
126
+ {
127
+ "data-dragging": dragging || void 0,
128
+ className: cn(
129
+ "flex items-end gap-1.5 rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface)] px-2 py-1.5 transition-colors focus-within:border-[var(--as-primary)]",
130
+ dragging && "border-dashed border-[var(--as-primary)] bg-[var(--as-primary)]/5",
131
+ disabled && "opacity-50"
132
+ ),
133
+ children: [
134
+ toolbarStart ? /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-0.5 pb-0.5", children: toolbarStart }) : null,
135
+ /* @__PURE__ */ jsx(
136
+ "textarea",
137
+ {
138
+ ref: setTextarea,
139
+ value,
140
+ rows: 1,
141
+ disabled,
142
+ placeholder: placeholder ?? labels?.placeholder ?? "Send a message\u2026",
143
+ "aria-label": ariaLabel ?? labels?.ariaLabel ?? "Message",
144
+ onChange: (event) => onValueChange(event.target.value),
145
+ onKeyDown,
146
+ onPaste: (event) => emitFiles(event.clipboardData.files),
147
+ className: "max-h-40 min-h-[1.5rem] w-full flex-1 resize-none bg-transparent px-1 py-1 text-sm leading-relaxed text-[var(--as-fg)] placeholder:text-[var(--as-muted-fg)] focus-visible:outline-none disabled:cursor-not-allowed"
148
+ }
149
+ ),
150
+ toolbarEnd ? /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-0.5 pb-0.5", children: toolbarEnd }) : null,
151
+ showStop ? /* @__PURE__ */ jsx(
152
+ "button",
153
+ {
154
+ type: "button",
155
+ "aria-label": labels?.stop ?? "Stop generating",
156
+ title: labels?.stop ?? "Stop generating",
157
+ onClick: onStop,
158
+ className: "mb-0.5 flex size-7 shrink-0 items-center justify-center rounded-full border border-[var(--as-border)] bg-[var(--as-surface-raised)] text-[var(--as-fg)] transition-colors hover:bg-[var(--as-secondary)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
159
+ children: /* @__PURE__ */ jsx(StopIcon, { className: "size-3.5", "aria-hidden": true })
160
+ }
161
+ ) : /* @__PURE__ */ jsx(
162
+ "button",
163
+ {
164
+ type: "submit",
165
+ "aria-label": labels?.send ?? "Send message",
166
+ title: labels?.send ?? "Send message",
167
+ disabled: disabled || value.trim() === "" || sending,
168
+ className: "mb-0.5 flex size-7 shrink-0 items-center justify-center rounded-full bg-[var(--as-primary)] text-[var(--as-primary-fg)] transition-opacity hover:opacity-90 disabled:pointer-events-none disabled:opacity-30 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
169
+ children: /* @__PURE__ */ jsx(SendIcon, { className: "size-4", "aria-hidden": true })
170
+ }
171
+ )
172
+ ]
173
+ }
174
+ ),
175
+ dragging && onAttachFiles ? /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--as-muted-fg)]", role: "status", children: labels?.dropFiles ?? "Drop files to attach" }) : null
176
+ ]
177
+ }
178
+ );
179
+ }
180
+ );
181
+
182
+ export { ChatComposer };
183
+ //# sourceMappingURL=chunk-743FJDRA.js.map
184
+ //# sourceMappingURL=chunk-743FJDRA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/chat-composer/ChatComposer.tsx"],"names":["ChatComposer"],"mappings":"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","file":"chunk-743FJDRA.js","sourcesContent":["import * as React from 'react'\nimport { ArrowUp, Square } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\n\nimport { cn } from '../../lib/utils'\n\nexport interface ChatComposerLabels {\n send: string\n stop: string\n placeholder: string\n ariaLabel: string\n dropFiles: string\n}\n\nexport interface ChatComposerIcons {\n send: LucideIcon\n stop: LucideIcon\n}\n\nexport interface ChatComposerProps {\n value: string\n onValueChange: (value: string) => void\n /** Enter / Send — IME-safe (composition and keyCode 229 are ignored). */\n onSubmit: () => void\n /** A turn is in flight — renders the stop control when `onStop` is provided. */\n sending?: boolean\n onStop?: () => void\n disabled?: boolean\n placeholder?: string\n ariaLabel?: string\n /** Auto-grow cap before the textarea scrolls. Default 8. */\n maxRows?: number\n /** Leading slot inside the input row (attach menu, equation, draw…). */\n toolbarStart?: React.ReactNode\n /** Trailing slot inside the input row (dictation, tools…). */\n toolbarEnd?: React.ReactNode\n /** Attachment rail above the input (usually `FileQueue` / chips). */\n attachments?: React.ReactNode\n /** Suggestion chips / context strip above everything. */\n suggestions?: React.ReactNode\n /** Drag-drop + paste wiring for file attachments. */\n onAttachFiles?: (files: File[]) => void\n labels?: Partial<ChatComposerLabels>\n icons?: Partial<ChatComposerIcons>\n textareaRef?: React.Ref<HTMLTextAreaElement>\n className?: string\n}\n\nfunction isIMEComposition(event: React.KeyboardEvent<HTMLTextAreaElement>): boolean {\n return event.nativeEvent.isComposing || event.keyCode === 229\n}\n\n/**\n * The family chat input: auto-growing IME-safe textarea, send/stop\n * states, attachment/dictation/toolbar slots, drag-drop + paste file\n * wiring. Presentational + controlled; transports stay app-side.\n */\nexport const ChatComposer = React.forwardRef<HTMLFormElement, ChatComposerProps>(\n function ChatComposer(\n {\n value,\n onValueChange,\n onSubmit,\n sending = false,\n onStop,\n disabled = false,\n placeholder,\n ariaLabel,\n maxRows = 8,\n toolbarStart,\n toolbarEnd,\n attachments,\n suggestions,\n onAttachFiles,\n labels,\n icons,\n textareaRef,\n className,\n },\n ref,\n ) {\n const internalTextareaRef = React.useRef<HTMLTextAreaElement | null>(null)\n const [dragging, setDragging] = React.useState(false)\n const dragDepth = React.useRef(0)\n\n const setTextarea = (element: HTMLTextAreaElement | null) => {\n internalTextareaRef.current = element\n if (typeof textareaRef === 'function') {\n textareaRef(element)\n } else if (textareaRef && typeof textareaRef === 'object') {\n ;(textareaRef as React.MutableRefObject<HTMLTextAreaElement | null>).current = element\n }\n }\n\n React.useLayoutEffect(() => {\n const element = internalTextareaRef.current\n if (element === null) {\n return\n }\n element.style.height = 'auto'\n const cap = maxRows * 22\n element.style.height = element.scrollHeight > 0 ? `${Math.min(element.scrollHeight, cap)}px` : 'auto'\n element.style.overflowY = element.scrollHeight > cap ? 'auto' : 'hidden'\n }, [value, maxRows])\n\n const submit = () => {\n if (disabled || sending) {\n return\n }\n onSubmit()\n }\n\n const onKeyDown: React.KeyboardEventHandler<HTMLTextAreaElement> = (event) => {\n if (event.key !== 'Enter' || event.shiftKey) {\n return\n }\n if (isIMEComposition(event)) {\n return\n }\n event.preventDefault()\n submit()\n }\n\n const emitFiles = (fileList: FileList | null | undefined) => {\n const files = fileList ? Array.from(fileList) : []\n if (files.length > 0) {\n onAttachFiles?.(files)\n }\n }\n\n const dragCounterHandlers = {\n onDragEnter: (event: React.DragEvent) => {\n if (onAttachFiles === undefined || !event.dataTransfer.types.includes('Files')) {\n return\n }\n event.preventDefault()\n dragDepth.current += 1\n setDragging(true)\n },\n onDragOver: (event: React.DragEvent) => {\n if (onAttachFiles === undefined || !event.dataTransfer.types.includes('Files')) {\n return\n }\n event.preventDefault()\n },\n onDragLeave: () => {\n if (dragDepth.current > 0) {\n dragDepth.current -= 1\n if (dragDepth.current === 0) {\n setDragging(false)\n }\n }\n },\n onDrop: (event: React.DragEvent) => {\n if (onAttachFiles === undefined) {\n return\n }\n event.preventDefault()\n dragDepth.current = 0\n setDragging(false)\n emitFiles(event.dataTransfer.files)\n },\n }\n\n const SendIcon = icons?.send ?? ArrowUp\n const StopIcon = icons?.stop ?? Square\n const showStop = sending && onStop !== undefined\n\n return (\n <form\n ref={ref}\n data-as=\"chat-composer\"\n data-sending={sending}\n onSubmit={(event) => {\n event.preventDefault()\n submit()\n }}\n className={cn('flex w-full flex-col gap-2', className)}\n {...dragCounterHandlers}\n >\n {suggestions ? <div className=\"flex flex-wrap gap-1.5\">{suggestions}</div> : null}\n {attachments ? <div className=\"flex flex-wrap gap-1.5\">{attachments}</div> : null}\n <div\n data-dragging={dragging || undefined}\n className={cn(\n 'flex items-end gap-1.5 rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface)] px-2 py-1.5 transition-colors focus-within:border-[var(--as-primary)]',\n dragging && 'border-dashed border-[var(--as-primary)] bg-[var(--as-primary)]/5',\n disabled && 'opacity-50',\n )}\n >\n {toolbarStart ? <div className=\"flex shrink-0 items-center gap-0.5 pb-0.5\">{toolbarStart}</div> : null}\n <textarea\n ref={setTextarea}\n value={value}\n rows={1}\n disabled={disabled}\n placeholder={placeholder ?? labels?.placeholder ?? 'Send a message…'}\n aria-label={ariaLabel ?? labels?.ariaLabel ?? 'Message'}\n onChange={(event) => onValueChange(event.target.value)}\n onKeyDown={onKeyDown}\n onPaste={(event) => emitFiles(event.clipboardData.files)}\n className=\"max-h-40 min-h-[1.5rem] w-full flex-1 resize-none bg-transparent px-1 py-1 text-sm leading-relaxed text-[var(--as-fg)] placeholder:text-[var(--as-muted-fg)] focus-visible:outline-none disabled:cursor-not-allowed\"\n />\n {toolbarEnd ? <div className=\"flex shrink-0 items-center gap-0.5 pb-0.5\">{toolbarEnd}</div> : null}\n {showStop ? (\n <button\n type=\"button\"\n aria-label={labels?.stop ?? 'Stop generating'}\n title={labels?.stop ?? 'Stop generating'}\n onClick={onStop}\n className=\"mb-0.5 flex size-7 shrink-0 items-center justify-center rounded-full border border-[var(--as-border)] bg-[var(--as-surface-raised)] text-[var(--as-fg)] transition-colors hover:bg-[var(--as-secondary)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <StopIcon className=\"size-3.5\" aria-hidden />\n </button>\n ) : (\n <button\n type=\"submit\"\n aria-label={labels?.send ?? 'Send message'}\n title={labels?.send ?? 'Send message'}\n disabled={disabled || value.trim() === '' || sending}\n className=\"mb-0.5 flex size-7 shrink-0 items-center justify-center rounded-full bg-[var(--as-primary)] text-[var(--as-primary-fg)] transition-opacity hover:opacity-90 disabled:pointer-events-none disabled:opacity-30 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <SendIcon className=\"size-4\" aria-hidden />\n </button>\n )}\n </div>\n {dragging && onAttachFiles ? (\n <p className=\"text-xs text-[var(--as-muted-fg)]\" role=\"status\">\n {labels?.dropFiles ?? 'Drop files to attach'}\n </p>\n ) : null}\n </form>\n )\n },\n)\n"]}
@@ -0,0 +1,217 @@
1
+ import { Button } from './chunk-GTXGTQ6F.js';
2
+ import { cn } from './chunk-BLJWR4ZV.js';
3
+ import * as React from 'react';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
+ import { ChevronLeft, ChevronRight, Copy, Pencil, RotateCcw, TriangleAlert } from 'lucide-react';
6
+
7
+ var ChatMessageEditor = React.forwardRef(
8
+ function ChatMessageEditor2({ value, onValueChange, onSubmit, onCancel, submitDisabled = false, rows = 3, labels, className }, ref) {
9
+ const onKeyDown = (event) => {
10
+ if (event.key === "Escape") {
11
+ event.preventDefault();
12
+ onCancel();
13
+ return;
14
+ }
15
+ if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) {
16
+ event.preventDefault();
17
+ if (!submitDisabled) {
18
+ onSubmit();
19
+ }
20
+ }
21
+ };
22
+ return /* @__PURE__ */ jsxs("div", { ref, "data-as": "chat-message-editor", className: cn("flex w-full flex-col gap-2", className), children: [
23
+ /* @__PURE__ */ jsx(
24
+ "textarea",
25
+ {
26
+ value,
27
+ rows,
28
+ "aria-label": labels?.ariaLabel ?? "Edit message",
29
+ onChange: (event) => onValueChange(event.target.value),
30
+ onKeyDown,
31
+ className: "w-full resize-y rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] px-3 py-2 text-sm text-[var(--as-fg)] placeholder:text-[var(--as-muted-fg)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]"
32
+ }
33
+ ),
34
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2", children: [
35
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: onCancel, children: labels?.cancel ?? "Cancel" }),
36
+ /* @__PURE__ */ jsx(Button, { size: "sm", disabled: submitDisabled, onClick: onSubmit, children: labels?.save ?? "Save & resend" })
37
+ ] })
38
+ ] });
39
+ }
40
+ );
41
+ var MessageVariantSwitcher = React.forwardRef(
42
+ function MessageVariantSwitcher2({ variants, onSelect, labels, className }, ref) {
43
+ const { index, count, siblingIds } = variants;
44
+ const previousId = index > 1 ? siblingIds[index - 2] : void 0;
45
+ const nextId = index < count ? siblingIds[index] : void 0;
46
+ const select = (event, messageId) => {
47
+ event.stopPropagation();
48
+ if (messageId !== void 0) {
49
+ onSelect(messageId);
50
+ }
51
+ };
52
+ return /* @__PURE__ */ jsxs(
53
+ "div",
54
+ {
55
+ ref,
56
+ role: "group",
57
+ "aria-label": labels?.group ?? "Message variants",
58
+ "data-as": "message-variant-switcher",
59
+ className: cn("flex items-center gap-0.5 text-xs text-[var(--as-muted-fg)]", className),
60
+ children: [
61
+ /* @__PURE__ */ jsx(
62
+ "button",
63
+ {
64
+ type: "button",
65
+ disabled: previousId === void 0,
66
+ "aria-label": labels?.previous ?? "Previous variant",
67
+ title: labels?.previous ?? "Previous variant",
68
+ onClick: (event) => select(event, previousId),
69
+ className: "rounded-[var(--as-radius-sm)] p-0.5 transition-colors hover:bg-[var(--as-surface-raised)] hover:text-[var(--as-fg)] disabled:pointer-events-none disabled:opacity-30 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
70
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-3.5", "aria-hidden": true })
71
+ }
72
+ ),
73
+ /* @__PURE__ */ jsxs("span", { className: "tabular-nums", title: labels?.variantOf?.(index, count) ?? `Variant ${index} of ${count}`, children: [
74
+ index,
75
+ "/",
76
+ count
77
+ ] }),
78
+ /* @__PURE__ */ jsx(
79
+ "button",
80
+ {
81
+ type: "button",
82
+ disabled: nextId === void 0,
83
+ "aria-label": labels?.next ?? "Next variant",
84
+ title: labels?.next ?? "Next variant",
85
+ onClick: (event) => select(event, nextId),
86
+ className: "rounded-[var(--as-radius-sm)] p-0.5 transition-colors hover:bg-[var(--as-surface-raised)] hover:text-[var(--as-fg)] disabled:pointer-events-none disabled:opacity-30 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
87
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5", "aria-hidden": true })
88
+ }
89
+ )
90
+ ]
91
+ }
92
+ );
93
+ }
94
+ );
95
+ var actionButtonClass = "rounded-[var(--as-radius-sm)] p-1 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-surface-raised)] hover:text-[var(--as-fg)] focus-visible:opacity-100 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]";
96
+ var ChatMessage = React.forwardRef(
97
+ function ChatMessage2({
98
+ role,
99
+ content,
100
+ reasoning,
101
+ children,
102
+ status = "done",
103
+ error,
104
+ actions,
105
+ editing = false,
106
+ variants,
107
+ onSelectVariant,
108
+ meta,
109
+ chips,
110
+ attachments,
111
+ labels,
112
+ compact = false,
113
+ className
114
+ }, ref) {
115
+ const isUser = role === "user";
116
+ const isSystem = role === "system";
117
+ const showActions = actions !== void 0 && (actions.onCopy !== void 0 || actions.onEdit !== void 0 || actions.onRegenerate !== void 0 || (actions.extras?.length ?? 0) > 0);
118
+ const editorLabels = { save: labels?.save, cancel: labels?.cancel, ariaLabel: labels?.ariaLabel };
119
+ const switcherLabels = { group: labels?.group, variantOf: labels?.variantOf, previous: labels?.previous, next: labels?.next };
120
+ if (isSystem) {
121
+ return /* @__PURE__ */ jsx(
122
+ "div",
123
+ {
124
+ ref,
125
+ "data-as": "chat-message",
126
+ "data-role": role,
127
+ className: cn("flex justify-center", className),
128
+ children: /* @__PURE__ */ jsx("div", { className: "rounded-full border border-[var(--as-border)] bg-[var(--as-surface)] px-3 py-1 text-xs text-[var(--as-muted-fg)]", children: content })
129
+ }
130
+ );
131
+ }
132
+ return /* @__PURE__ */ jsxs(
133
+ "div",
134
+ {
135
+ ref,
136
+ "data-as": "chat-message",
137
+ "data-role": role,
138
+ "data-status": status,
139
+ className: cn("group/message flex w-full flex-col", isUser ? "items-end" : "items-start", className),
140
+ children: [
141
+ attachments ? /* @__PURE__ */ jsx("div", { className: "mb-1.5 flex w-full flex-wrap justify-end gap-1.5", children: attachments }) : null,
142
+ /* @__PURE__ */ jsxs(
143
+ "div",
144
+ {
145
+ className: cn(
146
+ "relative flex max-w-[92%] flex-col gap-1.5 rounded-[var(--as-radius-lg)] px-3 py-2 text-sm",
147
+ compact && "px-2.5 py-1.5",
148
+ isUser ? "rounded-br-[var(--as-radius-sm)] bg-[var(--as-primary)] text-[var(--as-primary-fg)]" : "rounded-bl-[var(--as-radius-sm)] border border-[var(--as-border)] bg-[var(--as-surface-raised)] text-[var(--as-fg)]"
149
+ ),
150
+ children: [
151
+ reasoning ? /* @__PURE__ */ jsx("div", { className: "not-prose", children: reasoning }) : null,
152
+ editing ? /* @__PURE__ */ jsx(
153
+ ChatMessageEditor,
154
+ {
155
+ value: editing.value,
156
+ onValueChange: editing.onValueChange,
157
+ onSubmit: editing.onSubmit,
158
+ onCancel: editing.onCancel,
159
+ submitDisabled: editing.submitDisabled,
160
+ labels: editorLabels
161
+ }
162
+ ) : /* @__PURE__ */ jsx("div", { className: "min-w-0", children: content }),
163
+ status === "interrupted" ? /* @__PURE__ */ jsx("span", { className: "self-start rounded-full bg-[var(--as-warning)]/15 px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide text-[var(--as-warning)]", children: labels?.interrupted ?? "Stopped" }) : null,
164
+ status === "error" && error ? /* @__PURE__ */ jsxs(
165
+ "div",
166
+ {
167
+ role: "alert",
168
+ className: "flex flex-col gap-1 rounded-[var(--as-radius)] border border-[var(--as-danger)]/40 bg-[var(--as-danger)]/10 px-2.5 py-1.5 text-xs text-[var(--as-danger)]",
169
+ children: [
170
+ /* @__PURE__ */ jsx("span", { className: "font-mono font-semibold uppercase tracking-wide", children: error.code }),
171
+ /* @__PURE__ */ jsx("span", { className: "whitespace-pre-wrap break-words", children: error.message }),
172
+ error.retryable && actions?.onRetry ? /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: "self-start", onClick: actions.onRetry, children: labels?.retry ?? "Retry" }) : null
173
+ ]
174
+ }
175
+ ) : null,
176
+ chips ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: chips }) : null,
177
+ children ? /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1.5", children }) : null,
178
+ showActions && !editing ? /* @__PURE__ */ jsxs(
179
+ "div",
180
+ {
181
+ className: cn(
182
+ "absolute top-1 flex items-center gap-0.5 rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] p-0.5 opacity-0 shadow-[var(--as-shadow-1)] transition-opacity focus-within:opacity-100 group-hover/message:opacity-100",
183
+ isUser ? "-left-9" : "-right-9"
184
+ ),
185
+ children: [
186
+ actions?.onCopy ? /* @__PURE__ */ jsx("button", { type: "button", className: actionButtonClass, "aria-label": labels?.copy ?? "Copy", title: labels?.copy ?? "Copy", onClick: actions.onCopy, children: /* @__PURE__ */ jsx(Copy, { className: "size-3.5", "aria-hidden": true }) }) : null,
187
+ actions?.onEdit ? /* @__PURE__ */ jsx("button", { type: "button", className: actionButtonClass, "aria-label": labels?.edit ?? "Edit", title: labels?.edit ?? "Edit", onClick: actions.onEdit, children: /* @__PURE__ */ jsx(Pencil, { className: "size-3.5", "aria-hidden": true }) }) : null,
188
+ actions?.onRegenerate ? /* @__PURE__ */ jsx("button", { type: "button", className: actionButtonClass, "aria-label": labels?.regenerate ?? "Regenerate", title: labels?.regenerate ?? "Regenerate", onClick: actions.onRegenerate, children: /* @__PURE__ */ jsx(RotateCcw, { className: "size-3.5", "aria-hidden": true }) }) : null,
189
+ actions?.extras?.map((extra) => /* @__PURE__ */ jsx(
190
+ "button",
191
+ {
192
+ type: "button",
193
+ className: cn(actionButtonClass, extra.danger && "hover:text-[var(--as-danger)]"),
194
+ "aria-label": extra.label,
195
+ title: extra.label,
196
+ onClick: extra.onClick,
197
+ children: extra.icon ? /* @__PURE__ */ jsx(extra.icon, { className: "size-3.5", "aria-hidden": true }) : /* @__PURE__ */ jsx(TriangleAlert, { className: "size-3.5", "aria-hidden": true })
198
+ },
199
+ extra.key
200
+ ))
201
+ ]
202
+ }
203
+ ) : null
204
+ ]
205
+ }
206
+ ),
207
+ meta ? /* @__PURE__ */ jsx("div", { className: "mt-0.5 flex flex-wrap items-center gap-1.5 px-1 text-[10px] text-[var(--as-muted-fg)]", children: meta }) : null,
208
+ variants && variants.count > 1 && onSelectVariant ? /* @__PURE__ */ jsx(MessageVariantSwitcher, { variants, onSelect: onSelectVariant, labels: switcherLabels, className: "mt-0.5 px-1" }) : null
209
+ ]
210
+ }
211
+ );
212
+ }
213
+ );
214
+
215
+ export { ChatMessage, ChatMessageEditor, MessageVariantSwitcher };
216
+ //# sourceMappingURL=chunk-BSIWLVDX.js.map
217
+ //# sourceMappingURL=chunk-BSIWLVDX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/chat-message/ChatMessageEditor.tsx","../src/components/chat-message/MessageVariantSwitcher.tsx","../src/components/chat-message/ChatMessage.tsx"],"names":["ChatMessageEditor","React2","MessageVariantSwitcher","jsxs","jsx","React3","ChatMessage"],"mappings":"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","file":"chunk-BSIWLVDX.js","sourcesContent":["import * as React from 'react'\n\nimport { cn } from '../../lib/utils'\nimport { Button } from '../button/Button'\n\nexport interface ChatMessageEditorLabels {\n save: string\n cancel: string\n ariaLabel: string\n}\n\nexport interface ChatMessageEditorProps {\n value: string\n onValueChange: (value: string) => void\n /** Save & resend — branches the tree (family `edit` endpoint) and reruns the turn. */\n onSubmit: () => void\n onCancel: () => void\n submitDisabled?: boolean\n rows?: number\n labels?: Partial<ChatMessageEditorLabels>\n className?: string\n}\n\n/**\n * Inline user-prompt editor for the edit-and-resend flow (study pattern):\n * controlled textarea, Cmd/Ctrl+Enter saves, Escape cancels.\n */\nexport const ChatMessageEditor = React.forwardRef<HTMLDivElement, ChatMessageEditorProps>(\n function ChatMessageEditor(\n { value, onValueChange, onSubmit, onCancel, submitDisabled = false, rows = 3, labels, className },\n ref,\n ) {\n const onKeyDown: React.KeyboardEventHandler<HTMLTextAreaElement> = (event) => {\n if (event.key === 'Escape') {\n event.preventDefault()\n onCancel()\n return\n }\n if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n if (!submitDisabled) {\n onSubmit()\n }\n }\n }\n\n return (\n <div ref={ref} data-as=\"chat-message-editor\" className={cn('flex w-full flex-col gap-2', className)}>\n <textarea\n value={value}\n rows={rows}\n aria-label={labels?.ariaLabel ?? 'Edit message'}\n onChange={(event) => onValueChange(event.target.value)}\n onKeyDown={onKeyDown}\n className=\"w-full resize-y rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] px-3 py-2 text-sm text-[var(--as-fg)] placeholder:text-[var(--as-muted-fg)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]\"\n />\n <div className=\"flex items-center justify-end gap-2\">\n <Button variant=\"ghost\" size=\"sm\" onClick={onCancel}>\n {labels?.cancel ?? 'Cancel'}\n </Button>\n <Button size=\"sm\" disabled={submitDisabled} onClick={onSubmit}>\n {labels?.save ?? 'Save & resend'}\n </Button>\n </div>\n </div>\n )\n },\n)\n","import * as React from 'react'\nimport { ChevronLeft, ChevronRight } from 'lucide-react'\n\nimport type { ChatMessageVariants } from '../chat-core/types'\nimport { cn } from '../../lib/utils'\n\nexport interface MessageVariantSwitcherLabels {\n group: string\n variantOf: (index: number, count: number) => string\n previous: string\n next: string\n}\n\nexport interface MessageVariantSwitcherProps {\n variants: ChatMessageVariants\n /** Flip the parent's active-child pointer to the sibling id (family `select` endpoint). */\n onSelect: (messageId: string) => void\n labels?: Partial<MessageVariantSwitcherLabels>\n className?: string\n}\n\n/**\n * The `‹ n/N ›` sibling-variant switcher under a branched message\n * (study's inline switcher, family branching contract).\n */\nexport const MessageVariantSwitcher = React.forwardRef<HTMLDivElement, MessageVariantSwitcherProps>(\n function MessageVariantSwitcher({ variants, onSelect, labels, className }, ref) {\n const { index, count, siblingIds } = variants\n const previousId = index > 1 ? siblingIds[index - 2] : undefined\n const nextId = index < count ? siblingIds[index] : undefined\n\n const select = (event: React.MouseEvent<HTMLButtonElement>, messageId: string | undefined) => {\n event.stopPropagation()\n if (messageId !== undefined) {\n onSelect(messageId)\n }\n }\n\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-label={labels?.group ?? 'Message variants'}\n data-as=\"message-variant-switcher\"\n className={cn('flex items-center gap-0.5 text-xs text-[var(--as-muted-fg)]', className)}\n >\n <button\n type=\"button\"\n disabled={previousId === undefined}\n aria-label={labels?.previous ?? 'Previous variant'}\n title={labels?.previous ?? 'Previous variant'}\n onClick={(event) => select(event, previousId)}\n className=\"rounded-[var(--as-radius-sm)] p-0.5 transition-colors hover:bg-[var(--as-surface-raised)] hover:text-[var(--as-fg)] disabled:pointer-events-none disabled:opacity-30 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <ChevronLeft className=\"size-3.5\" aria-hidden />\n </button>\n <span className=\"tabular-nums\" title={labels?.variantOf?.(index, count) ?? `Variant ${index} of ${count}`}>\n {index}/{count}\n </span>\n <button\n type=\"button\"\n disabled={nextId === undefined}\n aria-label={labels?.next ?? 'Next variant'}\n title={labels?.next ?? 'Next variant'}\n onClick={(event) => select(event, nextId)}\n className=\"rounded-[var(--as-radius-sm)] p-0.5 transition-colors hover:bg-[var(--as-surface-raised)] hover:text-[var(--as-fg)] disabled:pointer-events-none disabled:opacity-30 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <ChevronRight className=\"size-3.5\" aria-hidden />\n </button>\n </div>\n )\n },\n)\n","import * as React from 'react'\nimport { Copy, Pencil, RotateCcw, TriangleAlert } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\n\nimport { cn } from '../../lib/utils'\nimport { Button } from '../button/Button'\nimport type { ChatError, ChatMessageVariants, ChatRole } from '../chat-core/types'\nimport { ChatMessageEditor, type ChatMessageEditorLabels } from './ChatMessageEditor'\nimport { MessageVariantSwitcher, type MessageVariantSwitcherLabels } from './MessageVariantSwitcher'\n\nexport interface ChatMessageAction {\n key: string\n label: string\n icon?: LucideIcon\n onClick: () => void\n danger?: boolean\n}\n\nexport interface ChatMessageActions {\n onCopy?: () => void\n onEdit?: () => void\n onRegenerate?: () => void\n onRetry?: () => void\n /** Extra icon actions (app-specific). */\n extras?: ChatMessageAction[]\n}\n\nexport interface ChatMessageLabels {\n copy: string\n edit: string\n regenerate: string\n retry: string\n interrupted: string\n error: string\n}\n\nexport interface ChatMessageProps {\n role: ChatRole\n /** Usually a `MarkdownSurface`; any node graph renders here. */\n content: React.ReactNode\n /** Thinking block slot (usually `ChatReasoning`). */\n reasoning?: React.ReactNode\n /** Tool cards / HITL cards below the content. */\n children?: React.ReactNode\n status?: 'streaming' | 'done' | 'error' | 'interrupted'\n error?: ChatError\n actions?: ChatMessageActions\n /** Edit-and-resend mode for user messages (renders `ChatMessageEditor`). */\n editing?: false | { value: string; onValueChange: (value: string) => void; onSubmit: () => void; onCancel: () => void; submitDisabled?: boolean }\n variants?: ChatMessageVariants\n onSelectVariant?: (messageId: string) => void\n /** Badge row (model, latency, tokens — app-defined). */\n meta?: React.ReactNode\n /** Deep-link chip row (career references, health citations). */\n chips?: React.ReactNode\n /** Attachment rail above the content. */\n attachments?: React.ReactNode\n labels?: Partial<ChatMessageLabels & ChatMessageEditorLabels & MessageVariantSwitcherLabels>\n compact?: boolean\n className?: string\n}\n\nconst actionButtonClass =\n 'rounded-[var(--as-radius-sm)] p-1 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-surface-raised)] hover:text-[var(--as-fg)] focus-visible:opacity-100 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]'\n\n/**\n * The family chat bubble: role-aligned, hover action row (copy / edit /\n * regenerate + app extras), variant switcher under branched messages,\n * reasoning and below-content slots, inline edit-and-resend, error +\n * interrupted states. Presentational — state lives app-side.\n */\nexport const ChatMessage = React.forwardRef<HTMLDivElement, ChatMessageProps>(\n function ChatMessage(\n {\n role,\n content,\n reasoning,\n children,\n status = 'done',\n error,\n actions,\n editing = false,\n variants,\n onSelectVariant,\n meta,\n chips,\n attachments,\n labels,\n compact = false,\n className,\n },\n ref,\n ) {\n const isUser = role === 'user'\n const isSystem = role === 'system'\n const showActions =\n actions !== undefined && (actions.onCopy !== undefined || actions.onEdit !== undefined || actions.onRegenerate !== undefined || (actions.extras?.length ?? 0) > 0)\n const editorLabels = { save: labels?.save, cancel: labels?.cancel, ariaLabel: labels?.ariaLabel }\n const switcherLabels = { group: labels?.group, variantOf: labels?.variantOf, previous: labels?.previous, next: labels?.next }\n\n if (isSystem) {\n return (\n <div\n ref={ref}\n data-as=\"chat-message\"\n data-role={role}\n className={cn('flex justify-center', className)}\n >\n <div className=\"rounded-full border border-[var(--as-border)] bg-[var(--as-surface)] px-3 py-1 text-xs text-[var(--as-muted-fg)]\">\n {content}\n </div>\n </div>\n )\n }\n\n return (\n <div\n ref={ref}\n data-as=\"chat-message\"\n data-role={role}\n data-status={status}\n className={cn('group/message flex w-full flex-col', isUser ? 'items-end' : 'items-start', className)}\n >\n {attachments ? <div className=\"mb-1.5 flex w-full flex-wrap justify-end gap-1.5\">{attachments}</div> : null}\n <div\n className={cn(\n 'relative flex max-w-[92%] flex-col gap-1.5 rounded-[var(--as-radius-lg)] px-3 py-2 text-sm',\n compact && 'px-2.5 py-1.5',\n isUser\n ? 'rounded-br-[var(--as-radius-sm)] bg-[var(--as-primary)] text-[var(--as-primary-fg)]'\n : 'rounded-bl-[var(--as-radius-sm)] border border-[var(--as-border)] bg-[var(--as-surface-raised)] text-[var(--as-fg)]',\n )}\n >\n {reasoning ? <div className=\"not-prose\">{reasoning}</div> : null}\n {editing ? (\n <ChatMessageEditor\n value={editing.value}\n onValueChange={editing.onValueChange}\n onSubmit={editing.onSubmit}\n onCancel={editing.onCancel}\n submitDisabled={editing.submitDisabled}\n labels={editorLabels}\n />\n ) : (\n <div className=\"min-w-0\">{content}</div>\n )}\n {status === 'interrupted' ? (\n <span className=\"self-start rounded-full bg-[var(--as-warning)]/15 px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide text-[var(--as-warning)]\">\n {labels?.interrupted ?? 'Stopped'}\n </span>\n ) : null}\n {status === 'error' && error ? (\n <div\n role=\"alert\"\n className=\"flex flex-col gap-1 rounded-[var(--as-radius)] border border-[var(--as-danger)]/40 bg-[var(--as-danger)]/10 px-2.5 py-1.5 text-xs text-[var(--as-danger)]\"\n >\n <span className=\"font-mono font-semibold uppercase tracking-wide\">{error.code}</span>\n <span className=\"whitespace-pre-wrap break-words\">{error.message}</span>\n {error.retryable && actions?.onRetry ? (\n <Button variant=\"outline\" size=\"sm\" className=\"self-start\" onClick={actions.onRetry}>\n {labels?.retry ?? 'Retry'}\n </Button>\n ) : null}\n </div>\n ) : null}\n {chips ? <div className=\"flex flex-wrap gap-1.5\">{chips}</div> : null}\n {children ? <div className=\"flex flex-col gap-1.5\">{children}</div> : null}\n {showActions && !editing ? (\n <div\n className={cn(\n 'absolute top-1 flex items-center gap-0.5 rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] p-0.5 opacity-0 shadow-[var(--as-shadow-1)] transition-opacity focus-within:opacity-100 group-hover/message:opacity-100',\n isUser ? '-left-9' : '-right-9',\n )}\n >\n {actions?.onCopy ? (\n <button type=\"button\" className={actionButtonClass} aria-label={labels?.copy ?? 'Copy'} title={labels?.copy ?? 'Copy'} onClick={actions.onCopy}>\n <Copy className=\"size-3.5\" aria-hidden />\n </button>\n ) : null}\n {actions?.onEdit ? (\n <button type=\"button\" className={actionButtonClass} aria-label={labels?.edit ?? 'Edit'} title={labels?.edit ?? 'Edit'} onClick={actions.onEdit}>\n <Pencil className=\"size-3.5\" aria-hidden />\n </button>\n ) : null}\n {actions?.onRegenerate ? (\n <button type=\"button\" className={actionButtonClass} aria-label={labels?.regenerate ?? 'Regenerate'} title={labels?.regenerate ?? 'Regenerate'} onClick={actions.onRegenerate}>\n <RotateCcw className=\"size-3.5\" aria-hidden />\n </button>\n ) : null}\n {actions?.extras?.map((extra) => (\n <button\n key={extra.key}\n type=\"button\"\n className={cn(actionButtonClass, extra.danger && 'hover:text-[var(--as-danger)]')}\n aria-label={extra.label}\n title={extra.label}\n onClick={extra.onClick}\n >\n {extra.icon ? <extra.icon className=\"size-3.5\" aria-hidden /> : <TriangleAlert className=\"size-3.5\" aria-hidden />}\n </button>\n ))}\n </div>\n ) : null}\n </div>\n {meta ? <div className=\"mt-0.5 flex flex-wrap items-center gap-1.5 px-1 text-[10px] text-[var(--as-muted-fg)]\">{meta}</div> : null}\n {variants && variants.count > 1 && onSelectVariant ? (\n <MessageVariantSwitcher variants={variants} onSelect={onSelectVariant} labels={switcherLabels} className=\"mt-0.5 px-1\" />\n ) : null}\n </div>\n )\n },\n)\n"]}
@@ -0,0 +1,134 @@
1
+ import { ChatMessage } from './chunk-BSIWLVDX.js';
2
+ import { MarkdownSurface } from './chunk-DZBTJZZV.js';
3
+ import { cn } from './chunk-BLJWR4ZV.js';
4
+ import * as React from 'react';
5
+ import { ArrowDown } from 'lucide-react';
6
+ import { useVirtualizer } from '@tanstack/react-virtual';
7
+ import { jsxs, jsx } from 'react/jsx-runtime';
8
+
9
+ function defaultRenderItem(message) {
10
+ return /* @__PURE__ */ jsx(
11
+ ChatMessage,
12
+ {
13
+ role: message.role,
14
+ status: message.status,
15
+ content: /* @__PURE__ */ jsx(MarkdownSurface, { value: message.content, streaming: message.status === "streaming" }),
16
+ variants: message.variants
17
+ }
18
+ );
19
+ }
20
+ var ChatTranscript = React.forwardRef(
21
+ function ChatTranscript2({
22
+ items,
23
+ renderItem = defaultRenderItem,
24
+ live,
25
+ emptyState,
26
+ autoScroll = true,
27
+ scrollThresholdPx = 120,
28
+ virtualized = false,
29
+ labels,
30
+ className
31
+ }, ref) {
32
+ const scrollRef = React.useRef(null);
33
+ const atBottomRef = React.useRef(true);
34
+ const [showJump, setShowJump] = React.useState(false);
35
+ const [announcement, setAnnouncement] = React.useState("");
36
+ const previousCountRef = React.useRef(-1);
37
+ const onScroll = () => {
38
+ const element = scrollRef.current;
39
+ if (element === null) {
40
+ return;
41
+ }
42
+ const distance = element.scrollHeight - element.scrollTop - element.clientHeight;
43
+ const atBottom = distance <= scrollThresholdPx;
44
+ atBottomRef.current = atBottom;
45
+ setShowJump(element.scrollHeight > element.clientHeight && !atBottom);
46
+ };
47
+ React.useEffect(() => {
48
+ const element = scrollRef.current;
49
+ if (element === null || !autoScroll || !atBottomRef.current) {
50
+ return;
51
+ }
52
+ const frame = requestAnimationFrame(() => {
53
+ element.scrollTop = element.scrollHeight;
54
+ });
55
+ return () => cancelAnimationFrame(frame);
56
+ });
57
+ React.useEffect(() => {
58
+ if (previousCountRef.current === -1) {
59
+ previousCountRef.current = items.length;
60
+ return;
61
+ }
62
+ if (items.length > previousCountRef.current) {
63
+ const added = items[items.length - 1];
64
+ previousCountRef.current = items.length;
65
+ if (added && added.role !== "user") {
66
+ setAnnouncement(labels?.replied ?? "Assistant replied");
67
+ }
68
+ return;
69
+ }
70
+ previousCountRef.current = items.length;
71
+ }, [items, labels]);
72
+ const scrollToBottom = () => {
73
+ const element = scrollRef.current;
74
+ if (element !== null) {
75
+ element.scrollTo({ top: element.scrollHeight, behavior: "smooth" });
76
+ }
77
+ };
78
+ const rowCount = items.length + (live !== void 0 ? 1 : 0);
79
+ const virtualizer = useVirtualizer({
80
+ count: virtualized ? rowCount : 0,
81
+ getScrollElement: () => scrollRef.current,
82
+ estimateSize: () => 120,
83
+ overscan: 8,
84
+ initialRect: { width: 480, height: 600 }
85
+ });
86
+ return /* @__PURE__ */ jsxs("div", { ref, "data-as": "chat-transcript", className: cn("relative min-h-0 flex-1", className), children: [
87
+ /* @__PURE__ */ jsx(
88
+ "div",
89
+ {
90
+ ref: scrollRef,
91
+ onScroll,
92
+ role: "log",
93
+ "aria-label": labels?.log ?? "Conversation",
94
+ "aria-busy": live !== void 0,
95
+ className: "h-full overflow-y-auto overscroll-contain px-1 py-2",
96
+ children: items.length === 0 && live === void 0 ? /* @__PURE__ */ jsx("div", { className: "flex h-full min-h-40 items-center justify-center p-4 text-sm text-[var(--as-muted-fg)]", children: emptyState ?? (labels?.empty ?? "Start the conversation") }) : virtualized ? /* @__PURE__ */ jsx("div", { style: { height: virtualizer.getTotalSize(), position: "relative" }, children: virtualizer.getVirtualItems().map((row) => {
97
+ const isLive = row.index >= items.length;
98
+ const node = isLive ? live : renderItem(items[row.index], row.index);
99
+ return /* @__PURE__ */ jsx(
100
+ "div",
101
+ {
102
+ "data-index": row.index,
103
+ ref: virtualizer.measureElement,
104
+ style: { position: "absolute", top: 0, left: 0, width: "100%", transform: `translateY(${row.start}px)` },
105
+ className: "px-2 py-1.5",
106
+ children: node
107
+ },
108
+ row.key
109
+ );
110
+ }) }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 px-2", children: [
111
+ items.map((message, index) => /* @__PURE__ */ jsx("div", { children: renderItem(message, index) }, message.id)),
112
+ live !== void 0 ? /* @__PURE__ */ jsx("div", { "data-as": "chat-transcript-live", children: live }) : null
113
+ ] })
114
+ }
115
+ ),
116
+ /* @__PURE__ */ jsx("div", { "aria-live": "polite", className: "sr-only", children: announcement }),
117
+ showJump ? /* @__PURE__ */ jsx(
118
+ "button",
119
+ {
120
+ type: "button",
121
+ onClick: scrollToBottom,
122
+ "aria-label": labels?.scrollToBottom ?? "Scroll to latest",
123
+ title: labels?.scrollToBottom ?? "Scroll to latest",
124
+ className: "absolute bottom-3 left-1/2 flex size-8 -translate-x-1/2 items-center justify-center rounded-full border border-[var(--as-border)] bg-[var(--as-surface-raised)] text-[var(--as-fg)] shadow-[var(--as-shadow-2)] transition-colors hover:bg-[var(--as-secondary)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]",
125
+ children: /* @__PURE__ */ jsx(ArrowDown, { className: "size-4", "aria-hidden": true })
126
+ }
127
+ ) : null
128
+ ] });
129
+ }
130
+ );
131
+
132
+ export { ChatTranscript };
133
+ //# sourceMappingURL=chunk-BVJT67B5.js.map
134
+ //# sourceMappingURL=chunk-BVJT67B5.js.map