@ryanhe919/lumen-ui 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/LICENSE +21 -0
  2. package/dist/{LMBadge-OEGi87jW.js → LMBadge-BBDOGTps.js} +3 -3
  3. package/dist/{LMBadge-OEGi87jW.js.map → LMBadge-BBDOGTps.js.map} +1 -1
  4. package/dist/{LMBadge-1rGc3lqC.cjs → LMBadge-D95iccla.cjs} +3 -3
  5. package/dist/{LMBadge-1rGc3lqC.cjs.map → LMBadge-D95iccla.cjs.map} +1 -1
  6. package/dist/{LMCard-Sulq0Yjh.js → LMCard-D7ABNC95.js} +2 -2
  7. package/dist/{LMCard-Sulq0Yjh.js.map → LMCard-D7ABNC95.js.map} +1 -1
  8. package/dist/{LMCard-C48UclOk.cjs → LMCard-D_K051f2.cjs} +2 -2
  9. package/dist/{LMCard-C48UclOk.cjs.map → LMCard-D_K051f2.cjs.map} +1 -1
  10. package/dist/{LMDatePicker-BlKctoyr.cjs → LMDatePicker-BlUwN4On.cjs} +12 -12
  11. package/dist/LMDatePicker-BlUwN4On.cjs.map +1 -0
  12. package/dist/{LMDatePicker-CXiYSome.js → LMDatePicker-DSv28BFH.js} +12 -12
  13. package/dist/LMDatePicker-DSv28BFH.js.map +1 -0
  14. package/dist/{LMDrawer-BcVtcYCN.cjs → LMDrawer--lFV_a3m.cjs} +3 -3
  15. package/dist/{LMDrawer-BcVtcYCN.cjs.map → LMDrawer--lFV_a3m.cjs.map} +1 -1
  16. package/dist/{LMDrawer-DcPqwiuo.js → LMDrawer-DJ5ugeZR.js} +3 -3
  17. package/dist/{LMDrawer-DcPqwiuo.js.map → LMDrawer-DJ5ugeZR.js.map} +1 -1
  18. package/dist/{LMStatCard-4mDqhlHt.js → LMStatCard-D5HV9r6d.js} +4 -4
  19. package/dist/{LMStatCard-4mDqhlHt.js.map → LMStatCard-D5HV9r6d.js.map} +1 -1
  20. package/dist/{LMStatCard-Du5Mti-p.cjs → LMStatCard-MXs9Z0qH.cjs} +4 -4
  21. package/dist/{LMStatCard-Du5Mti-p.cjs.map → LMStatCard-MXs9Z0qH.cjs.map} +1 -1
  22. package/dist/{LMSwitch-CVFdgSPh.js → LMSwitch-CP1_nrfU.js} +2 -2
  23. package/dist/LMSwitch-CP1_nrfU.js.map +1 -0
  24. package/dist/{LMSwitch-CKnrY30F.cjs → LMSwitch-DYoSH6wE.cjs} +2 -2
  25. package/dist/LMSwitch-DYoSH6wE.cjs.map +1 -0
  26. package/dist/{LMTabs-DZFAU58t.js → LMTabs-D5n9lB8X.js} +3 -3
  27. package/dist/{LMTabs-DZFAU58t.js.map → LMTabs-D5n9lB8X.js.map} +1 -1
  28. package/dist/{LMTabs-DCVaqbrn.cjs → LMTabs-NPmOzPat.cjs} +3 -3
  29. package/dist/{LMTabs-DCVaqbrn.cjs.map → LMTabs-NPmOzPat.cjs.map} +1 -1
  30. package/dist/{LMUpload-B_GA4O8W.js → LMUpload-BwXoxIfE.js} +5 -5
  31. package/dist/LMUpload-BwXoxIfE.js.map +1 -0
  32. package/dist/{LMUpload-BpISVQGz.cjs → LMUpload-CJopkWc6.cjs} +5 -5
  33. package/dist/LMUpload-CJopkWc6.cjs.map +1 -0
  34. package/dist/components/Chat/LMChatBubble/LMChatBubble.d.ts +28 -0
  35. package/dist/components/Chat/LMChatBubble/LMChatBubble.d.ts.map +1 -0
  36. package/dist/components/Chat/LMChatBubble/LMChatBubble.stories.d.ts +25 -0
  37. package/dist/components/Chat/LMChatBubble/LMChatBubble.stories.d.ts.map +1 -0
  38. package/dist/components/Chat/LMChatBubble/index.d.ts +3 -0
  39. package/dist/components/Chat/LMChatBubble/index.d.ts.map +1 -0
  40. package/dist/components/Chat/LMChatContainer/LMChatContainer.d.ts +85 -0
  41. package/dist/components/Chat/LMChatContainer/LMChatContainer.d.ts.map +1 -0
  42. package/dist/components/Chat/LMChatContainer/LMChatContainer.stories.d.ts +23 -0
  43. package/dist/components/Chat/LMChatContainer/LMChatContainer.stories.d.ts.map +1 -0
  44. package/dist/components/Chat/LMChatContainer/index.d.ts +3 -0
  45. package/dist/components/Chat/LMChatContainer/index.d.ts.map +1 -0
  46. package/dist/components/Chat/LMChatInput/LMChatInput.d.ts +55 -0
  47. package/dist/components/Chat/LMChatInput/LMChatInput.d.ts.map +1 -0
  48. package/dist/components/Chat/LMChatInput/LMChatInput.stories.d.ts +27 -0
  49. package/dist/components/Chat/LMChatInput/LMChatInput.stories.d.ts.map +1 -0
  50. package/dist/components/Chat/LMChatInput/index.d.ts +3 -0
  51. package/dist/components/Chat/LMChatInput/index.d.ts.map +1 -0
  52. package/dist/components/Chat/LMChatList/LMChatList.d.ts +60 -0
  53. package/dist/components/Chat/LMChatList/LMChatList.d.ts.map +1 -0
  54. package/dist/components/Chat/LMChatList/LMChatList.stories.d.ts +21 -0
  55. package/dist/components/Chat/LMChatList/LMChatList.stories.d.ts.map +1 -0
  56. package/dist/components/Chat/LMChatList/index.d.ts +3 -0
  57. package/dist/components/Chat/LMChatList/index.d.ts.map +1 -0
  58. package/dist/components/Chat/LMChatMessage/LMChatMessage.d.ts +56 -0
  59. package/dist/components/Chat/LMChatMessage/LMChatMessage.d.ts.map +1 -0
  60. package/dist/components/Chat/LMChatMessage/LMChatMessage.stories.d.ts +27 -0
  61. package/dist/components/Chat/LMChatMessage/LMChatMessage.stories.d.ts.map +1 -0
  62. package/dist/components/Chat/LMChatMessage/index.d.ts +3 -0
  63. package/dist/components/Chat/LMChatMessage/index.d.ts.map +1 -0
  64. package/dist/components/Chat/LMCodeBlock/LMCodeBlock.d.ts +32 -0
  65. package/dist/components/Chat/LMCodeBlock/LMCodeBlock.d.ts.map +1 -0
  66. package/dist/components/Chat/LMCodeBlock/LMCodeBlock.stories.d.ts +27 -0
  67. package/dist/components/Chat/LMCodeBlock/LMCodeBlock.stories.d.ts.map +1 -0
  68. package/dist/components/Chat/LMCodeBlock/index.d.ts +3 -0
  69. package/dist/components/Chat/LMCodeBlock/index.d.ts.map +1 -0
  70. package/dist/components/Chat/LMMarkdownRenderer/LMMarkdownRenderer.d.ts +24 -0
  71. package/dist/components/Chat/LMMarkdownRenderer/LMMarkdownRenderer.d.ts.map +1 -0
  72. package/dist/components/Chat/LMMarkdownRenderer/LMMarkdownRenderer.stories.d.ts +23 -0
  73. package/dist/components/Chat/LMMarkdownRenderer/LMMarkdownRenderer.stories.d.ts.map +1 -0
  74. package/dist/components/Chat/LMMarkdownRenderer/index.d.ts +3 -0
  75. package/dist/components/Chat/LMMarkdownRenderer/index.d.ts.map +1 -0
  76. package/dist/components/Chat/LMTypingIndicator/LMTypingIndicator.d.ts +21 -0
  77. package/dist/components/Chat/LMTypingIndicator/LMTypingIndicator.d.ts.map +1 -0
  78. package/dist/components/Chat/LMTypingIndicator/LMTypingIndicator.stories.d.ts +25 -0
  79. package/dist/components/Chat/LMTypingIndicator/LMTypingIndicator.stories.d.ts.map +1 -0
  80. package/dist/components/Chat/LMTypingIndicator/index.d.ts +3 -0
  81. package/dist/components/Chat/LMTypingIndicator/index.d.ts.map +1 -0
  82. package/dist/components/Chat/index.d.ts +9 -0
  83. package/dist/components/Chat/index.d.ts.map +1 -0
  84. package/dist/components/badge/index.cjs +1 -1
  85. package/dist/components/badge/index.js +1 -1
  86. package/dist/components/card/index.cjs +1 -1
  87. package/dist/components/card/index.js +1 -1
  88. package/dist/components/date-picker/index.cjs +1 -1
  89. package/dist/components/date-picker/index.js +1 -1
  90. package/dist/components/drawer/index.cjs +1 -1
  91. package/dist/components/drawer/index.js +1 -1
  92. package/dist/components/stat-card/index.cjs +1 -1
  93. package/dist/components/stat-card/index.js +1 -1
  94. package/dist/components/switch/index.cjs +1 -1
  95. package/dist/components/switch/index.js +1 -1
  96. package/dist/components/tabs/index.cjs +1 -1
  97. package/dist/components/tabs/index.js +1 -1
  98. package/dist/components/upload/index.cjs +1 -1
  99. package/dist/components/upload/index.js +1 -1
  100. package/dist/index.cjs +2017 -8
  101. package/dist/index.cjs.map +1 -1
  102. package/dist/index.d.ts +1 -0
  103. package/dist/index.d.ts.map +1 -1
  104. package/dist/index.js +2023 -13
  105. package/dist/index.js.map +1 -1
  106. package/dist/style.css +254 -2
  107. package/package.json +7 -7
  108. package/dist/LMDatePicker-BlKctoyr.cjs.map +0 -1
  109. package/dist/LMDatePicker-CXiYSome.js.map +0 -1
  110. package/dist/LMSwitch-CKnrY30F.cjs.map +0 -1
  111. package/dist/LMSwitch-CVFdgSPh.js.map +0 -1
  112. package/dist/LMUpload-B_GA4O8W.js.map +0 -1
  113. package/dist/LMUpload-BpISVQGz.cjs.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const LMBadge = require("./LMBadge-1rGc3lqC.cjs");
3
+ const LMBadge = require("./LMBadge-D95iccla.cjs");
4
4
  const LMInput = require("./LMInput-CVjawj1F.cjs");
5
5
  const LMTextarea = require("./LMTextarea-yG0OBZjA.cjs");
6
6
  const LMNumberInput = require("./LMNumberInput-B1tU7T_W.cjs");
@@ -8,25 +8,2025 @@ const LMSearchInput = require("./LMSearchInput-nBlAX734.cjs");
8
8
  const LMSelect = require("./LMSelect-ByQcUp2S.cjs");
9
9
  const LMCheckbox = require("./LMCheckbox-C2YvEQ8f.cjs");
10
10
  const LMRadio = require("./LMRadio-CHn6nFSy.cjs");
11
- const LMSwitch = require("./LMSwitch-CKnrY30F.cjs");
11
+ const LMSwitch = require("./LMSwitch-DYoSH6wE.cjs");
12
12
  const LMField = require("./LMField-DhDHj64z.cjs");
13
- const LMDatePicker = require("./LMDatePicker-BlKctoyr.cjs");
14
- const LMUpload = require("./LMUpload-BpISVQGz.cjs");
15
- const LMStatCard = require("./LMStatCard-Du5Mti-p.cjs");
13
+ const LMDatePicker = require("./LMDatePicker-BlUwN4On.cjs");
14
+ const LMUpload = require("./LMUpload-CJopkWc6.cjs");
15
+ const LMStatCard = require("./LMStatCard-MXs9Z0qH.cjs");
16
16
  const LMTooltip = require("./LMTooltip-30_lOAnH.cjs");
17
- const LMCard = require("./LMCard-C48UclOk.cjs");
18
- const LMTabs = require("./LMTabs-DCVaqbrn.cjs");
17
+ const LMCard = require("./LMCard-D_K051f2.cjs");
18
+ const LMTabs = require("./LMTabs-NPmOzPat.cjs");
19
19
  const LMEmpty = require("./LMEmpty-BedQxpbi.cjs");
20
20
  const LMMenu = require("./LMMenu-IDAgZFuC.cjs");
21
21
  const LMDropdown = require("./LMDropdown-yl5l7qtz.cjs");
22
22
  const LMPagination = require("./LMPagination-CouDFIwd.cjs");
23
23
  const useMessage = require("./useMessage-BBxUPe8b.cjs");
24
24
  const useConfirm = require("./useConfirm-BO5Ch3Bf.cjs");
25
- const LMDrawer = require("./LMDrawer-BcVtcYCN.cjs");
25
+ const LMDrawer = require("./LMDrawer--lFV_a3m.cjs");
26
26
  const LMButton = require("./LMButton-B258yfky.cjs");
27
27
  const LMTable = require("./LMTable-j1ZzAzXB.cjs");
28
28
  const LMModal = require("./LMModal-BCVVPLot.cjs");
29
+ const jsxRuntime = require("react/jsx-runtime");
30
+ const React = require("react");
29
31
  const componentSizes = require("./componentSizes-DUTZ7uEM.cjs");
32
+ const chatInputThemeStyles = `
33
+ :root, [data-theme="light"] {
34
+ --lm-chat-input-bg: rgba(255, 255, 255, 0.85);
35
+ --lm-chat-input-bg-focus: rgba(255, 255, 255, 0.98);
36
+ --lm-chat-input-border: rgba(0, 0, 0, 0.06);
37
+ --lm-chat-input-border-focus: rgba(0, 0, 0, 0.12);
38
+ --lm-chat-input-shadow: 0 2px 12px rgba(0, 0, 0, 0.03), 0 1px 2px rgba(0, 0, 0, 0.02);
39
+ --lm-chat-input-shadow-focus: 0 4px 24px rgba(0, 0, 0, 0.08), 0 1px 3px rgba(0, 0, 0, 0.04);
40
+ }
41
+ [data-theme="dark"] {
42
+ --lm-chat-input-bg: rgba(30, 41, 59, 0.85);
43
+ --lm-chat-input-bg-focus: rgba(30, 41, 59, 0.98);
44
+ --lm-chat-input-border: rgba(255, 255, 255, 0.08);
45
+ --lm-chat-input-border-focus: rgba(255, 255, 255, 0.15);
46
+ --lm-chat-input-shadow: 0 2px 12px rgba(0, 0, 0, 0.2), 0 1px 2px rgba(0, 0, 0, 0.1);
47
+ --lm-chat-input-shadow-focus: 0 4px 24px rgba(0, 0, 0, 0.3), 0 1px 3px rgba(0, 0, 0, 0.15);
48
+ }
49
+ `;
50
+ const ToolbarButton = ({
51
+ icon,
52
+ label,
53
+ onClick,
54
+ disabled = false,
55
+ active = false
56
+ }) => /* @__PURE__ */ jsxRuntime.jsxs(
57
+ "button",
58
+ {
59
+ type: "button",
60
+ onClick,
61
+ disabled,
62
+ className: `
63
+ flex items-center gap-1 px-2 py-1 rounded-md cursor-pointer
64
+ transition-all duration-150
65
+ disabled:opacity-40 disabled:cursor-not-allowed
66
+ hover:bg-(--lm-bg-hover) active:scale-95
67
+ `.trim().replace(/\s+/g, " "),
68
+ style: {
69
+ backgroundColor: active ? "var(--lm-primary-50)" : "transparent",
70
+ color: active ? "var(--lm-primary-600)" : "var(--lm-text-tertiary)"
71
+ },
72
+ title: label,
73
+ children: [
74
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-4 h-4 flex items-center justify-center", children: icon }),
75
+ label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium", children: label })
76
+ ]
77
+ }
78
+ );
79
+ const SendIcon = ({ className }) => /* @__PURE__ */ jsxRuntime.jsxs(
80
+ "svg",
81
+ {
82
+ className,
83
+ viewBox: "0 0 24 24",
84
+ fill: "none",
85
+ stroke: "currentColor",
86
+ strokeWidth: 2,
87
+ strokeLinecap: "round",
88
+ strokeLinejoin: "round",
89
+ children: [
90
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 12h14" }),
91
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 5l7 7-7 7" })
92
+ ]
93
+ }
94
+ );
95
+ const StopIcon = ({ className }) => /* @__PURE__ */ jsxRuntime.jsx(
96
+ "svg",
97
+ {
98
+ className,
99
+ viewBox: "0 0 24 24",
100
+ fill: "currentColor",
101
+ children: /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "6", y: "6", width: "12", height: "12", rx: "3" })
102
+ }
103
+ );
104
+ const LoadingIcon = ({ className }) => /* @__PURE__ */ jsxRuntime.jsxs(
105
+ "svg",
106
+ {
107
+ className,
108
+ viewBox: "0 0 24 24",
109
+ fill: "none",
110
+ children: [
111
+ /* @__PURE__ */ jsxRuntime.jsx(
112
+ "circle",
113
+ {
114
+ cx: "12",
115
+ cy: "12",
116
+ r: "10",
117
+ stroke: "currentColor",
118
+ strokeWidth: "2",
119
+ strokeOpacity: "0.2"
120
+ }
121
+ ),
122
+ /* @__PURE__ */ jsxRuntime.jsx(
123
+ "path",
124
+ {
125
+ d: "M12 2C6.5 2 2 6.5 2 12",
126
+ stroke: "currentColor",
127
+ strokeWidth: "2",
128
+ strokeLinecap: "round",
129
+ children: /* @__PURE__ */ jsxRuntime.jsx(
130
+ "animateTransform",
131
+ {
132
+ attributeName: "transform",
133
+ type: "rotate",
134
+ from: "0 12 12",
135
+ to: "360 12 12",
136
+ dur: "0.8s",
137
+ repeatCount: "indefinite"
138
+ }
139
+ )
140
+ }
141
+ )
142
+ ]
143
+ }
144
+ );
145
+ const SIZE_CONFIG$1 = {
146
+ xs: { minHeight: "28px", padding: "px-3 py-2", buttonSize: "w-6 h-6", iconSize: "w-3 h-3", gap: "gap-2", sendButtonSize: "w-6 h-6", sendIconSize: "w-3 h-3" },
147
+ sm: { minHeight: "32px", padding: "px-3.5 py-2.5", buttonSize: "w-7 h-7", iconSize: "w-3.5 h-3.5", gap: "gap-2.5", sendButtonSize: "w-7 h-7", sendIconSize: "w-3.5 h-3.5" },
148
+ md: { minHeight: "36px", padding: "px-4 py-3", buttonSize: "w-8 h-8", iconSize: "w-4 h-4", gap: "gap-3", sendButtonSize: "w-8 h-8", sendIconSize: "w-4 h-4" },
149
+ lg: { minHeight: "44px", padding: "px-5 py-3.5", buttonSize: "w-9 h-9", iconSize: "w-4.5 h-4.5", gap: "gap-3", sendButtonSize: "w-9 h-9", sendIconSize: "w-4.5 h-4.5" },
150
+ xl: { minHeight: "52px", padding: "px-5 py-4", buttonSize: "w-10 h-10", iconSize: "w-5 h-5", gap: "gap-4", sendButtonSize: "w-10 h-10", sendIconSize: "w-5 h-5" },
151
+ "2xl": { minHeight: "60px", padding: "px-6 py-4.5", buttonSize: "w-11 h-11", iconSize: "w-5.5 h-5.5", gap: "gap-4", sendButtonSize: "w-11 h-11", sendIconSize: "w-5.5 h-5.5" }
152
+ };
153
+ const LMChatInput$1 = ({
154
+ value = "",
155
+ onChange,
156
+ onSend,
157
+ placeholder = "输入消息...",
158
+ size = "md",
159
+ disabled = false,
160
+ sending = false,
161
+ maxLength,
162
+ showCount = false,
163
+ maxRows = 6,
164
+ className = "",
165
+ autoFocus = false,
166
+ sendButtonText,
167
+ showSendButton = true,
168
+ rightSlot,
169
+ toolbar,
170
+ enterToSend = true,
171
+ onStop,
172
+ isGenerating = false
173
+ }) => {
174
+ const textareaRef = React.useRef(null);
175
+ const [isFocused, setIsFocused] = React.useState(false);
176
+ const [internalValue, setInternalValue] = React.useState(value);
177
+ const resolvedSize = componentSizes.clampComponentSize(size, componentSizes.COMPONENT_SIZE_ORDER);
178
+ const config = SIZE_CONFIG$1[resolvedSize];
179
+ React.useEffect(() => {
180
+ setInternalValue(value);
181
+ }, [value]);
182
+ const adjustHeight = React.useCallback(() => {
183
+ const textarea = textareaRef.current;
184
+ if (!textarea) return;
185
+ textarea.style.height = "auto";
186
+ const lineHeight = parseInt(getComputedStyle(textarea).lineHeight) || 24;
187
+ const maxHeight = lineHeight * maxRows;
188
+ const newHeight = Math.min(textarea.scrollHeight, maxHeight);
189
+ textarea.style.height = `${newHeight}px`;
190
+ }, [maxRows]);
191
+ React.useEffect(() => {
192
+ adjustHeight();
193
+ }, [internalValue, adjustHeight]);
194
+ const handleChange = React.useCallback((e) => {
195
+ const newValue = e.target.value;
196
+ if (maxLength && newValue.length > maxLength) return;
197
+ setInternalValue(newValue);
198
+ onChange == null ? void 0 : onChange(newValue);
199
+ }, [maxLength, onChange]);
200
+ const handleSend = React.useCallback(() => {
201
+ const trimmedValue = internalValue.trim();
202
+ if (!trimmedValue || disabled || sending) return;
203
+ onSend == null ? void 0 : onSend(trimmedValue);
204
+ setInternalValue("");
205
+ onChange == null ? void 0 : onChange("");
206
+ if (textareaRef.current) {
207
+ textareaRef.current.style.height = "auto";
208
+ }
209
+ }, [internalValue, disabled, sending, onSend, onChange]);
210
+ const handleKeyDown = React.useCallback((e) => {
211
+ if (enterToSend && e.key === "Enter" && !e.shiftKey) {
212
+ e.preventDefault();
213
+ handleSend();
214
+ }
215
+ }, [enterToSend, handleSend]);
216
+ const handleStop = React.useCallback(() => {
217
+ onStop == null ? void 0 : onStop();
218
+ }, [onStop]);
219
+ const isDisabled = disabled || sending;
220
+ const canSend = internalValue.trim().length > 0 && !isDisabled;
221
+ const showStopButton = isGenerating && onStop;
222
+ const getContainerStyles = () => ({
223
+ backgroundColor: isFocused ? "var(--lm-chat-input-bg-focus)" : "var(--lm-chat-input-bg)",
224
+ backdropFilter: "blur(20px) saturate(180%)",
225
+ WebkitBackdropFilter: "blur(20px) saturate(180%)",
226
+ borderColor: isFocused ? "var(--lm-chat-input-border-focus)" : "var(--lm-chat-input-border)",
227
+ boxShadow: isFocused ? "var(--lm-chat-input-shadow-focus)" : "var(--lm-chat-input-shadow)",
228
+ transition: "all var(--lm-transition-normal) var(--lm-ease-out)"
229
+ });
230
+ const getSendButtonStyles = () => {
231
+ const baseStyles = {
232
+ transition: "all var(--lm-transition-fast) var(--lm-ease-spring)",
233
+ borderRadius: "var(--lm-radius-md)"
234
+ };
235
+ if (showStopButton) {
236
+ return {
237
+ ...baseStyles,
238
+ background: "linear-gradient(135deg, var(--lm-error-500) 0%, var(--lm-error-600) 100%)",
239
+ color: "white",
240
+ boxShadow: "0 2px 8px rgba(239, 68, 68, 0.3)"
241
+ };
242
+ }
243
+ if (canSend) {
244
+ return {
245
+ ...baseStyles,
246
+ background: "linear-gradient(135deg, var(--lm-primary-500) 0%, var(--lm-primary-600) 100%)",
247
+ color: "white",
248
+ boxShadow: "0 2px 8px rgba(0, 122, 255, 0.3)"
249
+ };
250
+ }
251
+ return {
252
+ ...baseStyles,
253
+ backgroundColor: "var(--lm-gray-100)",
254
+ color: "var(--lm-gray-400)"
255
+ };
256
+ };
257
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
258
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: chatInputThemeStyles }),
259
+ /* @__PURE__ */ jsxRuntime.jsxs(
260
+ "div",
261
+ {
262
+ className: `
263
+ relative flex flex-col border rounded-2xl
264
+ ${config.padding}
265
+ ${className}
266
+ `.trim().replace(/\s+/g, " "),
267
+ style: getContainerStyles(),
268
+ children: [
269
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-start ${config.gap}`, children: [
270
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxRuntime.jsx(
271
+ "textarea",
272
+ {
273
+ ref: textareaRef,
274
+ value: internalValue,
275
+ onChange: handleChange,
276
+ onKeyDown: handleKeyDown,
277
+ onFocus: () => setIsFocused(true),
278
+ onBlur: () => setIsFocused(false),
279
+ placeholder,
280
+ disabled: isDisabled,
281
+ autoFocus,
282
+ rows: 1,
283
+ className: `
284
+ w-full resize-none bg-transparent leading-relaxed
285
+ outline-none focus:outline-none focus:ring-0 border-none
286
+ ${componentSizes.SIZE_TEXT_CLASSES[resolvedSize]}
287
+ placeholder:text-(--lm-text-tertiary)
288
+ disabled:text-(--lm-text-disabled) disabled:cursor-not-allowed
289
+ `.trim().replace(/\s+/g, " "),
290
+ style: {
291
+ color: "var(--lm-text-primary)",
292
+ minHeight: config.minHeight,
293
+ maxHeight: `calc(${config.minHeight} * ${maxRows})`,
294
+ outline: "none",
295
+ boxShadow: "none"
296
+ }
297
+ }
298
+ ) }),
299
+ rightSlot && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center shrink-0 pt-0.5", children: rightSlot })
300
+ ] }),
301
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mt-2 -mb-1", children: [
302
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-0.5", children: toolbar }),
303
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
304
+ showCount && /* @__PURE__ */ jsxRuntime.jsxs(
305
+ "span",
306
+ {
307
+ className: "text-xs font-medium",
308
+ style: {
309
+ color: maxLength && internalValue.length > maxLength * 0.9 ? "var(--lm-warning-500)" : "var(--lm-text-tertiary)"
310
+ },
311
+ children: [
312
+ internalValue.length,
313
+ maxLength ? `/${maxLength}` : ""
314
+ ]
315
+ }
316
+ ),
317
+ showSendButton && /* @__PURE__ */ jsxRuntime.jsx(
318
+ "button",
319
+ {
320
+ type: "button",
321
+ onClick: showStopButton ? handleStop : handleSend,
322
+ disabled: !showStopButton && !canSend,
323
+ className: `
324
+ ${config.sendButtonSize}
325
+ flex items-center justify-center shrink-0
326
+ cursor-pointer select-none
327
+ focus:outline-none
328
+ disabled:cursor-not-allowed disabled:opacity-50
329
+ hover:scale-105 active:scale-95
330
+ `.trim().replace(/\s+/g, " "),
331
+ style: getSendButtonStyles(),
332
+ "aria-label": showStopButton ? "停止生成" : "发送消息",
333
+ children: sending ? /* @__PURE__ */ jsxRuntime.jsx(LoadingIcon, { className: config.sendIconSize }) : showStopButton ? /* @__PURE__ */ jsxRuntime.jsx(StopIcon, { className: config.sendIconSize }) : sendButtonText ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${componentSizes.SIZE_TEXT_CLASSES[resolvedSize]} font-medium`, children: sendButtonText }) : /* @__PURE__ */ jsxRuntime.jsx(SendIcon, { className: config.sendIconSize })
334
+ }
335
+ )
336
+ ] })
337
+ ] })
338
+ ]
339
+ }
340
+ )
341
+ ] });
342
+ };
343
+ const LMChatInput = React.memo(LMChatInput$1);
344
+ const chatBubbleThemeStyles = `
345
+ :root, [data-theme="light"] {
346
+ --lm-bubble-assistant-bg: rgba(255, 255, 255, 0.85);
347
+ --lm-bubble-assistant-border: rgba(0, 0, 0, 0.06);
348
+ --lm-bubble-assistant-shadow: 0 4px 16px rgba(0, 0, 0, 0.06), 0 1px 3px rgba(0, 0, 0, 0.04);
349
+ --lm-bubble-outline-bg: rgba(255, 255, 255, 0.9);
350
+ --lm-bubble-outline-border: rgba(0, 0, 0, 0.1);
351
+ --lm-bubble-soft-bg: rgba(249, 250, 251, 0.9);
352
+ --lm-bubble-soft-border: rgba(0, 0, 0, 0.04);
353
+ --lm-bubble-system-bg: rgba(249, 250, 251, 0.7);
354
+ --lm-bubble-system-border: rgba(0, 0, 0, 0.1);
355
+ --lm-bubble-error-bg: rgba(254, 226, 226, 0.9);
356
+ --lm-bubble-error-border: rgba(239, 68, 68, 0.2);
357
+ }
358
+ [data-theme="dark"] {
359
+ --lm-bubble-assistant-bg: rgba(51, 65, 85, 0.85);
360
+ --lm-bubble-assistant-border: rgba(255, 255, 255, 0.08);
361
+ --lm-bubble-assistant-shadow: 0 4px 16px rgba(0, 0, 0, 0.2), 0 1px 3px rgba(0, 0, 0, 0.1);
362
+ --lm-bubble-outline-bg: rgba(51, 65, 85, 0.6);
363
+ --lm-bubble-outline-border: rgba(255, 255, 255, 0.12);
364
+ --lm-bubble-soft-bg: rgba(30, 41, 59, 0.9);
365
+ --lm-bubble-soft-border: rgba(255, 255, 255, 0.06);
366
+ --lm-bubble-system-bg: rgba(30, 41, 59, 0.7);
367
+ --lm-bubble-system-border: rgba(255, 255, 255, 0.1);
368
+ --lm-bubble-error-bg: rgba(127, 29, 29, 0.5);
369
+ --lm-bubble-error-border: rgba(239, 68, 68, 0.3);
370
+ }
371
+ `;
372
+ const BUBBLE_CONFIG = {
373
+ user: {
374
+ align: "right",
375
+ borderRadius: "20px 20px 4px 20px"
376
+ },
377
+ assistant: {
378
+ align: "left",
379
+ borderRadius: "20px 20px 20px 4px"
380
+ },
381
+ system: {
382
+ align: "left",
383
+ borderRadius: "16px"
384
+ }
385
+ };
386
+ const LMChatBubble$1 = ({
387
+ role = "user",
388
+ variant = "default",
389
+ size = "md",
390
+ children,
391
+ isStreaming = false,
392
+ className = "",
393
+ error = false,
394
+ footer,
395
+ style
396
+ }) => {
397
+ const resolvedSize = componentSizes.clampComponentSize(size, componentSizes.COMPONENT_SIZE_ORDER);
398
+ const config = BUBBLE_CONFIG[role];
399
+ const getBubbleStyles = () => {
400
+ const baseStyles = {
401
+ borderRadius: config.borderRadius,
402
+ transition: "all var(--lm-transition-normal) var(--lm-ease-out)",
403
+ maxWidth: "85%",
404
+ wordBreak: "break-word",
405
+ position: "relative"
406
+ };
407
+ if (error) {
408
+ return {
409
+ ...baseStyles,
410
+ backgroundColor: "var(--lm-bubble-error-bg)",
411
+ color: "var(--lm-error-700)",
412
+ border: "1px solid var(--lm-bubble-error-border)",
413
+ backdropFilter: "blur(8px)",
414
+ boxShadow: "0 2px 8px rgba(239, 68, 68, 0.1)"
415
+ };
416
+ }
417
+ if (role === "user") {
418
+ switch (variant) {
419
+ case "filled":
420
+ case "default":
421
+ return {
422
+ ...baseStyles,
423
+ background: "linear-gradient(135deg, var(--lm-primary-500) 0%, var(--lm-primary-600) 100%)",
424
+ color: "white",
425
+ boxShadow: "0 2px 12px rgba(0, 122, 255, 0.25), 0 1px 3px rgba(0, 122, 255, 0.1)"
426
+ };
427
+ case "outline":
428
+ return {
429
+ ...baseStyles,
430
+ backgroundColor: "var(--lm-bubble-outline-bg)",
431
+ backdropFilter: "blur(12px)",
432
+ color: "var(--lm-primary-600)",
433
+ border: "1.5px solid var(--lm-primary-300)",
434
+ boxShadow: "0 2px 8px rgba(0, 122, 255, 0.08)"
435
+ };
436
+ case "soft":
437
+ return {
438
+ ...baseStyles,
439
+ backgroundColor: "var(--lm-bubble-soft-bg)",
440
+ backdropFilter: "blur(8px)",
441
+ color: "var(--lm-primary-700)",
442
+ border: "1px solid rgba(0, 122, 255, 0.12)"
443
+ };
444
+ default:
445
+ return {
446
+ ...baseStyles,
447
+ background: "linear-gradient(135deg, var(--lm-primary-500) 0%, var(--lm-primary-600) 100%)",
448
+ color: "white",
449
+ boxShadow: "0 2px 12px rgba(0, 122, 255, 0.25)"
450
+ };
451
+ }
452
+ }
453
+ if (role === "assistant") {
454
+ switch (variant) {
455
+ case "filled":
456
+ case "default":
457
+ return {
458
+ ...baseStyles,
459
+ backgroundColor: "var(--lm-bubble-assistant-bg)",
460
+ backdropFilter: "blur(20px) saturate(180%)",
461
+ WebkitBackdropFilter: "blur(20px) saturate(180%)",
462
+ color: "var(--lm-text-primary)",
463
+ boxShadow: "var(--lm-bubble-assistant-shadow)",
464
+ border: "1px solid var(--lm-bubble-assistant-border)"
465
+ };
466
+ case "outline":
467
+ return {
468
+ ...baseStyles,
469
+ backgroundColor: "var(--lm-bubble-outline-bg)",
470
+ backdropFilter: "blur(12px)",
471
+ color: "var(--lm-text-primary)",
472
+ border: "1.5px solid var(--lm-bubble-outline-border)"
473
+ };
474
+ case "soft":
475
+ return {
476
+ ...baseStyles,
477
+ backgroundColor: "var(--lm-bubble-soft-bg)",
478
+ backdropFilter: "blur(8px)",
479
+ color: "var(--lm-text-primary)",
480
+ border: "1px solid var(--lm-bubble-soft-border)"
481
+ };
482
+ default:
483
+ return {
484
+ ...baseStyles,
485
+ backgroundColor: "var(--lm-bubble-assistant-bg)",
486
+ backdropFilter: "blur(20px) saturate(180%)",
487
+ color: "var(--lm-text-primary)",
488
+ boxShadow: "var(--lm-bubble-assistant-shadow)",
489
+ border: "1px solid var(--lm-bubble-assistant-border)"
490
+ };
491
+ }
492
+ }
493
+ return {
494
+ ...baseStyles,
495
+ backgroundColor: "var(--lm-bubble-system-bg)",
496
+ backdropFilter: "blur(8px)",
497
+ color: "var(--lm-text-secondary)",
498
+ border: "1px dashed var(--lm-bubble-system-border)"
499
+ };
500
+ };
501
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
502
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: chatBubbleThemeStyles }),
503
+ /* @__PURE__ */ jsxRuntime.jsxs(
504
+ "div",
505
+ {
506
+ className: `
507
+ w-full
508
+ ${componentSizes.SIZE_PADDING_CLASSES[resolvedSize]}
509
+ ${componentSizes.SIZE_TEXT_CLASSES[resolvedSize]}
510
+ ${className}
511
+ `.trim().replace(/\s+/g, " "),
512
+ style: {
513
+ ...getBubbleStyles(),
514
+ ...style
515
+ },
516
+ children: [
517
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative leading-relaxed", children: [
518
+ children,
519
+ isStreaming && /* @__PURE__ */ jsxRuntime.jsx(
520
+ "span",
521
+ {
522
+ className: "inline-block w-0.5 h-[1.1em] ml-1 align-middle rounded-full",
523
+ style: {
524
+ backgroundColor: role === "user" ? "rgba(255, 255, 255, 0.8)" : "var(--lm-primary-500)",
525
+ animation: "lm-cursor-blink 1s ease-in-out infinite"
526
+ }
527
+ }
528
+ )
529
+ ] }),
530
+ footer && /* @__PURE__ */ jsxRuntime.jsx(
531
+ "div",
532
+ {
533
+ className: "mt-2 text-xs font-medium",
534
+ style: {
535
+ color: role === "user" ? "rgba(255, 255, 255, 0.7)" : "var(--lm-text-tertiary)"
536
+ },
537
+ children: footer
538
+ }
539
+ ),
540
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
541
+ @keyframes lm-cursor-blink {
542
+ 0%, 100% { opacity: 1; }
543
+ 50% { opacity: 0.3; }
544
+ }
545
+ @media (prefers-reduced-motion: reduce) {
546
+ .lm-cursor-blink { animation: none; opacity: 1; }
547
+ }
548
+ ` })
549
+ ]
550
+ }
551
+ )
552
+ ] });
553
+ };
554
+ const LMChatBubble = React.memo(LMChatBubble$1);
555
+ const CopyIcon$1 = ({ className }) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: [
556
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }),
557
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" })
558
+ ] });
559
+ const RefreshIcon = ({ className }) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: [
560
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 2v6h-6" }),
561
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 12a9 9 0 0115-6.7L21 8" }),
562
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 22v-6h6" }),
563
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12a9 9 0 01-15 6.7L3 16" })
564
+ ] });
565
+ const CheckIcon$1 = ({ className }) => /* @__PURE__ */ jsxRuntime.jsx("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "20,6 9,17 4,12" }) });
566
+ const ErrorIcon = ({ className }) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: [
567
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
568
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
569
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
570
+ ] });
571
+ const LoadingDots = ({ className }) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: `inline-flex items-center gap-1 ${className}`, children: [0, 1, 2].map((i) => /* @__PURE__ */ jsxRuntime.jsx(
572
+ "span",
573
+ {
574
+ className: "w-1.5 h-1.5 rounded-full animate-bounce",
575
+ style: {
576
+ backgroundColor: "currentColor",
577
+ animationDelay: `${i * 0.15}s`
578
+ }
579
+ },
580
+ i
581
+ )) });
582
+ const AVATAR_SIZE$1 = {
583
+ xs: "w-6 h-6",
584
+ sm: "w-7 h-7",
585
+ md: "w-8 h-8",
586
+ lg: "w-10 h-10",
587
+ xl: "w-12 h-12",
588
+ "2xl": "w-14 h-14"
589
+ };
590
+ const ACTION_BUTTON_SIZE = {
591
+ xs: "w-5 h-5",
592
+ sm: "w-6 h-6",
593
+ md: "w-7 h-7",
594
+ lg: "w-8 h-8",
595
+ xl: "w-9 h-9",
596
+ "2xl": "w-10 h-10"
597
+ };
598
+ const LMChatMessage$1 = ({
599
+ id,
600
+ role = "user",
601
+ content,
602
+ avatar,
603
+ name,
604
+ timestamp,
605
+ status,
606
+ size = "md",
607
+ variant = "default",
608
+ actions,
609
+ showActions = true,
610
+ errorMessage,
611
+ onRetry,
612
+ className = "",
613
+ hideAvatar = false,
614
+ renderBubble,
615
+ bubbleMaxWidth = "95%"
616
+ }) => {
617
+ const [isHovered, setIsHovered] = React.useState(false);
618
+ const [copiedAction, setCopiedAction] = React.useState(null);
619
+ const resolvedSize = componentSizes.clampComponentSize(size, componentSizes.COMPONENT_SIZE_ORDER);
620
+ const isUser = role === "user";
621
+ const isStreaming = status === "streaming";
622
+ const isError = status === "error";
623
+ const formatTimestamp = React.useCallback((ts) => {
624
+ if (typeof ts === "string") return ts;
625
+ const date = new Date(ts);
626
+ return date.toLocaleTimeString("zh-CN", {
627
+ hour: "2-digit",
628
+ minute: "2-digit"
629
+ });
630
+ }, []);
631
+ const handleCopy = React.useCallback(async (actionKey) => {
632
+ try {
633
+ const text = typeof content === "string" ? content : "";
634
+ await navigator.clipboard.writeText(text);
635
+ setCopiedAction(actionKey);
636
+ setTimeout(() => setCopiedAction(null), 2e3);
637
+ } catch (err) {
638
+ console.error("Failed to copy:", err);
639
+ }
640
+ }, [content]);
641
+ const defaultActions = role === "assistant" ? [
642
+ {
643
+ key: "copy",
644
+ icon: copiedAction === "copy" ? /* @__PURE__ */ jsxRuntime.jsx(CheckIcon$1, { className: "w-4 h-4" }) : /* @__PURE__ */ jsxRuntime.jsx(CopyIcon$1, { className: "w-4 h-4" }),
645
+ tooltip: copiedAction === "copy" ? "已复制" : "复制",
646
+ onClick: () => handleCopy("copy")
647
+ },
648
+ ...onRetry ? [{
649
+ key: "retry",
650
+ icon: /* @__PURE__ */ jsxRuntime.jsx(RefreshIcon, { className: "w-4 h-4" }),
651
+ tooltip: "重新生成",
652
+ onClick: onRetry
653
+ }] : []
654
+ ] : [];
655
+ const finalActions = actions || defaultActions;
656
+ const renderAvatar = () => {
657
+ if (hideAvatar) return null;
658
+ const avatarClasses = `
659
+ ${AVATAR_SIZE$1[resolvedSize]}
660
+ rounded-full overflow-hidden shrink-0
661
+ flex items-center justify-center
662
+ `.trim().replace(/\s+/g, " ");
663
+ if (typeof avatar === "string") {
664
+ return /* @__PURE__ */ jsxRuntime.jsx(
665
+ "img",
666
+ {
667
+ src: avatar,
668
+ alt: name || role,
669
+ className: avatarClasses,
670
+ style: { objectFit: "cover" }
671
+ }
672
+ );
673
+ }
674
+ if (avatar) {
675
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: avatarClasses, children: avatar });
676
+ }
677
+ const defaultAvatarStyle = {
678
+ backgroundColor: isUser ? "var(--lm-primary-100)" : "var(--lm-gray-100)",
679
+ color: isUser ? "var(--lm-primary-600)" : "var(--lm-gray-600)"
680
+ };
681
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: avatarClasses, style: defaultAvatarStyle, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: componentSizes.SIZE_TEXT_CLASSES[resolvedSize], children: isUser ? "U" : "AI" }) });
682
+ };
683
+ const renderActions = () => {
684
+ if (!showActions || finalActions.length === 0) return null;
685
+ return /* @__PURE__ */ jsxRuntime.jsx(
686
+ "div",
687
+ {
688
+ className: `
689
+ flex items-center gap-1 mt-1
690
+ transition-opacity duration-150
691
+ ${isHovered ? "opacity-100" : "opacity-0"}
692
+ `.trim().replace(/\s+/g, " "),
693
+ children: finalActions.map((action) => /* @__PURE__ */ jsxRuntime.jsx(
694
+ "button",
695
+ {
696
+ onClick: action.onClick,
697
+ disabled: action.disabled,
698
+ className: `
699
+ ${ACTION_BUTTON_SIZE[resolvedSize]}
700
+ flex items-center justify-center
701
+ rounded-lg cursor-pointer
702
+ hover:bg-(--lm-bg-hover)
703
+ active:scale-95
704
+ disabled:opacity-50 disabled:cursor-not-allowed
705
+ transition-all duration-150
706
+ `.trim().replace(/\s+/g, " "),
707
+ style: { color: "var(--lm-text-tertiary)" },
708
+ title: action.tooltip,
709
+ "aria-label": action.tooltip,
710
+ children: action.icon
711
+ },
712
+ action.key
713
+ ))
714
+ }
715
+ );
716
+ };
717
+ const renderStatus = () => {
718
+ if (!status || status === "sent") return null;
719
+ if (status === "sending") {
720
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 mt-1", style: { color: "var(--lm-text-tertiary)" }, children: [
721
+ /* @__PURE__ */ jsxRuntime.jsx(LoadingDots, { className: "text-xs" }),
722
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", children: "发送中" })
723
+ ] });
724
+ }
725
+ if (status === "error") {
726
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mt-2", children: [
727
+ /* @__PURE__ */ jsxRuntime.jsxs(
728
+ "div",
729
+ {
730
+ className: "flex items-center gap-1 text-xs",
731
+ style: { color: "var(--lm-error-500)" },
732
+ children: [
733
+ /* @__PURE__ */ jsxRuntime.jsx(ErrorIcon, { className: "w-3.5 h-3.5" }),
734
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: errorMessage || "发送失败" })
735
+ ]
736
+ }
737
+ ),
738
+ onRetry && /* @__PURE__ */ jsxRuntime.jsx(
739
+ "button",
740
+ {
741
+ onClick: onRetry,
742
+ className: "text-xs px-2 py-0.5 rounded-md cursor-pointer hover:opacity-80 active:scale-95",
743
+ style: {
744
+ backgroundColor: "var(--lm-error-50)",
745
+ color: "var(--lm-error-600)"
746
+ },
747
+ children: "重试"
748
+ }
749
+ )
750
+ ] });
751
+ }
752
+ return null;
753
+ };
754
+ const bubbleContent = /* @__PURE__ */ jsxRuntime.jsx(
755
+ LMChatBubble,
756
+ {
757
+ role,
758
+ variant,
759
+ size: resolvedSize,
760
+ isStreaming,
761
+ error: isError,
762
+ children: content
763
+ }
764
+ );
765
+ return /* @__PURE__ */ jsxRuntime.jsxs(
766
+ "div",
767
+ {
768
+ id,
769
+ className: `
770
+ flex gap-3
771
+ ${isUser ? "flex-row-reverse" : "flex-row"}
772
+ ${className}
773
+ `.trim().replace(/\s+/g, " "),
774
+ onMouseEnter: () => setIsHovered(true),
775
+ onMouseLeave: () => setIsHovered(false),
776
+ children: [
777
+ renderAvatar(),
778
+ /* @__PURE__ */ jsxRuntime.jsxs(
779
+ "div",
780
+ {
781
+ className: `
782
+ flex flex-col min-w-0
783
+ ${isUser ? "items-end" : "items-start"}
784
+ `.trim().replace(/\s+/g, " "),
785
+ style: {
786
+ maxWidth: typeof bubbleMaxWidth === "number" ? `${bubbleMaxWidth}px` : bubbleMaxWidth,
787
+ width: typeof bubbleMaxWidth === "number" ? `${bubbleMaxWidth}px` : bubbleMaxWidth
788
+ },
789
+ children: [
790
+ (name || timestamp) && /* @__PURE__ */ jsxRuntime.jsxs(
791
+ "div",
792
+ {
793
+ className: `
794
+ flex items-center gap-2 mb-1
795
+ ${isUser ? "flex-row-reverse" : "flex-row"}
796
+ `.trim().replace(/\s+/g, " "),
797
+ children: [
798
+ name && /* @__PURE__ */ jsxRuntime.jsx(
799
+ "span",
800
+ {
801
+ className: "text-xs font-medium",
802
+ style: { color: "var(--lm-text-secondary)" },
803
+ children: name
804
+ }
805
+ ),
806
+ timestamp && /* @__PURE__ */ jsxRuntime.jsx(
807
+ "span",
808
+ {
809
+ className: "text-xs",
810
+ style: { color: "var(--lm-text-tertiary)" },
811
+ children: formatTimestamp(timestamp)
812
+ }
813
+ )
814
+ ]
815
+ }
816
+ ),
817
+ renderBubble ? renderBubble(bubbleContent) : bubbleContent,
818
+ renderStatus(),
819
+ !isError && renderActions()
820
+ ]
821
+ }
822
+ )
823
+ ]
824
+ }
825
+ );
826
+ };
827
+ const LMChatMessage = React.memo(LMChatMessage$1);
828
+ const CopyIcon = ({ className }) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", children: [
829
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }),
830
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" })
831
+ ] });
832
+ const CheckIcon = ({ className }) => /* @__PURE__ */ jsxRuntime.jsx("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "20,6 9,17 4,12" }) });
833
+ const HIGHLIGHT_VARS = {
834
+ keyword: "--lm-code-keyword",
835
+ string: "--lm-code-string",
836
+ number: "--lm-code-number",
837
+ comment: "--lm-code-comment",
838
+ function: "--lm-code-function",
839
+ operator: "--lm-code-operator",
840
+ className: "--lm-code-class",
841
+ tag: "--lm-code-tag"
842
+ };
843
+ const highlightCode = (code, language) => {
844
+ const lang = language.toLowerCase();
845
+ const keywords = {
846
+ js: /\b(const|let|var|function|return|if|else|for|while|do|switch|case|break|continue|try|catch|finally|throw|new|typeof|instanceof|in|of|class|extends|super|import|export|default|from|as|async|await|yield|static|get|set|null|undefined|true|false|this|void|delete|debugger)\b/g,
847
+ ts: /\b(const|let|var|function|return|if|else|for|while|do|switch|case|break|continue|try|catch|finally|throw|new|typeof|instanceof|in|of|class|extends|super|import|export|default|from|as|async|await|yield|static|get|set|null|undefined|true|false|this|void|delete|debugger|type|interface|enum|namespace|module|declare|abstract|implements|private|protected|public|readonly|keyof|infer|never|unknown|any)\b/g,
848
+ py: /\b(def|return|if|elif|else|for|while|break|continue|try|except|finally|raise|import|from|as|class|with|yield|lambda|pass|None|True|False|and|or|not|in|is|global|nonlocal|assert|del|async|await|self)\b/g,
849
+ go: /\b(func|return|if|else|for|range|switch|case|break|continue|go|select|defer|panic|recover|type|struct|interface|map|chan|package|import|const|var|nil|true|false|make|new|append|len|cap)\b/g,
850
+ rust: /\b(fn|return|if|else|for|while|loop|match|break|continue|let|mut|const|static|struct|enum|impl|trait|type|pub|mod|use|crate|self|super|where|async|await|move|ref|unsafe|extern|dyn|true|false|None|Some|Ok|Err)\b/g
851
+ };
852
+ const patterns = [
853
+ // 注释
854
+ { pattern: /(\/\/.*$|\/\*[\s\S]*?\*\/|#.*$)/gm, varName: HIGHLIGHT_VARS.comment },
855
+ // 字符串
856
+ { pattern: /(['"`])(?:(?!\1)[^\\]|\\.)*\1/g, varName: HIGHLIGHT_VARS.string },
857
+ // 模板字符串中的表达式
858
+ { pattern: /\$\{[^}]+\}/g, varName: HIGHLIGHT_VARS.operator },
859
+ // 数字
860
+ { pattern: /\b(\d+\.?\d*|0x[a-fA-F0-9]+|0b[01]+|0o[0-7]+)\b/g, varName: HIGHLIGHT_VARS.number },
861
+ // 函数调用
862
+ { pattern: /\b([a-zA-Z_]\w*)\s*(?=\()/g, varName: HIGHLIGHT_VARS.function },
863
+ // JSX/HTML 标签
864
+ { pattern: /<\/?([a-zA-Z][a-zA-Z0-9]*)/g, varName: HIGHLIGHT_VARS.tag },
865
+ // 类名 (大写开头)
866
+ { pattern: /\b([A-Z][a-zA-Z0-9]*)\b/g, varName: HIGHLIGHT_VARS.className },
867
+ // 操作符
868
+ { pattern: /[+\-*/%=<>!&|^~?:]+|\.{3}/g, varName: HIGHLIGHT_VARS.operator }
869
+ ];
870
+ const keywordPattern = keywords[lang] || keywords.js;
871
+ const allMatches = [];
872
+ let match;
873
+ while ((match = keywordPattern.exec(code)) !== null) {
874
+ allMatches.push({
875
+ start: match.index,
876
+ end: match.index + match[0].length,
877
+ text: match[0],
878
+ varName: HIGHLIGHT_VARS.keyword
879
+ });
880
+ }
881
+ for (const { pattern, varName } of patterns) {
882
+ pattern.lastIndex = 0;
883
+ while ((match = pattern.exec(code)) !== null) {
884
+ allMatches.push({
885
+ start: match.index,
886
+ end: match.index + match[0].length,
887
+ text: match[0],
888
+ varName
889
+ });
890
+ }
891
+ }
892
+ allMatches.sort((a, b) => a.start - b.start || b.end - a.end);
893
+ const tokens = [];
894
+ let lastIndex = 0;
895
+ for (const m of allMatches) {
896
+ if (m.start >= lastIndex) {
897
+ if (m.start > lastIndex) {
898
+ tokens.push({ text: code.slice(lastIndex, m.start) });
899
+ }
900
+ tokens.push({ text: m.text, varName: m.varName });
901
+ lastIndex = m.end;
902
+ }
903
+ }
904
+ if (lastIndex < code.length) {
905
+ tokens.push({ text: code.slice(lastIndex) });
906
+ }
907
+ return tokens.map((token, i) => token.varName ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: `var(${token.varName})` }, children: token.text }, i) : /* @__PURE__ */ jsxRuntime.jsx("span", { children: token.text }, i));
908
+ };
909
+ const LANGUAGE_DISPLAY = {
910
+ javascript: "JS",
911
+ typescript: "TS",
912
+ python: "Python",
913
+ java: "Java",
914
+ cpp: "C++",
915
+ c: "C",
916
+ csharp: "C#",
917
+ go: "Go",
918
+ rust: "Rust",
919
+ swift: "Swift",
920
+ kotlin: "Kotlin",
921
+ ruby: "Ruby",
922
+ php: "PHP",
923
+ html: "HTML",
924
+ css: "CSS",
925
+ json: "JSON",
926
+ yaml: "YAML",
927
+ sql: "SQL",
928
+ bash: "Bash",
929
+ shell: "Shell",
930
+ jsx: "JSX",
931
+ tsx: "TSX",
932
+ vue: "Vue",
933
+ svelte: "Svelte",
934
+ markdown: "MD"
935
+ };
936
+ const CODE_SIZE = {
937
+ xs: { fontSize: "11px", lineHeight: "1.5", padding: "12px" },
938
+ sm: { fontSize: "12px", lineHeight: "1.5", padding: "14px" },
939
+ md: { fontSize: "13px", lineHeight: "1.6", padding: "16px" },
940
+ lg: { fontSize: "14px", lineHeight: "1.6", padding: "18px" },
941
+ xl: { fontSize: "14px", lineHeight: "1.7", padding: "20px" },
942
+ "2xl": { fontSize: "15px", lineHeight: "1.7", padding: "24px" }
943
+ };
944
+ const codeThemeStyles = `
945
+ :root, [data-theme="light"] {
946
+ --lm-code-bg: #f8fafc;
947
+ --lm-code-border: #e2e8f0;
948
+ --lm-code-text: #334155;
949
+ --lm-code-line-number: #94a3b8;
950
+ --lm-code-keyword: #8250df;
951
+ --lm-code-string: #0a7d46;
952
+ --lm-code-number: #cf5c00;
953
+ --lm-code-comment: #6b7280;
954
+ --lm-code-function: #0969da;
955
+ --lm-code-operator: #cf222e;
956
+ --lm-code-class: #953800;
957
+ --lm-code-tag: #1a7f37;
958
+ --lm-code-property: #0550ae;
959
+ }
960
+ [data-theme="dark"] {
961
+ --lm-code-bg: #1e293b;
962
+ --lm-code-border: #334155;
963
+ --lm-code-text: #e2e8f0;
964
+ --lm-code-line-number: #64748b;
965
+ --lm-code-keyword: #c792ea;
966
+ --lm-code-string: #c3e88d;
967
+ --lm-code-number: #f78c6c;
968
+ --lm-code-comment: #64748b;
969
+ --lm-code-function: #82aaff;
970
+ --lm-code-operator: #89ddff;
971
+ --lm-code-class: #ffcb6b;
972
+ --lm-code-tag: #f07178;
973
+ --lm-code-property: #89ddff;
974
+ }
975
+ `;
976
+ const LMCodeBlock$1 = ({
977
+ code,
978
+ language = "plaintext",
979
+ filename,
980
+ size = "md",
981
+ showLineNumbers = true,
982
+ startLineNumber = 1,
983
+ highlightLines = [],
984
+ showCopyButton = true,
985
+ className = "",
986
+ maxHeight,
987
+ wrapLines = false,
988
+ enableHighlight = true
989
+ }) => {
990
+ const [copied, setCopied] = React.useState(false);
991
+ const resolvedSize = componentSizes.clampComponentSize(size, componentSizes.COMPONENT_SIZE_ORDER);
992
+ const sizeConfig = CODE_SIZE[resolvedSize];
993
+ const lines = code.split("\n");
994
+ const displayLang = LANGUAGE_DISPLAY[language.toLowerCase()] || language;
995
+ const handleCopy = React.useCallback(async () => {
996
+ try {
997
+ await navigator.clipboard.writeText(code);
998
+ setCopied(true);
999
+ setTimeout(() => setCopied(false), 2e3);
1000
+ } catch (err) {
1001
+ console.error("Failed to copy:", err);
1002
+ }
1003
+ }, [code]);
1004
+ const lineNumWidth = String(lines.length + startLineNumber - 1).length;
1005
+ const highlightedLines = React.useMemo(() => {
1006
+ if (!enableHighlight) return lines;
1007
+ return lines.map((line) => highlightCode(line, language));
1008
+ }, [lines, language, enableHighlight]);
1009
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1010
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: codeThemeStyles }),
1011
+ /* @__PURE__ */ jsxRuntime.jsxs(
1012
+ "div",
1013
+ {
1014
+ className: `overflow-hidden ${className}`.trim(),
1015
+ style: {
1016
+ backgroundColor: "var(--lm-code-bg)",
1017
+ borderRadius: "var(--lm-radius-lg)",
1018
+ border: "1px solid var(--lm-code-border)",
1019
+ ...maxHeight ? { maxHeight: typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight } : {}
1020
+ },
1021
+ children: [
1022
+ /* @__PURE__ */ jsxRuntime.jsxs(
1023
+ "div",
1024
+ {
1025
+ className: "flex items-center justify-between px-3 py-2",
1026
+ style: {
1027
+ borderBottom: "1px solid var(--lm-code-border)"
1028
+ },
1029
+ children: [
1030
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
1031
+ /* @__PURE__ */ jsxRuntime.jsx(
1032
+ "span",
1033
+ {
1034
+ className: "text-xs font-medium px-1.5 py-0.5 rounded",
1035
+ style: {
1036
+ backgroundColor: "var(--lm-code-border)",
1037
+ color: "var(--lm-code-line-number)"
1038
+ },
1039
+ children: displayLang
1040
+ }
1041
+ ),
1042
+ filename && /* @__PURE__ */ jsxRuntime.jsx(
1043
+ "span",
1044
+ {
1045
+ className: "text-xs truncate max-w-45",
1046
+ style: { color: "var(--lm-code-line-number)" },
1047
+ children: filename
1048
+ }
1049
+ )
1050
+ ] }),
1051
+ showCopyButton && /* @__PURE__ */ jsxRuntime.jsxs(
1052
+ "button",
1053
+ {
1054
+ onClick: handleCopy,
1055
+ className: "flex items-center gap-1 px-2 py-1 rounded cursor-pointer transition-colors duration-150 active:scale-95",
1056
+ style: {
1057
+ color: copied ? "var(--lm-success-500)" : "var(--lm-code-line-number)"
1058
+ },
1059
+ "aria-label": copied ? "已复制" : "复制",
1060
+ children: [
1061
+ copied ? /* @__PURE__ */ jsxRuntime.jsx(CheckIcon, { className: "w-3.5 h-3.5" }) : /* @__PURE__ */ jsxRuntime.jsx(CopyIcon, { className: "w-3.5 h-3.5" }),
1062
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", children: copied ? "已复制" : "复制" })
1063
+ ]
1064
+ }
1065
+ )
1066
+ ]
1067
+ }
1068
+ ),
1069
+ /* @__PURE__ */ jsxRuntime.jsx(
1070
+ "pre",
1071
+ {
1072
+ className: "m-0 overflow-auto",
1073
+ style: {
1074
+ padding: sizeConfig.padding,
1075
+ fontSize: sizeConfig.fontSize,
1076
+ lineHeight: sizeConfig.lineHeight,
1077
+ fontFamily: '"SF Mono", ui-monospace, Menlo, Monaco, "Cascadia Code", monospace',
1078
+ color: "var(--lm-code-text)"
1079
+ },
1080
+ children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: lines.map((line, index) => {
1081
+ const lineNum = startLineNumber + index;
1082
+ const isHighlighted = highlightLines.includes(lineNum);
1083
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1084
+ "div",
1085
+ {
1086
+ className: "flex",
1087
+ style: {
1088
+ backgroundColor: isHighlighted ? "var(--lm-primary-50)" : "transparent",
1089
+ marginLeft: isHighlighted ? "-4px" : "0",
1090
+ paddingLeft: isHighlighted ? "4px" : "0",
1091
+ borderLeft: isHighlighted ? "2px solid var(--lm-primary-500)" : "none"
1092
+ },
1093
+ children: [
1094
+ showLineNumbers && /* @__PURE__ */ jsxRuntime.jsx(
1095
+ "span",
1096
+ {
1097
+ className: "select-none text-right shrink-0 pr-4",
1098
+ style: {
1099
+ width: `${lineNumWidth + 2}ch`,
1100
+ color: isHighlighted ? "var(--lm-primary-500)" : "var(--lm-code-line-number)"
1101
+ },
1102
+ children: lineNum
1103
+ }
1104
+ ),
1105
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `flex-1 min-w-0 ${wrapLines ? "whitespace-pre-wrap break-all" : "whitespace-pre"}`, children: enableHighlight ? highlightedLines[index] : line || " " })
1106
+ ]
1107
+ },
1108
+ index
1109
+ );
1110
+ }) })
1111
+ }
1112
+ )
1113
+ ]
1114
+ }
1115
+ )
1116
+ ] });
1117
+ };
1118
+ const LMCodeBlock = React.memo(LMCodeBlock$1);
1119
+ const TEXT_SIZE_CONFIG = {
1120
+ xs: { base: "text-xs", h1: "text-lg", h2: "text-base", h3: "text-sm", code: "text-[10px]" },
1121
+ sm: { base: "text-sm", h1: "text-xl", h2: "text-lg", h3: "text-base", code: "text-xs" },
1122
+ md: { base: "text-base", h1: "text-2xl", h2: "text-xl", h3: "text-lg", code: "text-sm" },
1123
+ lg: { base: "text-lg", h1: "text-3xl", h2: "text-2xl", h3: "text-xl", code: "text-base" },
1124
+ xl: { base: "text-xl", h1: "text-4xl", h2: "text-3xl", h3: "text-2xl", code: "text-lg" },
1125
+ "2xl": { base: "text-2xl", h1: "text-5xl", h2: "text-4xl", h3: "text-3xl", code: "text-xl" }
1126
+ };
1127
+ const parseMarkdown = (content) => {
1128
+ const tokens = [];
1129
+ const lines = content.split("\n");
1130
+ let i = 0;
1131
+ while (i < lines.length) {
1132
+ const line = lines[i];
1133
+ if (line.startsWith("```")) {
1134
+ const match = line.match(/^```(\w+)?(?:\s+(.+))?/);
1135
+ const language = (match == null ? void 0 : match[1]) || "plaintext";
1136
+ const filename = match == null ? void 0 : match[2];
1137
+ const codeLines = [];
1138
+ i++;
1139
+ while (i < lines.length && !lines[i].startsWith("```")) {
1140
+ codeLines.push(lines[i]);
1141
+ i++;
1142
+ }
1143
+ tokens.push({
1144
+ type: "codeBlock",
1145
+ content: codeLines.join("\n"),
1146
+ language,
1147
+ filename
1148
+ });
1149
+ i++;
1150
+ continue;
1151
+ }
1152
+ const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
1153
+ if (headingMatch) {
1154
+ tokens.push({
1155
+ type: "heading",
1156
+ content: headingMatch[2],
1157
+ level: headingMatch[1].length
1158
+ });
1159
+ i++;
1160
+ continue;
1161
+ }
1162
+ if (/^(-{3,}|_{3,}|\*{3,})$/.test(line.trim())) {
1163
+ tokens.push({ type: "hr", content: "" });
1164
+ i++;
1165
+ continue;
1166
+ }
1167
+ if (line.startsWith(">")) {
1168
+ const quoteLines = [];
1169
+ while (i < lines.length && (lines[i].startsWith(">") || lines[i].trim() === "")) {
1170
+ if (lines[i].startsWith(">")) {
1171
+ quoteLines.push(lines[i].replace(/^>\s?/, ""));
1172
+ } else {
1173
+ quoteLines.push("");
1174
+ }
1175
+ i++;
1176
+ }
1177
+ tokens.push({
1178
+ type: "blockquote",
1179
+ content: quoteLines.join("\n").trim()
1180
+ });
1181
+ continue;
1182
+ }
1183
+ if (/^[-*+]\s/.test(line)) {
1184
+ const items = [];
1185
+ while (i < lines.length && /^[-*+]\s/.test(lines[i])) {
1186
+ items.push(lines[i].replace(/^[-*+]\s/, ""));
1187
+ i++;
1188
+ }
1189
+ tokens.push({
1190
+ type: "list",
1191
+ content: "",
1192
+ ordered: false,
1193
+ items
1194
+ });
1195
+ continue;
1196
+ }
1197
+ if (/^\d+\.\s/.test(line)) {
1198
+ const items = [];
1199
+ while (i < lines.length && /^\d+\.\s/.test(lines[i])) {
1200
+ items.push(lines[i].replace(/^\d+\.\s/, ""));
1201
+ i++;
1202
+ }
1203
+ tokens.push({
1204
+ type: "list",
1205
+ content: "",
1206
+ ordered: true,
1207
+ items
1208
+ });
1209
+ continue;
1210
+ }
1211
+ if (line.includes("|") && i + 1 < lines.length && /^\|?[\s-:|]+\|?$/.test(lines[i + 1])) {
1212
+ const rows = [];
1213
+ while (i < lines.length && lines[i].includes("|")) {
1214
+ const cells = lines[i].split("|").map((cell) => cell.trim()).filter((cell) => cell !== "");
1215
+ if (cells.length > 0 && !/^[\s-:]+$/.test(cells.join(""))) {
1216
+ rows.push(cells);
1217
+ }
1218
+ i++;
1219
+ }
1220
+ if (rows.length > 0) {
1221
+ tokens.push({
1222
+ type: "table",
1223
+ content: "",
1224
+ rows
1225
+ });
1226
+ }
1227
+ continue;
1228
+ }
1229
+ if (line.trim() === "") {
1230
+ i++;
1231
+ continue;
1232
+ }
1233
+ const paragraphLines = [line];
1234
+ i++;
1235
+ while (i < lines.length && lines[i].trim() !== "" && !lines[i].startsWith("#") && !lines[i].startsWith("```") && !lines[i].startsWith(">") && !/^[-*+]\s/.test(lines[i]) && !/^\d+\.\s/.test(lines[i]) && !/^(-{3,}|_{3,}|\*{3,})$/.test(lines[i].trim())) {
1236
+ paragraphLines.push(lines[i]);
1237
+ i++;
1238
+ }
1239
+ tokens.push({
1240
+ type: "paragraph",
1241
+ content: paragraphLines.join("\n")
1242
+ });
1243
+ }
1244
+ return tokens;
1245
+ };
1246
+ const parseInlineStyles = (text, onLinkClick, openLinksInNewTab) => {
1247
+ const patterns = [
1248
+ // 粗体
1249
+ {
1250
+ regex: /\*\*(.+?)\*\*|__(.+?)__/g,
1251
+ render: (match, key) => /* @__PURE__ */ jsxRuntime.jsx("strong", { className: "font-semibold", children: match[1] || match[2] }, key)
1252
+ },
1253
+ // 斜体
1254
+ {
1255
+ regex: /\*(.+?)\*|_(.+?)_/g,
1256
+ render: (match, key) => /* @__PURE__ */ jsxRuntime.jsx("em", { className: "italic", children: match[1] || match[2] }, key)
1257
+ },
1258
+ // 删除线
1259
+ {
1260
+ regex: /~~(.+?)~~/g,
1261
+ render: (match, key) => /* @__PURE__ */ jsxRuntime.jsx("del", { className: "line-through", children: match[1] }, key)
1262
+ },
1263
+ // 行内代码
1264
+ {
1265
+ regex: /`([^`]+)`/g,
1266
+ render: (match, key) => /* @__PURE__ */ jsxRuntime.jsx(
1267
+ "code",
1268
+ {
1269
+ className: "px-1.5 py-0.5 rounded-md font-mono text-[0.9em]",
1270
+ style: {
1271
+ backgroundColor: "var(--lm-bg-paper)",
1272
+ color: "var(--lm-primary-600)"
1273
+ },
1274
+ children: match[1]
1275
+ },
1276
+ key
1277
+ )
1278
+ },
1279
+ // 链接
1280
+ {
1281
+ regex: /\[([^\]]+)\]\(([^)]+)\)/g,
1282
+ render: (match, key) => /* @__PURE__ */ jsxRuntime.jsx(
1283
+ "a",
1284
+ {
1285
+ href: match[2],
1286
+ onClick: (e) => onLinkClick == null ? void 0 : onLinkClick(match[2], e),
1287
+ target: openLinksInNewTab ? "_blank" : void 0,
1288
+ rel: openLinksInNewTab ? "noopener noreferrer" : void 0,
1289
+ className: "underline decoration-1 underline-offset-2 hover:opacity-80",
1290
+ style: { color: "var(--lm-primary-500)" },
1291
+ children: match[1]
1292
+ },
1293
+ key
1294
+ )
1295
+ }
1296
+ ];
1297
+ let result = [text];
1298
+ let keyCounter = 0;
1299
+ patterns.forEach(({ regex, render }) => {
1300
+ const newResult = [];
1301
+ result.forEach((node) => {
1302
+ if (typeof node !== "string") {
1303
+ newResult.push(node);
1304
+ return;
1305
+ }
1306
+ let lastIndex = 0;
1307
+ let match;
1308
+ const clonedRegex = new RegExp(regex.source, regex.flags);
1309
+ while ((match = clonedRegex.exec(node)) !== null) {
1310
+ if (match.index > lastIndex) {
1311
+ newResult.push(node.slice(lastIndex, match.index));
1312
+ }
1313
+ newResult.push(render(match, keyCounter++));
1314
+ lastIndex = match.index + match[0].length;
1315
+ }
1316
+ if (lastIndex < node.length) {
1317
+ newResult.push(node.slice(lastIndex));
1318
+ }
1319
+ });
1320
+ result = newResult;
1321
+ });
1322
+ return result;
1323
+ };
1324
+ const LMMarkdownRenderer = ({
1325
+ content,
1326
+ size = "md",
1327
+ className = "",
1328
+ allowHtml: _allowHtml = false,
1329
+ renderCodeBlock,
1330
+ onLinkClick,
1331
+ openLinksInNewTab = true,
1332
+ customRenderer
1333
+ }) => {
1334
+ const resolvedSize = componentSizes.clampComponentSize(size, componentSizes.COMPONENT_SIZE_ORDER);
1335
+ const sizeConfig = TEXT_SIZE_CONFIG[resolvedSize];
1336
+ const tokens = React.useMemo(() => parseMarkdown(content), [content]);
1337
+ if (customRenderer) {
1338
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: customRenderer(content) });
1339
+ }
1340
+ const renderToken = (token, index) => {
1341
+ var _a, _b, _c, _d;
1342
+ switch (token.type) {
1343
+ case "heading": {
1344
+ const HeadingTag = `h${token.level}`;
1345
+ const headingSizeClass = token.level === 1 ? sizeConfig.h1 : token.level === 2 ? sizeConfig.h2 : sizeConfig.h3;
1346
+ return /* @__PURE__ */ jsxRuntime.jsx(
1347
+ HeadingTag,
1348
+ {
1349
+ className: `
1350
+ ${headingSizeClass}
1351
+ font-semibold leading-tight tracking-tight
1352
+ mt-6 mb-3 first:mt-0
1353
+ `.trim().replace(/\s+/g, " "),
1354
+ style: { color: "var(--lm-text-primary)" },
1355
+ children: parseInlineStyles(token.content, onLinkClick, openLinksInNewTab)
1356
+ },
1357
+ index
1358
+ );
1359
+ }
1360
+ case "paragraph":
1361
+ return /* @__PURE__ */ jsxRuntime.jsx(
1362
+ "p",
1363
+ {
1364
+ className: `
1365
+ ${sizeConfig.base}
1366
+ leading-relaxed my-3 first:mt-0 last:mb-0
1367
+ `.trim().replace(/\s+/g, " "),
1368
+ style: { color: "var(--lm-text-primary)" },
1369
+ children: parseInlineStyles(token.content, onLinkClick, openLinksInNewTab)
1370
+ },
1371
+ index
1372
+ );
1373
+ case "codeBlock":
1374
+ if (renderCodeBlock) {
1375
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "my-4", children: renderCodeBlock(token.content, token.language, token.filename) }, index);
1376
+ }
1377
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "my-4", children: /* @__PURE__ */ jsxRuntime.jsx(
1378
+ LMCodeBlock,
1379
+ {
1380
+ code: token.content,
1381
+ language: token.language,
1382
+ filename: token.filename,
1383
+ size: resolvedSize
1384
+ }
1385
+ ) }, index);
1386
+ case "blockquote":
1387
+ return /* @__PURE__ */ jsxRuntime.jsx(
1388
+ "blockquote",
1389
+ {
1390
+ className: "my-4 pl-4 border-l-4 italic",
1391
+ style: {
1392
+ borderColor: "var(--lm-primary-300)",
1393
+ color: "var(--lm-text-secondary)"
1394
+ },
1395
+ children: parseInlineStyles(token.content, onLinkClick, openLinksInNewTab)
1396
+ },
1397
+ index
1398
+ );
1399
+ case "list": {
1400
+ const ListTag = token.ordered ? "ol" : "ul";
1401
+ return /* @__PURE__ */ jsxRuntime.jsx(
1402
+ ListTag,
1403
+ {
1404
+ className: `
1405
+ ${sizeConfig.base}
1406
+ my-3 pl-6
1407
+ ${token.ordered ? "list-decimal" : "list-disc"}
1408
+ `.trim().replace(/\s+/g, " "),
1409
+ style: { color: "var(--lm-text-primary)" },
1410
+ children: (_a = token.items) == null ? void 0 : _a.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { className: "my-1", children: parseInlineStyles(item, onLinkClick, openLinksInNewTab) }, i))
1411
+ },
1412
+ index
1413
+ );
1414
+ }
1415
+ case "hr":
1416
+ return /* @__PURE__ */ jsxRuntime.jsx(
1417
+ "hr",
1418
+ {
1419
+ className: "my-6 border-0 h-px",
1420
+ style: { backgroundColor: "var(--lm-border-default)" }
1421
+ },
1422
+ index
1423
+ );
1424
+ case "table":
1425
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "my-4 overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsxs(
1426
+ "table",
1427
+ {
1428
+ className: "w-full border-collapse",
1429
+ style: { borderColor: "var(--lm-border-default)" },
1430
+ children: [
1431
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx("tr", { children: (_c = (_b = token.rows) == null ? void 0 : _b[0]) == null ? void 0 : _c.map((cell, i) => /* @__PURE__ */ jsxRuntime.jsx(
1432
+ "th",
1433
+ {
1434
+ className: "px-3 py-2 text-left font-semibold border",
1435
+ style: {
1436
+ backgroundColor: "var(--lm-bg-paper)",
1437
+ borderColor: "var(--lm-border-default)",
1438
+ color: "var(--lm-text-primary)"
1439
+ },
1440
+ children: parseInlineStyles(cell, onLinkClick, openLinksInNewTab)
1441
+ },
1442
+ i
1443
+ )) }) }),
1444
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: (_d = token.rows) == null ? void 0 : _d.slice(1).map((row, rowIndex) => /* @__PURE__ */ jsxRuntime.jsx("tr", { children: row.map((cell, cellIndex) => /* @__PURE__ */ jsxRuntime.jsx(
1445
+ "td",
1446
+ {
1447
+ className: "px-3 py-2 border",
1448
+ style: {
1449
+ borderColor: "var(--lm-border-default)",
1450
+ color: "var(--lm-text-primary)"
1451
+ },
1452
+ children: parseInlineStyles(cell, onLinkClick, openLinksInNewTab)
1453
+ },
1454
+ cellIndex
1455
+ )) }, rowIndex)) })
1456
+ ]
1457
+ }
1458
+ ) }, index);
1459
+ default:
1460
+ return null;
1461
+ }
1462
+ };
1463
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${sizeConfig.base} ${className}`.trim(), children: tokens.map((token, index) => renderToken(token, index)) });
1464
+ };
1465
+ const LMMarkdownRenderer_default = React.memo(LMMarkdownRenderer);
1466
+ const typingIndicatorThemeStyles = `
1467
+ :root, [data-theme="light"] {
1468
+ --lm-typing-bg: rgba(255, 255, 255, 0.85);
1469
+ --lm-typing-border: rgba(0, 0, 0, 0.06);
1470
+ --lm-typing-shadow: 0 4px 16px rgba(0, 0, 0, 0.06), 0 1px 3px rgba(0, 0, 0, 0.04);
1471
+ --lm-typing-avatar-bg: linear-gradient(135deg, var(--lm-gray-100) 0%, var(--lm-gray-200) 100%);
1472
+ }
1473
+ [data-theme="dark"] {
1474
+ --lm-typing-bg: rgba(51, 65, 85, 0.85);
1475
+ --lm-typing-border: rgba(255, 255, 255, 0.08);
1476
+ --lm-typing-shadow: 0 4px 16px rgba(0, 0, 0, 0.2), 0 1px 3px rgba(0, 0, 0, 0.1);
1477
+ --lm-typing-avatar-bg: linear-gradient(135deg, var(--lm-gray-700) 0%, var(--lm-gray-600) 100%);
1478
+ }
1479
+ `;
1480
+ const SIZE_CONFIG = {
1481
+ xs: { dotSize: "w-1.5 h-1.5", gap: "gap-1", padding: "px-3 py-2", fontSize: "text-xs", avatarGap: "gap-2" },
1482
+ sm: { dotSize: "w-1.5 h-1.5", gap: "gap-1", padding: "px-3.5 py-2.5", fontSize: "text-sm", avatarGap: "gap-2.5" },
1483
+ md: { dotSize: "w-2 h-2", gap: "gap-1.5", padding: "px-4 py-3", fontSize: "text-sm", avatarGap: "gap-3" },
1484
+ lg: { dotSize: "w-2.5 h-2.5", gap: "gap-1.5", padding: "px-5 py-3.5", fontSize: "text-base", avatarGap: "gap-3" },
1485
+ xl: { dotSize: "w-3 h-3", gap: "gap-2", padding: "px-5 py-4", fontSize: "text-base", avatarGap: "gap-4" },
1486
+ "2xl": { dotSize: "w-3.5 h-3.5", gap: "gap-2", padding: "px-6 py-4.5", fontSize: "text-lg", avatarGap: "gap-4" }
1487
+ };
1488
+ const AVATAR_SIZE = {
1489
+ xs: "w-6 h-6",
1490
+ sm: "w-7 h-7",
1491
+ md: "w-8 h-8",
1492
+ lg: "w-10 h-10",
1493
+ xl: "w-12 h-12",
1494
+ "2xl": "w-14 h-14"
1495
+ };
1496
+ const LMTypingIndicator$1 = ({
1497
+ variant = "dots",
1498
+ size = "md",
1499
+ text,
1500
+ avatar,
1501
+ className = "",
1502
+ color
1503
+ }) => {
1504
+ const resolvedSize = componentSizes.clampComponentSize(size, componentSizes.COMPONENT_SIZE_ORDER);
1505
+ const config = SIZE_CONFIG[resolvedSize];
1506
+ const getDotAnimation = (index) => {
1507
+ const baseDelay = index * 0.16;
1508
+ switch (variant) {
1509
+ case "dots":
1510
+ return {
1511
+ animation: `lm-typing-dots 1.4s cubic-bezier(0.4, 0, 0.2, 1) ${baseDelay}s infinite`
1512
+ };
1513
+ case "pulse":
1514
+ return {
1515
+ animation: `lm-typing-pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) ${baseDelay}s infinite`
1516
+ };
1517
+ case "wave":
1518
+ return {
1519
+ animation: `lm-typing-wave 1.2s cubic-bezier(0.4, 0, 0.2, 1) ${baseDelay}s infinite`
1520
+ };
1521
+ case "bounce":
1522
+ return {
1523
+ animation: `lm-typing-bounce 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) ${baseDelay}s infinite alternate`
1524
+ };
1525
+ default:
1526
+ return {};
1527
+ }
1528
+ };
1529
+ const animationStyles = `
1530
+ @keyframes lm-typing-dots {
1531
+ 0%, 80%, 100% {
1532
+ opacity: 0.35;
1533
+ transform: scale(0.85);
1534
+ }
1535
+ 40% {
1536
+ opacity: 1;
1537
+ transform: scale(1.1);
1538
+ }
1539
+ }
1540
+ @keyframes lm-typing-pulse {
1541
+ 0%, 100% {
1542
+ opacity: 0.3;
1543
+ transform: scale(0.9);
1544
+ }
1545
+ 50% {
1546
+ opacity: 1;
1547
+ transform: scale(1.05);
1548
+ }
1549
+ }
1550
+ @keyframes lm-typing-wave {
1551
+ 0%, 100% {
1552
+ transform: translateY(0);
1553
+ }
1554
+ 50% {
1555
+ transform: translateY(-5px);
1556
+ }
1557
+ }
1558
+ @keyframes lm-typing-bounce {
1559
+ 0% {
1560
+ transform: translateY(0) scale(1);
1561
+ }
1562
+ 100% {
1563
+ transform: translateY(-6px) scale(1.1);
1564
+ }
1565
+ }
1566
+ @media (prefers-reduced-motion: reduce) {
1567
+ [data-typing-indicator] span {
1568
+ animation: none !important;
1569
+ opacity: 0.6;
1570
+ }
1571
+ }
1572
+ `;
1573
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1574
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: typingIndicatorThemeStyles }),
1575
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: animationStyles }),
1576
+ /* @__PURE__ */ jsxRuntime.jsxs(
1577
+ "div",
1578
+ {
1579
+ className: `
1580
+ flex items-center ${config.avatarGap}
1581
+ ${className}
1582
+ `.trim().replace(/\s+/g, " "),
1583
+ "data-typing-indicator": true,
1584
+ children: [
1585
+ avatar && /* @__PURE__ */ jsxRuntime.jsx(
1586
+ "div",
1587
+ {
1588
+ className: `
1589
+ ${AVATAR_SIZE[resolvedSize]}
1590
+ rounded-full overflow-hidden shrink-0
1591
+ flex items-center justify-center
1592
+ `.trim().replace(/\s+/g, " "),
1593
+ style: {
1594
+ background: "var(--lm-typing-avatar-bg)",
1595
+ boxShadow: "0 2px 8px rgba(0, 0, 0, 0.06)"
1596
+ },
1597
+ children: avatar
1598
+ }
1599
+ ),
1600
+ /* @__PURE__ */ jsxRuntime.jsxs(
1601
+ "div",
1602
+ {
1603
+ className: `
1604
+ inline-flex items-center
1605
+ ${config.padding}
1606
+ `.trim().replace(/\s+/g, " "),
1607
+ style: {
1608
+ backgroundColor: "var(--lm-typing-bg)",
1609
+ backdropFilter: "blur(20px) saturate(180%)",
1610
+ WebkitBackdropFilter: "blur(20px) saturate(180%)",
1611
+ border: "1px solid var(--lm-typing-border)",
1612
+ boxShadow: "var(--lm-typing-shadow)",
1613
+ borderRadius: "20px 20px 20px 4px"
1614
+ },
1615
+ children: [
1616
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex items-center ${config.gap}`, children: [0, 1, 2].map((i) => /* @__PURE__ */ jsxRuntime.jsx(
1617
+ "span",
1618
+ {
1619
+ className: `
1620
+ ${config.dotSize}
1621
+ rounded-full
1622
+ `.trim(),
1623
+ style: {
1624
+ backgroundColor: color || "var(--lm-primary-500)",
1625
+ ...getDotAnimation(i)
1626
+ }
1627
+ },
1628
+ i
1629
+ )) }),
1630
+ text && /* @__PURE__ */ jsxRuntime.jsx(
1631
+ "span",
1632
+ {
1633
+ className: `ml-2.5 font-medium ${config.fontSize}`,
1634
+ style: { color: "var(--lm-text-secondary)" },
1635
+ children: text
1636
+ }
1637
+ )
1638
+ ]
1639
+ }
1640
+ )
1641
+ ]
1642
+ }
1643
+ )
1644
+ ] });
1645
+ };
1646
+ const LMTypingIndicator = React.memo(LMTypingIndicator$1);
1647
+ const scrollbarThemeStyles = `
1648
+ :root, [data-theme="light"] {
1649
+ --lm-scrollbar-track: rgba(0, 0, 0, 0.02);
1650
+ --lm-scrollbar-thumb: rgba(0, 0, 0, 0.15);
1651
+ --lm-scrollbar-thumb-hover: rgba(0, 0, 0, 0.25);
1652
+ }
1653
+ [data-theme="dark"] {
1654
+ --lm-scrollbar-track: rgba(255, 255, 255, 0.02);
1655
+ --lm-scrollbar-thumb: rgba(255, 255, 255, 0.15);
1656
+ --lm-scrollbar-thumb-hover: rgba(255, 255, 255, 0.25);
1657
+ }
1658
+
1659
+ /* WebKit scrollbar styling */
1660
+ .lm-chat-list-scrollbar::-webkit-scrollbar {
1661
+ width: 6px;
1662
+ }
1663
+ .lm-chat-list-scrollbar::-webkit-scrollbar-track {
1664
+ background: var(--lm-scrollbar-track);
1665
+ border-radius: 3px;
1666
+ }
1667
+ .lm-chat-list-scrollbar::-webkit-scrollbar-thumb {
1668
+ background: var(--lm-scrollbar-thumb);
1669
+ border-radius: 3px;
1670
+ transition: background 0.2s ease;
1671
+ }
1672
+ .lm-chat-list-scrollbar::-webkit-scrollbar-thumb:hover {
1673
+ background: var(--lm-scrollbar-thumb-hover);
1674
+ }
1675
+
1676
+ /* Firefox scrollbar styling */
1677
+ .lm-chat-list-scrollbar {
1678
+ scrollbar-width: thin;
1679
+ scrollbar-color: var(--lm-scrollbar-thumb) var(--lm-scrollbar-track);
1680
+ }
1681
+ `;
1682
+ const LoadingSkeleton = () => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-4 p-4 animate-pulse", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex gap-3 ${i % 2 === 0 ? "flex-row-reverse" : ""}`, children: [
1683
+ /* @__PURE__ */ jsxRuntime.jsx(
1684
+ "div",
1685
+ {
1686
+ className: "w-8 h-8 rounded-full shrink-0",
1687
+ style: { backgroundColor: "var(--lm-bg-hover)" }
1688
+ }
1689
+ ),
1690
+ /* @__PURE__ */ jsxRuntime.jsx(
1691
+ "div",
1692
+ {
1693
+ className: "h-16 rounded-2xl flex-1 max-w-[70%]",
1694
+ style: { backgroundColor: "var(--lm-bg-hover)" }
1695
+ }
1696
+ )
1697
+ ] }, i)) });
1698
+ const EmptyState = ({ children }) => /* @__PURE__ */ jsxRuntime.jsx(
1699
+ "div",
1700
+ {
1701
+ className: "flex flex-col items-center justify-center h-full p-8 text-center",
1702
+ style: { color: "var(--lm-text-tertiary)" },
1703
+ children: children || /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1704
+ /* @__PURE__ */ jsxRuntime.jsx(
1705
+ "svg",
1706
+ {
1707
+ className: "w-16 h-16 mb-4 opacity-40",
1708
+ viewBox: "0 0 24 24",
1709
+ fill: "none",
1710
+ stroke: "currentColor",
1711
+ strokeWidth: 1.5,
1712
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2v10z" })
1713
+ }
1714
+ ),
1715
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm", children: "暂无消息" }),
1716
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs mt-1 opacity-60", children: "开始一段新的对话吧" })
1717
+ ] })
1718
+ }
1719
+ );
1720
+ const LoadMoreTrigger = ({ loading, hasMore, text = "加载更多", onLoadMore }) => {
1721
+ if (!hasMore) return null;
1722
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxRuntime.jsx(
1723
+ "button",
1724
+ {
1725
+ onClick: onLoadMore,
1726
+ disabled: loading,
1727
+ className: "px-4 py-2 text-sm rounded-lg cursor-pointer hover:opacity-80 disabled:opacity-50",
1728
+ style: {
1729
+ backgroundColor: "var(--lm-bg-paper)",
1730
+ color: "var(--lm-text-secondary)"
1731
+ },
1732
+ children: loading ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-2", children: [
1733
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-4 h-4 border-2 rounded-full animate-spin border-current border-t-transparent" }),
1734
+ "加载中..."
1735
+ ] }) : text
1736
+ }
1737
+ ) });
1738
+ };
1739
+ const LMChatList$1 = React.forwardRef(({
1740
+ messages,
1741
+ size = "md",
1742
+ variant = "default",
1743
+ showTypingIndicator = false,
1744
+ typingIndicatorVariant = "dots",
1745
+ typingIndicatorText,
1746
+ typingIndicatorAvatar,
1747
+ autoScrollToBottom = true,
1748
+ scrollBehavior = "smooth",
1749
+ messageGap = 16,
1750
+ className = "",
1751
+ emptyContent,
1752
+ loading = false,
1753
+ onLoadMore,
1754
+ hasMore = false,
1755
+ loadMoreText,
1756
+ renderMessage,
1757
+ onMessageRetry,
1758
+ bubbleMaxWidth
1759
+ }, ref) => {
1760
+ const containerRef = React.useRef(null);
1761
+ const bottomRef = React.useRef(null);
1762
+ const resolvedSize = componentSizes.clampComponentSize(size, componentSizes.COMPONENT_SIZE_ORDER);
1763
+ React.useImperativeHandle(ref, () => ({
1764
+ scrollToBottom: (behavior = scrollBehavior) => {
1765
+ var _a;
1766
+ (_a = bottomRef.current) == null ? void 0 : _a.scrollIntoView({ behavior });
1767
+ },
1768
+ scrollToMessage: (messageId, behavior = scrollBehavior) => {
1769
+ var _a;
1770
+ const element = (_a = containerRef.current) == null ? void 0 : _a.querySelector(`[id="${messageId}"]`);
1771
+ element == null ? void 0 : element.scrollIntoView({ behavior, block: "center" });
1772
+ },
1773
+ getContainer: () => containerRef.current
1774
+ }), [scrollBehavior]);
1775
+ React.useEffect(() => {
1776
+ var _a;
1777
+ if (autoScrollToBottom && messages.length > 0) {
1778
+ (_a = bottomRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: scrollBehavior });
1779
+ }
1780
+ }, [messages.length, autoScrollToBottom, scrollBehavior]);
1781
+ React.useEffect(() => {
1782
+ var _a;
1783
+ if (showTypingIndicator && autoScrollToBottom) {
1784
+ (_a = bottomRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: scrollBehavior });
1785
+ }
1786
+ }, [showTypingIndicator, autoScrollToBottom, scrollBehavior]);
1787
+ const handleScroll = React.useCallback((e) => {
1788
+ if (!onLoadMore || !hasMore) return;
1789
+ const { scrollTop } = e.currentTarget;
1790
+ if (scrollTop < 50) {
1791
+ onLoadMore();
1792
+ }
1793
+ }, [onLoadMore, hasMore]);
1794
+ if (loading && messages.length === 0) {
1795
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1796
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: scrollbarThemeStyles }),
1797
+ /* @__PURE__ */ jsxRuntime.jsx(
1798
+ "div",
1799
+ {
1800
+ ref: containerRef,
1801
+ className: `flex-1 overflow-y-auto lm-chat-list-scrollbar ${className}`.trim(),
1802
+ children: /* @__PURE__ */ jsxRuntime.jsx(LoadingSkeleton, {})
1803
+ }
1804
+ )
1805
+ ] });
1806
+ }
1807
+ if (messages.length === 0 && !showTypingIndicator) {
1808
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1809
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: scrollbarThemeStyles }),
1810
+ /* @__PURE__ */ jsxRuntime.jsx(
1811
+ "div",
1812
+ {
1813
+ ref: containerRef,
1814
+ className: `flex-1 overflow-y-auto lm-chat-list-scrollbar ${className}`.trim(),
1815
+ children: /* @__PURE__ */ jsxRuntime.jsx(EmptyState, { children: emptyContent })
1816
+ }
1817
+ )
1818
+ ] });
1819
+ }
1820
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1821
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: scrollbarThemeStyles }),
1822
+ /* @__PURE__ */ jsxRuntime.jsxs(
1823
+ "div",
1824
+ {
1825
+ ref: containerRef,
1826
+ className: `flex-1 overflow-y-auto lm-chat-list-scrollbar ${className}`.trim(),
1827
+ onScroll: handleScroll,
1828
+ children: [
1829
+ /* @__PURE__ */ jsxRuntime.jsx(
1830
+ LoadMoreTrigger,
1831
+ {
1832
+ loading,
1833
+ hasMore,
1834
+ text: loadMoreText,
1835
+ onLoadMore
1836
+ }
1837
+ ),
1838
+ /* @__PURE__ */ jsxRuntime.jsxs(
1839
+ "div",
1840
+ {
1841
+ className: "flex flex-col p-4",
1842
+ style: { gap: `${messageGap}px` },
1843
+ children: [
1844
+ messages.map((message, index) => renderMessage ? /* @__PURE__ */ jsxRuntime.jsx(React.Fragment, { children: renderMessage(message, index) }, message.id) : /* @__PURE__ */ jsxRuntime.jsx(
1845
+ LMChatMessage,
1846
+ {
1847
+ ...message,
1848
+ size: resolvedSize,
1849
+ variant,
1850
+ onRetry: onMessageRetry ? () => onMessageRetry(message.id) : void 0,
1851
+ bubbleMaxWidth
1852
+ },
1853
+ message.id
1854
+ )),
1855
+ showTypingIndicator && /* @__PURE__ */ jsxRuntime.jsx(
1856
+ LMTypingIndicator,
1857
+ {
1858
+ variant: typingIndicatorVariant,
1859
+ size: resolvedSize,
1860
+ text: typingIndicatorText,
1861
+ avatar: typingIndicatorAvatar
1862
+ }
1863
+ ),
1864
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: bottomRef })
1865
+ ]
1866
+ }
1867
+ )
1868
+ ]
1869
+ }
1870
+ )
1871
+ ] });
1872
+ });
1873
+ LMChatList$1.displayName = "LMChatList";
1874
+ const LMChatList = React.memo(LMChatList$1);
1875
+ const LMChatContainer = React.forwardRef(({
1876
+ messages,
1877
+ size = "md",
1878
+ variant = "default",
1879
+ onSend,
1880
+ onStop,
1881
+ isGenerating = false,
1882
+ disabled = false,
1883
+ placeholder = "输入消息...",
1884
+ inputValue,
1885
+ onInputChange,
1886
+ maxInputLength,
1887
+ showInputCount = false,
1888
+ showTypingIndicator = false,
1889
+ typingIndicatorText,
1890
+ typingIndicatorAvatar,
1891
+ className = "",
1892
+ header,
1893
+ footer,
1894
+ inputRightSlot,
1895
+ inputToolbar,
1896
+ emptyContent,
1897
+ loading = false,
1898
+ onLoadMore,
1899
+ hasMore = false,
1900
+ onMessageRetry,
1901
+ renderMessage,
1902
+ autoFocus = false,
1903
+ enterToSend = true,
1904
+ height,
1905
+ maxHeight,
1906
+ bubbleMaxWidth
1907
+ }, ref) => {
1908
+ const chatListRef = React.useRef(null);
1909
+ const inputRef = React.useRef("");
1910
+ const resolvedSize = componentSizes.clampComponentSize(size, componentSizes.COMPONENT_SIZE_ORDER);
1911
+ const handleSend = React.useCallback((value) => {
1912
+ if (!value.trim() || disabled || isGenerating) return;
1913
+ onSend == null ? void 0 : onSend(value);
1914
+ }, [disabled, isGenerating, onSend]);
1915
+ const handleInputChange = React.useCallback((value) => {
1916
+ inputRef.current = value;
1917
+ onInputChange == null ? void 0 : onInputChange(value);
1918
+ }, [onInputChange]);
1919
+ React.useImperativeHandle(ref, () => ({
1920
+ scrollToBottom: (behavior) => {
1921
+ var _a;
1922
+ (_a = chatListRef.current) == null ? void 0 : _a.scrollToBottom(behavior);
1923
+ },
1924
+ scrollToMessage: (messageId, behavior) => {
1925
+ var _a;
1926
+ (_a = chatListRef.current) == null ? void 0 : _a.scrollToMessage(messageId, behavior);
1927
+ },
1928
+ focusInput: () => {
1929
+ },
1930
+ getInputValue: () => inputRef.current,
1931
+ setInputValue: (value) => {
1932
+ inputRef.current = value;
1933
+ onInputChange == null ? void 0 : onInputChange(value);
1934
+ },
1935
+ clearInput: () => {
1936
+ inputRef.current = "";
1937
+ onInputChange == null ? void 0 : onInputChange("");
1938
+ }
1939
+ }), [onInputChange]);
1940
+ const getContainerStyles = () => ({
1941
+ backgroundColor: "var(--lm-bg-default)",
1942
+ borderColor: "var(--lm-border-default)",
1943
+ ...height ? { height: typeof height === "number" ? `${height}px` : height } : {},
1944
+ ...maxHeight ? { maxHeight: typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight } : {}
1945
+ });
1946
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1947
+ "div",
1948
+ {
1949
+ className: `
1950
+ flex flex-col border rounded-2xl overflow-hidden
1951
+ ${className}
1952
+ `.trim().replace(/\s+/g, " "),
1953
+ style: getContainerStyles(),
1954
+ children: [
1955
+ header && /* @__PURE__ */ jsxRuntime.jsx(
1956
+ "div",
1957
+ {
1958
+ className: "shrink-0 border-b px-4 py-3",
1959
+ style: {
1960
+ backgroundColor: "var(--lm-bg-elevated)",
1961
+ borderColor: "var(--lm-border-default)"
1962
+ },
1963
+ children: header
1964
+ }
1965
+ ),
1966
+ /* @__PURE__ */ jsxRuntime.jsx(
1967
+ LMChatList,
1968
+ {
1969
+ ref: chatListRef,
1970
+ messages,
1971
+ size: resolvedSize,
1972
+ variant,
1973
+ showTypingIndicator,
1974
+ typingIndicatorText,
1975
+ typingIndicatorAvatar,
1976
+ emptyContent,
1977
+ loading,
1978
+ onLoadMore,
1979
+ hasMore,
1980
+ onMessageRetry,
1981
+ renderMessage,
1982
+ className: "flex-1 min-h-0",
1983
+ bubbleMaxWidth
1984
+ }
1985
+ ),
1986
+ footer && /* @__PURE__ */ jsxRuntime.jsx(
1987
+ "div",
1988
+ {
1989
+ className: "shrink-0 px-4 py-2 border-t",
1990
+ style: { borderColor: "var(--lm-border-light)" },
1991
+ children: footer
1992
+ }
1993
+ ),
1994
+ /* @__PURE__ */ jsxRuntime.jsx(
1995
+ "div",
1996
+ {
1997
+ className: "shrink-0 p-4 border-t",
1998
+ style: {
1999
+ backgroundColor: "var(--lm-bg-elevated)",
2000
+ borderColor: "var(--lm-border-default)"
2001
+ },
2002
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2003
+ LMChatInput,
2004
+ {
2005
+ value: inputValue,
2006
+ onChange: handleInputChange,
2007
+ onSend: handleSend,
2008
+ onStop,
2009
+ isGenerating,
2010
+ placeholder,
2011
+ size: resolvedSize,
2012
+ disabled,
2013
+ sending: isGenerating,
2014
+ maxLength: maxInputLength,
2015
+ showCount: showInputCount,
2016
+ autoFocus,
2017
+ enterToSend,
2018
+ rightSlot: inputRightSlot,
2019
+ toolbar: inputToolbar
2020
+ }
2021
+ )
2022
+ }
2023
+ )
2024
+ ]
2025
+ }
2026
+ );
2027
+ });
2028
+ LMChatContainer.displayName = "LMChatContainer";
2029
+ const LMChatContainer_default = React.memo(LMChatContainer);
30
2030
  function cn(...inputs) {
31
2031
  return inputs.flat().filter((x) => typeof x === "string" && x.length > 0).join(" ");
32
2032
  }
@@ -73,5 +2073,14 @@ exports.SIZE_TABLE_CONFIG = componentSizes.SIZE_TABLE_CONFIG;
73
2073
  exports.SIZE_TEXT_CLASSES = componentSizes.SIZE_TEXT_CLASSES;
74
2074
  exports.SIZE_TOOLTIP_CONFIG = componentSizes.SIZE_TOOLTIP_CONFIG;
75
2075
  exports.clampComponentSize = componentSizes.clampComponentSize;
2076
+ exports.LMChatBubble = LMChatBubble;
2077
+ exports.LMChatContainer = LMChatContainer_default;
2078
+ exports.LMChatInput = LMChatInput;
2079
+ exports.LMChatList = LMChatList;
2080
+ exports.LMChatMessage = LMChatMessage;
2081
+ exports.LMCodeBlock = LMCodeBlock;
2082
+ exports.LMMarkdownRenderer = LMMarkdownRenderer_default;
2083
+ exports.LMTypingIndicator = LMTypingIndicator;
2084
+ exports.ToolbarButton = ToolbarButton;
76
2085
  exports.cn = cn;
77
2086
  //# sourceMappingURL=index.cjs.map