@oakkles/llm-ui-react 0.1.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.
package/dist/index.cjs ADDED
@@ -0,0 +1,2566 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+ var clsx = require('clsx');
6
+ var tailwindMerge = require('tailwind-merge');
7
+ var ReactMarkdown = require('react-markdown');
8
+ var remarkGfm = require('remark-gfm');
9
+ var reactShiki = require('react-shiki');
10
+
11
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
+
13
+ var ReactMarkdown__default = /*#__PURE__*/_interopDefault(ReactMarkdown);
14
+ var remarkGfm__default = /*#__PURE__*/_interopDefault(remarkGfm);
15
+
16
+ // src/components/bubble/BubblePrimitive.tsx
17
+ var statusTextMap = {
18
+ sending: "\u53D1\u9001\u4E2D",
19
+ sent: "\u5DF2\u53D1\u9001",
20
+ error: "\u53D1\u9001\u5931\u8D25"
21
+ };
22
+ function formatTimestamp(timestamp) {
23
+ if (typeof timestamp === "string") {
24
+ return timestamp;
25
+ }
26
+ return timestamp.toLocaleTimeString([], {
27
+ hour: "2-digit",
28
+ minute: "2-digit"
29
+ });
30
+ }
31
+ function BubblePrimitive({
32
+ role,
33
+ content,
34
+ avatar,
35
+ timestamp,
36
+ status,
37
+ loading,
38
+ children,
39
+ actions,
40
+ className,
41
+ style
42
+ }) {
43
+ return /* @__PURE__ */ jsxRuntime.jsxs(
44
+ "div",
45
+ {
46
+ className,
47
+ style,
48
+ "data-role": role,
49
+ "data-status": status,
50
+ "data-loading": loading,
51
+ "data-has-actions": actions ? "" : void 0,
52
+ children: [
53
+ avatar && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-bubble__avatar", "data-slot": "bubble-avatar", children: avatar }),
54
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "llm-bubble__body", "data-slot": "bubble-body", children: [
55
+ (children ?? content) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-bubble__content", "data-slot": "bubble-content", children: children ?? content }),
56
+ (timestamp || status || loading) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "llm-bubble__meta", "data-slot": "bubble-meta", children: [
57
+ timestamp && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "llm-bubble__time", "data-slot": "bubble-time", children: formatTimestamp(timestamp) }),
58
+ status && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "llm-bubble__status", "data-slot": "bubble-status", children: statusTextMap[status] }),
59
+ loading && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "llm-bubble__loading", "data-slot": "bubble-loading", children: "\u601D\u8003\u4E2D" })
60
+ ] }),
61
+ actions ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-bubble__actions", "data-slot": "bubble-actions", children: actions }) : null
62
+ ] })
63
+ ]
64
+ }
65
+ );
66
+ }
67
+ function cn(...inputs) {
68
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
69
+ }
70
+ function Bubble({ className, ...props }) {
71
+ return /* @__PURE__ */ jsxRuntime.jsx(BubblePrimitive, { className: cn("llm-bubble", className), ...props });
72
+ }
73
+ function getLanguageLabel(language) {
74
+ const normalizedLanguage = language.toLowerCase();
75
+ const labels = {
76
+ astro: "Astro",
77
+ bash: "Bash",
78
+ bat: "Batch",
79
+ c: "C",
80
+ cpp: "C++",
81
+ "c++": "C++",
82
+ csharp: "C#",
83
+ cs: "C#",
84
+ css: "CSS",
85
+ dart: "Dart",
86
+ diff: "Diff",
87
+ docker: "Dockerfile",
88
+ dockerfile: "Dockerfile",
89
+ elixir: "Elixir",
90
+ ex: "Elixir",
91
+ go: "Go",
92
+ graphql: "GraphQL",
93
+ groovy: "Groovy",
94
+ html: "HTML",
95
+ java: "Java",
96
+ javascript: "JavaScript",
97
+ js: "JavaScript",
98
+ json: "JSON",
99
+ jsonc: "JSONC",
100
+ jsx: "JavaScript JSX",
101
+ kotlin: "Kotlin",
102
+ kt: "Kotlin",
103
+ less: "Less",
104
+ lua: "Lua",
105
+ markdown: "Markdown",
106
+ md: "Markdown",
107
+ objectivec: "Objective-C",
108
+ objc: "Objective-C",
109
+ perl: "Perl",
110
+ php: "PHP",
111
+ plaintext: "Plain Text",
112
+ powershell: "PowerShell",
113
+ prisma: "Prisma",
114
+ ps1: "PowerShell",
115
+ py: "Python",
116
+ python: "Python",
117
+ r: "R",
118
+ rb: "Ruby",
119
+ ruby: "Ruby",
120
+ rust: "Rust",
121
+ rs: "Rust",
122
+ sass: "Sass",
123
+ scala: "Scala",
124
+ scss: "SCSS",
125
+ shell: "Shell",
126
+ sh: "Shell",
127
+ sql: "SQL",
128
+ svelte: "Svelte",
129
+ swift: "Swift",
130
+ text: "Plain Text",
131
+ toml: "TOML",
132
+ ts: "TypeScript",
133
+ tsx: "TypeScript TSX",
134
+ typescript: "TypeScript",
135
+ vue: "Vue",
136
+ wasm: "WebAssembly",
137
+ xml: "XML",
138
+ yaml: "YAML",
139
+ yml: "YAML",
140
+ zig: "Zig"
141
+ };
142
+ return labels[normalizedLanguage] ?? language;
143
+ }
144
+ function CodeIcon() {
145
+ return /* @__PURE__ */ jsxRuntime.jsxs(
146
+ "svg",
147
+ {
148
+ "aria-hidden": "true",
149
+ className: "llm-code-highlighter__code-icon",
150
+ fill: "none",
151
+ height: "16",
152
+ viewBox: "0 0 24 24",
153
+ width: "16",
154
+ xmlns: "http://www.w3.org/2000/svg",
155
+ children: [
156
+ /* @__PURE__ */ jsxRuntime.jsx(
157
+ "path",
158
+ {
159
+ d: "m9 18-6-6 6-6",
160
+ stroke: "currentColor",
161
+ strokeLinecap: "round",
162
+ strokeLinejoin: "round",
163
+ strokeWidth: "2"
164
+ }
165
+ ),
166
+ /* @__PURE__ */ jsxRuntime.jsx(
167
+ "path",
168
+ {
169
+ d: "m15 6 6 6-6 6",
170
+ stroke: "currentColor",
171
+ strokeLinecap: "round",
172
+ strokeLinejoin: "round",
173
+ strokeWidth: "2"
174
+ }
175
+ )
176
+ ]
177
+ }
178
+ );
179
+ }
180
+ function CopyIcon() {
181
+ return /* @__PURE__ */ jsxRuntime.jsxs(
182
+ "svg",
183
+ {
184
+ "aria-hidden": "true",
185
+ className: "llm-code-highlighter__copy-icon",
186
+ fill: "none",
187
+ height: "16",
188
+ viewBox: "0 0 24 24",
189
+ width: "16",
190
+ xmlns: "http://www.w3.org/2000/svg",
191
+ children: [
192
+ /* @__PURE__ */ jsxRuntime.jsx(
193
+ "rect",
194
+ {
195
+ height: "13",
196
+ rx: "2",
197
+ stroke: "currentColor",
198
+ strokeLinecap: "round",
199
+ strokeLinejoin: "round",
200
+ strokeWidth: "2",
201
+ width: "13",
202
+ x: "9",
203
+ y: "2"
204
+ }
205
+ ),
206
+ /* @__PURE__ */ jsxRuntime.jsx(
207
+ "path",
208
+ {
209
+ d: "M5 9H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2-2v-1",
210
+ stroke: "currentColor",
211
+ strokeLinecap: "round",
212
+ strokeLinejoin: "round",
213
+ strokeWidth: "2"
214
+ }
215
+ )
216
+ ]
217
+ }
218
+ );
219
+ }
220
+ var codeTheme = { light: "github-light", dark: "github-dark" };
221
+ function CheckIcon() {
222
+ return /* @__PURE__ */ jsxRuntime.jsx(
223
+ "svg",
224
+ {
225
+ "aria-hidden": "true",
226
+ className: "llm-code-highlighter__copy-icon",
227
+ fill: "none",
228
+ height: "16",
229
+ viewBox: "0 0 24 24",
230
+ width: "16",
231
+ xmlns: "http://www.w3.org/2000/svg",
232
+ children: /* @__PURE__ */ jsxRuntime.jsx(
233
+ "path",
234
+ {
235
+ d: "m20 6-11 11-5-5",
236
+ stroke: "currentColor",
237
+ strokeLinecap: "round",
238
+ strokeLinejoin: "round",
239
+ strokeWidth: "2"
240
+ }
241
+ )
242
+ }
243
+ );
244
+ }
245
+ function CodeHighlighterPrimitive({
246
+ code,
247
+ copyable = true,
248
+ language = "text",
249
+ showLineNumbers = false,
250
+ startingLineNumber = 1,
251
+ className
252
+ }) {
253
+ const [copied, setCopied] = react.useState(false);
254
+ const languageLabel = getLanguageLabel(language);
255
+ const highlightedCode = reactShiki.useShikiHighlighter(code, language, codeTheme, {
256
+ defaultColor: "light-dark()",
257
+ showLineNumbers,
258
+ startingLineNumber
259
+ });
260
+ react.useEffect(() => {
261
+ if (!copied) return;
262
+ const timer = window.setTimeout(() => {
263
+ setCopied(false);
264
+ }, 1500);
265
+ return () => window.clearTimeout(timer);
266
+ }, [copied]);
267
+ const handleCopy = async () => {
268
+ await navigator.clipboard.writeText(code);
269
+ setCopied(true);
270
+ };
271
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
272
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "llm-code-highlighter__header", children: [
273
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "llm-code-highlighter__title", children: [
274
+ /* @__PURE__ */ jsxRuntime.jsx(CodeIcon, {}),
275
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "llm-code-highlighter__language", children: languageLabel })
276
+ ] }),
277
+ copyable ? /* @__PURE__ */ jsxRuntime.jsx(
278
+ "button",
279
+ {
280
+ "aria-label": copied ? "Code copied" : "Copy code",
281
+ className: "llm-code-highlighter__copy",
282
+ onClick: handleCopy,
283
+ type: "button",
284
+ children: copied ? /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(CopyIcon, {})
285
+ }
286
+ ) : null
287
+ ] }),
288
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-code-highlighter__body", children: highlightedCode ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rs-root not-prose", children: highlightedCode }) : /* @__PURE__ */ jsxRuntime.jsx("pre", { "aria-hidden": "true", className: "llm-code-highlighter__placeholder", children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: code }) }) })
289
+ ] });
290
+ }
291
+ function CodeHighlighter({
292
+ className,
293
+ ...props
294
+ }) {
295
+ return /* @__PURE__ */ jsxRuntime.jsx(
296
+ CodeHighlighterPrimitive,
297
+ {
298
+ className: cn("llm-code-highlighter", className),
299
+ ...props
300
+ }
301
+ );
302
+ }
303
+
304
+ // src/utils/markdown.ts
305
+ function sanitizeMarkdown(content) {
306
+ const codeBlockCount = (content.match(/```/g) || []).length;
307
+ if (codeBlockCount % 2 !== 0) {
308
+ content += `${content.endsWith("\n") ? "" : "\n"}
309
+ \`\`\``;
310
+ }
311
+ const stripped = content.replace(/```/g, "");
312
+ const inlineCount = (stripped.match(/`/g) || []).length;
313
+ if (inlineCount % 2 !== 0) {
314
+ content += "`";
315
+ }
316
+ return content;
317
+ }
318
+ function getCodeLanguage(className) {
319
+ return className?.match(/language-([\w-]+)/)?.[1] ?? "text";
320
+ }
321
+ function getCodeContent(children) {
322
+ return String(children ?? "").replace(/\n$/, "");
323
+ }
324
+ function hasOpenCodeFence(content) {
325
+ return (content.match(/```/g) ?? []).length % 2 !== 0;
326
+ }
327
+ function isCodeElement(node) {
328
+ return react.isValidElement(node);
329
+ }
330
+ var markdownComponents = {
331
+ code: ({ children, className }) => /* @__PURE__ */ jsxRuntime.jsx("code", { className, children }),
332
+ img: ({ alt, src, title }) => /* @__PURE__ */ jsxRuntime.jsx("img", { alt, loading: "lazy", src, title }),
333
+ pre: ({ children }) => {
334
+ if (!isCodeElement(children)) return null;
335
+ return /* @__PURE__ */ jsxRuntime.jsx(
336
+ CodeHighlighter,
337
+ {
338
+ code: getCodeContent(children.props.children),
339
+ language: getCodeLanguage(children.props.className)
340
+ }
341
+ );
342
+ }
343
+ };
344
+ function MarkPrimitive({
345
+ content,
346
+ streaming = false,
347
+ className
348
+ }) {
349
+ const hasStreamingCodeFence = streaming && hasOpenCodeFence(content);
350
+ const parsedContent = hasStreamingCodeFence ? sanitizeMarkdown(content) : content;
351
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: /* @__PURE__ */ jsxRuntime.jsx(
352
+ ReactMarkdown__default.default,
353
+ {
354
+ remarkPlugins: [remarkGfm__default.default],
355
+ components: markdownComponents,
356
+ children: parsedContent
357
+ }
358
+ ) });
359
+ }
360
+ function Mark({ className, ...props }) {
361
+ return /* @__PURE__ */ jsxRuntime.jsx(MarkPrimitive, { className: cn("llm-mark", className), ...props });
362
+ }
363
+ var senderPrefixActions = [
364
+ { key: "upload", label: "\u4E0A\u4F20\u9644\u4EF6" },
365
+ { key: "image", label: "\u6DFB\u52A0\u56FE\u7247" },
366
+ { key: "search", label: "\u8054\u7F51\u641C\u7D22" },
367
+ { key: "reasoning", label: "\u6DF1\u5EA6\u601D\u8003" }
368
+ ];
369
+ var defaultModelOptions = [
370
+ { value: "gpt-4o", label: "GPT-4o" },
371
+ { value: "claude-sonnet-4", label: "Claude Sonnet 4" },
372
+ { value: "gemini-2.5-pro", label: "Gemini 2.5 Pro" },
373
+ { value: "deepseek-r1", label: "DeepSeek R1" }
374
+ ];
375
+ function PlusIcon() {
376
+ return /* @__PURE__ */ jsxRuntime.jsxs(
377
+ "svg",
378
+ {
379
+ "aria-hidden": "true",
380
+ className: "llm-sender__prefix-icon",
381
+ fill: "none",
382
+ height: "16",
383
+ viewBox: "0 0 24 24",
384
+ width: "16",
385
+ xmlns: "http://www.w3.org/2000/svg",
386
+ children: [
387
+ /* @__PURE__ */ jsxRuntime.jsx(
388
+ "path",
389
+ {
390
+ d: "M12 5v14",
391
+ stroke: "currentColor",
392
+ strokeLinecap: "round",
393
+ strokeLinejoin: "round",
394
+ strokeWidth: "2"
395
+ }
396
+ ),
397
+ /* @__PURE__ */ jsxRuntime.jsx(
398
+ "path",
399
+ {
400
+ d: "M5 12h14",
401
+ stroke: "currentColor",
402
+ strokeLinecap: "round",
403
+ strokeLinejoin: "round",
404
+ strokeWidth: "2"
405
+ }
406
+ )
407
+ ]
408
+ }
409
+ );
410
+ }
411
+ function VoiceIcon() {
412
+ return /* @__PURE__ */ jsxRuntime.jsxs(
413
+ "svg",
414
+ {
415
+ "aria-hidden": "true",
416
+ className: "llm-sender__suffix-icon",
417
+ fill: "none",
418
+ height: "16",
419
+ viewBox: "0 0 24 24",
420
+ width: "16",
421
+ xmlns: "http://www.w3.org/2000/svg",
422
+ children: [
423
+ /* @__PURE__ */ jsxRuntime.jsx(
424
+ "path",
425
+ {
426
+ d: "M12 3a3 3 0 0 0-3 3v6a3 3 0 0 0 6 0V6a3 3 0 0 0-3-3Z",
427
+ stroke: "currentColor",
428
+ strokeLinecap: "round",
429
+ strokeLinejoin: "round",
430
+ strokeWidth: "2"
431
+ }
432
+ ),
433
+ /* @__PURE__ */ jsxRuntime.jsx(
434
+ "path",
435
+ {
436
+ d: "M19 10v2a7 7 0 0 1-14 0v-2",
437
+ stroke: "currentColor",
438
+ strokeLinecap: "round",
439
+ strokeLinejoin: "round",
440
+ strokeWidth: "2"
441
+ }
442
+ ),
443
+ /* @__PURE__ */ jsxRuntime.jsx(
444
+ "path",
445
+ {
446
+ d: "M12 19v3",
447
+ stroke: "currentColor",
448
+ strokeLinecap: "round",
449
+ strokeLinejoin: "round",
450
+ strokeWidth: "2"
451
+ }
452
+ )
453
+ ]
454
+ }
455
+ );
456
+ }
457
+ function SendIcon() {
458
+ return /* @__PURE__ */ jsxRuntime.jsxs(
459
+ "svg",
460
+ {
461
+ "aria-hidden": "true",
462
+ className: "llm-sender__send-icon",
463
+ fill: "none",
464
+ height: "16",
465
+ viewBox: "0 0 24 24",
466
+ width: "16",
467
+ xmlns: "http://www.w3.org/2000/svg",
468
+ children: [
469
+ /* @__PURE__ */ jsxRuntime.jsx(
470
+ "path",
471
+ {
472
+ d: "M5 12h14",
473
+ stroke: "currentColor",
474
+ strokeLinecap: "round",
475
+ strokeLinejoin: "round",
476
+ strokeWidth: "2"
477
+ }
478
+ ),
479
+ /* @__PURE__ */ jsxRuntime.jsx(
480
+ "path",
481
+ {
482
+ d: "m13 6 6 6-6 6",
483
+ stroke: "currentColor",
484
+ strokeLinecap: "round",
485
+ strokeLinejoin: "round",
486
+ strokeWidth: "2"
487
+ }
488
+ )
489
+ ]
490
+ }
491
+ );
492
+ }
493
+ function StopIcon() {
494
+ return /* @__PURE__ */ jsxRuntime.jsx(
495
+ "svg",
496
+ {
497
+ "aria-hidden": "true",
498
+ className: "llm-sender__send-icon",
499
+ fill: "none",
500
+ height: "16",
501
+ viewBox: "0 0 24 24",
502
+ width: "16",
503
+ xmlns: "http://www.w3.org/2000/svg",
504
+ children: /* @__PURE__ */ jsxRuntime.jsx(
505
+ "rect",
506
+ {
507
+ height: "8",
508
+ rx: "1.5",
509
+ stroke: "currentColor",
510
+ strokeWidth: "2",
511
+ width: "8",
512
+ x: "8",
513
+ y: "8"
514
+ }
515
+ )
516
+ }
517
+ );
518
+ }
519
+ function SenderPrimitive({
520
+ value,
521
+ defaultValue = "",
522
+ onChange,
523
+ onSend,
524
+ onCancel,
525
+ onPrefixAction,
526
+ onModelChange,
527
+ onVoiceClick,
528
+ loading = false,
529
+ disabled = false,
530
+ placeholder = "\u8F93\u5165\u6D88\u606F...",
531
+ model,
532
+ modelOptions = defaultModelOptions,
533
+ prefix,
534
+ suffix,
535
+ className
536
+ }) {
537
+ const [innerMessage, setInnerMessage] = react.useState(defaultValue);
538
+ const controlled = value !== void 0;
539
+ const message = controlled ? value : innerMessage;
540
+ const textareaRef = react.useRef(null);
541
+ const prefixMenuRef = react.useRef(null);
542
+ const modelMenuRef = react.useRef(null);
543
+ const [prefixMenuOpen, setPrefixMenuOpen] = react.useState(false);
544
+ const [modelMenuOpen, setModelMenuOpen] = react.useState(false);
545
+ const [selectedModel, setSelectedModel] = react.useState(
546
+ model ?? modelOptions[0]?.value ?? ""
547
+ );
548
+ react.useEffect(() => {
549
+ const textarea = textareaRef.current;
550
+ if (!textarea) return;
551
+ textarea.style.height = "auto";
552
+ textarea.style.height = `${textarea.scrollHeight}px`;
553
+ }, [message]);
554
+ react.useEffect(() => {
555
+ const handlePointerDown = (event) => {
556
+ const target = event.target;
557
+ if (!(target instanceof Node)) return;
558
+ if (!prefixMenuRef.current?.contains(target)) {
559
+ setPrefixMenuOpen(false);
560
+ }
561
+ if (!modelMenuRef.current?.contains(target)) {
562
+ setModelMenuOpen(false);
563
+ }
564
+ };
565
+ document.addEventListener("pointerdown", handlePointerDown);
566
+ return () => {
567
+ document.removeEventListener("pointerdown", handlePointerDown);
568
+ };
569
+ }, []);
570
+ const handleMessageChange = (nextMessage) => {
571
+ if (!controlled) {
572
+ setInnerMessage(nextMessage);
573
+ }
574
+ onChange?.(nextMessage);
575
+ };
576
+ const handleSend = () => {
577
+ const nextMessage = message.trim();
578
+ if (!nextMessage || disabled || loading) return;
579
+ onSend?.(nextMessage);
580
+ handleMessageChange("");
581
+ };
582
+ const handleAction = () => {
583
+ if (loading) {
584
+ onCancel?.();
585
+ return;
586
+ }
587
+ handleSend();
588
+ };
589
+ const handleKeyDown = (event) => {
590
+ if (event.key !== "Enter" || event.shiftKey) return;
591
+ event.preventDefault();
592
+ handleSend();
593
+ };
594
+ const handlePrefixAction = (action) => {
595
+ onPrefixAction?.(action);
596
+ setPrefixMenuOpen(false);
597
+ };
598
+ const selectedModelLabel = modelOptions.find((option) => option.value === selectedModel)?.label ?? selectedModel;
599
+ const handleModelChange = (nextModel) => {
600
+ setSelectedModel(nextModel);
601
+ onModelChange?.(nextModel);
602
+ setModelMenuOpen(false);
603
+ };
604
+ const handleVoiceClick = () => {
605
+ if (disabled) return;
606
+ onVoiceClick?.();
607
+ };
608
+ return /* @__PURE__ */ jsxRuntime.jsxs(
609
+ "div",
610
+ {
611
+ className,
612
+ "data-disabled": disabled ? "" : void 0,
613
+ "data-loading": loading ? "" : void 0,
614
+ children: [
615
+ /* @__PURE__ */ jsxRuntime.jsx(
616
+ "textarea",
617
+ {
618
+ className: "llm-sender__textarea",
619
+ disabled,
620
+ onChange: (event) => handleMessageChange(event.target.value),
621
+ ref: textareaRef,
622
+ onKeyDown: handleKeyDown,
623
+ placeholder,
624
+ rows: 1,
625
+ value: message
626
+ }
627
+ ),
628
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "llm-sender__footer", children: [
629
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-sender__prefix", children: prefix ?? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "llm-sender__prefix-menu", ref: prefixMenuRef, children: [
630
+ /* @__PURE__ */ jsxRuntime.jsx(
631
+ "button",
632
+ {
633
+ "aria-expanded": prefixMenuOpen,
634
+ "aria-label": "\u6253\u5F00\u66F4\u591A\u64CD\u4F5C",
635
+ className: "llm-sender__prefix-trigger",
636
+ disabled,
637
+ onClick: () => setPrefixMenuOpen((open) => !open),
638
+ type: "button",
639
+ children: /* @__PURE__ */ jsxRuntime.jsx(PlusIcon, {})
640
+ }
641
+ ),
642
+ prefixMenuOpen ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-sender__prefix-list", role: "menu", children: senderPrefixActions.map((action) => /* @__PURE__ */ jsxRuntime.jsx(
643
+ "button",
644
+ {
645
+ className: "llm-sender__prefix-item",
646
+ onClick: () => handlePrefixAction(action.key),
647
+ role: "menuitem",
648
+ type: "button",
649
+ children: action.label
650
+ },
651
+ action.key
652
+ )) }) : null
653
+ ] }) }),
654
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "llm-sender__actions", children: [
655
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-sender__suffix", children: suffix ?? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
656
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "llm-sender__model-menu", ref: modelMenuRef, children: [
657
+ /* @__PURE__ */ jsxRuntime.jsx(
658
+ "button",
659
+ {
660
+ "aria-expanded": modelMenuOpen,
661
+ className: "llm-sender__suffix-button llm-sender__model",
662
+ disabled,
663
+ onClick: () => setModelMenuOpen((open) => !open),
664
+ type: "button",
665
+ children: selectedModelLabel
666
+ }
667
+ ),
668
+ modelMenuOpen ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-sender__model-list", role: "menu", children: modelOptions.map((option) => /* @__PURE__ */ jsxRuntime.jsx(
669
+ "button",
670
+ {
671
+ "aria-current": option.value === selectedModel ? "true" : void 0,
672
+ className: "llm-sender__model-item",
673
+ onClick: () => handleModelChange(option.value),
674
+ role: "menuitem",
675
+ type: "button",
676
+ children: option.label
677
+ },
678
+ option.value
679
+ )) }) : null
680
+ ] }),
681
+ /* @__PURE__ */ jsxRuntime.jsx(
682
+ "button",
683
+ {
684
+ "aria-label": "\u8BED\u97F3\u8F93\u5165",
685
+ className: "llm-sender__suffix-button",
686
+ disabled,
687
+ onClick: handleVoiceClick,
688
+ type: "button",
689
+ children: /* @__PURE__ */ jsxRuntime.jsx(VoiceIcon, {})
690
+ }
691
+ )
692
+ ] }) }),
693
+ /* @__PURE__ */ jsxRuntime.jsx(
694
+ "button",
695
+ {
696
+ "aria-label": loading ? "\u505C\u6B62\u751F\u6210" : "\u53D1\u9001\u6D88\u606F",
697
+ className: "llm-sender__send",
698
+ "data-loading": loading ? "" : void 0,
699
+ disabled: disabled || !loading && !message.trim(),
700
+ onClick: handleAction,
701
+ type: "button",
702
+ children: loading ? /* @__PURE__ */ jsxRuntime.jsx(StopIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(SendIcon, {})
703
+ }
704
+ )
705
+ ] })
706
+ ] })
707
+ ]
708
+ }
709
+ );
710
+ }
711
+ function Sender({ className, ...props }) {
712
+ return /* @__PURE__ */ jsxRuntime.jsx(SenderPrimitive, { className: cn("llm-sender", className), ...props });
713
+ }
714
+ function SparkIcon() {
715
+ return /* @__PURE__ */ jsxRuntime.jsxs(
716
+ "svg",
717
+ {
718
+ "aria-hidden": "true",
719
+ className: "llm-think__icon",
720
+ fill: "none",
721
+ height: "16",
722
+ viewBox: "0 0 24 24",
723
+ width: "16",
724
+ xmlns: "http://www.w3.org/2000/svg",
725
+ children: [
726
+ /* @__PURE__ */ jsxRuntime.jsx(
727
+ "path",
728
+ {
729
+ d: "M12 3 9.9 8.2 5 10.1l4.9 1.9L12 17l2.1-5 4.9-1.9-4.9-1.9L12 3Z",
730
+ stroke: "currentColor",
731
+ strokeLinejoin: "round",
732
+ strokeWidth: "1.8"
733
+ }
734
+ ),
735
+ /* @__PURE__ */ jsxRuntime.jsx(
736
+ "path",
737
+ {
738
+ d: "m18 15-.9 2.1L15 18l2.1.9L18 21l.9-2.1L21 18l-2.1-.9L18 15Z",
739
+ stroke: "currentColor",
740
+ strokeLinejoin: "round",
741
+ strokeWidth: "1.8"
742
+ }
743
+ )
744
+ ]
745
+ }
746
+ );
747
+ }
748
+ function ChevronIcon() {
749
+ return /* @__PURE__ */ jsxRuntime.jsx(
750
+ "svg",
751
+ {
752
+ "aria-hidden": "true",
753
+ className: "llm-think__chevron",
754
+ fill: "none",
755
+ height: "16",
756
+ viewBox: "0 0 24 24",
757
+ width: "16",
758
+ xmlns: "http://www.w3.org/2000/svg",
759
+ children: /* @__PURE__ */ jsxRuntime.jsx(
760
+ "path",
761
+ {
762
+ d: "m6 9 6 6 6-6",
763
+ stroke: "currentColor",
764
+ strokeLinecap: "round",
765
+ strokeLinejoin: "round",
766
+ strokeWidth: "2"
767
+ }
768
+ )
769
+ }
770
+ );
771
+ }
772
+ function ThinkingDots() {
773
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { "aria-hidden": "true", className: "llm-think__dots", children: [
774
+ /* @__PURE__ */ jsxRuntime.jsx("span", {}),
775
+ /* @__PURE__ */ jsxRuntime.jsx("span", {}),
776
+ /* @__PURE__ */ jsxRuntime.jsx("span", {})
777
+ ] });
778
+ }
779
+ function ThinkPrimitive({
780
+ content,
781
+ status = "thinking",
782
+ label,
783
+ defaultOpen = true,
784
+ className,
785
+ style
786
+ }) {
787
+ const [open, setOpen] = react.useState(defaultOpen);
788
+ const title = label ?? (status === "thinking" ? "\u601D\u8003\u4E2D" : "\u5DF2\u5B8C\u6210\u601D\u8003");
789
+ return /* @__PURE__ */ jsxRuntime.jsxs(
790
+ "section",
791
+ {
792
+ className,
793
+ "data-open": open ? "" : void 0,
794
+ "data-status": status,
795
+ style,
796
+ children: [
797
+ /* @__PURE__ */ jsxRuntime.jsxs(
798
+ "button",
799
+ {
800
+ "aria-expanded": open,
801
+ className: "llm-think__header",
802
+ onClick: () => setOpen((nextOpen) => !nextOpen),
803
+ type: "button",
804
+ children: [
805
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "llm-think__title", children: [
806
+ /* @__PURE__ */ jsxRuntime.jsx(SparkIcon, {}),
807
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: title }),
808
+ status === "thinking" ? /* @__PURE__ */ jsxRuntime.jsx(ThinkingDots, {}) : null
809
+ ] }),
810
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronIcon, {})
811
+ ]
812
+ }
813
+ ),
814
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-think__content", hidden: !open, children: content ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-think__text", children: content }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-think__placeholder", children: "\u6B63\u5728\u5206\u6790\u4E0A\u4E0B\u6587\u4E0E\u7528\u6237\u610F\u56FE" }) })
815
+ ]
816
+ }
817
+ );
818
+ }
819
+ function Think({ className, ...props }) {
820
+ return /* @__PURE__ */ jsxRuntime.jsx(ThinkPrimitive, { className: cn("llm-think", className), ...props });
821
+ }
822
+ var defaultTitleMap = {
823
+ success: "\u64CD\u4F5C\u6210\u529F",
824
+ error: "\u64CD\u4F5C\u5931\u8D25",
825
+ loading: "\u5904\u7406\u4E2D"
826
+ };
827
+ function SuccessIcon() {
828
+ return /* @__PURE__ */ jsxRuntime.jsx(
829
+ "svg",
830
+ {
831
+ "aria-hidden": "true",
832
+ className: "llm-notification__icon-svg",
833
+ fill: "none",
834
+ height: "18",
835
+ viewBox: "0 0 24 24",
836
+ width: "18",
837
+ xmlns: "http://www.w3.org/2000/svg",
838
+ children: /* @__PURE__ */ jsxRuntime.jsx(
839
+ "path",
840
+ {
841
+ d: "m6 12 4 4 8-8",
842
+ stroke: "currentColor",
843
+ strokeLinecap: "round",
844
+ strokeLinejoin: "round",
845
+ strokeWidth: "2.4"
846
+ }
847
+ )
848
+ }
849
+ );
850
+ }
851
+ function ErrorIcon() {
852
+ return /* @__PURE__ */ jsxRuntime.jsxs(
853
+ "svg",
854
+ {
855
+ "aria-hidden": "true",
856
+ className: "llm-notification__icon-svg",
857
+ fill: "none",
858
+ height: "18",
859
+ viewBox: "0 0 24 24",
860
+ width: "18",
861
+ xmlns: "http://www.w3.org/2000/svg",
862
+ children: [
863
+ /* @__PURE__ */ jsxRuntime.jsx(
864
+ "path",
865
+ {
866
+ d: "m8 8 8 8",
867
+ stroke: "currentColor",
868
+ strokeLinecap: "round",
869
+ strokeWidth: "2.4"
870
+ }
871
+ ),
872
+ /* @__PURE__ */ jsxRuntime.jsx(
873
+ "path",
874
+ {
875
+ d: "m16 8-8 8",
876
+ stroke: "currentColor",
877
+ strokeLinecap: "round",
878
+ strokeWidth: "2.4"
879
+ }
880
+ )
881
+ ]
882
+ }
883
+ );
884
+ }
885
+ function LoadingIcon() {
886
+ return /* @__PURE__ */ jsxRuntime.jsx(
887
+ "svg",
888
+ {
889
+ "aria-hidden": "true",
890
+ className: "llm-notification__icon-svg llm-notification__icon-svg--loading",
891
+ fill: "none",
892
+ height: "18",
893
+ viewBox: "0 0 24 24",
894
+ width: "18",
895
+ xmlns: "http://www.w3.org/2000/svg",
896
+ children: /* @__PURE__ */ jsxRuntime.jsx(
897
+ "path",
898
+ {
899
+ d: "M21 12a9 9 0 1 1-3.2-6.9",
900
+ stroke: "currentColor",
901
+ strokeLinecap: "round",
902
+ strokeWidth: "2.4"
903
+ }
904
+ )
905
+ }
906
+ );
907
+ }
908
+ function CloseIcon() {
909
+ return /* @__PURE__ */ jsxRuntime.jsxs(
910
+ "svg",
911
+ {
912
+ "aria-hidden": "true",
913
+ className: "llm-notification__close-icon",
914
+ fill: "none",
915
+ height: "16",
916
+ viewBox: "0 0 24 24",
917
+ width: "16",
918
+ xmlns: "http://www.w3.org/2000/svg",
919
+ children: [
920
+ /* @__PURE__ */ jsxRuntime.jsx(
921
+ "path",
922
+ {
923
+ d: "m7 7 10 10",
924
+ stroke: "currentColor",
925
+ strokeLinecap: "round",
926
+ strokeWidth: "2"
927
+ }
928
+ ),
929
+ /* @__PURE__ */ jsxRuntime.jsx(
930
+ "path",
931
+ {
932
+ d: "m17 7-10 10",
933
+ stroke: "currentColor",
934
+ strokeLinecap: "round",
935
+ strokeWidth: "2"
936
+ }
937
+ )
938
+ ]
939
+ }
940
+ );
941
+ }
942
+ function DefaultIcon({ type }) {
943
+ if (type === "success") return /* @__PURE__ */ jsxRuntime.jsx(SuccessIcon, {});
944
+ if (type === "error") return /* @__PURE__ */ jsxRuntime.jsx(ErrorIcon, {});
945
+ return /* @__PURE__ */ jsxRuntime.jsx(LoadingIcon, {});
946
+ }
947
+ function NotificationPrimitive({
948
+ type = "success",
949
+ title,
950
+ description,
951
+ duration = 4500,
952
+ showProgress = false,
953
+ closeable = true,
954
+ icon,
955
+ onClose,
956
+ className,
957
+ style
958
+ }) {
959
+ react.useEffect(() => {
960
+ if (!duration || type === "loading") return;
961
+ const timer = window.setTimeout(() => {
962
+ onClose?.();
963
+ }, duration);
964
+ return () => {
965
+ window.clearTimeout(timer);
966
+ };
967
+ }, [duration, onClose, type]);
968
+ return /* @__PURE__ */ jsxRuntime.jsxs(
969
+ "section",
970
+ {
971
+ "aria-live": type === "error" ? "assertive" : "polite",
972
+ className,
973
+ "data-type": type,
974
+ role: type === "error" ? "alert" : "status",
975
+ style,
976
+ children: [
977
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-notification__icon", children: icon ?? /* @__PURE__ */ jsxRuntime.jsx(DefaultIcon, { type }) }),
978
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "llm-notification__body", children: [
979
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-notification__title", children: title ?? defaultTitleMap[type] }),
980
+ description ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-notification__description", children: description }) : null
981
+ ] }),
982
+ closeable ? /* @__PURE__ */ jsxRuntime.jsx(
983
+ "button",
984
+ {
985
+ "aria-label": "\u5173\u95ED\u901A\u77E5",
986
+ className: "llm-notification__close",
987
+ onClick: onClose,
988
+ type: "button",
989
+ children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon, {})
990
+ }
991
+ ) : null,
992
+ showProgress && duration > 0 && type !== "loading" ? /* @__PURE__ */ jsxRuntime.jsx(
993
+ "div",
994
+ {
995
+ "aria-hidden": "true",
996
+ className: "llm-notification__progress",
997
+ style: { animationDuration: `${duration}ms` }
998
+ }
999
+ ) : null
1000
+ ]
1001
+ }
1002
+ );
1003
+ }
1004
+ function Notification({ className, ...props }) {
1005
+ return /* @__PURE__ */ jsxRuntime.jsx(NotificationPrimitive, { className: cn("llm-notification", className), ...props });
1006
+ }
1007
+ function NotificationStack({
1008
+ items,
1009
+ onClose,
1010
+ className
1011
+ }) {
1012
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("llm-notification-stack", className), children: items.map(({ id, onClose: itemOnClose, ...item }) => /* @__PURE__ */ jsxRuntime.jsx(
1013
+ Notification,
1014
+ {
1015
+ ...item,
1016
+ onClose: () => {
1017
+ itemOnClose?.();
1018
+ onClose?.(id);
1019
+ }
1020
+ },
1021
+ id
1022
+ )) });
1023
+ }
1024
+ function CopyIcon2() {
1025
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1026
+ "svg",
1027
+ {
1028
+ "aria-hidden": "true",
1029
+ fill: "none",
1030
+ height: "16",
1031
+ viewBox: "0 0 24 24",
1032
+ width: "16",
1033
+ xmlns: "http://www.w3.org/2000/svg",
1034
+ children: [
1035
+ /* @__PURE__ */ jsxRuntime.jsx(
1036
+ "path",
1037
+ {
1038
+ d: "M8 8.5V6a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.5",
1039
+ stroke: "currentColor",
1040
+ strokeLinecap: "round",
1041
+ strokeLinejoin: "round",
1042
+ strokeWidth: "1.8"
1043
+ }
1044
+ ),
1045
+ /* @__PURE__ */ jsxRuntime.jsx(
1046
+ "path",
1047
+ {
1048
+ d: "M4 10a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2z",
1049
+ stroke: "currentColor",
1050
+ strokeLinejoin: "round",
1051
+ strokeWidth: "1.8"
1052
+ }
1053
+ )
1054
+ ]
1055
+ }
1056
+ );
1057
+ }
1058
+ function CheckIcon2() {
1059
+ return /* @__PURE__ */ jsxRuntime.jsx(
1060
+ "svg",
1061
+ {
1062
+ "aria-hidden": "true",
1063
+ fill: "none",
1064
+ height: "16",
1065
+ viewBox: "0 0 24 24",
1066
+ width: "16",
1067
+ xmlns: "http://www.w3.org/2000/svg",
1068
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1069
+ "path",
1070
+ {
1071
+ d: "m5 12.5 4.2 4.2L19 6.8",
1072
+ stroke: "currentColor",
1073
+ strokeLinecap: "round",
1074
+ strokeLinejoin: "round",
1075
+ strokeWidth: "2"
1076
+ }
1077
+ )
1078
+ }
1079
+ );
1080
+ }
1081
+ function ReloadIcon() {
1082
+ return /* @__PURE__ */ jsxRuntime.jsx(
1083
+ "svg",
1084
+ {
1085
+ "aria-hidden": "true",
1086
+ fill: "none",
1087
+ height: "16",
1088
+ viewBox: "0 0 24 24",
1089
+ width: "16",
1090
+ xmlns: "http://www.w3.org/2000/svg",
1091
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1092
+ "path",
1093
+ {
1094
+ d: "M20 12a8 8 0 1 1-2.34-5.66L20 8.68M20 4v4.68h-4.68",
1095
+ stroke: "currentColor",
1096
+ strokeLinecap: "round",
1097
+ strokeLinejoin: "round",
1098
+ strokeWidth: "1.8"
1099
+ }
1100
+ )
1101
+ }
1102
+ );
1103
+ }
1104
+ function EditIcon() {
1105
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1106
+ "svg",
1107
+ {
1108
+ "aria-hidden": "true",
1109
+ fill: "none",
1110
+ height: "16",
1111
+ viewBox: "0 0 24 24",
1112
+ width: "16",
1113
+ xmlns: "http://www.w3.org/2000/svg",
1114
+ children: [
1115
+ /* @__PURE__ */ jsxRuntime.jsx(
1116
+ "path",
1117
+ {
1118
+ d: "m4 20 4.8-1.2L19 8.6 15.4 5 5.2 15.2z",
1119
+ stroke: "currentColor",
1120
+ strokeLinecap: "round",
1121
+ strokeLinejoin: "round",
1122
+ strokeWidth: "1.8"
1123
+ }
1124
+ ),
1125
+ /* @__PURE__ */ jsxRuntime.jsx(
1126
+ "path",
1127
+ {
1128
+ d: "m13.8 6.6 3.6 3.6",
1129
+ stroke: "currentColor",
1130
+ strokeLinecap: "round",
1131
+ strokeWidth: "1.8"
1132
+ }
1133
+ )
1134
+ ]
1135
+ }
1136
+ );
1137
+ }
1138
+ function ThumbsUpIcon() {
1139
+ return /* @__PURE__ */ jsxRuntime.jsx(
1140
+ "svg",
1141
+ {
1142
+ "aria-hidden": "true",
1143
+ fill: "none",
1144
+ height: "16",
1145
+ viewBox: "0 0 24 24",
1146
+ width: "16",
1147
+ xmlns: "http://www.w3.org/2000/svg",
1148
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1149
+ "path",
1150
+ {
1151
+ d: "M7 21H4a1 1 0 0 1-1-1v-8a1 1 0 0 1 1-1h3m0 10V10l4-7 1.4 1.4a3 3 0 0 1 .8 2.7L12.8 10H19a2 2 0 0 1 2 2.3l-1 6A3 3 0 0 1 17 21z",
1152
+ stroke: "currentColor",
1153
+ strokeLinecap: "round",
1154
+ strokeLinejoin: "round",
1155
+ strokeWidth: "1.8"
1156
+ }
1157
+ )
1158
+ }
1159
+ );
1160
+ }
1161
+ function ThumbsDownIcon() {
1162
+ return /* @__PURE__ */ jsxRuntime.jsx(
1163
+ "svg",
1164
+ {
1165
+ "aria-hidden": "true",
1166
+ fill: "none",
1167
+ height: "16",
1168
+ viewBox: "0 0 24 24",
1169
+ width: "16",
1170
+ xmlns: "http://www.w3.org/2000/svg",
1171
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1172
+ "path",
1173
+ {
1174
+ d: "M17 3h3a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1h-3m0-10v11l-4 7-1.4-1.4a3 3 0 0 1-.8-2.7l.4-2.9H5a2 2 0 0 1-2-2.3l1-6A3 3 0 0 1 7 3z",
1175
+ stroke: "currentColor",
1176
+ strokeLinecap: "round",
1177
+ strokeLinejoin: "round",
1178
+ strokeWidth: "1.8"
1179
+ }
1180
+ )
1181
+ }
1182
+ );
1183
+ }
1184
+ function SummaryIcon() {
1185
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1186
+ "svg",
1187
+ {
1188
+ "aria-hidden": "true",
1189
+ fill: "none",
1190
+ height: "16",
1191
+ viewBox: "0 0 24 24",
1192
+ width: "16",
1193
+ xmlns: "http://www.w3.org/2000/svg",
1194
+ children: [
1195
+ /* @__PURE__ */ jsxRuntime.jsx(
1196
+ "path",
1197
+ {
1198
+ d: "M5 5.5h14M5 10h10M5 14.5h7",
1199
+ stroke: "currentColor",
1200
+ strokeLinecap: "round",
1201
+ strokeWidth: "1.8"
1202
+ }
1203
+ ),
1204
+ /* @__PURE__ */ jsxRuntime.jsx(
1205
+ "path",
1206
+ {
1207
+ d: "m15 17 1.6 1.6L20 15",
1208
+ stroke: "currentColor",
1209
+ strokeLinecap: "round",
1210
+ strokeLinejoin: "round",
1211
+ strokeWidth: "1.8"
1212
+ }
1213
+ )
1214
+ ]
1215
+ }
1216
+ );
1217
+ }
1218
+ function PolishIcon() {
1219
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1220
+ "svg",
1221
+ {
1222
+ "aria-hidden": "true",
1223
+ fill: "none",
1224
+ height: "16",
1225
+ viewBox: "0 0 24 24",
1226
+ width: "16",
1227
+ xmlns: "http://www.w3.org/2000/svg",
1228
+ children: [
1229
+ /* @__PURE__ */ jsxRuntime.jsx(
1230
+ "path",
1231
+ {
1232
+ d: "m4 20 6.2-1.4L19 9.8 14.2 5 5.4 13.8z",
1233
+ stroke: "currentColor",
1234
+ strokeLinecap: "round",
1235
+ strokeLinejoin: "round",
1236
+ strokeWidth: "1.8"
1237
+ }
1238
+ ),
1239
+ /* @__PURE__ */ jsxRuntime.jsx(
1240
+ "path",
1241
+ {
1242
+ d: "M15.5 3.5 20.5 8.5M5 4.5h3M6.5 3v3M19 17h2M20 16v2",
1243
+ stroke: "currentColor",
1244
+ strokeLinecap: "round",
1245
+ strokeWidth: "1.8"
1246
+ }
1247
+ )
1248
+ ]
1249
+ }
1250
+ );
1251
+ }
1252
+ function ExplainCodeIcon() {
1253
+ return /* @__PURE__ */ jsxRuntime.jsx(
1254
+ "svg",
1255
+ {
1256
+ "aria-hidden": "true",
1257
+ fill: "none",
1258
+ height: "16",
1259
+ viewBox: "0 0 24 24",
1260
+ width: "16",
1261
+ xmlns: "http://www.w3.org/2000/svg",
1262
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1263
+ "path",
1264
+ {
1265
+ d: "m8 9-3 3 3 3M16 9l3 3-3 3M13.5 6.5l-3 11",
1266
+ stroke: "currentColor",
1267
+ strokeLinecap: "round",
1268
+ strokeLinejoin: "round",
1269
+ strokeWidth: "1.8"
1270
+ }
1271
+ )
1272
+ }
1273
+ );
1274
+ }
1275
+ var defaultIcons = {
1276
+ copy: /* @__PURE__ */ jsxRuntime.jsx(CopyIcon2, {}),
1277
+ regenerate: /* @__PURE__ */ jsxRuntime.jsx(ReloadIcon, {}),
1278
+ reload: /* @__PURE__ */ jsxRuntime.jsx(ReloadIcon, {}),
1279
+ edit: /* @__PURE__ */ jsxRuntime.jsx(EditIcon, {}),
1280
+ correct: /* @__PURE__ */ jsxRuntime.jsx(EditIcon, {}),
1281
+ like: /* @__PURE__ */ jsxRuntime.jsx(ThumbsUpIcon, {}),
1282
+ dislike: /* @__PURE__ */ jsxRuntime.jsx(ThumbsDownIcon, {}),
1283
+ summary: /* @__PURE__ */ jsxRuntime.jsx(SummaryIcon, {}),
1284
+ polish: /* @__PURE__ */ jsxRuntime.jsx(PolishIcon, {}),
1285
+ "explain-code": /* @__PURE__ */ jsxRuntime.jsx(ExplainCodeIcon, {})
1286
+ };
1287
+ function getActionIcon(item, copied) {
1288
+ if (copied) return /* @__PURE__ */ jsxRuntime.jsx(CheckIcon2, {});
1289
+ return item.icon ?? defaultIcons[item.key];
1290
+ }
1291
+ function ActionsPrimitive({
1292
+ items,
1293
+ onAction,
1294
+ copiedKey = "copy",
1295
+ copiedDuration = 1800,
1296
+ orientation = "horizontal",
1297
+ size = "md",
1298
+ className,
1299
+ style
1300
+ }) {
1301
+ const [copied, setCopied] = react.useState(false);
1302
+ react.useEffect(() => {
1303
+ if (!copied) return;
1304
+ const timer = window.setTimeout(() => setCopied(false), copiedDuration);
1305
+ return () => window.clearTimeout(timer);
1306
+ }, [copied, copiedDuration]);
1307
+ const handleAction = (item) => {
1308
+ if (item.disabled) return;
1309
+ if (item.key === copiedKey) {
1310
+ setCopied(true);
1311
+ }
1312
+ onAction?.(item.key, item);
1313
+ };
1314
+ return /* @__PURE__ */ jsxRuntime.jsx(
1315
+ "div",
1316
+ {
1317
+ className,
1318
+ "data-orientation": orientation,
1319
+ "data-size": size,
1320
+ role: "toolbar",
1321
+ style,
1322
+ children: items.map((item) => {
1323
+ const itemCopied = item.key === copiedKey && copied;
1324
+ const icon = getActionIcon(item, itemCopied);
1325
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1326
+ "button",
1327
+ {
1328
+ "aria-label": item.label,
1329
+ className: "llm-actions__item",
1330
+ "data-copied": itemCopied ? "" : void 0,
1331
+ "data-variant": item.variant,
1332
+ disabled: item.disabled,
1333
+ onClick: () => handleAction(item),
1334
+ title: item.label,
1335
+ type: "button",
1336
+ children: [
1337
+ icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "llm-actions__icon", children: icon }) : null,
1338
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "llm-actions__label", children: itemCopied ? "\u5DF2\u590D\u5236" : item.label })
1339
+ ]
1340
+ },
1341
+ item.key
1342
+ );
1343
+ })
1344
+ }
1345
+ );
1346
+ }
1347
+ function Actions({ className, ...props }) {
1348
+ return /* @__PURE__ */ jsxRuntime.jsx(ActionsPrimitive, { className: cn("llm-actions", className), ...props });
1349
+ }
1350
+
1351
+ // src/components/actions/ActionsPreset.ts
1352
+ var actionPresets = {
1353
+ copy: { key: "copy", label: "\u590D\u5236" },
1354
+ regenerate: { key: "regenerate", label: "\u91CD\u65B0\u751F\u6210" },
1355
+ correct: { key: "correct", label: "\u7EA0\u9519" },
1356
+ summary: { key: "summary", label: "\u603B\u7ED3" },
1357
+ polish: { key: "polish", label: "\u6DA6\u8272" },
1358
+ "explain-code": { key: "explain-code", label: "\u89E3\u91CA\u4EE3\u7801" }
1359
+ };
1360
+ function createPresetActions(keys) {
1361
+ return keys.map((key) => actionPresets[key]);
1362
+ }
1363
+ function PromptsPrimitive({
1364
+ items,
1365
+ onPrompt,
1366
+ title,
1367
+ description,
1368
+ emptyText = "\u6682\u65E0\u63D0\u793A\u8BCD",
1369
+ columns = 2,
1370
+ className,
1371
+ style
1372
+ }) {
1373
+ const hasHeader = Boolean(title || description);
1374
+ const handlePrompt = (item) => {
1375
+ if (item.disabled) return;
1376
+ onPrompt?.(item.key, item);
1377
+ };
1378
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { className, "data-columns": columns, style, children: [
1379
+ hasHeader ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "llm-prompts__header", children: [
1380
+ title ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-prompts__title", children: title }) : null,
1381
+ description ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-prompts__description", children: description }) : null
1382
+ ] }) : null,
1383
+ items.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-prompts__list", children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(
1384
+ "button",
1385
+ {
1386
+ "aria-label": typeof item.title === "string" ? item.title : void 0,
1387
+ className: "llm-prompts__item",
1388
+ disabled: item.disabled,
1389
+ onClick: () => handlePrompt(item),
1390
+ type: "button",
1391
+ children: [
1392
+ item.icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "llm-prompts__icon", children: item.icon }) : null,
1393
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "llm-prompts__item-title", children: item.title })
1394
+ ]
1395
+ },
1396
+ item.key
1397
+ )) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-prompts__empty", children: emptyText })
1398
+ ] });
1399
+ }
1400
+ function Prompts({ className, ...props }) {
1401
+ return /* @__PURE__ */ jsxRuntime.jsx(PromptsPrimitive, { className: cn("llm-prompts", className), ...props });
1402
+ }
1403
+ function formatTimestamp2(timestamp) {
1404
+ if (!timestamp) return void 0;
1405
+ if (typeof timestamp === "string") return timestamp;
1406
+ return timestamp.toLocaleTimeString([], {
1407
+ hour: "2-digit",
1408
+ minute: "2-digit"
1409
+ });
1410
+ }
1411
+ function PinIcon() {
1412
+ return /* @__PURE__ */ jsxRuntime.jsx(
1413
+ "svg",
1414
+ {
1415
+ "aria-hidden": "true",
1416
+ fill: "none",
1417
+ height: "14",
1418
+ viewBox: "0 0 24 24",
1419
+ width: "14",
1420
+ xmlns: "http://www.w3.org/2000/svg",
1421
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1422
+ "path",
1423
+ {
1424
+ d: "m14.5 4.5 5 5-3.2 3.2.8 5.4-1.2 1.2-4.4-4.4-4.7 4.7-2.4.8.8-2.4 4.7-4.7-4.4-4.4 1.2-1.2 5.4.8 3.2-3.2Z",
1425
+ stroke: "currentColor",
1426
+ strokeLinejoin: "round",
1427
+ strokeWidth: "1.8"
1428
+ }
1429
+ )
1430
+ }
1431
+ );
1432
+ }
1433
+ function StarIcon() {
1434
+ return /* @__PURE__ */ jsxRuntime.jsx(
1435
+ "svg",
1436
+ {
1437
+ "aria-hidden": "true",
1438
+ fill: "currentColor",
1439
+ height: "14",
1440
+ viewBox: "0 0 24 24",
1441
+ width: "14",
1442
+ xmlns: "http://www.w3.org/2000/svg",
1443
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m12 3 2.7 5.47 6.03.88-4.36 4.25 1.03 6-5.4-2.84-5.4 2.84 1.03-6-4.36-4.25 6.03-.88L12 3Z" })
1444
+ }
1445
+ );
1446
+ }
1447
+ function MoreIcon() {
1448
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1449
+ "svg",
1450
+ {
1451
+ "aria-hidden": "true",
1452
+ className: "llm-conversation-item__menu-icon",
1453
+ fill: "none",
1454
+ height: "16",
1455
+ viewBox: "0 0 24 24",
1456
+ width: "16",
1457
+ xmlns: "http://www.w3.org/2000/svg",
1458
+ children: [
1459
+ /* @__PURE__ */ jsxRuntime.jsx(
1460
+ "path",
1461
+ {
1462
+ d: "M12 13a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z",
1463
+ fill: "currentColor"
1464
+ }
1465
+ ),
1466
+ /* @__PURE__ */ jsxRuntime.jsx(
1467
+ "path",
1468
+ {
1469
+ d: "M19 13a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z",
1470
+ fill: "currentColor"
1471
+ }
1472
+ ),
1473
+ /* @__PURE__ */ jsxRuntime.jsx(
1474
+ "path",
1475
+ {
1476
+ d: "M5 13a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z",
1477
+ fill: "currentColor"
1478
+ }
1479
+ )
1480
+ ]
1481
+ }
1482
+ );
1483
+ }
1484
+ function ConversationItemPrimitive({
1485
+ conversation,
1486
+ active = false,
1487
+ onSelect,
1488
+ onDelete,
1489
+ onPin,
1490
+ onFavorite,
1491
+ className,
1492
+ style
1493
+ }) {
1494
+ const rootRef = react.useRef(null);
1495
+ const [menuOpen, setMenuOpen] = react.useState(false);
1496
+ const timestamp = formatTimestamp2(conversation.timestamp);
1497
+ react.useEffect(() => {
1498
+ if (!menuOpen) return;
1499
+ const handlePointerDown = (event) => {
1500
+ if (!rootRef.current?.contains(event.target)) {
1501
+ setMenuOpen(false);
1502
+ }
1503
+ };
1504
+ document.addEventListener("pointerdown", handlePointerDown);
1505
+ return () => {
1506
+ document.removeEventListener("pointerdown", handlePointerDown);
1507
+ };
1508
+ }, [menuOpen]);
1509
+ const handleAction = (action) => {
1510
+ if (action === "pin") {
1511
+ onPin?.(conversation.id);
1512
+ }
1513
+ if (action === "favorite") {
1514
+ onFavorite?.(conversation.id);
1515
+ }
1516
+ if (action === "delete") {
1517
+ onDelete?.(conversation.id);
1518
+ }
1519
+ setMenuOpen(false);
1520
+ };
1521
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1522
+ "article",
1523
+ {
1524
+ ref: rootRef,
1525
+ className,
1526
+ "data-active": active ? "" : void 0,
1527
+ "data-favorite": conversation.favorite ? "" : void 0,
1528
+ "data-pinned": conversation.pinned ? "" : void 0,
1529
+ style,
1530
+ children: [
1531
+ /* @__PURE__ */ jsxRuntime.jsx(
1532
+ "button",
1533
+ {
1534
+ "aria-current": active ? "true" : void 0,
1535
+ className: "llm-conversation-item__main",
1536
+ onClick: () => onSelect?.(conversation.id),
1537
+ type: "button",
1538
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "llm-conversation-item__content", children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "llm-conversation-item__header", children: [
1539
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "llm-conversation-item__title", children: conversation.title }),
1540
+ timestamp ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "llm-conversation-item__time", children: timestamp }) : null
1541
+ ] }) })
1542
+ }
1543
+ ),
1544
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "llm-conversation-item__actions", children: [
1545
+ conversation.pinned ? /* @__PURE__ */ jsxRuntime.jsx(
1546
+ "button",
1547
+ {
1548
+ "aria-label": "\u53D6\u6D88\u7F6E\u9876",
1549
+ className: "llm-conversation-item__status-button",
1550
+ onClick: () => handleAction("pin"),
1551
+ type: "button",
1552
+ children: /* @__PURE__ */ jsxRuntime.jsx(PinIcon, {})
1553
+ }
1554
+ ) : null,
1555
+ conversation.favorite ? /* @__PURE__ */ jsxRuntime.jsx(
1556
+ "button",
1557
+ {
1558
+ "aria-label": "\u53D6\u6D88\u6536\u85CF",
1559
+ className: "llm-conversation-item__status-button",
1560
+ onClick: () => handleAction("favorite"),
1561
+ type: "button",
1562
+ children: /* @__PURE__ */ jsxRuntime.jsx(StarIcon, {})
1563
+ }
1564
+ ) : null,
1565
+ /* @__PURE__ */ jsxRuntime.jsx(
1566
+ "button",
1567
+ {
1568
+ "aria-expanded": menuOpen,
1569
+ "aria-label": "\u6253\u5F00\u4F1A\u8BDD\u64CD\u4F5C",
1570
+ className: "llm-conversation-item__menu-trigger",
1571
+ onClick: () => setMenuOpen((open) => !open),
1572
+ type: "button",
1573
+ children: /* @__PURE__ */ jsxRuntime.jsx(MoreIcon, {})
1574
+ }
1575
+ ),
1576
+ menuOpen ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "llm-conversation-item__menu", role: "menu", children: [
1577
+ /* @__PURE__ */ jsxRuntime.jsx(
1578
+ "button",
1579
+ {
1580
+ className: "llm-conversation-item__menu-item",
1581
+ onClick: () => handleAction("pin"),
1582
+ role: "menuitem",
1583
+ type: "button",
1584
+ children: conversation.pinned ? "\u53D6\u6D88\u7F6E\u9876" : "\u7F6E\u9876"
1585
+ }
1586
+ ),
1587
+ /* @__PURE__ */ jsxRuntime.jsx(
1588
+ "button",
1589
+ {
1590
+ className: "llm-conversation-item__menu-item",
1591
+ onClick: () => handleAction("favorite"),
1592
+ role: "menuitem",
1593
+ type: "button",
1594
+ children: conversation.favorite ? "\u53D6\u6D88\u6536\u85CF" : "\u6536\u85CF"
1595
+ }
1596
+ ),
1597
+ /* @__PURE__ */ jsxRuntime.jsx(
1598
+ "button",
1599
+ {
1600
+ className: "llm-conversation-item__menu-item",
1601
+ "data-danger": "",
1602
+ onClick: () => handleAction("delete"),
1603
+ role: "menuitem",
1604
+ type: "button",
1605
+ children: "\u5220\u9664"
1606
+ }
1607
+ )
1608
+ ] }) : null
1609
+ ] })
1610
+ ]
1611
+ }
1612
+ );
1613
+ }
1614
+ function ConversationItem({
1615
+ className,
1616
+ ...props
1617
+ }) {
1618
+ return /* @__PURE__ */ jsxRuntime.jsx(
1619
+ ConversationItemPrimitive,
1620
+ {
1621
+ className: cn("llm-conversation-item", className),
1622
+ ...props
1623
+ }
1624
+ );
1625
+ }
1626
+ function clamp(value, min, max) {
1627
+ return Math.min(Math.max(value, min), max);
1628
+ }
1629
+ function useVirtualList({
1630
+ items,
1631
+ itemHeight,
1632
+ overscan = 5,
1633
+ enabled = true,
1634
+ getItemKey
1635
+ }) {
1636
+ const containerRef = react.useRef(null);
1637
+ const [scrollTop, setScrollTop] = react.useState(0);
1638
+ const [viewportHeight, setViewportHeight] = react.useState(0);
1639
+ const safeItemHeight = Math.max(1, itemHeight);
1640
+ const safeOverscan = Math.max(0, overscan);
1641
+ const totalSize = items.length * safeItemHeight;
1642
+ react.useEffect(() => {
1643
+ if (!enabled) return;
1644
+ const container = containerRef.current;
1645
+ if (!container) return;
1646
+ const updateViewportHeight = () => {
1647
+ setViewportHeight(container.clientHeight);
1648
+ };
1649
+ updateViewportHeight();
1650
+ if (typeof ResizeObserver === "undefined") {
1651
+ return;
1652
+ }
1653
+ const observer = new ResizeObserver(updateViewportHeight);
1654
+ observer.observe(container);
1655
+ return () => observer.disconnect();
1656
+ }, [enabled]);
1657
+ react.useEffect(() => {
1658
+ if (!enabled) return;
1659
+ const container = containerRef.current;
1660
+ if (!container) return;
1661
+ const handleScroll = () => {
1662
+ setScrollTop(container.scrollTop);
1663
+ };
1664
+ handleScroll();
1665
+ container.addEventListener("scroll", handleScroll, { passive: true });
1666
+ return () => container.removeEventListener("scroll", handleScroll);
1667
+ }, [enabled]);
1668
+ const virtualItems = react.useMemo(() => {
1669
+ const listItems = [];
1670
+ if (!enabled) {
1671
+ for (let index = 0; index < items.length; index += 1) {
1672
+ const item = items[index];
1673
+ if (item === void 0) continue;
1674
+ listItems.push({
1675
+ item,
1676
+ index,
1677
+ key: getItemKey?.(item, index) ?? index,
1678
+ start: index * safeItemHeight,
1679
+ size: safeItemHeight
1680
+ });
1681
+ }
1682
+ return listItems;
1683
+ }
1684
+ const startIndex = clamp(
1685
+ Math.floor(scrollTop / safeItemHeight) - safeOverscan,
1686
+ 0,
1687
+ items.length
1688
+ );
1689
+ const endIndex = clamp(
1690
+ Math.ceil((scrollTop + viewportHeight) / safeItemHeight) + safeOverscan,
1691
+ startIndex,
1692
+ items.length
1693
+ );
1694
+ for (let index = startIndex; index < endIndex; index += 1) {
1695
+ const item = items[index];
1696
+ if (item === void 0) continue;
1697
+ listItems.push({
1698
+ item,
1699
+ index,
1700
+ key: getItemKey?.(item, index) ?? index,
1701
+ start: index * safeItemHeight,
1702
+ size: safeItemHeight
1703
+ });
1704
+ }
1705
+ return listItems;
1706
+ }, [
1707
+ enabled,
1708
+ getItemKey,
1709
+ items,
1710
+ safeItemHeight,
1711
+ safeOverscan,
1712
+ scrollTop,
1713
+ viewportHeight
1714
+ ]);
1715
+ const scrollToIndex = react.useCallback(
1716
+ (index, align = "start") => {
1717
+ const container = containerRef.current;
1718
+ if (!container) return;
1719
+ const clampedIndex = clamp(index, 0, Math.max(0, items.length - 1));
1720
+ const itemStart = clampedIndex * safeItemHeight;
1721
+ const itemEnd = itemStart + safeItemHeight;
1722
+ const maxScrollTop = Math.max(0, totalSize - container.clientHeight);
1723
+ let nextScrollTop = itemStart;
1724
+ if (align === "center") {
1725
+ nextScrollTop = itemStart - (container.clientHeight - safeItemHeight) / 2;
1726
+ }
1727
+ if (align === "end") {
1728
+ nextScrollTop = itemEnd - container.clientHeight;
1729
+ }
1730
+ container.scrollTo({
1731
+ top: clamp(nextScrollTop, 0, maxScrollTop),
1732
+ behavior: "smooth"
1733
+ });
1734
+ },
1735
+ [items.length, safeItemHeight, totalSize]
1736
+ );
1737
+ return {
1738
+ containerRef,
1739
+ virtualItems,
1740
+ totalSize,
1741
+ scrollToIndex
1742
+ };
1743
+ }
1744
+ function normalizeSearchText(value) {
1745
+ return value.trim().toLowerCase();
1746
+ }
1747
+ function SearchIcon() {
1748
+ return /* @__PURE__ */ jsxRuntime.jsx(
1749
+ "svg",
1750
+ {
1751
+ "aria-hidden": "true",
1752
+ fill: "none",
1753
+ height: "16",
1754
+ viewBox: "0 0 24 24",
1755
+ width: "16",
1756
+ xmlns: "http://www.w3.org/2000/svg",
1757
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1758
+ "path",
1759
+ {
1760
+ d: "m20 20-4.3-4.3m1.8-5.2a7 7 0 1 1-14 0 7 7 0 0 1 14 0Z",
1761
+ stroke: "currentColor",
1762
+ strokeLinecap: "round",
1763
+ strokeLinejoin: "round",
1764
+ strokeWidth: "1.8"
1765
+ }
1766
+ )
1767
+ }
1768
+ );
1769
+ }
1770
+ function ConversationListPrimitive({
1771
+ conversations,
1772
+ activeId,
1773
+ onSelect,
1774
+ onDelete,
1775
+ onPin,
1776
+ onFavorite,
1777
+ onCollapsedChange,
1778
+ onNewConversation,
1779
+ collapsed = false,
1780
+ searchable = false,
1781
+ searchPlaceholder = "\u641C\u7D22\u4F1A\u8BDD...",
1782
+ title,
1783
+ emptyText,
1784
+ virtualized = false,
1785
+ itemHeight = 56,
1786
+ overscan = 6,
1787
+ className,
1788
+ style
1789
+ }) {
1790
+ const [searchOpen, setSearchOpen] = react.useState(false);
1791
+ const [searchValue, setSearchValue] = react.useState("");
1792
+ const keyword = normalizeSearchText(searchValue);
1793
+ const visibleConversations = react.useMemo(() => {
1794
+ const filteredConversations = keyword ? conversations.filter((conversation) => {
1795
+ const titleMatched = conversation.title.toLowerCase().includes(keyword);
1796
+ const messageMatched = conversation.lastMessage?.toLowerCase().includes(keyword) ?? false;
1797
+ return titleMatched || messageMatched;
1798
+ }) : conversations;
1799
+ return filteredConversations.map((conversation, index) => ({ conversation, index })).sort((a, b) => {
1800
+ if (a.conversation.pinned === b.conversation.pinned) {
1801
+ return a.index - b.index;
1802
+ }
1803
+ return a.conversation.pinned ? -1 : 1;
1804
+ }).map(({ conversation }) => conversation);
1805
+ }, [conversations, keyword]);
1806
+ const resolvedEmptyText = emptyText ?? (keyword ? "\u672A\u627E\u5230\u76F8\u5173\u4F1A\u8BDD" : "\u6682\u65E0\u4F1A\u8BDD");
1807
+ const { containerRef, totalSize, virtualItems } = useVirtualList({
1808
+ items: visibleConversations,
1809
+ itemHeight,
1810
+ overscan,
1811
+ enabled: virtualized,
1812
+ getItemKey: (conversation) => conversation.id
1813
+ });
1814
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1815
+ "aside",
1816
+ {
1817
+ className,
1818
+ "data-collapsed": collapsed ? "" : void 0,
1819
+ "data-virtualized": virtualized ? "" : void 0,
1820
+ style,
1821
+ children: [
1822
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "llm-conversation-list__header", children: [
1823
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "llm-conversation-list__toolbar", children: [
1824
+ /* @__PURE__ */ jsxRuntime.jsx(
1825
+ "button",
1826
+ {
1827
+ "aria-label": collapsed ? "\u6253\u5F00\u8FB9\u680F" : "\u5173\u95ED\u8FB9\u680F",
1828
+ className: "llm-conversation-list__icon-button",
1829
+ onClick: () => onCollapsedChange?.(!collapsed),
1830
+ type: "button",
1831
+ children: collapsed ? "\u203A" : "\u2039"
1832
+ }
1833
+ ),
1834
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "llm-conversation-list__toolbar-actions", children: [
1835
+ searchable && !collapsed ? /* @__PURE__ */ jsxRuntime.jsx(
1836
+ "button",
1837
+ {
1838
+ "aria-expanded": searchOpen,
1839
+ "aria-label": "\u641C\u7D22\u4F1A\u8BDD",
1840
+ className: "llm-conversation-list__icon-button",
1841
+ onClick: () => setSearchOpen((open) => !open),
1842
+ type: "button",
1843
+ children: /* @__PURE__ */ jsxRuntime.jsx(SearchIcon, {})
1844
+ }
1845
+ ) : null,
1846
+ /* @__PURE__ */ jsxRuntime.jsx(
1847
+ "button",
1848
+ {
1849
+ "aria-label": "\u6253\u5F00\u65B0\u804A\u5929",
1850
+ className: "llm-conversation-list__icon-button",
1851
+ onClick: onNewConversation,
1852
+ type: "button",
1853
+ children: "+"
1854
+ }
1855
+ )
1856
+ ] })
1857
+ ] }),
1858
+ title ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-conversation-list__title", children: title }) : null,
1859
+ searchable && searchOpen && !collapsed ? /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "llm-conversation-list__search", children: [
1860
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "llm-conversation-list__search-label", children: "\u641C\u7D22\u4F1A\u8BDD" }),
1861
+ /* @__PURE__ */ jsxRuntime.jsx(
1862
+ "input",
1863
+ {
1864
+ "aria-label": "\u641C\u7D22\u4F1A\u8BDD",
1865
+ className: "llm-conversation-list__search-input",
1866
+ onChange: (event) => setSearchValue(event.target.value),
1867
+ placeholder: searchPlaceholder,
1868
+ type: "search",
1869
+ value: searchValue
1870
+ }
1871
+ )
1872
+ ] }) : null
1873
+ ] }),
1874
+ !collapsed ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-conversation-list__body", ref: containerRef, children: visibleConversations.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
1875
+ "div",
1876
+ {
1877
+ className: "llm-conversation-list__items",
1878
+ "data-virtualized": virtualized ? "" : void 0,
1879
+ role: "list",
1880
+ style: virtualized ? { height: totalSize } : void 0,
1881
+ children: (virtualized ? virtualItems : visibleConversations.map((conversation, index) => ({
1882
+ item: conversation,
1883
+ index,
1884
+ key: conversation.id,
1885
+ start: index * itemHeight,
1886
+ size: itemHeight
1887
+ }))).map((virtualItem) => /* @__PURE__ */ jsxRuntime.jsx(
1888
+ "div",
1889
+ {
1890
+ className: "llm-conversation-list__item",
1891
+ "data-virtualized": virtualized ? "" : void 0,
1892
+ role: "listitem",
1893
+ style: virtualized ? {
1894
+ height: virtualItem.size,
1895
+ transform: `translateY(${virtualItem.start}px)`
1896
+ } : void 0,
1897
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1898
+ ConversationItem,
1899
+ {
1900
+ active: virtualItem.item.id === activeId,
1901
+ conversation: virtualItem.item,
1902
+ ...onSelect && { onSelect },
1903
+ ...onDelete && { onDelete },
1904
+ ...onFavorite && { onFavorite },
1905
+ ...onPin && { onPin }
1906
+ }
1907
+ )
1908
+ },
1909
+ virtualItem.key
1910
+ ))
1911
+ }
1912
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-conversation-list__empty", children: resolvedEmptyText }) }) : null
1913
+ ]
1914
+ }
1915
+ );
1916
+ }
1917
+ function ConversationList({
1918
+ className,
1919
+ ...props
1920
+ }) {
1921
+ return /* @__PURE__ */ jsxRuntime.jsx(
1922
+ ConversationListPrimitive,
1923
+ {
1924
+ className: cn("llm-conversation-list", className),
1925
+ ...props
1926
+ }
1927
+ );
1928
+ }
1929
+ function defaultRenderMessage(message) {
1930
+ return /* @__PURE__ */ jsxRuntime.jsx(
1931
+ Bubble,
1932
+ {
1933
+ role: message.role,
1934
+ ...message.actions && { actions: message.actions },
1935
+ ...message.avatar && { avatar: message.avatar },
1936
+ ...message.content && { content: message.content },
1937
+ ...message.loading !== void 0 && { loading: message.loading },
1938
+ ...message.status && { status: message.status },
1939
+ ...message.timestamp && { timestamp: message.timestamp }
1940
+ }
1941
+ );
1942
+ }
1943
+ function MessageListPrimitive({
1944
+ messages,
1945
+ virtualized = false,
1946
+ itemHeight = 112,
1947
+ overscan = 5,
1948
+ emptyText = "\u6682\u65E0\u6D88\u606F",
1949
+ renderMessage,
1950
+ role = "log",
1951
+ ariaLabel = "\u6D88\u606F\u5217\u8868",
1952
+ className,
1953
+ style
1954
+ }) {
1955
+ const { containerRef, totalSize, virtualItems } = useVirtualList({
1956
+ items: messages,
1957
+ itemHeight,
1958
+ overscan,
1959
+ enabled: virtualized,
1960
+ getItemKey: (message) => message.id
1961
+ });
1962
+ const isLog = role === "log";
1963
+ const listItems = virtualized ? virtualItems : messages.map((message, index) => ({
1964
+ item: message,
1965
+ index,
1966
+ key: message.id,
1967
+ start: index * itemHeight,
1968
+ size: itemHeight
1969
+ }));
1970
+ return /* @__PURE__ */ jsxRuntime.jsx(
1971
+ "section",
1972
+ {
1973
+ "aria-label": ariaLabel,
1974
+ "aria-live": isLog ? "polite" : void 0,
1975
+ className,
1976
+ "data-virtualized": virtualized ? "" : void 0,
1977
+ role,
1978
+ style,
1979
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-message-list__body", ref: containerRef, children: messages.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
1980
+ "div",
1981
+ {
1982
+ className: "llm-message-list__items",
1983
+ "data-virtualized": virtualized ? "" : void 0,
1984
+ role: isLog ? void 0 : "list",
1985
+ style: virtualized ? { height: totalSize } : void 0,
1986
+ children: listItems.map((virtualItem) => /* @__PURE__ */ jsxRuntime.jsx(
1987
+ "div",
1988
+ {
1989
+ className: "llm-message-list__item",
1990
+ "data-virtualized": virtualized ? "" : void 0,
1991
+ role: isLog ? "article" : "listitem",
1992
+ style: virtualized ? {
1993
+ height: virtualItem.size,
1994
+ transform: `translateY(${virtualItem.start}px)`
1995
+ } : void 0,
1996
+ children: renderMessage ? renderMessage(virtualItem.item, virtualItem.index) : defaultRenderMessage(virtualItem.item)
1997
+ },
1998
+ virtualItem.key
1999
+ ))
2000
+ }
2001
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-message-list__empty", children: emptyText }) })
2002
+ }
2003
+ );
2004
+ }
2005
+ function MessageList({ className, ...props }) {
2006
+ return /* @__PURE__ */ jsxRuntime.jsx(
2007
+ MessageListPrimitive,
2008
+ {
2009
+ className: cn("llm-message-list", className),
2010
+ ...props
2011
+ }
2012
+ );
2013
+ }
2014
+ function ChevronIcon2() {
2015
+ return /* @__PURE__ */ jsxRuntime.jsx(
2016
+ "svg",
2017
+ {
2018
+ "aria-hidden": "true",
2019
+ className: "llm-thought__chevron",
2020
+ fill: "none",
2021
+ height: "16",
2022
+ viewBox: "0 0 24 24",
2023
+ width: "16",
2024
+ xmlns: "http://www.w3.org/2000/svg",
2025
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2026
+ "path",
2027
+ {
2028
+ d: "m9 6 6 6-6 6",
2029
+ stroke: "currentColor",
2030
+ strokeLinecap: "round",
2031
+ strokeLinejoin: "round",
2032
+ strokeWidth: "2"
2033
+ }
2034
+ )
2035
+ }
2036
+ );
2037
+ }
2038
+ function StatusIcon({ status }) {
2039
+ if (status === "loading") {
2040
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: "llm-thought__spinner", children: /* @__PURE__ */ jsxRuntime.jsx("span", {}) });
2041
+ }
2042
+ if (status === "success") {
2043
+ return /* @__PURE__ */ jsxRuntime.jsx(
2044
+ "svg",
2045
+ {
2046
+ "aria-hidden": "true",
2047
+ fill: "none",
2048
+ height: "14",
2049
+ viewBox: "0 0 24 24",
2050
+ width: "14",
2051
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2052
+ "path",
2053
+ {
2054
+ d: "m5 12.5 4.2 4.2L19 6.8",
2055
+ stroke: "currentColor",
2056
+ strokeLinecap: "round",
2057
+ strokeLinejoin: "round",
2058
+ strokeWidth: "2.4"
2059
+ }
2060
+ )
2061
+ }
2062
+ );
2063
+ }
2064
+ if (status === "error") {
2065
+ return /* @__PURE__ */ jsxRuntime.jsx(
2066
+ "svg",
2067
+ {
2068
+ "aria-hidden": "true",
2069
+ fill: "none",
2070
+ height: "14",
2071
+ viewBox: "0 0 24 24",
2072
+ width: "14",
2073
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2074
+ "path",
2075
+ {
2076
+ d: "m7 7 10 10M17 7 7 17",
2077
+ stroke: "currentColor",
2078
+ strokeLinecap: "round",
2079
+ strokeWidth: "2.4"
2080
+ }
2081
+ )
2082
+ }
2083
+ );
2084
+ }
2085
+ if (status === "abort") {
2086
+ return /* @__PURE__ */ jsxRuntime.jsx(
2087
+ "svg",
2088
+ {
2089
+ "aria-hidden": "true",
2090
+ fill: "none",
2091
+ height: "14",
2092
+ viewBox: "0 0 24 24",
2093
+ width: "14",
2094
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2095
+ "path",
2096
+ {
2097
+ d: "M7 12h10",
2098
+ stroke: "currentColor",
2099
+ strokeLinecap: "round",
2100
+ strokeWidth: "2.4"
2101
+ }
2102
+ )
2103
+ }
2104
+ );
2105
+ }
2106
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: "llm-thought__pending-dot" });
2107
+ }
2108
+ function hasExpandableContent(item) {
2109
+ return Boolean(item.status === "loading" && item.content);
2110
+ }
2111
+ function ThoughtNode({
2112
+ item,
2113
+ depth,
2114
+ line,
2115
+ isExpanded,
2116
+ onToggle
2117
+ }) {
2118
+ const status = item.status ?? "pending";
2119
+ const expandable = item.collapsible ?? hasExpandableContent(item);
2120
+ const expanded = expandable && isExpanded(item.key);
2121
+ const nodeId = `llm-thought-node-${item.key}`;
2122
+ const contentId = `llm-thought-content-${item.key}`;
2123
+ return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "llm-thought__item", "data-depth": depth, "data-status": status, children: [
2124
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-thought__rail", "data-line": line ? "" : void 0, children: item.icon === false ? null : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "llm-thought__icon", children: item.icon ?? /* @__PURE__ */ jsxRuntime.jsx(StatusIcon, { status }) }) }),
2125
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "llm-thought__node", children: [
2126
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "llm-thought__header", children: [
2127
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-thought__heading", id: nodeId, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "llm-thought__item-title", children: item.title }) }),
2128
+ expandable ? /* @__PURE__ */ jsxRuntime.jsx(
2129
+ "button",
2130
+ {
2131
+ "aria-controls": contentId,
2132
+ "aria-expanded": expanded,
2133
+ "aria-labelledby": nodeId,
2134
+ className: "llm-thought__toggle",
2135
+ onClick: () => onToggle(item),
2136
+ type: "button",
2137
+ children: /* @__PURE__ */ jsxRuntime.jsx(ChevronIcon2, {})
2138
+ }
2139
+ ) : null
2140
+ ] }),
2141
+ hasExpandableContent(item) ? /* @__PURE__ */ jsxRuntime.jsx(
2142
+ "div",
2143
+ {
2144
+ className: "llm-thought__content",
2145
+ hidden: !expanded,
2146
+ id: contentId,
2147
+ children: status === "loading" && item.content ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-thought__body", children: item.content }) : null
2148
+ }
2149
+ ) : null
2150
+ ] })
2151
+ ] });
2152
+ }
2153
+ function ThoughtList({
2154
+ items,
2155
+ depth,
2156
+ line,
2157
+ isExpanded,
2158
+ onToggle
2159
+ }) {
2160
+ return /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "llm-thought__list", children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
2161
+ ThoughtNode,
2162
+ {
2163
+ depth,
2164
+ isExpanded,
2165
+ item,
2166
+ line,
2167
+ onToggle
2168
+ },
2169
+ item.key
2170
+ )) });
2171
+ }
2172
+ function ThoughtPrimitive({
2173
+ items,
2174
+ title,
2175
+ description,
2176
+ defaultExpandedKeys,
2177
+ expandedKeys,
2178
+ onExpand,
2179
+ line = true,
2180
+ compact = false,
2181
+ emptyText = "\u6682\u65E0\u601D\u8003\u6B65\u9AA4",
2182
+ className,
2183
+ style
2184
+ }) {
2185
+ const [innerExpandedKeys, setInnerExpandedKeys] = react.useState(
2186
+ () => defaultExpandedKeys ?? []
2187
+ );
2188
+ const mergedExpandedKeys = expandedKeys ?? innerExpandedKeys;
2189
+ const hasHeader = Boolean(title || description);
2190
+ const isExpanded = (key) => mergedExpandedKeys.includes(key);
2191
+ const handleToggle = (item) => {
2192
+ const nextExpandedKeys = isExpanded(item.key) ? mergedExpandedKeys.filter((key) => key !== item.key) : [...mergedExpandedKeys, item.key];
2193
+ if (!expandedKeys) setInnerExpandedKeys(nextExpandedKeys);
2194
+ onExpand?.(nextExpandedKeys, item);
2195
+ };
2196
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2197
+ "section",
2198
+ {
2199
+ className,
2200
+ "data-compact": compact ? "" : void 0,
2201
+ style,
2202
+ children: [
2203
+ hasHeader ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "llm-thought__section-header", children: [
2204
+ title ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-thought__title", children: title }) : null,
2205
+ description ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-thought__summary", children: description }) : null
2206
+ ] }) : null,
2207
+ items.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
2208
+ ThoughtList,
2209
+ {
2210
+ depth: 0,
2211
+ isExpanded,
2212
+ items,
2213
+ line,
2214
+ onToggle: handleToggle
2215
+ }
2216
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-thought__empty", children: emptyText })
2217
+ ]
2218
+ }
2219
+ );
2220
+ }
2221
+ function Thought({ className, ...props }) {
2222
+ return /* @__PURE__ */ jsxRuntime.jsx(ThoughtPrimitive, { className: cn("llm-thought", className), ...props });
2223
+ }
2224
+ function ChevronIcon3() {
2225
+ return /* @__PURE__ */ jsxRuntime.jsx(
2226
+ "svg",
2227
+ {
2228
+ "aria-hidden": "true",
2229
+ className: "llm-citation__chevron",
2230
+ fill: "none",
2231
+ height: "16",
2232
+ viewBox: "0 0 24 24",
2233
+ width: "16",
2234
+ xmlns: "http://www.w3.org/2000/svg",
2235
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2236
+ "path",
2237
+ {
2238
+ d: "m6 9 6 6 6-6",
2239
+ stroke: "currentColor",
2240
+ strokeLinecap: "round",
2241
+ strokeLinejoin: "round",
2242
+ strokeWidth: "2"
2243
+ }
2244
+ )
2245
+ }
2246
+ );
2247
+ }
2248
+ function getHost(url) {
2249
+ if (!url) return void 0;
2250
+ try {
2251
+ return new URL(url).host;
2252
+ } catch {
2253
+ return url;
2254
+ }
2255
+ }
2256
+ function CitationPreview({ item }) {
2257
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "llm-citation__preview", role: "tooltip", children: [
2258
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "llm-citation__preview-title", children: item.title }),
2259
+ item.description ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "llm-citation__preview-description", children: item.description }) : null,
2260
+ item.url ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "llm-citation__preview-url", children: getHost(item.url) }) : null
2261
+ ] });
2262
+ }
2263
+ function CitationInlinePrimitive({
2264
+ item,
2265
+ index = 1,
2266
+ active = false,
2267
+ onCitation,
2268
+ className,
2269
+ style
2270
+ }) {
2271
+ const label = `\u5F15\u7528\u6765\u6E90 ${index}`;
2272
+ const handleClick = () => {
2273
+ onCitation?.(item.key, item);
2274
+ };
2275
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2276
+ "span",
2277
+ {
2278
+ className,
2279
+ "data-active": active ? "" : void 0,
2280
+ style,
2281
+ children: [
2282
+ /* @__PURE__ */ jsxRuntime.jsxs(
2283
+ "button",
2284
+ {
2285
+ "aria-label": label,
2286
+ className: "llm-citation__inline-trigger",
2287
+ onClick: handleClick,
2288
+ type: "button",
2289
+ children: [
2290
+ "[",
2291
+ index,
2292
+ "]"
2293
+ ]
2294
+ }
2295
+ ),
2296
+ /* @__PURE__ */ jsxRuntime.jsx(CitationPreview, { item })
2297
+ ]
2298
+ }
2299
+ );
2300
+ }
2301
+ function CitationPrimitive({
2302
+ items,
2303
+ title,
2304
+ defaultExpanded = true,
2305
+ expanded,
2306
+ onExpand,
2307
+ onCitation,
2308
+ emptyText = "\u6682\u65E0\u5F15\u7528\u6765\u6E90",
2309
+ className,
2310
+ style
2311
+ }) {
2312
+ const [innerExpanded, setInnerExpanded] = react.useState(defaultExpanded);
2313
+ const mergedExpanded = expanded ?? innerExpanded;
2314
+ const heading = title ?? `Used ${items.length} sources`;
2315
+ const handleExpand = () => {
2316
+ const nextExpanded = !mergedExpanded;
2317
+ if (expanded === void 0) setInnerExpanded(nextExpanded);
2318
+ onExpand?.(nextExpanded);
2319
+ };
2320
+ const handleCitation = (item) => {
2321
+ onCitation?.(item.key, item);
2322
+ };
2323
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2324
+ "section",
2325
+ {
2326
+ className,
2327
+ "data-expanded": mergedExpanded ? "" : void 0,
2328
+ style,
2329
+ children: [
2330
+ /* @__PURE__ */ jsxRuntime.jsxs(
2331
+ "button",
2332
+ {
2333
+ "aria-expanded": mergedExpanded,
2334
+ className: "llm-citation__header",
2335
+ onClick: handleExpand,
2336
+ type: "button",
2337
+ children: [
2338
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "llm-citation__title", children: heading }),
2339
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronIcon3, {})
2340
+ ]
2341
+ }
2342
+ ),
2343
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-citation__content", hidden: !mergedExpanded, children: items.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "llm-citation__list", children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsx("li", { className: "llm-citation__item", children: /* @__PURE__ */ jsxRuntime.jsxs(
2344
+ "button",
2345
+ {
2346
+ className: "llm-citation__item-button",
2347
+ onClick: () => handleCitation(item),
2348
+ type: "button",
2349
+ children: [
2350
+ item.icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "llm-citation__item-icon", children: item.icon }) : null,
2351
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "llm-citation__item-title", children: item.title })
2352
+ ]
2353
+ }
2354
+ ) }, item.key)) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "llm-citation__empty", children: emptyText }) })
2355
+ ]
2356
+ }
2357
+ );
2358
+ }
2359
+ function Citation({ className, ...props }) {
2360
+ return /* @__PURE__ */ jsxRuntime.jsx(CitationPrimitive, { className: cn("llm-citation", className), ...props });
2361
+ }
2362
+ function CitationInline({ className, ...props }) {
2363
+ return /* @__PURE__ */ jsxRuntime.jsx(
2364
+ CitationInlinePrimitive,
2365
+ {
2366
+ className: cn("llm-citation-inline", className),
2367
+ ...props
2368
+ }
2369
+ );
2370
+ }
2371
+ Citation.Inline = CitationInline;
2372
+
2373
+ // src/locale/zh-CN.ts
2374
+ var zhCN = {
2375
+ think: {
2376
+ loading: "\u601D\u8003\u4E2D...",
2377
+ done: "\u5DF2\u5B8C\u6210"
2378
+ },
2379
+ sender: {
2380
+ placeholder: "\u8F93\u5165\u6D88\u606F...",
2381
+ send: "\u53D1\u9001"
2382
+ }
2383
+ };
2384
+ var zh_CN_default = zhCN;
2385
+
2386
+ // src/locale/en-US.ts
2387
+ var enUS = {
2388
+ think: {
2389
+ loading: "Thinking...",
2390
+ done: "Done"
2391
+ },
2392
+ sender: {
2393
+ placeholder: "Type a message...",
2394
+ send: "Send"
2395
+ }
2396
+ };
2397
+ var en_US_default = enUS;
2398
+ var configContext = react.createContext(
2399
+ void 0
2400
+ );
2401
+ function ConfigProvider({
2402
+ theme,
2403
+ locale,
2404
+ ai,
2405
+ children,
2406
+ components
2407
+ }) {
2408
+ const [themeState, setThemeState] = react.useState({
2409
+ mode: theme?.mode ?? "system",
2410
+ primaryColor: theme?.primaryColor ?? "oklch(0.205 0 0)"
2411
+ });
2412
+ const setTheme = (newTheme) => {
2413
+ setThemeState((prev) => ({ ...prev, ...newTheme }));
2414
+ };
2415
+ const value = {
2416
+ theme: themeState,
2417
+ setTheme,
2418
+ locale: typeof locale === "string" ? locale === "en-US" ? en_US_default : zh_CN_default : locale ?? zh_CN_default,
2419
+ ...ai && { ai },
2420
+ ...components && { components }
2421
+ };
2422
+ const resolveMode = value.theme.mode === "system" ? window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : value.theme.mode;
2423
+ document.documentElement.setAttribute("data-theme", resolveMode);
2424
+ return /* @__PURE__ */ jsxRuntime.jsx(configContext.Provider, { value, children });
2425
+ }
2426
+ function useConfig() {
2427
+ const context = react.useContext(configContext);
2428
+ if (!context) {
2429
+ throw new Error("useConfig \u5FC5\u987B\u5728 ConfigProvider \u5185\u90E8\u4F7F\u7528");
2430
+ }
2431
+ return context;
2432
+ }
2433
+
2434
+ // src/hooks/useLocale.ts
2435
+ function useLocale() {
2436
+ const context = useConfig();
2437
+ return context.locale;
2438
+ }
2439
+
2440
+ // src/hooks/useTheme.ts
2441
+ function useTheme() {
2442
+ const context = useConfig();
2443
+ const setMode = (mode) => {
2444
+ context.setTheme({ mode });
2445
+ };
2446
+ const isDark = context.theme.mode === "system" ? window.matchMedia("(prefers-color-scheme: dark)").matches : context.theme.mode === "dark";
2447
+ return {
2448
+ mode: context.theme.mode,
2449
+ isDark,
2450
+ setMode,
2451
+ primaryColor: context.theme.primaryColor
2452
+ };
2453
+ }
2454
+ function useStream() {
2455
+ const [state, setState] = react.useState("idle");
2456
+ const [content, setContent] = react.useState("");
2457
+ const abortRef = react.useRef(new AbortController());
2458
+ const start = react.useCallback(
2459
+ async (generator, options) => {
2460
+ abortRef.current = new AbortController();
2461
+ setState("streaming");
2462
+ setContent("");
2463
+ let nextContent = "";
2464
+ try {
2465
+ for await (const token of generator) {
2466
+ if (abortRef.current.signal.aborted) break;
2467
+ nextContent += token;
2468
+ setContent(nextContent);
2469
+ options?.onToken?.(nextContent, token);
2470
+ }
2471
+ if (!abortRef.current.signal.aborted) {
2472
+ setState("complete");
2473
+ options?.onComplete?.(nextContent);
2474
+ }
2475
+ } catch {
2476
+ setState("error");
2477
+ options?.onError?.();
2478
+ }
2479
+ },
2480
+ []
2481
+ );
2482
+ const cancel = react.useCallback(() => {
2483
+ abortRef.current.abort();
2484
+ setState("idle");
2485
+ }, []);
2486
+ return { content, state, start, cancel };
2487
+ }
2488
+
2489
+ // src/utils/stream.ts
2490
+ async function* streamToGenerator(stream, encoding = "utf-8") {
2491
+ const reader = stream.getReader();
2492
+ const decoder = new TextDecoder(encoding);
2493
+ try {
2494
+ while (true) {
2495
+ const { value, done } = await reader.read();
2496
+ if (done) break;
2497
+ yield decoder.decode(value, { stream: true });
2498
+ }
2499
+ } finally {
2500
+ reader.releaseLock();
2501
+ }
2502
+ }
2503
+ function generatorToStream(gen) {
2504
+ const encoder = new TextEncoder();
2505
+ return new ReadableStream({
2506
+ async pull(controller) {
2507
+ const { value, done } = await gen.next();
2508
+ if (done) {
2509
+ controller.close();
2510
+ } else {
2511
+ controller.enqueue(encoder.encode(value));
2512
+ }
2513
+ }
2514
+ });
2515
+ }
2516
+ async function* mockStream(text, delay = 50) {
2517
+ for (const char of text) {
2518
+ yield char;
2519
+ await new Promise((resolve) => setTimeout(resolve, delay));
2520
+ }
2521
+ }
2522
+
2523
+ exports.Actions = Actions;
2524
+ exports.ActionsPrimitive = ActionsPrimitive;
2525
+ exports.Bubble = Bubble;
2526
+ exports.BubblePrimitive = BubblePrimitive;
2527
+ exports.Citation = Citation;
2528
+ exports.CitationInlinePrimitive = CitationInlinePrimitive;
2529
+ exports.CitationPrimitive = CitationPrimitive;
2530
+ exports.CodeHighlighter = CodeHighlighter;
2531
+ exports.CodeHighlighterPrimitive = CodeHighlighterPrimitive;
2532
+ exports.ConfigProvider = ConfigProvider;
2533
+ exports.ConversationItem = ConversationItem;
2534
+ exports.ConversationItemPrimitive = ConversationItemPrimitive;
2535
+ exports.ConversationList = ConversationList;
2536
+ exports.ConversationListPrimitive = ConversationListPrimitive;
2537
+ exports.Mark = Mark;
2538
+ exports.MarkPrimitive = MarkPrimitive;
2539
+ exports.MessageList = MessageList;
2540
+ exports.MessageListPrimitive = MessageListPrimitive;
2541
+ exports.Notification = Notification;
2542
+ exports.NotificationPrimitive = NotificationPrimitive;
2543
+ exports.NotificationStack = NotificationStack;
2544
+ exports.Prompts = Prompts;
2545
+ exports.PromptsPrimitive = PromptsPrimitive;
2546
+ exports.Sender = Sender;
2547
+ exports.SenderPrimitive = SenderPrimitive;
2548
+ exports.Think = Think;
2549
+ exports.ThinkPrimitive = ThinkPrimitive;
2550
+ exports.Thought = Thought;
2551
+ exports.ThoughtPrimitive = ThoughtPrimitive;
2552
+ exports.actionPresets = actionPresets;
2553
+ exports.createPresetActions = createPresetActions;
2554
+ exports.enUS = en_US_default;
2555
+ exports.generatorToStream = generatorToStream;
2556
+ exports.mockStream = mockStream;
2557
+ exports.sanitizeMarkdown = sanitizeMarkdown;
2558
+ exports.streamToGenerator = streamToGenerator;
2559
+ exports.useConfig = useConfig;
2560
+ exports.useLocale = useLocale;
2561
+ exports.useStream = useStream;
2562
+ exports.useTheme = useTheme;
2563
+ exports.useVirtualList = useVirtualList;
2564
+ exports.zhCN = zh_CN_default;
2565
+ //# sourceMappingURL=index.cjs.map
2566
+ //# sourceMappingURL=index.cjs.map