@neuronection/assistant-ui 0.52.0 → 0.53.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.
- package/CHANGELOG.md +91 -0
- package/README.md +4 -1
- package/dist/chat-branch-tree.d.ts +15 -2
- package/dist/chat-composer.d.ts +43 -3
- package/dist/chat-composer.js +1 -1
- package/dist/chat-core.d.ts +54 -4
- package/dist/chat-launcher.js +1 -1
- package/dist/chat-markdown.d.ts +8 -1
- package/dist/chat-markdown.js +1 -1
- package/dist/chat-message.d.ts +16 -1
- package/dist/chat-message.js +1 -1
- package/dist/chat-panel.js +1 -1
- package/dist/chat-pending.d.ts +25 -0
- package/dist/chat-pending.js +3 -0
- package/dist/chat-pending.js.map +1 -0
- package/dist/chat-reasoning.d.ts +10 -2
- package/dist/chat-reasoning.js +1 -1
- package/dist/chat-session-list.d.ts +7 -0
- package/dist/chat-session-list.js +1 -1
- package/dist/chat-suggestion-card.d.ts +19 -0
- package/dist/chat-suggestion-card.js +3 -0
- package/dist/chat-suggestion-card.js.map +1 -0
- package/dist/chat-tool-card.js +1 -1
- package/dist/chat-transcript.d.ts +81 -8
- package/dist/chat-transcript.js +1 -1
- package/dist/chat-turn-status.js +1 -1
- package/dist/chat-welcome.d.ts +23 -0
- package/dist/chat-welcome.js +3 -0
- package/dist/chat-welcome.js.map +1 -0
- package/dist/{chunk-PC3CE4W2.js → chunk-22MIIWC7.js} +45 -7
- package/dist/chunk-22MIIWC7.js.map +1 -0
- package/dist/{chunk-GDGKARRB.js → chunk-5C2OLUDR.js} +11 -2
- package/dist/chunk-5C2OLUDR.js.map +1 -0
- package/dist/{chunk-5BJ4VULP.js → chunk-EQPXM2R4.js} +23 -10
- package/dist/chunk-EQPXM2R4.js.map +1 -0
- package/dist/chunk-F4BTJQLK.js +43 -0
- package/dist/chunk-F4BTJQLK.js.map +1 -0
- package/dist/{chunk-TQDTFPM4.js → chunk-GIQZNEG5.js} +3 -3
- package/dist/chunk-GIQZNEG5.js.map +1 -0
- package/dist/{chunk-E3NZM4U7.js → chunk-GO4ILLS6.js} +17 -4
- package/dist/chunk-GO4ILLS6.js.map +1 -0
- package/dist/chunk-HXU6P5FD.js +479 -0
- package/dist/chunk-HXU6P5FD.js.map +1 -0
- package/dist/{chunk-MFM4W6ZO.js → chunk-I4R5BRC7.js} +3 -3
- package/dist/{chunk-MFM4W6ZO.js.map → chunk-I4R5BRC7.js.map} +1 -1
- package/dist/{chunk-5U45TQJE.js → chunk-I5H4KYEZ.js} +3 -3
- package/dist/{chunk-5U45TQJE.js.map → chunk-I5H4KYEZ.js.map} +1 -1
- package/dist/{chunk-S63SYV6O.js → chunk-INXQJM2K.js} +27 -6
- package/dist/chunk-INXQJM2K.js.map +1 -0
- package/dist/{chunk-HQ3X5SVR.js → chunk-KQLKITZL.js} +105 -16
- package/dist/chunk-KQLKITZL.js.map +1 -0
- package/dist/chunk-M4SYVPC4.js +3 -0
- package/dist/chunk-M4SYVPC4.js.map +1 -0
- package/dist/chunk-NOAKHVBJ.js +264 -0
- package/dist/chunk-NOAKHVBJ.js.map +1 -0
- package/dist/chunk-OOQO6VGO.js +33 -0
- package/dist/chunk-OOQO6VGO.js.map +1 -0
- package/dist/chunk-P3QUNCBR.js +366 -0
- package/dist/chunk-P3QUNCBR.js.map +1 -0
- package/dist/chunk-PABMR4RA.js +30 -0
- package/dist/chunk-PABMR4RA.js.map +1 -0
- package/dist/{chunk-SEZZVAGP.js → chunk-SR4IN564.js} +10 -3
- package/dist/chunk-SR4IN564.js.map +1 -0
- package/dist/index.d.ts +243 -36
- package/dist/index.js +40 -37
- package/dist/markdown-diff-view.js +1 -1
- package/dist/model-registry.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/theme-scope.d.ts +1 -1
- package/dist/tokens.d.ts +1 -1
- package/dist/tokens.js +1 -1
- package/dist/{types-LhE1KFBf.d.ts → types-CxieoN2u.d.ts} +1 -1
- package/dist/types-pivJdPC8.d.ts +14 -0
- package/package.json +13 -1
- package/dist/chunk-5BJ4VULP.js.map +0 -1
- package/dist/chunk-C5JZCCU7.js +0 -220
- package/dist/chunk-C5JZCCU7.js.map +0 -1
- package/dist/chunk-E3NZM4U7.js.map +0 -1
- package/dist/chunk-GDGKARRB.js.map +0 -1
- package/dist/chunk-HQ3X5SVR.js.map +0 -1
- package/dist/chunk-PC3CE4W2.js.map +0 -1
- package/dist/chunk-S63SYV6O.js.map +0 -1
- package/dist/chunk-SEZZVAGP.js.map +0 -1
- package/dist/chunk-TQDTFPM4.js.map +0 -1
- package/dist/chunk-XOODPYJ3.js +0 -173
- package/dist/chunk-XOODPYJ3.js.map +0 -1
- package/dist/chunk-YQQZMWHL.js +0 -201
- package/dist/chunk-YQQZMWHL.js.map +0 -1
- package/dist/tree-4Hv9stjF.d.ts +0 -54
|
@@ -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 }) =>
|
|
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
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
children:
|
|
266
|
+
ref,
|
|
267
|
+
"data-as": "chat-markdown",
|
|
268
|
+
"data-streaming": streaming || void 0,
|
|
269
|
+
className: cn("min-w-0 text-[var(--as-fg)]", 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-
|
|
201
|
-
//# sourceMappingURL=chunk-
|
|
289
|
+
//# sourceMappingURL=chunk-KQLKITZL.js.map
|
|
290
|
+
//# sourceMappingURL=chunk-KQLKITZL.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-KQLKITZL.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 text-[var(--as-fg)]', 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 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-M4SYVPC4.js"}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { Button } from './chunk-4WBBF5LH.js';
|
|
2
|
+
import { cn } from './chunk-RVVXVTH3.js';
|
|
3
|
+
import * as React3 from 'react';
|
|
4
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
5
|
+
import { ChevronLeft, ChevronRight, Check, Copy, Pencil, RotateCcw, TriangleAlert } from 'lucide-react';
|
|
6
|
+
|
|
7
|
+
var ChatMessageEditor = React3.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("as-anim-slide-up flex w-full flex-col gap-2", className), children: [
|
|
23
|
+
/* @__PURE__ */ jsx(
|
|
24
|
+
"textarea",
|
|
25
|
+
{
|
|
26
|
+
autoFocus: true,
|
|
27
|
+
value,
|
|
28
|
+
rows,
|
|
29
|
+
"aria-label": labels?.ariaLabel ?? "Edit message",
|
|
30
|
+
onChange: (event) => onValueChange(event.target.value),
|
|
31
|
+
onKeyDown,
|
|
32
|
+
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)]"
|
|
33
|
+
}
|
|
34
|
+
),
|
|
35
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2", children: [
|
|
36
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: onCancel, children: labels?.cancel ?? "Cancel" }),
|
|
37
|
+
/* @__PURE__ */ jsx(Button, { size: "sm", disabled: submitDisabled, onClick: onSubmit, children: labels?.save ?? "Save & resend" })
|
|
38
|
+
] })
|
|
39
|
+
] });
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
var MessageVariantSwitcher = React3.forwardRef(
|
|
43
|
+
function MessageVariantSwitcher2({ variants, onSelect, labels, className }, ref) {
|
|
44
|
+
const { index, count, siblingIds } = variants;
|
|
45
|
+
const previousId = index > 1 ? siblingIds[index - 2] : void 0;
|
|
46
|
+
const nextId = index < count ? siblingIds[index] : void 0;
|
|
47
|
+
const select = (event, messageId) => {
|
|
48
|
+
event.stopPropagation();
|
|
49
|
+
if (messageId !== void 0) {
|
|
50
|
+
onSelect(messageId);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
return /* @__PURE__ */ jsxs(
|
|
54
|
+
"div",
|
|
55
|
+
{
|
|
56
|
+
ref,
|
|
57
|
+
role: "group",
|
|
58
|
+
"aria-label": labels?.group ?? "Message variants",
|
|
59
|
+
"data-as": "message-variant-switcher",
|
|
60
|
+
className: cn("flex items-center gap-0.5 text-xs text-[var(--as-muted-fg)]", className),
|
|
61
|
+
children: [
|
|
62
|
+
/* @__PURE__ */ jsx(
|
|
63
|
+
"button",
|
|
64
|
+
{
|
|
65
|
+
type: "button",
|
|
66
|
+
disabled: previousId === void 0,
|
|
67
|
+
"aria-label": labels?.previous ?? "Previous variant",
|
|
68
|
+
title: labels?.previous ?? "Previous variant",
|
|
69
|
+
onClick: (event) => select(event, previousId),
|
|
70
|
+
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)]",
|
|
71
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-3.5", "aria-hidden": true })
|
|
72
|
+
}
|
|
73
|
+
),
|
|
74
|
+
/* @__PURE__ */ jsxs("span", { className: "tabular-nums", title: labels?.variantOf?.(index, count) ?? `Variant ${index} of ${count}`, children: [
|
|
75
|
+
index,
|
|
76
|
+
"/",
|
|
77
|
+
count
|
|
78
|
+
] }),
|
|
79
|
+
/* @__PURE__ */ jsx(
|
|
80
|
+
"button",
|
|
81
|
+
{
|
|
82
|
+
type: "button",
|
|
83
|
+
disabled: nextId === void 0,
|
|
84
|
+
"aria-label": labels?.next ?? "Next variant",
|
|
85
|
+
title: labels?.next ?? "Next variant",
|
|
86
|
+
onClick: (event) => select(event, nextId),
|
|
87
|
+
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)]",
|
|
88
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5", "aria-hidden": true })
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
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)]";
|
|
97
|
+
var COPIED_FEEDBACK_MS = 1500;
|
|
98
|
+
var ChatMessage = React3.forwardRef(
|
|
99
|
+
function ChatMessage2({
|
|
100
|
+
role,
|
|
101
|
+
content,
|
|
102
|
+
reasoning,
|
|
103
|
+
children,
|
|
104
|
+
status = "done",
|
|
105
|
+
error,
|
|
106
|
+
actions,
|
|
107
|
+
editing = false,
|
|
108
|
+
variants,
|
|
109
|
+
onSelectVariant,
|
|
110
|
+
meta,
|
|
111
|
+
chips,
|
|
112
|
+
attachments,
|
|
113
|
+
avatar,
|
|
114
|
+
design = "bubbles",
|
|
115
|
+
labels,
|
|
116
|
+
compact = false,
|
|
117
|
+
className
|
|
118
|
+
}, ref) {
|
|
119
|
+
const isUser = role === "user";
|
|
120
|
+
const isSystem = role === "system";
|
|
121
|
+
const showActions = actions !== void 0 && (actions.onCopy !== void 0 || actions.onEdit !== void 0 || actions.onRegenerate !== void 0 || (actions.extras?.length ?? 0) > 0);
|
|
122
|
+
const editorLabels = { save: labels?.save, cancel: labels?.cancel, ariaLabel: labels?.ariaLabel };
|
|
123
|
+
const switcherLabels = { group: labels?.group, variantOf: labels?.variantOf, previous: labels?.previous, next: labels?.next };
|
|
124
|
+
const [copiedAt, setCopiedAt] = React3.useState(null);
|
|
125
|
+
const copied = actions?.copied ?? copiedAt !== null;
|
|
126
|
+
React3.useEffect(() => {
|
|
127
|
+
if (copiedAt === null) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const id = window.setTimeout(() => setCopiedAt(null), COPIED_FEEDBACK_MS);
|
|
131
|
+
return () => window.clearTimeout(id);
|
|
132
|
+
}, [copiedAt]);
|
|
133
|
+
const onCopyClick = () => {
|
|
134
|
+
actions?.onCopy?.();
|
|
135
|
+
if (actions?.copied === void 0) {
|
|
136
|
+
setCopiedAt(Date.now());
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
if (isSystem) {
|
|
140
|
+
return /* @__PURE__ */ jsx(
|
|
141
|
+
"div",
|
|
142
|
+
{
|
|
143
|
+
ref,
|
|
144
|
+
"data-as": "chat-message",
|
|
145
|
+
"data-role": role,
|
|
146
|
+
className: cn("flex justify-center", className),
|
|
147
|
+
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 })
|
|
148
|
+
}
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
return /* @__PURE__ */ jsxs(
|
|
152
|
+
"div",
|
|
153
|
+
{
|
|
154
|
+
ref,
|
|
155
|
+
"data-as": "chat-message",
|
|
156
|
+
"data-role": role,
|
|
157
|
+
"data-status": status,
|
|
158
|
+
"data-design": design,
|
|
159
|
+
className: cn("as-anim-message-in group/message flex w-full flex-col", isUser ? "items-end" : "items-start", className),
|
|
160
|
+
children: [
|
|
161
|
+
attachments ? /* @__PURE__ */ jsx("div", { className: "mb-1.5 flex w-full flex-wrap justify-end gap-1.5", children: attachments }) : null,
|
|
162
|
+
(() => {
|
|
163
|
+
const bubble = /* @__PURE__ */ jsxs(
|
|
164
|
+
"div",
|
|
165
|
+
{
|
|
166
|
+
className: cn(
|
|
167
|
+
"relative flex max-w-[92%] flex-col gap-1.5 rounded-[var(--as-radius)] px-3 py-2 text-sm",
|
|
168
|
+
compact && "px-2.5 py-1.5",
|
|
169
|
+
isUser ? "rounded-br-[var(--as-radius-sm)] bg-[var(--as-primary)] text-[var(--as-primary-fg)]" : design === "flat" ? "max-w-full rounded-bl-[var(--as-radius-sm)] bg-transparent px-0 py-0" : "rounded-bl-[var(--as-radius-sm)] bg-[var(--as-muted)] text-[var(--as-fg)]"
|
|
170
|
+
),
|
|
171
|
+
children: [
|
|
172
|
+
reasoning ? /* @__PURE__ */ jsx("div", { className: "not-prose", children: reasoning }) : null,
|
|
173
|
+
editing ? /* @__PURE__ */ jsx(
|
|
174
|
+
ChatMessageEditor,
|
|
175
|
+
{
|
|
176
|
+
value: editing.value,
|
|
177
|
+
onValueChange: editing.onValueChange,
|
|
178
|
+
onSubmit: editing.onSubmit,
|
|
179
|
+
onCancel: editing.onCancel,
|
|
180
|
+
submitDisabled: editing.submitDisabled,
|
|
181
|
+
labels: editorLabels
|
|
182
|
+
}
|
|
183
|
+
) : /* @__PURE__ */ jsx(
|
|
184
|
+
"div",
|
|
185
|
+
{
|
|
186
|
+
className: cn("min-w-0", variants && variants.count > 1 && "as-anim-fade-swap"),
|
|
187
|
+
children: content
|
|
188
|
+
},
|
|
189
|
+
variants && variants.count > 1 ? `variant-${variants.index}` : void 0
|
|
190
|
+
),
|
|
191
|
+
status === "interrupted" ? /* @__PURE__ */ jsx("span", { className: "as-anim-slide-up as-anim-pulse-once [--as-pulse-color:var(--as-warning)] 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,
|
|
192
|
+
status === "error" && error ? /* @__PURE__ */ jsxs(
|
|
193
|
+
"div",
|
|
194
|
+
{
|
|
195
|
+
role: "alert",
|
|
196
|
+
className: "as-anim-slide-up 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)]",
|
|
197
|
+
children: [
|
|
198
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono font-semibold uppercase tracking-wide", children: error.code }),
|
|
199
|
+
/* @__PURE__ */ jsx("span", { className: "whitespace-pre-wrap break-words", children: error.message }),
|
|
200
|
+
error.retryable && actions?.onRetry ? /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [
|
|
201
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: "self-start", onClick: actions.onRetry, children: labels?.retry ?? "Retry" }),
|
|
202
|
+
actions?.errorExtra
|
|
203
|
+
] }) : actions?.errorExtra ? /* @__PURE__ */ jsx("div", { className: "self-start", children: actions.errorExtra }) : null
|
|
204
|
+
]
|
|
205
|
+
}
|
|
206
|
+
) : null,
|
|
207
|
+
chips ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: chips }) : null,
|
|
208
|
+
children ? /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1.5", children }) : null,
|
|
209
|
+
showActions && !editing ? /* @__PURE__ */ jsxs(
|
|
210
|
+
"div",
|
|
211
|
+
{
|
|
212
|
+
"data-as": "chat-message-actions",
|
|
213
|
+
className: cn(
|
|
214
|
+
"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 shadow-[var(--as-shadow-1)]",
|
|
215
|
+
isUser ? "-left-9" : "-right-9"
|
|
216
|
+
),
|
|
217
|
+
children: [
|
|
218
|
+
actions?.onCopy ? /* @__PURE__ */ jsx(
|
|
219
|
+
"button",
|
|
220
|
+
{
|
|
221
|
+
type: "button",
|
|
222
|
+
className: actionButtonClass,
|
|
223
|
+
"aria-label": copied ? labels?.copied ?? "Copied" : labels?.copy ?? "Copy",
|
|
224
|
+
title: copied ? labels?.copied ?? "Copied" : labels?.copy ?? "Copy",
|
|
225
|
+
onClick: onCopyClick,
|
|
226
|
+
children: copied ? /* @__PURE__ */ jsx(Check, { className: "as-anim-fade-swap size-3.5 text-[var(--as-success)]", "aria-hidden": true }) : /* @__PURE__ */ jsx(Copy, { className: "size-3.5", "aria-hidden": true })
|
|
227
|
+
}
|
|
228
|
+
) : null,
|
|
229
|
+
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,
|
|
230
|
+
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,
|
|
231
|
+
actions?.extras?.map((extra) => /* @__PURE__ */ jsx(
|
|
232
|
+
"button",
|
|
233
|
+
{
|
|
234
|
+
type: "button",
|
|
235
|
+
className: cn(actionButtonClass, extra.danger && "hover:text-[var(--as-danger)]"),
|
|
236
|
+
"aria-label": extra.label,
|
|
237
|
+
title: extra.label,
|
|
238
|
+
onClick: extra.onClick,
|
|
239
|
+
children: extra.icon ? /* @__PURE__ */ jsx(extra.icon, { className: "size-3.5", "aria-hidden": true }) : /* @__PURE__ */ jsx(TriangleAlert, { className: "size-3.5", "aria-hidden": true })
|
|
240
|
+
},
|
|
241
|
+
extra.key
|
|
242
|
+
))
|
|
243
|
+
]
|
|
244
|
+
}
|
|
245
|
+
) : null
|
|
246
|
+
]
|
|
247
|
+
}
|
|
248
|
+
);
|
|
249
|
+
return avatar !== void 0 ? /* @__PURE__ */ jsxs("div", { className: cn("flex w-full items-end gap-2", isUser ? "flex-row-reverse" : "flex-row"), children: [
|
|
250
|
+
/* @__PURE__ */ jsx("div", { "data-as": "chat-message-avatar", className: "shrink-0 pb-1", children: avatar }),
|
|
251
|
+
bubble
|
|
252
|
+
] }) : bubble;
|
|
253
|
+
})(),
|
|
254
|
+
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,
|
|
255
|
+
variants && variants.count > 1 && onSelectVariant ? /* @__PURE__ */ jsx(MessageVariantSwitcher, { variants, onSelect: onSelectVariant, labels: switcherLabels, className: "mt-0.5 px-1" }) : null
|
|
256
|
+
]
|
|
257
|
+
}
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
export { ChatMessage, ChatMessageEditor, MessageVariantSwitcher };
|
|
263
|
+
//# sourceMappingURL=chunk-NOAKHVBJ.js.map
|
|
264
|
+
//# sourceMappingURL=chunk-NOAKHVBJ.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":["React","ChatMessageEditor","React2","MessageVariantSwitcher","jsxs","jsx","ChatMessage"],"mappings":"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","file":"chunk-NOAKHVBJ.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('as-anim-slide-up flex w-full flex-col gap-2', className)}>\n <textarea\n autoFocus\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 { Check, 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 /**\n * Controlled copied feedback for the copy action (check morph).\n * Uncontrolled by default: the library shows the check for 1.5 s\n * after each copy click.\n */\n copied?: boolean\n onEdit?: () => void\n onRegenerate?: () => void\n onRetry?: () => void\n /** Extra icon actions (app-specific). */\n extras?: ChatMessageAction[]\n /**\n * Extra node rendered inside the error card next to the Retry button —\n * e.g. an \"Open AI settings\" link when the error is a not-configured\n * failure. Rendered only while the error card is visible.\n */\n errorExtra?: React.ReactNode\n}\n\nexport interface ChatMessageLabels {\n copy: string\n copied: 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 /** Leading identity node beside the bubble (user: trailing). Render group-start avatars only when grouping transcripts. */\n avatar?: React.ReactNode\n /**\n * Visual design: `bubbles` (default) — paired role bubbles; `flat` —\n * assistant content renders as prose on the panel surface (user keeps\n * its bubble). Reasoning/tool cards are unaffected.\n */\n design?: 'bubbles' | 'flat'\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\nconst COPIED_FEEDBACK_MS = 1500\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 avatar,\n design = 'bubbles',\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 const [copiedAt, setCopiedAt] = React.useState<number | null>(null)\n const copied = actions?.copied ?? copiedAt !== null\n\n React.useEffect(() => {\n if (copiedAt === null) {\n return\n }\n const id = window.setTimeout(() => setCopiedAt(null), COPIED_FEEDBACK_MS)\n return () => window.clearTimeout(id)\n }, [copiedAt])\n\n const onCopyClick = () => {\n actions?.onCopy?.()\n if (actions?.copied === undefined) {\n setCopiedAt(Date.now())\n }\n }\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 data-design={design}\n className={cn('as-anim-message-in 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 {(() => {\n const bubble = (\n <div\n className={cn(\n 'relative flex max-w-[92%] flex-col gap-1.5 rounded-[var(--as-radius)] 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 : design === 'flat'\n ? 'max-w-full rounded-bl-[var(--as-radius-sm)] bg-transparent px-0 py-0'\n : 'rounded-bl-[var(--as-radius-sm)] bg-[var(--as-muted)] 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\n key={variants && variants.count > 1 ? `variant-${variants.index}` : undefined}\n className={cn('min-w-0', variants && variants.count > 1 && 'as-anim-fade-swap')}\n >\n {content}\n </div>\n )}\n {status === 'interrupted' ? (\n <span className=\"as-anim-slide-up as-anim-pulse-once [--as-pulse-color:var(--as-warning)] 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=\"as-anim-slide-up 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 <div className=\"flex flex-wrap items-center gap-1.5\">\n <Button variant=\"outline\" size=\"sm\" className=\"self-start\" onClick={actions.onRetry}>\n {labels?.retry ?? 'Retry'}\n </Button>\n {actions?.errorExtra}\n </div>\n ) : actions?.errorExtra ? (\n <div className=\"self-start\">{actions.errorExtra}</div>\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 data-as=\"chat-message-actions\"\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 shadow-[var(--as-shadow-1)]',\n isUser ? '-left-9' : '-right-9',\n )}\n >\n {actions?.onCopy ? (\n <button\n type=\"button\"\n className={actionButtonClass}\n aria-label={copied ? (labels?.copied ?? 'Copied') : (labels?.copy ?? 'Copy')}\n title={copied ? (labels?.copied ?? 'Copied') : (labels?.copy ?? 'Copy')}\n onClick={onCopyClick}\n >\n {copied ? (\n <Check className=\"as-anim-fade-swap size-3.5 text-[var(--as-success)]\" aria-hidden />\n ) : (\n <Copy className=\"size-3.5\" aria-hidden />\n )}\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 )\n return avatar !== undefined ? (\n <div className={cn('flex w-full items-end gap-2', isUser ? 'flex-row-reverse' : 'flex-row')}>\n <div data-as=\"chat-message-avatar\" className=\"shrink-0 pb-1\">\n {avatar}\n </div>\n {bubble}\n </div>\n ) : (\n bubble\n )\n })()}\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,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"]}
|