@neuronection/assistant-ui 0.52.0 → 0.53.1

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 (89) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/README.md +4 -1
  3. package/dist/chat-branch-tree.d.ts +15 -2
  4. package/dist/chat-composer.d.ts +43 -3
  5. package/dist/chat-composer.js +1 -1
  6. package/dist/chat-core.d.ts +54 -4
  7. package/dist/chat-launcher.js +1 -1
  8. package/dist/chat-markdown.d.ts +8 -1
  9. package/dist/chat-markdown.js +1 -1
  10. package/dist/chat-message.d.ts +16 -1
  11. package/dist/chat-message.js +1 -1
  12. package/dist/chat-panel.js +1 -1
  13. package/dist/chat-pending.d.ts +25 -0
  14. package/dist/chat-pending.js +3 -0
  15. package/dist/chat-pending.js.map +1 -0
  16. package/dist/chat-reasoning.d.ts +10 -2
  17. package/dist/chat-reasoning.js +1 -1
  18. package/dist/chat-session-list.d.ts +7 -0
  19. package/dist/chat-session-list.js +1 -1
  20. package/dist/chat-suggestion-card.d.ts +19 -0
  21. package/dist/chat-suggestion-card.js +3 -0
  22. package/dist/chat-suggestion-card.js.map +1 -0
  23. package/dist/chat-tool-card.js +1 -1
  24. package/dist/chat-transcript.d.ts +81 -8
  25. package/dist/chat-transcript.js +1 -1
  26. package/dist/chat-turn-status.js +1 -1
  27. package/dist/chat-welcome.d.ts +23 -0
  28. package/dist/chat-welcome.js +3 -0
  29. package/dist/chat-welcome.js.map +1 -0
  30. package/dist/{chunk-PC3CE4W2.js → chunk-22MIIWC7.js} +45 -7
  31. package/dist/chunk-22MIIWC7.js.map +1 -0
  32. package/dist/chunk-56QSIK4Y.js +264 -0
  33. package/dist/chunk-56QSIK4Y.js.map +1 -0
  34. package/dist/{chunk-GDGKARRB.js → chunk-5C2OLUDR.js} +11 -2
  35. package/dist/chunk-5C2OLUDR.js.map +1 -0
  36. package/dist/chunk-E2NNTHFB.js +479 -0
  37. package/dist/chunk-E2NNTHFB.js.map +1 -0
  38. package/dist/{chunk-5BJ4VULP.js → chunk-EQPXM2R4.js} +23 -10
  39. package/dist/chunk-EQPXM2R4.js.map +1 -0
  40. package/dist/chunk-F4BTJQLK.js +43 -0
  41. package/dist/chunk-F4BTJQLK.js.map +1 -0
  42. package/dist/{chunk-TQDTFPM4.js → chunk-GIQZNEG5.js} +3 -3
  43. package/dist/chunk-GIQZNEG5.js.map +1 -0
  44. package/dist/{chunk-E3NZM4U7.js → chunk-GO4ILLS6.js} +17 -4
  45. package/dist/chunk-GO4ILLS6.js.map +1 -0
  46. package/dist/{chunk-5U45TQJE.js → chunk-I5H4KYEZ.js} +3 -3
  47. package/dist/{chunk-5U45TQJE.js.map → chunk-I5H4KYEZ.js.map} +1 -1
  48. package/dist/{chunk-S63SYV6O.js → chunk-INXQJM2K.js} +27 -6
  49. package/dist/chunk-INXQJM2K.js.map +1 -0
  50. package/dist/{chunk-HQ3X5SVR.js → chunk-KLCULTOQ.js} +105 -16
  51. package/dist/chunk-KLCULTOQ.js.map +1 -0
  52. package/dist/chunk-M4SYVPC4.js +3 -0
  53. package/dist/chunk-M4SYVPC4.js.map +1 -0
  54. package/dist/chunk-OOQO6VGO.js +33 -0
  55. package/dist/chunk-OOQO6VGO.js.map +1 -0
  56. package/dist/chunk-P3QUNCBR.js +366 -0
  57. package/dist/chunk-P3QUNCBR.js.map +1 -0
  58. package/dist/chunk-PABMR4RA.js +30 -0
  59. package/dist/chunk-PABMR4RA.js.map +1 -0
  60. package/dist/{chunk-SEZZVAGP.js → chunk-SR4IN564.js} +10 -3
  61. package/dist/chunk-SR4IN564.js.map +1 -0
  62. package/dist/{chunk-MFM4W6ZO.js → chunk-TLFEVL42.js} +3 -3
  63. package/dist/{chunk-MFM4W6ZO.js.map → chunk-TLFEVL42.js.map} +1 -1
  64. package/dist/index.d.ts +243 -36
  65. package/dist/index.js +40 -37
  66. package/dist/markdown-diff-view.js +1 -1
  67. package/dist/model-registry.js +1 -1
  68. package/dist/styles.css +1 -1
  69. package/dist/theme-scope.d.ts +1 -1
  70. package/dist/tokens.d.ts +1 -1
  71. package/dist/tokens.js +1 -1
  72. package/dist/{types-LhE1KFBf.d.ts → types-CxieoN2u.d.ts} +1 -1
  73. package/dist/types-pivJdPC8.d.ts +14 -0
  74. package/package.json +13 -1
  75. package/dist/chunk-5BJ4VULP.js.map +0 -1
  76. package/dist/chunk-C5JZCCU7.js +0 -220
  77. package/dist/chunk-C5JZCCU7.js.map +0 -1
  78. package/dist/chunk-E3NZM4U7.js.map +0 -1
  79. package/dist/chunk-GDGKARRB.js.map +0 -1
  80. package/dist/chunk-HQ3X5SVR.js.map +0 -1
  81. package/dist/chunk-PC3CE4W2.js.map +0 -1
  82. package/dist/chunk-S63SYV6O.js.map +0 -1
  83. package/dist/chunk-SEZZVAGP.js.map +0 -1
  84. package/dist/chunk-TQDTFPM4.js.map +0 -1
  85. package/dist/chunk-XOODPYJ3.js +0 -173
  86. package/dist/chunk-XOODPYJ3.js.map +0 -1
  87. package/dist/chunk-YQQZMWHL.js +0 -201
  88. package/dist/chunk-YQQZMWHL.js.map +0 -1
  89. package/dist/tree-4Hv9stjF.d.ts +0 -54
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/chat-reasoning/ChatReasoning.tsx"],"names":["ChatReasoning"],"mappings":"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","file":"chunk-INXQJM2K.js","sourcesContent":["import * as React from 'react'\nimport { Brain, ChevronDown } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\n\nimport { cn } from '../../lib/utils'\nimport { Spinner } from '../spinner/Spinner'\n\nexport interface ChatReasoningLabels {\n title: string\n streaming: string\n}\n\nexport interface ChatReasoningProps {\n /** Accumulated reasoning text (family `delta kind=\"reasoning\"`). */\n text: string\n /** Live reasoning stream — spinner + streaming label + `--as-ai` edge while on. */\n streaming?: boolean\n open?: boolean\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n /**\n * Collapse the block when a live stream finishes (`streaming`\n * true → false). Uncontrolled mode only — a controlled `open` stays\n * app-driven. Default true.\n */\n autoCollapse?: boolean\n labels?: Partial<ChatReasoningLabels>\n icon?: LucideIcon\n className?: string\n}\n\n/**\n * Collapsible thinking block for reasoning models (study's\n * `ReasoningBubble`, family-standard): streams live while the model\n * reasons (AI-accent edge + spinner), collapses to a summary row when\n * done — with a height reveal, and auto-collapse on the streaming →\n * done transition in uncontrolled mode. Controlled-first.\n */\nexport const ChatReasoning = React.forwardRef<HTMLDivElement, ChatReasoningProps>(\n function ChatReasoning(\n {\n text,\n streaming = false,\n open: openProp,\n defaultOpen = true,\n onOpenChange,\n autoCollapse = true,\n labels,\n icon: Icon = Brain,\n className,\n },\n ref,\n ) {\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)\n const isControlled = openProp !== undefined\n const open = isControlled ? openProp : uncontrolledOpen\n const regionId = React.useId()\n const wasStreamingRef = React.useRef(streaming)\n\n React.useEffect(() => {\n const wasStreaming = wasStreamingRef.current\n wasStreamingRef.current = streaming\n if (wasStreaming && !streaming && autoCollapse && !isControlled && uncontrolledOpen) {\n setUncontrolledOpen(false)\n onOpenChange?.(false)\n }\n }, [streaming, autoCollapse, isControlled, uncontrolledOpen, onOpenChange])\n\n const toggle = () => {\n const next = !open\n if (!isControlled) {\n setUncontrolledOpen(next)\n }\n onOpenChange?.(next)\n }\n\n const onKeyDown: React.KeyboardEventHandler<HTMLButtonElement> = (event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n toggle()\n }\n }\n\n return (\n <div\n ref={ref}\n data-as=\"chat-reasoning\"\n data-open={open}\n data-streaming={streaming || undefined}\n className={cn(\n 'rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] text-xs text-[var(--as-muted-fg)] transition-shadow',\n streaming && 'shadow-[inset_2px_0_0_var(--as-ai)]',\n className,\n )}\n >\n <button\n type=\"button\"\n aria-expanded={open}\n aria-controls={regionId}\n onClick={toggle}\n onKeyDown={onKeyDown}\n className=\"flex w-full items-center gap-2 px-2.5 py-1.5 text-left font-medium text-[var(--as-muted-fg)] transition-colors hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <Icon className=\"size-3.5 shrink-0\" aria-hidden />\n <span className=\"flex-1\">{labels?.title ?? 'Thinking'}</span>\n {streaming ? (\n <span role=\"status\" className=\"flex items-center gap-1.5 font-normal\">\n <Spinner size=\"sm\" className=\"text-[var(--as-muted-fg)]\" />\n <span className=\"sr-only\">{labels?.streaming ?? 'Thinking…'}</span>\n </span>\n ) : null}\n <ChevronDown\n className={cn('size-3.5 shrink-0 transition-transform duration-[var(--as-duration-base)] ease-[var(--as-ease-out)]', open && 'rotate-180')}\n aria-hidden\n />\n </button>\n <div id={regionId} data-open={open} className=\"as-collapse\">\n <div className=\"as-collapse-inner border-t border-[var(--as-border)] px-2.5 py-2\">\n <p className=\"whitespace-pre-wrap break-words leading-relaxed\">{text}</p>\n </div>\n </div>\n </div>\n )\n },\n)\n"]}
@@ -1,4 +1,5 @@
1
1
  import { CopyButton } from './chunk-XFYCNCPS.js';
2
+ import { Modal, ModalContent } from './chunk-FVQWIDB5.js';
2
3
  import { cn } from './chunk-RVVXVTH3.js';
3
4
  import * as React3 from 'react';
4
5
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -112,10 +113,61 @@ var MermaidDiagram = React3.forwardRef(
112
113
  }
113
114
  );
114
115
  var fenceLanguage = (className) => /language-([\w+-]+)/.exec(className ?? "")?.[1];
116
+ function splitMatches(value, needle) {
117
+ const segments = [];
118
+ const lower = value.toLowerCase();
119
+ let index = 0;
120
+ for (; ; ) {
121
+ const found = lower.indexOf(needle, index);
122
+ if (found === -1) {
123
+ segments.push({ text: value.slice(index), match: false });
124
+ break;
125
+ }
126
+ if (found > index) {
127
+ segments.push({ text: value.slice(index, found), match: false });
128
+ }
129
+ segments.push({ text: value.slice(found, found + needle.length), match: true });
130
+ index = found + needle.length;
131
+ }
132
+ return segments.filter((segment) => segment.text !== "");
133
+ }
134
+ function highlightTree(node, needle) {
135
+ const children = node.children;
136
+ if (children === void 0) {
137
+ return;
138
+ }
139
+ for (let index = 0; index < children.length; index += 1) {
140
+ const child = children[index];
141
+ if (child.type === "text" && child.value !== void 0) {
142
+ const segments = splitMatches(child.value, needle);
143
+ if (segments.length > 1 || segments.length === 1 && segments[0].match) {
144
+ const replaced = segments.map(
145
+ (segment) => segment.match ? {
146
+ type: "element",
147
+ tagName: "mark",
148
+ properties: { "data-as": "chat-search-match" },
149
+ children: [{ type: "text", value: segment.text }]
150
+ } : { type: "text", value: segment.text }
151
+ );
152
+ children.splice(index, 1, ...replaced);
153
+ index += replaced.length - 1;
154
+ }
155
+ } else {
156
+ highlightTree(child, needle);
157
+ }
158
+ }
159
+ }
160
+ function rehypeHighlightMatches(needle) {
161
+ return () => (tree) => {
162
+ highlightTree(tree, needle);
163
+ };
164
+ }
115
165
  var MarkdownSurface = React3.memo(
116
166
  React3.forwardRef(function MarkdownSurface2({
117
167
  value,
118
168
  streaming = false,
169
+ query,
170
+ lightbox = false,
119
171
  gfm = true,
120
172
  math = true,
121
173
  mermaid = true,
@@ -127,6 +179,8 @@ var MarkdownSurface = React3.memo(
127
179
  className
128
180
  }, ref) {
129
181
  const mermaidEnabled = mermaid && !streaming;
182
+ const highlightQuery = query !== void 0 && query.trim().length > 0 ? query.trim().toLowerCase() : null;
183
+ const [zoom, setZoom] = React3.useState(null);
130
184
  const defaultComponents = React3.useMemo(
131
185
  () => ({
132
186
  pre: ({ children }) => {
@@ -167,9 +221,26 @@ var MarkdownSurface = React3.memo(
167
221
  );
168
222
  },
169
223
  table: ({ children, ...rest }) => /* @__PURE__ */ jsx("div", { className: "my-3 overflow-x-auto rounded-[var(--as-radius)] border border-[var(--as-border)]", children: /* @__PURE__ */ jsx("table", { className: "w-full border-collapse text-[0.875em]", ...rest, children }) }),
170
- img: ({ alt, ...rest }) => /* @__PURE__ */ jsx("img", { alt: alt ?? "", className: "my-2 max-w-full rounded-[var(--as-radius)]", loading: "lazy", ...rest })
224
+ img: ({ alt, ...rest }) => {
225
+ const image = /* @__PURE__ */ jsx("img", { alt: alt ?? "", className: "my-2 max-w-full rounded-[var(--as-radius)]", loading: "lazy", ...rest });
226
+ if (!lightbox || typeof rest.src !== "string") {
227
+ return image;
228
+ }
229
+ const zoomLabel = `${labels?.zoomImage ?? "Zoom image"}${alt ? `: ${alt}` : ""}`;
230
+ return /* @__PURE__ */ jsx(
231
+ "button",
232
+ {
233
+ type: "button",
234
+ "aria-label": zoomLabel,
235
+ title: zoomLabel,
236
+ onClick: () => setZoom({ src: rest.src, alt: alt ?? "" }),
237
+ className: "block cursor-zoom-in rounded-[var(--as-radius)] transition-opacity hover:opacity-90 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]",
238
+ children: image
239
+ }
240
+ );
241
+ }
171
242
  }),
172
- [labels, mermaidEnabled, mermaidTheme, showCodeLanguage]
243
+ [labels, mermaidEnabled, mermaidTheme, showCodeLanguage, lightbox]
173
244
  );
174
245
  const mergedComponents = React3.useMemo(
175
246
  () => ({ ...defaultComponents, ...components }),
@@ -179,23 +250,41 @@ var MarkdownSurface = React3.memo(
179
250
  () => [gfm ? remarkGfm : null, math ? remarkMath : null].filter((plugin) => plugin !== null),
180
251
  [gfm, math]
181
252
  );
182
- const rehypePlugins = React3.useMemo(
183
- () => math ? [[rehypeKatex, { strict: false, throwOnError: false, output: "html" }]] : [],
184
- [math]
185
- );
186
- return /* @__PURE__ */ jsx("div", { ref, "data-as": "chat-markdown", className: cn("min-w-0 text-[var(--as-fg)]", className), children: /* @__PURE__ */ jsx(
187
- ReactMarkdown,
253
+ const rehypePlugins = React3.useMemo(() => {
254
+ const plugins = [];
255
+ if (math) {
256
+ plugins.push([rehypeKatex, { strict: false, throwOnError: false, output: "html" }]);
257
+ }
258
+ if (highlightQuery !== null) {
259
+ plugins.push(rehypeHighlightMatches(highlightQuery));
260
+ }
261
+ return plugins;
262
+ }, [math, highlightQuery]);
263
+ return /* @__PURE__ */ jsxs(
264
+ "div",
188
265
  {
189
- remarkPlugins,
190
- rehypePlugins,
191
- components: mergedComponents,
192
- urlTransform,
193
- children: value
266
+ ref,
267
+ "data-as": "chat-markdown",
268
+ "data-streaming": streaming || void 0,
269
+ className: cn("min-w-0", className),
270
+ children: [
271
+ /* @__PURE__ */ jsx(
272
+ ReactMarkdown,
273
+ {
274
+ remarkPlugins,
275
+ rehypePlugins,
276
+ components: mergedComponents,
277
+ urlTransform,
278
+ children: value
279
+ }
280
+ ),
281
+ /* @__PURE__ */ jsx(Modal, { open: zoom !== null, onOpenChange: (open) => open ? void 0 : setZoom(null), children: zoom !== null ? /* @__PURE__ */ jsx(ModalContent, { closeLabel: labels?.closeImage ?? "Close image", className: "w-fit max-w-[92vw] items-center justify-center p-3", children: /* @__PURE__ */ jsx("img", { src: zoom.src, alt: zoom.alt, className: "max-h-[78vh] max-w-[86vw] rounded-[var(--as-radius)]" }) }) : null })
282
+ ]
194
283
  }
195
- ) });
284
+ );
196
285
  })
197
286
  );
198
287
 
199
288
  export { MarkdownCodeBlock, MarkdownSurface, MermaidDiagram };
200
- //# sourceMappingURL=chunk-HQ3X5SVR.js.map
201
- //# sourceMappingURL=chunk-HQ3X5SVR.js.map
289
+ //# sourceMappingURL=chunk-KLCULTOQ.js.map
290
+ //# sourceMappingURL=chunk-KLCULTOQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/chat-markdown/MarkdownCodeBlock.tsx","../src/components/chat-markdown/MermaidDiagram.tsx","../src/components/chat-markdown/MarkdownSurface.tsx"],"names":["React","MarkdownCodeBlock","React2","MermaidDiagram","jsx","MarkdownSurface","jsxs"],"mappings":"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","file":"chunk-KLCULTOQ.js","sourcesContent":["import * as React from 'react'\n\nimport { cn } from '../../lib/utils'\nimport { CopyButton } from '../copy-button/CopyButton'\n\nexport interface MarkdownCodeBlockLabels {\n copy: string\n code: string\n}\n\nexport interface MarkdownCodeBlockProps {\n code: string\n language?: string\n showLanguage?: boolean\n labels?: Partial<MarkdownCodeBlockLabels>\n className?: string\n}\n\n/**\n * Standalone fenced-code surface: mono block, optional language tag, hover\n * copy. Plain by design — no highlighting engine bundled (apps may wrap or\n * slot their own renderer through `MarkdownSurface.components`).\n */\nexport const MarkdownCodeBlock = React.forwardRef<HTMLDivElement, MarkdownCodeBlockProps>(\n function MarkdownCodeBlock({ code, language, showLanguage = true, labels, className }, ref) {\n const copyLabel = labels?.copy ?? 'Copy code'\n const codeLabel = labels?.code ?? 'Code'\n return (\n <div\n ref={ref}\n data-as=\"markdown-code-block\"\n className={cn(\n 'group/code relative my-3 overflow-hidden rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)]',\n className,\n )}\n >\n {showLanguage && language ? (\n <div className=\"flex items-center justify-between border-b border-[var(--as-border)] px-3 py-1 font-sans text-[10px] font-medium uppercase tracking-wide text-[var(--as-muted-fg)]\">\n <span>{language}</span>\n <CopyButton value={code} label={copyLabel} className=\"opacity-60 transition-opacity group-hover/code:opacity-100\" />\n </div>\n ) : (\n <CopyButton\n value={code}\n label={copyLabel}\n className=\"absolute right-2 top-2 opacity-0 transition-opacity focus-visible:opacity-100 group-hover/code:opacity-100\"\n />\n )}\n <pre className={cn('overflow-x-auto p-3 font-mono text-xs leading-relaxed', !showLanguage && 'pt-3')}>\n <code aria-label={language ? `${language} ${codeLabel}` : codeLabel}>{code}</code>\n </pre>\n </div>\n )\n },\n)\n","import * as React from 'react'\n\nimport { cn } from '../../lib/utils'\nimport { MarkdownCodeBlock } from './MarkdownCodeBlock'\n\nexport interface MermaidDiagramProps {\n code: string\n theme?: 'neutral' | 'default' | 'dark' | 'forest'\n labels?: { diagram?: string }\n className?: string\n}\n\nlet mermaidInitialized = false\n\nasync function renderMermaid(id: string, code: string, theme: NonNullable<MermaidDiagramProps['theme']>): Promise<string> {\n const mermaid = (await import('mermaid')).default\n if (!mermaidInitialized) {\n mermaid.initialize({ startOnLoad: false, securityLevel: 'strict', theme })\n mermaidInitialized = true\n }\n const { svg } = await mermaid.render(id, code)\n return svg\n}\n\nexport function extractText(node: React.ReactNode): string {\n if (node === null || node === undefined || typeof node === 'boolean') {\n return ''\n }\n if (typeof node === 'string' || typeof node === 'number') {\n return String(node)\n }\n if (Array.isArray(node)) {\n return node.map(extractText).join('')\n }\n if (React.isValidElement(node)) {\n return extractText((node.props as { children?: React.ReactNode }).children)\n }\n return ''\n}\n\n/**\n * Lazily-rendered mermaid diagram (dynamic `import('mermaid')`, strict\n * security). Falls back to the plain code block when rendering fails —\n * study-assistant's proven behavior.\n */\nexport const MermaidDiagram = React.forwardRef<HTMLDivElement, MermaidDiagramProps>(\n function MermaidDiagram({ code, theme = 'neutral', labels, className }, ref) {\n const reactId = React.useId().replace(/[^a-zA-Z0-9]/g, '')\n const [svg, setSvg] = React.useState<string | null>(null)\n const [failed, setFailed] = React.useState(false)\n\n React.useEffect(() => {\n let cancelled = false\n setFailed(false)\n setSvg(null)\n renderMermaid(`as-mermaid-${reactId}`, code, theme)\n .then((result) => {\n if (!cancelled) {\n setSvg(result)\n }\n })\n .catch(() => {\n if (!cancelled) {\n setFailed(true)\n }\n })\n return () => {\n cancelled = true\n }\n }, [code, theme, reactId])\n\n if (failed || svg === null) {\n return (\n <MarkdownCodeBlock\n ref={failed ? ref : undefined}\n code={code}\n language=\"mermaid\"\n labels={{ code: labels?.diagram ?? 'Diagram' }}\n className={cn('my-3', svg === null && !failed ? 'opacity-60' : undefined, className)}\n />\n )\n }\n return (\n <div\n ref={ref}\n data-as=\"mermaid-diagram\"\n className={cn(\n 'my-3 flex justify-center overflow-x-auto rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] p-3 [&_svg]:max-w-full',\n className,\n )}\n // mermaid runs with securityLevel 'strict' — no user styles, scripts or links survive\n dangerouslySetInnerHTML={{ __html: svg }}\n />\n )\n },\n)\n","import * as React from 'react'\nimport { defaultUrlTransform, type Components, type Options as ReactMarkdownOptions, type UrlTransform } from 'react-markdown'\nimport ReactMarkdown from 'react-markdown'\nimport rehypeKatex from 'rehype-katex'\nimport remarkGfm from 'remark-gfm'\nimport remarkMath from 'remark-math'\n\nimport { cn } from '../../lib/utils'\nimport { Modal, ModalContent } from '../modal/Modal'\nimport { MarkdownCodeBlock, type MarkdownCodeBlockLabels } from './MarkdownCodeBlock'\nimport { extractText, MermaidDiagram } from './MermaidDiagram'\n\nexport type MarkdownComponents = Components\nexport type { MarkdownCodeBlockLabels }\n\nexport interface MarkdownSurfaceProps {\n /** Markdown source (model output is untrusted — raw HTML never renders). */\n value: string\n /** Live turn: defers mermaid to the final render (streaming floods break diagrams). */\n streaming?: boolean\n /** Search highlight: case-insensitive matches wrap in `<mark data-as=\"chat-search-match\">`. */\n query?: string\n /** Click-to-zoom images in a modal (health's medical images, study screenshots). Default false. */\n lightbox?: boolean\n gfm?: boolean\n math?: boolean\n /** Render ```mermaid fences as diagrams (forced off while `streaming`). */\n mermaid?: boolean\n mermaidTheme?: 'neutral' | 'default' | 'dark' | 'forest'\n /** Per-key overrides of the default renderers (mentions, citations, links…). */\n components?: MarkdownComponents\n urlTransform?: UrlTransform\n showCodeLanguage?: boolean\n labels?: Partial<MarkdownCodeBlockLabels & { zoomImage: string; closeImage: string }>\n className?: string\n}\n\nconst fenceLanguage = (className: string | undefined): string | undefined =>\n /language-([\\w+-]+)/.exec(className ?? '')?.[1]\n\ninterface HastNode {\n type: string\n tagName?: string\n value?: string\n properties?: Record<string, unknown>\n children?: HastNode[]\n}\n\nfunction splitMatches(value: string, needle: string): { text: string; match: boolean }[] {\n const segments: { text: string; match: boolean }[] = []\n const lower = value.toLowerCase()\n let index = 0\n for (;;) {\n const found = lower.indexOf(needle, index)\n if (found === -1) {\n segments.push({ text: value.slice(index), match: false })\n break\n }\n if (found > index) {\n segments.push({ text: value.slice(index, found), match: false })\n }\n segments.push({ text: value.slice(found, found + needle.length), match: true })\n index = found + needle.length\n }\n return segments.filter((segment) => segment.text !== '')\n}\n\nfunction highlightTree(node: HastNode, needle: string): void {\n const children = node.children\n if (children === undefined) {\n return\n }\n for (let index = 0; index < children.length; index += 1) {\n const child = children[index]!\n if (child.type === 'text' && child.value !== undefined) {\n const segments = splitMatches(child.value, needle)\n if (segments.length > 1 || (segments.length === 1 && segments[0]!.match)) {\n const replaced = segments.map((segment) =>\n segment.match\n ? ({\n type: 'element',\n tagName: 'mark',\n properties: { 'data-as': 'chat-search-match' },\n children: [{ type: 'text', value: segment.text }],\n } satisfies HastNode)\n : { type: 'text', value: segment.text },\n )\n children.splice(index, 1, ...(replaced as HastNode[]))\n index += replaced.length - 1\n }\n } else {\n highlightTree(child, needle)\n }\n }\n}\n\nfunction rehypeHighlightMatches(needle: string) {\n return () => (tree: HastNode) => {\n highlightTree(tree, needle)\n }\n}\n\n/**\n * The family's read-only streaming markdown surface: GFM tables, KaTeX math\n * (error-tolerant while streaming), lazily-imported mermaid diagrams, and\n * copyable code blocks with language tags. Raw HTML is never rendered\n * (no `rehype-raw`) — model output is untrusted input (ai-features §7).\n */\nexport const MarkdownSurface = React.memo(\n React.forwardRef<HTMLDivElement, MarkdownSurfaceProps>(function MarkdownSurface(\n {\n value,\n streaming = false,\n query,\n lightbox = false,\n gfm = true,\n math = true,\n mermaid = true,\n mermaidTheme = 'neutral',\n components,\n urlTransform = defaultUrlTransform,\n showCodeLanguage = true,\n labels,\n className,\n },\n ref,\n ) {\n const mermaidEnabled = mermaid && !streaming\n const highlightQuery =\n query !== undefined && query.trim().length > 0 ? query.trim().toLowerCase() : null\n const [zoom, setZoom] = React.useState<{ src: string; alt: string } | null>(null)\n\n const defaultComponents = React.useMemo<MarkdownComponents>(\n () => ({\n pre: ({ children }) => {\n const child = Array.isArray(children) ? children[0] : children\n if (React.isValidElement(child)) {\n const childProps = child.props as { className?: string; children?: React.ReactNode }\n const text = extractText(childProps.children).replace(/\\n$/, '')\n const language = fenceLanguage(childProps.className)\n if (language === 'mermaid' && mermaidEnabled) {\n return <MermaidDiagram code={text} theme={mermaidTheme} labels={{ diagram: labels?.code }} />\n }\n return (\n <MarkdownCodeBlock code={text} language={language} showLanguage={showCodeLanguage} labels={labels} />\n )\n }\n return <pre>{children}</pre>\n },\n code: ({ className: codeClassName, children, ...rest }) => (\n <code\n className={cn(\n 'rounded-[var(--as-radius-sm)] bg-[var(--as-surface-raised)] px-1 py-0.5 font-mono text-[0.85em]',\n codeClassName,\n )}\n {...rest}\n >\n {children}\n </code>\n ),\n a: ({ href, children, ...rest }) => {\n const external =\n typeof href === 'string' && (href.startsWith('http://') || href.startsWith('https://'))\n return (\n <a\n href={href}\n className=\"text-[var(--as-primary)] underline underline-offset-2\"\n {...(external ? { target: '_blank', rel: 'noopener noreferrer' } : {})}\n {...rest}\n >\n {children}\n </a>\n )\n },\n table: ({ children, ...rest }) => (\n <div className=\"my-3 overflow-x-auto rounded-[var(--as-radius)] border border-[var(--as-border)]\">\n <table className=\"w-full border-collapse text-[0.875em]\" {...rest}>\n {children}\n </table>\n </div>\n ),\n img: ({ alt, ...rest }) => {\n const image = <img alt={alt ?? ''} className=\"my-2 max-w-full rounded-[var(--as-radius)]\" loading=\"lazy\" {...rest} />\n if (!lightbox || typeof rest.src !== 'string') {\n return image\n }\n const zoomLabel = `${labels?.zoomImage ?? 'Zoom image'}${alt ? `: ${alt}` : ''}`\n return (\n <button\n type=\"button\"\n aria-label={zoomLabel}\n title={zoomLabel}\n onClick={() => setZoom({ src: rest.src as string, alt: alt ?? '' })}\n className=\"block cursor-zoom-in rounded-[var(--as-radius)] transition-opacity hover:opacity-90 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n {image}\n </button>\n )\n },\n }),\n [labels, mermaidEnabled, mermaidTheme, showCodeLanguage, lightbox],\n )\n\n const mergedComponents = React.useMemo(\n () => ({ ...defaultComponents, ...components }),\n [defaultComponents, components],\n )\n\n const remarkPlugins = React.useMemo(\n () => [gfm ? remarkGfm : null, math ? remarkMath : null].filter((plugin) => plugin !== null),\n [gfm, math],\n )\n const rehypePlugins = React.useMemo<ReactMarkdownOptions['rehypePlugins']>(() => {\n const plugins: ReactMarkdownOptions['rehypePlugins'] = []\n if (math) {\n plugins.push([rehypeKatex, { strict: false, throwOnError: false, output: 'html' }])\n }\n if (highlightQuery !== null) {\n plugins.push(rehypeHighlightMatches(highlightQuery))\n }\n return plugins\n }, [math, highlightQuery])\n\n return (\n <div\n ref={ref}\n data-as=\"chat-markdown\"\n data-streaming={streaming || undefined}\n className={cn('min-w-0', className)}\n >\n <ReactMarkdown\n remarkPlugins={remarkPlugins}\n rehypePlugins={rehypePlugins}\n components={mergedComponents}\n urlTransform={urlTransform}\n >\n {value}\n </ReactMarkdown>\n <Modal open={zoom !== null} onOpenChange={(open) => (open ? undefined : setZoom(null))}>\n {zoom !== null ? (\n <ModalContent closeLabel={labels?.closeImage ?? 'Close image'} className=\"w-fit max-w-[92vw] items-center justify-center p-3\">\n <img src={zoom.src} alt={zoom.alt} className=\"max-h-[78vh] max-w-[86vw] rounded-[var(--as-radius)]\" />\n </ModalContent>\n ) : null}\n </Modal>\n </div>\n )\n }),\n)\n\nexport { MarkdownCodeBlock }\n"]}
@@ -0,0 +1,3 @@
1
+
2
+ //# sourceMappingURL=chunk-M4SYVPC4.js.map
3
+ //# sourceMappingURL=chunk-M4SYVPC4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-M4SYVPC4.js"}
@@ -0,0 +1,33 @@
1
+ import { cn } from './chunk-RVVXVTH3.js';
2
+ import * as React from 'react';
3
+ import { Sparkles } from 'lucide-react';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
+
6
+ var ChatWelcome = React.forwardRef(
7
+ function ChatWelcome2({ title, subtitle, suggestions, icon, variant = "sidebar", className }, ref) {
8
+ return /* @__PURE__ */ jsxs(
9
+ "div",
10
+ {
11
+ ref,
12
+ "data-as": "chat-welcome",
13
+ "data-variant": variant,
14
+ className: cn(
15
+ "as-anim-fade-swap flex min-h-40 w-full flex-col items-center justify-center gap-2 p-6 text-center",
16
+ variant === "page" && "min-h-56 gap-3 p-10",
17
+ variant === "bubble" && "min-h-32 gap-1.5 p-4",
18
+ className
19
+ ),
20
+ children: [
21
+ /* @__PURE__ */ jsx("div", { className: "as-anim-breathe flex size-12 items-center justify-center rounded-full bg-[linear-gradient(135deg,var(--as-ai),var(--as-primary))] shadow-[var(--as-shadow-2)]", children: icon ?? /* @__PURE__ */ jsx(Sparkles, { className: "size-6 text-[var(--as-ai-fg)]", "aria-hidden": true }) }),
22
+ /* @__PURE__ */ jsx("p", { className: "text-lg font-semibold text-[var(--as-fg)]", children: title }),
23
+ subtitle ? /* @__PURE__ */ jsx("p", { className: "max-w-sm text-sm text-[var(--as-muted-fg)]", children: subtitle }) : null,
24
+ suggestions ? /* @__PURE__ */ jsx("div", { "data-as": "chat-welcome-suggestions", className: "mt-2 flex w-full flex-col items-stretch gap-2", children: suggestions }) : null
25
+ ]
26
+ }
27
+ );
28
+ }
29
+ );
30
+
31
+ export { ChatWelcome };
32
+ //# sourceMappingURL=chunk-OOQO6VGO.js.map
33
+ //# sourceMappingURL=chunk-OOQO6VGO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/chat-welcome/ChatWelcome.tsx"],"names":["ChatWelcome"],"mappings":";;;;;AAwBO,IAAM,WAAA,GAAoB,KAAA,CAAA,UAAA;AAAA,EAC/B,SAASA,YAAAA,CAAY,EAAE,KAAA,EAAO,QAAA,EAAU,WAAA,EAAa,IAAA,EAAM,OAAA,GAAU,SAAA,EAAW,SAAA,EAAU,EAAG,GAAA,EAAK;AAChG,IAAA,uBACE,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,SAAA,EAAQ,cAAA;AAAA,QACR,cAAA,EAAc,OAAA;AAAA,QACd,SAAA,EAAW,EAAA;AAAA,UACT,mGAAA;AAAA,UACA,YAAY,MAAA,IAAU,qBAAA;AAAA,UACtB,YAAY,QAAA,IAAY,sBAAA;AAAA,UACxB;AAAA,SACF;AAAA,QAEA,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,+JAAA,EACZ,QAAA,EAAA,IAAA,oBAAQ,GAAA,CAAC,YAAS,SAAA,EAAU,+BAAA,EAAgC,aAAA,EAAW,IAAA,EAAC,CAAA,EAC3E,CAAA;AAAA,0BACA,GAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,2CAAA,EAA6C,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,UAC/D,2BAAW,GAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,4CAAA,EAA8C,oBAAS,CAAA,GAAO,IAAA;AAAA,UACtF,WAAA,uBACE,KAAA,EAAA,EAAI,SAAA,EAAQ,4BAA2B,SAAA,EAAU,+CAAA,EAC/C,uBACH,CAAA,GACE;AAAA;AAAA;AAAA,KACN;AAAA,EAEJ;AACF","file":"chunk-OOQO6VGO.js","sourcesContent":["import * as React from 'react'\nimport { Sparkles } from 'lucide-react'\n\nimport { cn } from '../../lib/utils'\n\nexport interface ChatWelcomeProps {\n /** Greeting headline, e.g. \"Ask anything about your courses\". */\n title: string\n subtitle?: string\n /** Suggestion cards / chips — children stagger in (40 ms steps). */\n suggestions?: React.ReactNode\n /** Node inside the breathing `--as-ai` orb. Default: `Sparkles`. */\n icon?: React.ReactNode\n /** `page` centers generously; `sidebar` is the default density; `bubble` is compact. */\n variant?: 'page' | 'sidebar' | 'bubble'\n className?: string\n}\n\n/**\n * The chat empty-state kit (plan 100 S6): breathing `--as-ai` identity\n * orb, greeting, subtitle and staggered suggestion slots — drop it into\n * `ChatTranscript emptyState`. Presentational; suggestion targets stay\n * app-side.\n */\nexport const ChatWelcome = React.forwardRef<HTMLDivElement, ChatWelcomeProps>(\n function ChatWelcome({ title, subtitle, suggestions, icon, variant = 'sidebar', className }, ref) {\n return (\n <div\n ref={ref}\n data-as=\"chat-welcome\"\n data-variant={variant}\n className={cn(\n 'as-anim-fade-swap flex min-h-40 w-full flex-col items-center justify-center gap-2 p-6 text-center',\n variant === 'page' && 'min-h-56 gap-3 p-10',\n variant === 'bubble' && 'min-h-32 gap-1.5 p-4',\n className,\n )}\n >\n <div className=\"as-anim-breathe flex size-12 items-center justify-center rounded-full bg-[linear-gradient(135deg,var(--as-ai),var(--as-primary))] shadow-[var(--as-shadow-2)]\">\n {icon ?? <Sparkles className=\"size-6 text-[var(--as-ai-fg)]\" aria-hidden />}\n </div>\n <p className=\"text-lg font-semibold text-[var(--as-fg)]\">{title}</p>\n {subtitle ? <p className=\"max-w-sm text-sm text-[var(--as-muted-fg)]\">{subtitle}</p> : null}\n {suggestions ? (\n <div data-as=\"chat-welcome-suggestions\" className=\"mt-2 flex w-full flex-col items-stretch gap-2\">\n {suggestions}\n </div>\n ) : null}\n </div>\n )\n },\n)\n"]}
@@ -0,0 +1,366 @@
1
+ import { searchScore } from './chunk-B2G66OSQ.js';
2
+ import { cn } from './chunk-RVVXVTH3.js';
3
+ import * as React from 'react';
4
+ import { Square, ArrowUp } from 'lucide-react';
5
+ import { jsxs, jsx } from 'react/jsx-runtime';
6
+
7
+ function isIMEComposition(event) {
8
+ return event.nativeEvent.isComposing || event.keyCode === 229;
9
+ }
10
+ function tokenAtCaret(value, caret, trigger) {
11
+ const pos = Math.min(caret, value.length);
12
+ let start = pos;
13
+ while (start > 0 && !/\s/.test(value[start - 1] ?? "")) {
14
+ start -= 1;
15
+ }
16
+ const text = value.slice(start, pos);
17
+ if (!text.startsWith(trigger)) {
18
+ return null;
19
+ }
20
+ const query = text.slice(trigger.length);
21
+ if (query.includes(trigger)) {
22
+ return null;
23
+ }
24
+ return { start, end: pos, query };
25
+ }
26
+ var ChatComposer = React.forwardRef(
27
+ function ChatComposer2({
28
+ value,
29
+ onValueChange,
30
+ onSubmit,
31
+ sending = false,
32
+ onStop,
33
+ disabled = false,
34
+ canSubmit,
35
+ commands,
36
+ history,
37
+ placeholder,
38
+ ariaLabel,
39
+ maxRows = 8,
40
+ toolbarStart,
41
+ toolbarEnd,
42
+ attachments,
43
+ suggestions,
44
+ onAttachFiles,
45
+ labels,
46
+ icons,
47
+ textareaRef,
48
+ className
49
+ }, ref) {
50
+ const internalTextareaRef = React.useRef(null);
51
+ const [dragging, setDragging] = React.useState(false);
52
+ const [multiline, setMultiline] = React.useState(false);
53
+ const multilineRef = React.useRef(false);
54
+ const dragDepth = React.useRef(0);
55
+ const prevSendingRef = React.useRef(sending);
56
+ const [morphTarget, setMorphTarget] = React.useState(null);
57
+ if (prevSendingRef.current !== sending) {
58
+ prevSendingRef.current = sending;
59
+ setMorphTarget(sending ? "stop" : "send");
60
+ }
61
+ const [caret, setCaret] = React.useState(0);
62
+ const [paletteDismissed, setPaletteDismissed] = React.useState(false);
63
+ const [activeIndex, setActiveIndex] = React.useState(0);
64
+ const [historyIndex, setHistoryIndex] = React.useState(null);
65
+ const paletteId = React.useId();
66
+ const setTextarea = (element) => {
67
+ internalTextareaRef.current = element;
68
+ if (typeof textareaRef === "function") {
69
+ textareaRef(element);
70
+ } else if (textareaRef && typeof textareaRef === "object") {
71
+ textareaRef.current = element;
72
+ }
73
+ };
74
+ const syncCaret = () => {
75
+ const element = internalTextareaRef.current;
76
+ if (element !== null) {
77
+ setCaret(element.selectionStart ?? 0);
78
+ }
79
+ };
80
+ React.useLayoutEffect(() => {
81
+ const element = internalTextareaRef.current;
82
+ if (element === null) {
83
+ return;
84
+ }
85
+ const style = window.getComputedStyle(element);
86
+ const lineHeight = Number.parseFloat(style.lineHeight) || 22;
87
+ const verticalPadding = (Number.parseFloat(style.paddingTop) || 0) + (Number.parseFloat(style.paddingBottom) || 0);
88
+ const cap = Number.parseFloat(style.maxHeight) || maxRows * 22;
89
+ element.style.height = "auto";
90
+ const next = element.scrollHeight > 0 ? Math.min(Math.ceil(element.scrollHeight) + 1, cap) : 0;
91
+ element.style.height = next > 0 ? `${next}px` : "auto";
92
+ const overflowing = element.scrollHeight > element.clientHeight + 1;
93
+ element.style.overflowY = overflowing ? "auto" : "hidden";
94
+ if (!overflowing && element.scrollTop !== 0) {
95
+ element.scrollTop = 0;
96
+ }
97
+ const grew = element.scrollHeight > lineHeight + verticalPadding + 1;
98
+ multilineRef.current = value === "" ? false : multilineRef.current || grew;
99
+ setMultiline(multilineRef.current);
100
+ }, [value, maxRows]);
101
+ const trigger = commands?.trigger ?? "/";
102
+ const token = React.useMemo(
103
+ () => commands === void 0 ? null : tokenAtCaret(value, caret, trigger),
104
+ [commands, value, caret, trigger]
105
+ );
106
+ const paletteItems = React.useMemo(() => {
107
+ if (commands === void 0 || token === null) {
108
+ return [];
109
+ }
110
+ const max = commands.maxItems ?? 8;
111
+ return commands.items.map((item) => ({
112
+ item,
113
+ score: token.query === "" ? 0 : searchScore(token.query, item.label) ?? searchScore(token.query, item.id)
114
+ })).filter((entry) => entry.score !== null).sort((a, b) => b.score - a.score).slice(0, max).map((entry) => entry.item);
115
+ }, [commands, token]);
116
+ const paletteVisible = token !== null && paletteItems.length > 0 && !paletteDismissed && !disabled;
117
+ React.useEffect(() => {
118
+ setPaletteDismissed(false);
119
+ setActiveIndex(0);
120
+ }, [token?.start, token?.query]);
121
+ React.useEffect(() => {
122
+ if (value === "") {
123
+ setHistoryIndex(null);
124
+ }
125
+ }, [value]);
126
+ const selectCommand = (command) => {
127
+ if (token !== null) {
128
+ commands?.onSelect(command.id, { start: token.start, end: token.end, query: token.query });
129
+ }
130
+ setPaletteDismissed(true);
131
+ internalTextareaRef.current?.focus();
132
+ };
133
+ const submit = () => {
134
+ if (disabled || sending) {
135
+ return;
136
+ }
137
+ onSubmit();
138
+ };
139
+ const onKeyDown = (event) => {
140
+ if (paletteVisible) {
141
+ if (event.key === "ArrowDown") {
142
+ event.preventDefault();
143
+ setActiveIndex((index) => (index + 1) % paletteItems.length);
144
+ return;
145
+ }
146
+ if (event.key === "ArrowUp") {
147
+ event.preventDefault();
148
+ setActiveIndex((index) => (index - 1 + paletteItems.length) % paletteItems.length);
149
+ return;
150
+ }
151
+ if ((event.key === "Enter" || event.key === "Tab") && !isIMEComposition(event)) {
152
+ event.preventDefault();
153
+ selectCommand(paletteItems[activeIndex] ?? paletteItems[0]);
154
+ return;
155
+ }
156
+ if (event.key === "Escape") {
157
+ event.preventDefault();
158
+ setPaletteDismissed(true);
159
+ return;
160
+ }
161
+ } else if (event.key === "Escape" && sending && onStop !== void 0) {
162
+ event.preventDefault();
163
+ onStop();
164
+ return;
165
+ } else if ((event.key === "ArrowUp" || event.key === "ArrowDown") && history !== void 0 && history.length > 0 && (value === "" || historyIndex !== null)) {
166
+ event.preventDefault();
167
+ if (event.key === "ArrowUp") {
168
+ const next = historyIndex === null ? history.length - 1 : Math.max(0, historyIndex - 1);
169
+ setHistoryIndex(next);
170
+ onValueChange(history[next] ?? "");
171
+ setCaret((history[next] ?? "").length);
172
+ } else if (historyIndex !== null) {
173
+ const next = historyIndex + 1;
174
+ if (next >= history.length) {
175
+ setHistoryIndex(null);
176
+ onValueChange("");
177
+ setCaret(0);
178
+ } else {
179
+ setHistoryIndex(next);
180
+ onValueChange(history[next] ?? "");
181
+ setCaret((history[next] ?? "").length);
182
+ }
183
+ }
184
+ return;
185
+ }
186
+ if (event.key !== "Enter" || event.shiftKey) {
187
+ return;
188
+ }
189
+ if (isIMEComposition(event)) {
190
+ return;
191
+ }
192
+ event.preventDefault();
193
+ submit();
194
+ };
195
+ const emitFiles = (fileList) => {
196
+ const files = fileList ? Array.from(fileList) : [];
197
+ if (files.length > 0) {
198
+ onAttachFiles?.(files);
199
+ }
200
+ };
201
+ const dragCounterHandlers = {
202
+ onDragEnter: (event) => {
203
+ if (onAttachFiles === void 0 || !event.dataTransfer.types.includes("Files")) {
204
+ return;
205
+ }
206
+ event.preventDefault();
207
+ dragDepth.current += 1;
208
+ setDragging(true);
209
+ },
210
+ onDragOver: (event) => {
211
+ if (onAttachFiles === void 0 || !event.dataTransfer.types.includes("Files")) {
212
+ return;
213
+ }
214
+ event.preventDefault();
215
+ },
216
+ onDragLeave: () => {
217
+ if (dragDepth.current > 0) {
218
+ dragDepth.current -= 1;
219
+ if (dragDepth.current === 0) {
220
+ setDragging(false);
221
+ }
222
+ }
223
+ },
224
+ onDrop: (event) => {
225
+ if (onAttachFiles === void 0) {
226
+ return;
227
+ }
228
+ event.preventDefault();
229
+ dragDepth.current = 0;
230
+ setDragging(false);
231
+ emitFiles(event.dataTransfer.files);
232
+ }
233
+ };
234
+ const SendIcon = icons?.send ?? ArrowUp;
235
+ const StopIcon = icons?.stop ?? Square;
236
+ const showStop = sending && onStop !== void 0;
237
+ const submitBlocked = disabled || sending || (canSubmit === void 0 ? value.trim() === "" : !canSubmit);
238
+ return /* @__PURE__ */ jsxs(
239
+ "form",
240
+ {
241
+ ref,
242
+ "data-as": "chat-composer",
243
+ "data-sending": sending,
244
+ onSubmit: (event) => {
245
+ event.preventDefault();
246
+ submit();
247
+ },
248
+ className: cn("flex w-full flex-col gap-2", className),
249
+ ...dragCounterHandlers,
250
+ children: [
251
+ suggestions ? /* @__PURE__ */ jsx("div", { className: "as-anim-slide-up flex flex-wrap gap-1.5", children: suggestions }) : null,
252
+ attachments ? /* @__PURE__ */ jsx("div", { className: "as-anim-slide-up flex flex-wrap gap-1.5", children: attachments }) : null,
253
+ /* @__PURE__ */ jsxs(
254
+ "div",
255
+ {
256
+ "data-as": "chat-composer-row",
257
+ "data-multiline": multiline || void 0,
258
+ "data-dragging": dragging || void 0,
259
+ className: cn(
260
+ "relative 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",
261
+ dragging && "border-dashed border-[var(--as-primary)] bg-[var(--as-primary)]/5",
262
+ disabled && "opacity-50"
263
+ ),
264
+ children: [
265
+ paletteVisible ? /* @__PURE__ */ jsx(
266
+ "div",
267
+ {
268
+ id: paletteId,
269
+ role: "listbox",
270
+ "aria-label": labels?.commands ?? "Commands",
271
+ "data-as": "chat-composer-commands",
272
+ className: "as-anim-slide-up absolute bottom-full left-0 right-0 z-[var(--as-z-popover)] mb-1.5 max-h-56 overflow-y-auto rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface-raised)] p-1 shadow-[var(--as-shadow-pop)]",
273
+ children: paletteItems.map((item, index) => /* @__PURE__ */ jsxs(
274
+ "button",
275
+ {
276
+ type: "button",
277
+ id: `${paletteId}-option-${index}`,
278
+ role: "option",
279
+ "aria-selected": index === activeIndex,
280
+ tabIndex: -1,
281
+ onMouseEnter: () => setActiveIndex(index),
282
+ onClick: () => selectCommand(item),
283
+ className: cn(
284
+ "flex w-full items-center gap-2 rounded-[var(--as-radius-sm)] px-2 py-1.5 text-left text-sm transition-colors",
285
+ index === activeIndex ? "bg-[var(--as-secondary)] text-[var(--as-secondary-fg)]" : "text-[var(--as-fg)]"
286
+ ),
287
+ children: [
288
+ item.icon ? /* @__PURE__ */ jsx(item.icon, { className: "size-4 shrink-0 text-[var(--as-muted-fg)]", "aria-hidden": true }) : null,
289
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: item.label }),
290
+ item.hint ? /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-[var(--as-muted-fg)]", children: item.hint }) : null
291
+ ]
292
+ },
293
+ item.id
294
+ ))
295
+ }
296
+ ) : null,
297
+ toolbarStart ? /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-0.5 pb-0.5", children: toolbarStart }) : null,
298
+ /* @__PURE__ */ jsx(
299
+ "textarea",
300
+ {
301
+ ref: setTextarea,
302
+ value,
303
+ rows: 1,
304
+ disabled,
305
+ placeholder: placeholder ?? labels?.placeholder ?? "Send a message\u2026",
306
+ "aria-label": ariaLabel ?? labels?.ariaLabel ?? "Message",
307
+ "aria-controls": paletteVisible ? paletteId : void 0,
308
+ "aria-activedescendant": paletteVisible ? `${paletteId}-option-${activeIndex}` : void 0,
309
+ "aria-autocomplete": commands !== void 0 ? "list" : void 0,
310
+ onChange: (event) => {
311
+ const next = event.target.value;
312
+ if (historyIndex !== null && history?.[historyIndex] !== next) {
313
+ setHistoryIndex(null);
314
+ }
315
+ onValueChange(next);
316
+ syncCaret();
317
+ },
318
+ onKeyDown,
319
+ onKeyUp: syncCaret,
320
+ onClick: syncCaret,
321
+ onSelect: syncCaret,
322
+ onPaste: (event) => emitFiles(event.clipboardData.files),
323
+ 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"
324
+ }
325
+ ),
326
+ toolbarEnd ? /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-0.5 pb-0.5", children: toolbarEnd }) : null,
327
+ showStop ? /* @__PURE__ */ jsx(
328
+ "button",
329
+ {
330
+ type: "button",
331
+ "aria-label": labels?.stop ?? "Stop generating",
332
+ title: labels?.stop ?? "Stop generating",
333
+ onClick: onStop,
334
+ className: cn(
335
+ "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)]",
336
+ morphTarget === "stop" && "as-anim-morph-in"
337
+ ),
338
+ children: /* @__PURE__ */ jsx(StopIcon, { className: "size-3.5", "aria-hidden": true })
339
+ }
340
+ ) : /* @__PURE__ */ jsx(
341
+ "button",
342
+ {
343
+ type: "submit",
344
+ "aria-label": labels?.send ?? "Send message",
345
+ title: labels?.send ?? "Send message",
346
+ disabled: submitBlocked,
347
+ className: cn(
348
+ "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)]",
349
+ morphTarget === "send" && "as-anim-morph-in"
350
+ ),
351
+ children: /* @__PURE__ */ jsx(SendIcon, { className: "size-4", "aria-hidden": true })
352
+ }
353
+ )
354
+ ]
355
+ }
356
+ ),
357
+ dragging && onAttachFiles ? /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--as-muted-fg)]", role: "status", children: labels?.dropFiles ?? "Drop files to attach" }) : null
358
+ ]
359
+ }
360
+ );
361
+ }
362
+ );
363
+
364
+ export { ChatComposer };
365
+ //# sourceMappingURL=chunk-P3QUNCBR.js.map
366
+ //# sourceMappingURL=chunk-P3QUNCBR.js.map