@gendive/chatllm 0.1.0 → 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.
@@ -0,0 +1,3768 @@
1
+ // src/react/ChatUI.tsx
2
+ import React8, { useState as useState8 } from "react";
3
+
4
+ // src/react/hooks/useChatUI.ts
5
+ import { useState, useRef, useCallback, useEffect } from "react";
6
+
7
+ // src/types.ts
8
+ var DEFAULT_PERSONALIZATION = {
9
+ responseStyle: {
10
+ warmth: "medium",
11
+ enthusiasm: "medium",
12
+ emojiUsage: "low",
13
+ formatting: "default",
14
+ verbosity: "balanced"
15
+ },
16
+ userProfile: {},
17
+ useMemory: true,
18
+ language: "auto"
19
+ };
20
+
21
+ // src/react/hooks/useChatUI.ts
22
+ var DEFAULT_STORAGE_KEY = "chatllm_sessions";
23
+ var DEFAULT_COMPRESSION_THRESHOLD = 20;
24
+ var DEFAULT_KEEP_RECENT = 6;
25
+ var generateId = (prefix) => `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
26
+ var generateTitle = (messages) => {
27
+ const firstUserMessage = messages.find((m) => m.role === "user");
28
+ if (!firstUserMessage) return "\uC0C8 \uB300\uD654";
29
+ const content = firstUserMessage.content;
30
+ return content.length > 30 ? content.substring(0, 30) + "..." : content;
31
+ };
32
+ var useChatUI = (options) => {
33
+ const {
34
+ models,
35
+ actions = [],
36
+ initialPersonalization,
37
+ apiKey,
38
+ apiEndpoint = "/api/chat",
39
+ initialModel,
40
+ storageKey = DEFAULT_STORAGE_KEY,
41
+ contextCompressionThreshold = DEFAULT_COMPRESSION_THRESHOLD,
42
+ keepRecentMessages = DEFAULT_KEEP_RECENT,
43
+ onSendMessage,
44
+ onSessionChange,
45
+ onError
46
+ } = options;
47
+ const [sessions, setSessions] = useState([]);
48
+ const [currentSessionId, setCurrentSessionId] = useState(null);
49
+ const [input, setInput] = useState("");
50
+ const [isLoading, setIsLoading] = useState(false);
51
+ const [selectedModel, setSelectedModel] = useState(initialModel || models[0]?.id || "");
52
+ const [sidebarOpen, setSidebarOpen] = useState(true);
53
+ const [settingsOpen, setSettingsOpen] = useState(false);
54
+ const [quotedText, setQuotedText] = useState(null);
55
+ const [selectedAction, setSelectedAction] = useState(null);
56
+ const [copiedMessageId, setCopiedMessageId] = useState(null);
57
+ const [editingMessageId, setEditingMessageId] = useState(null);
58
+ const [personalization, setPersonalization] = useState({
59
+ ...DEFAULT_PERSONALIZATION,
60
+ ...initialPersonalization
61
+ });
62
+ const abortControllerRef = useRef(null);
63
+ const currentSession = sessions.find((s) => s.id === currentSessionId) || null;
64
+ const messages = currentSession?.messages || [];
65
+ useEffect(() => {
66
+ if (typeof window === "undefined") return;
67
+ const saved = localStorage.getItem(storageKey);
68
+ if (saved) {
69
+ try {
70
+ const parsed = JSON.parse(saved);
71
+ setSessions(parsed);
72
+ if (parsed.length > 0) {
73
+ setCurrentSessionId(parsed[0].id);
74
+ setSelectedModel(parsed[0].model);
75
+ }
76
+ } catch {
77
+ }
78
+ }
79
+ const savedPersonalization = localStorage.getItem(`${storageKey}_personalization`);
80
+ if (savedPersonalization) {
81
+ try {
82
+ setPersonalization(JSON.parse(savedPersonalization));
83
+ } catch {
84
+ }
85
+ }
86
+ }, [storageKey]);
87
+ useEffect(() => {
88
+ if (typeof window === "undefined") return;
89
+ if (sessions.length > 0) {
90
+ localStorage.setItem(storageKey, JSON.stringify(sessions));
91
+ }
92
+ }, [sessions, storageKey]);
93
+ useEffect(() => {
94
+ if (typeof window === "undefined") return;
95
+ localStorage.setItem(`${storageKey}_personalization`, JSON.stringify(personalization));
96
+ }, [personalization, storageKey]);
97
+ useEffect(() => {
98
+ onSessionChange?.(currentSession);
99
+ }, [currentSession, onSessionChange]);
100
+ const buildSystemPrompt = useCallback(() => {
101
+ const parts = [];
102
+ const { userProfile, responseStyle, language } = personalization;
103
+ if (userProfile.nickname) {
104
+ parts.push(`\uC0AC\uC6A9\uC790\uC758 \uC774\uB984/\uB2C9\uB124\uC784: ${userProfile.nickname}`);
105
+ }
106
+ if (userProfile.occupation) {
107
+ parts.push(`\uC0AC\uC6A9\uC790\uC758 \uC9C1\uC5C5: ${userProfile.occupation}`);
108
+ }
109
+ if (userProfile.additionalInfo) {
110
+ parts.push(`\uC0AC\uC6A9\uC790 \uCD94\uAC00 \uC815\uBCF4: ${userProfile.additionalInfo}`);
111
+ }
112
+ const styleDescriptions = [];
113
+ if (responseStyle.warmth === "high") styleDescriptions.push("\uB530\uB73B\uD558\uACE0 \uCE5C\uADFC\uD558\uAC8C");
114
+ else if (responseStyle.warmth === "low") styleDescriptions.push("\uAC04\uACB0\uD558\uACE0 \uC0AC\uBB34\uC801\uC73C\uB85C");
115
+ if (responseStyle.enthusiasm === "high") styleDescriptions.push("\uC5F4\uC815\uC801\uC774\uACE0 \uC801\uADF9\uC801\uC73C\uB85C");
116
+ else if (responseStyle.enthusiasm === "low") styleDescriptions.push("\uCC28\uBD84\uD558\uAC8C");
117
+ if (responseStyle.emojiUsage === "high") styleDescriptions.push("\uC774\uBAA8\uC9C0\uB97C \uC801\uADF9 \uD65C\uC6A9\uD574\uC11C");
118
+ else if (responseStyle.emojiUsage === "low") styleDescriptions.push("\uC774\uBAA8\uC9C0 \uC0AC\uC6A9\uC744 \uC790\uC81C\uD574\uC11C");
119
+ if (responseStyle.verbosity === "concise") styleDescriptions.push("\uD575\uC2EC\uB9CC \uAC04\uACB0\uD558\uAC8C");
120
+ else if (responseStyle.verbosity === "detailed") styleDescriptions.push("\uC0C1\uC138\uD558\uACE0 \uC790\uC138\uD558\uAC8C");
121
+ if (styleDescriptions.length > 0) {
122
+ parts.push(`\uC751\uB2F5 \uC2A4\uD0C0\uC77C: ${styleDescriptions.join(", ")} \uC751\uB2F5\uD574\uC8FC\uC138\uC694.`);
123
+ }
124
+ if (language !== "auto") {
125
+ const languageNames = { ko: "\uD55C\uAD6D\uC5B4", en: "\uC601\uC5B4", ja: "\uC77C\uBCF8\uC5B4" };
126
+ parts.push(`\uC751\uB2F5 \uC5B8\uC5B4: ${languageNames[language] || language}\uB85C \uC751\uB2F5\uD574\uC8FC\uC138\uC694.`);
127
+ }
128
+ return parts.length > 0 ? parts.join("\n") : "";
129
+ }, [personalization]);
130
+ const compressContext = useCallback(async (messagesToCompress, model) => {
131
+ const conversationText = messagesToCompress.map((m) => `${m.role === "user" ? "\uC0AC\uC6A9\uC790" : "AI"}: ${m.content}`).join("\n\n");
132
+ const summaryPrompt = `\uB2E4\uC74C \uB300\uD654 \uB0B4\uC6A9\uC744 \uD575\uC2EC \uC815\uBCF4\uB9CC \uC720\uC9C0\uD558\uBA74\uC11C \uAC04\uACB0\uD558\uAC8C \uC694\uC57D\uD574\uC8FC\uC138\uC694.
133
+ \uC911\uC694\uD55C \uACB0\uC815\uC0AC\uD56D, \uC0AC\uC6A9\uC790 \uC694\uAD6C\uC0AC\uD56D, \uB9E5\uB77D \uC815\uBCF4\uB97C \uBCF4\uC874\uD558\uC138\uC694.
134
+
135
+ \uB300\uD654:
136
+ ${conversationText}
137
+
138
+ \uC694\uC57D:`;
139
+ try {
140
+ const response = await fetch(apiEndpoint, {
141
+ method: "POST",
142
+ headers: { "Content-Type": "application/json" },
143
+ body: JSON.stringify({
144
+ messages: [{ role: "user", content: summaryPrompt }],
145
+ model
146
+ })
147
+ });
148
+ if (!response.ok) return "";
149
+ const reader = response.body?.getReader();
150
+ if (!reader) return "";
151
+ const decoder = new TextDecoder();
152
+ let buffer = "";
153
+ let summary = "";
154
+ while (true) {
155
+ const { done, value } = await reader.read();
156
+ if (done) break;
157
+ buffer += decoder.decode(value, { stream: true });
158
+ const lines = buffer.split("\n");
159
+ buffer = lines.pop() || "";
160
+ for (const line of lines) {
161
+ if (line.startsWith("data: ")) {
162
+ const data = line.slice(6);
163
+ if (data === "[DONE]") continue;
164
+ try {
165
+ const parsed = JSON.parse(data);
166
+ if (parsed.content) summary += parsed.content;
167
+ } catch {
168
+ }
169
+ }
170
+ }
171
+ }
172
+ return summary;
173
+ } catch {
174
+ return "";
175
+ }
176
+ }, [apiEndpoint]);
177
+ const newSession = useCallback(() => {
178
+ const now = Date.now();
179
+ const newSess = {
180
+ id: generateId("session"),
181
+ title: "\uC0C8 \uB300\uD654",
182
+ messages: [],
183
+ model: selectedModel,
184
+ createdAt: now,
185
+ updatedAt: now
186
+ };
187
+ setSessions((prev) => [newSess, ...prev]);
188
+ setCurrentSessionId(newSess.id);
189
+ }, [selectedModel]);
190
+ const selectSession = useCallback((id) => {
191
+ const session = sessions.find((s) => s.id === id);
192
+ if (session) {
193
+ setCurrentSessionId(id);
194
+ setSelectedModel(session.model);
195
+ }
196
+ }, [sessions]);
197
+ const deleteSession = useCallback((id) => {
198
+ setSessions((prev) => {
199
+ const filtered = prev.filter((s) => s.id !== id);
200
+ if (currentSessionId === id) {
201
+ setCurrentSessionId(filtered.length > 0 ? filtered[0].id : null);
202
+ }
203
+ if (filtered.length === 0 && typeof window !== "undefined") {
204
+ localStorage.removeItem(storageKey);
205
+ }
206
+ return filtered;
207
+ });
208
+ }, [currentSessionId, storageKey]);
209
+ const setModel = useCallback((model) => {
210
+ setSelectedModel(model);
211
+ if (currentSessionId) {
212
+ setSessions(
213
+ (prev) => prev.map((s) => s.id === currentSessionId ? { ...s, model } : s)
214
+ );
215
+ }
216
+ }, [currentSessionId]);
217
+ const toggleSidebar = useCallback(() => setSidebarOpen((prev) => !prev), []);
218
+ const openSettings = useCallback(() => setSettingsOpen(true), []);
219
+ const closeSettings = useCallback(() => setSettingsOpen(false), []);
220
+ const copyMessage = useCallback((content, id) => {
221
+ if (typeof navigator !== "undefined") {
222
+ navigator.clipboard.writeText(content).then(() => {
223
+ setCopiedMessageId(id);
224
+ setTimeout(() => setCopiedMessageId(null), 2e3);
225
+ });
226
+ }
227
+ }, []);
228
+ const startEdit = useCallback((message) => {
229
+ if (message.role === "user") {
230
+ setEditingMessageId(message.id);
231
+ }
232
+ }, []);
233
+ const cancelEdit = useCallback(() => {
234
+ setEditingMessageId(null);
235
+ }, []);
236
+ const stopGeneration = useCallback(() => {
237
+ abortControllerRef.current?.abort();
238
+ }, []);
239
+ const updatePersonalization = useCallback((config) => {
240
+ setPersonalization((prev) => ({ ...prev, ...config }));
241
+ }, []);
242
+ const sendMessage = useCallback(async (content) => {
243
+ const messageContent = content || input;
244
+ if (!messageContent.trim() || isLoading) return;
245
+ let sessionId = currentSessionId;
246
+ if (!sessionId) {
247
+ const now = Date.now();
248
+ const newSess = {
249
+ id: generateId("session"),
250
+ title: "\uC0C8 \uB300\uD654",
251
+ messages: [],
252
+ model: selectedModel,
253
+ createdAt: now,
254
+ updatedAt: now
255
+ };
256
+ setSessions((prev) => [newSess, ...prev]);
257
+ sessionId = newSess.id;
258
+ setCurrentSessionId(sessionId);
259
+ }
260
+ let finalContent = messageContent.trim();
261
+ if (quotedText) {
262
+ finalContent = `"${quotedText}"
263
+
264
+ ${finalContent}`;
265
+ }
266
+ if (selectedAction) {
267
+ finalContent = `[${selectedAction.label}] ${finalContent}`;
268
+ }
269
+ const actionPrompt = selectedAction?.systemPrompt;
270
+ const userMessage = {
271
+ id: generateId("msg"),
272
+ role: "user",
273
+ content: finalContent,
274
+ timestamp: Date.now()
275
+ };
276
+ const assistantMessageId = generateId("msg");
277
+ const assistantMessage = {
278
+ id: assistantMessageId,
279
+ role: "assistant",
280
+ content: "",
281
+ model: selectedModel,
282
+ timestamp: Date.now()
283
+ };
284
+ setInput("");
285
+ setQuotedText(null);
286
+ setSelectedAction(null);
287
+ const capturedSessionId = sessionId;
288
+ setSessions(
289
+ (prev) => prev.map((s) => {
290
+ if (s.id === capturedSessionId) {
291
+ const newMessages = [...s.messages, userMessage, assistantMessage];
292
+ return {
293
+ ...s,
294
+ messages: newMessages,
295
+ title: s.messages.length === 0 ? generateTitle([userMessage]) : s.title,
296
+ updatedAt: Date.now()
297
+ };
298
+ }
299
+ return s;
300
+ })
301
+ );
302
+ setIsLoading(true);
303
+ abortControllerRef.current = new AbortController();
304
+ try {
305
+ const session = sessions.find((s) => s.id === capturedSessionId);
306
+ const existingMessages = session?.messages || [];
307
+ let messagesToSend = [...existingMessages, userMessage];
308
+ let contextSummary = session?.contextSummary;
309
+ if (messagesToSend.length > contextCompressionThreshold && !contextSummary) {
310
+ const toCompress = messagesToSend.slice(0, -keepRecentMessages);
311
+ const summary = await compressContext(toCompress, selectedModel);
312
+ if (summary) {
313
+ contextSummary = summary;
314
+ setSessions(
315
+ (prev) => prev.map(
316
+ (s) => s.id === capturedSessionId ? { ...s, contextSummary: summary, summaryAfterIndex: toCompress.length } : s
317
+ )
318
+ );
319
+ }
320
+ }
321
+ let chatMessages;
322
+ if (contextSummary) {
323
+ const recentMessages = messagesToSend.slice(-keepRecentMessages);
324
+ chatMessages = [
325
+ { role: "system", content: `[\uC774\uC804 \uB300\uD654 \uC694\uC57D]
326
+ ${contextSummary}` },
327
+ ...recentMessages.map((m) => ({ role: m.role, content: m.content }))
328
+ ];
329
+ } else {
330
+ chatMessages = messagesToSend.map((m) => ({ role: m.role, content: m.content }));
331
+ }
332
+ const baseSystemPrompt = buildSystemPrompt();
333
+ const combinedSystemPrompt = [baseSystemPrompt, actionPrompt].filter(Boolean).join("\n\n");
334
+ const messagesForApi = combinedSystemPrompt ? [{ role: "system", content: combinedSystemPrompt }, ...chatMessages] : chatMessages;
335
+ const modelConfig = models.find((m) => m.id === selectedModel);
336
+ const provider = modelConfig?.provider || "ollama";
337
+ let response;
338
+ if (onSendMessage) {
339
+ const result = await onSendMessage({
340
+ messages: messagesForApi,
341
+ model: selectedModel,
342
+ provider,
343
+ apiKey,
344
+ systemPrompt: combinedSystemPrompt
345
+ });
346
+ if (typeof result === "string") {
347
+ setSessions(
348
+ (prev) => prev.map((s) => {
349
+ if (s.id === capturedSessionId) {
350
+ return {
351
+ ...s,
352
+ messages: s.messages.map(
353
+ (m) => m.id === assistantMessageId ? { ...m, content: result } : m
354
+ )
355
+ };
356
+ }
357
+ return s;
358
+ })
359
+ );
360
+ return;
361
+ }
362
+ response = new Response(result);
363
+ } else {
364
+ response = await fetch(apiEndpoint, {
365
+ method: "POST",
366
+ headers: { "Content-Type": "application/json" },
367
+ body: JSON.stringify({
368
+ messages: messagesForApi,
369
+ model: selectedModel,
370
+ provider,
371
+ apiKey: provider === "devdive" ? apiKey : void 0
372
+ }),
373
+ signal: abortControllerRef.current.signal
374
+ });
375
+ }
376
+ if (!response.ok) throw new Error("API error");
377
+ const reader = response.body?.getReader();
378
+ if (!reader) throw new Error("No reader");
379
+ const decoder = new TextDecoder();
380
+ let buffer = "";
381
+ while (true) {
382
+ const { done, value } = await reader.read();
383
+ if (done) break;
384
+ buffer += decoder.decode(value, { stream: true });
385
+ const lines = buffer.split("\n");
386
+ buffer = lines.pop() || "";
387
+ for (const line of lines) {
388
+ if (line.startsWith("data: ")) {
389
+ const data = line.slice(6);
390
+ if (data === "[DONE]") continue;
391
+ try {
392
+ const parsed = JSON.parse(data);
393
+ if (parsed.content) {
394
+ setSessions(
395
+ (prev) => prev.map((s) => {
396
+ if (s.id === capturedSessionId) {
397
+ return {
398
+ ...s,
399
+ messages: s.messages.map(
400
+ (m) => m.id === assistantMessageId ? { ...m, content: m.content + parsed.content } : m
401
+ )
402
+ };
403
+ }
404
+ return s;
405
+ })
406
+ );
407
+ }
408
+ } catch {
409
+ }
410
+ }
411
+ }
412
+ }
413
+ } catch (error) {
414
+ if (error instanceof Error && error.name === "AbortError") {
415
+ return;
416
+ }
417
+ const err = error instanceof Error ? error : new Error("Unknown error");
418
+ onError?.(err);
419
+ setSessions(
420
+ (prev) => prev.map((s) => {
421
+ if (s.id === capturedSessionId) {
422
+ return {
423
+ ...s,
424
+ messages: s.messages.map(
425
+ (m) => m.id === assistantMessageId ? { ...m, content: "\uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694." } : m
426
+ )
427
+ };
428
+ }
429
+ return s;
430
+ })
431
+ );
432
+ } finally {
433
+ setIsLoading(false);
434
+ abortControllerRef.current = null;
435
+ }
436
+ }, [
437
+ input,
438
+ isLoading,
439
+ currentSessionId,
440
+ sessions,
441
+ selectedModel,
442
+ quotedText,
443
+ selectedAction,
444
+ apiEndpoint,
445
+ apiKey,
446
+ models,
447
+ contextCompressionThreshold,
448
+ keepRecentMessages,
449
+ buildSystemPrompt,
450
+ compressContext,
451
+ onSendMessage,
452
+ onError
453
+ ]);
454
+ const saveEdit = useCallback(async (content) => {
455
+ if (!editingMessageId || !currentSession || !currentSessionId) return;
456
+ const messageIndex = currentSession.messages.findIndex((m) => m.id === editingMessageId);
457
+ if (messageIndex === -1) return;
458
+ const capturedSessionId = currentSessionId;
459
+ setSessions(
460
+ (prev) => prev.map((s) => {
461
+ if (s.id === capturedSessionId) {
462
+ const newMessages = s.messages.slice(0, messageIndex);
463
+ return { ...s, messages: newMessages, updatedAt: Date.now() };
464
+ }
465
+ return s;
466
+ })
467
+ );
468
+ setEditingMessageId(null);
469
+ await sendMessage(content);
470
+ }, [editingMessageId, currentSession, currentSessionId, sendMessage]);
471
+ const regenerate = useCallback(async (messageId) => {
472
+ if (!currentSession || !currentSessionId || isLoading) return;
473
+ const assistantIndex = currentSession.messages.findIndex((m) => m.id === messageId);
474
+ if (assistantIndex === -1) return;
475
+ const userMessage = currentSession.messages[assistantIndex - 1];
476
+ if (!userMessage || userMessage.role !== "user") return;
477
+ const capturedSessionId = currentSessionId;
478
+ setSessions(
479
+ (prev) => prev.map((s) => {
480
+ if (s.id === capturedSessionId) {
481
+ return {
482
+ ...s,
483
+ messages: s.messages.slice(0, assistantIndex),
484
+ updatedAt: Date.now()
485
+ };
486
+ }
487
+ return s;
488
+ })
489
+ );
490
+ setInput(userMessage.content);
491
+ await sendMessage(userMessage.content);
492
+ }, [currentSession, currentSessionId, isLoading, sendMessage]);
493
+ return {
494
+ // State
495
+ sessions,
496
+ currentSession,
497
+ currentSessionId,
498
+ messages,
499
+ input,
500
+ isLoading,
501
+ selectedModel,
502
+ sidebarOpen,
503
+ settingsOpen,
504
+ quotedText,
505
+ selectedAction,
506
+ copiedMessageId,
507
+ editingMessageId,
508
+ personalization,
509
+ // Actions
510
+ setInput,
511
+ sendMessage,
512
+ stopGeneration,
513
+ newSession,
514
+ selectSession,
515
+ deleteSession,
516
+ setModel,
517
+ toggleSidebar,
518
+ openSettings,
519
+ closeSettings,
520
+ setQuotedText,
521
+ setSelectedAction,
522
+ copyMessage,
523
+ startEdit,
524
+ cancelEdit,
525
+ saveEdit,
526
+ regenerate,
527
+ updatePersonalization
528
+ };
529
+ };
530
+
531
+ // src/react/components/Icon.tsx
532
+ import { jsx } from "react/jsx-runtime";
533
+ var Icon = ({
534
+ name,
535
+ size = 20,
536
+ color,
537
+ className = "",
538
+ onClick,
539
+ "aria-label": ariaLabel
540
+ }) => {
541
+ const sizeValue = typeof size === "number" ? `${size}px` : size;
542
+ return /* @__PURE__ */ jsx(
543
+ "i",
544
+ {
545
+ className: `ri-${name} ${className}`,
546
+ style: {
547
+ fontSize: sizeValue,
548
+ color,
549
+ display: "inline-flex",
550
+ alignItems: "center",
551
+ justifyContent: "center",
552
+ lineHeight: 1
553
+ },
554
+ onClick,
555
+ "aria-label": ariaLabel,
556
+ role: onClick ? "button" : void 0,
557
+ tabIndex: onClick ? 0 : void 0
558
+ }
559
+ );
560
+ };
561
+ var IconSvg = ({
562
+ name,
563
+ size = 20,
564
+ color = "currentColor",
565
+ className = "",
566
+ style,
567
+ onClick,
568
+ "aria-label": ariaLabel
569
+ }) => {
570
+ const sizeValue = typeof size === "number" ? size : parseInt(size, 10) || 20;
571
+ const icons = {
572
+ "menu-line": /* @__PURE__ */ jsx("path", { d: "M3 4h18v2H3V4zm0 7h18v2H3v-2zm0 7h18v2H3v-2z" }),
573
+ "close-line": /* @__PURE__ */ jsx("path", { d: "M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z" }),
574
+ "add-line": /* @__PURE__ */ jsx("path", { d: "M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z" }),
575
+ "chat-1-line": /* @__PURE__ */ jsx("path", { d: "M10 3h4a8 8 0 1 1 0 16v3.5c-5-2-12-5-12-11.5a8 8 0 0 1 8-8zm2 14h2a6 6 0 0 0 0-12h-4a6 6 0 0 0-6 6c0 3.61 2.462 5.966 8 8.48V17z" }),
576
+ "send-plane-fill": /* @__PURE__ */ jsx("path", { d: "M1.946 9.315c-.522-.174-.527-.455.01-.634l19.087-6.362c.529-.176.832.12.684.638l-5.454 19.086c-.15.529-.455.547-.679.045L12 14l6-8-8 6-8.054-2.685z" }),
577
+ "send-plane-line": /* @__PURE__ */ jsx("path", { d: "M1.923 9.37c-.51-.205-.504-.51.034-.689l19.086-6.362c.529-.176.832.12.684.638l-5.454 19.086c-.15.529-.475.553-.717.07L12 14 3 10.5l8-4.25-9.077 3.12zM5.5 10.5l5.25 2.75 2.25 5.5 4-14-11.5 5.75z" }),
578
+ "search-line": /* @__PURE__ */ jsx("path", { d: "M18.031 16.617l4.283 4.282-1.415 1.415-4.282-4.283A8.96 8.96 0 0 1 11 20c-4.968 0-9-4.032-9-9s4.032-9 9-9 9 4.032 9 9a8.96 8.96 0 0 1-1.969 5.617zm-2.006-.742A6.977 6.977 0 0 0 18 11c0-3.868-3.133-7-7-7-3.868 0-7 3.132-7 7 0 3.867 3.132 7 7 7a6.977 6.977 0 0 0 4.875-1.975l.15-.15z" }),
579
+ "image-line": /* @__PURE__ */ jsx("path", { d: "M4.828 21l-.02.02-.021-.02H2.992A.993.993 0 0 1 2 20.007V3.993A1 1 0 0 1 2.992 3h18.016c.548 0 .992.445.992.993v16.014a1 1 0 0 1-.992.993H4.828zM20 15V5H4v14L14 9l6 6zm0 2.828l-6-6L6.828 19H20v-1.172zM8 11a2 2 0 1 1 0-4 2 2 0 0 1 0 4z" }),
580
+ "code-s-slash-line": /* @__PURE__ */ jsx("path", { d: "M24 12l-5.657 5.657-1.414-1.414L21.172 12l-4.243-4.243 1.414-1.414L24 12zM2.828 12l4.243 4.243-1.414 1.414L0 12l5.657-5.657L7.07 7.757 2.828 12zm6.96 9H7.66l6.552-18h2.128L9.788 21z" }),
581
+ "file-text-line": /* @__PURE__ */ jsx("path", { d: "M21 8v12.993A1 1 0 0 1 20.007 22H3.993A.993.993 0 0 1 3 21.008V2.992C3 2.455 3.449 2 4.002 2h10.995L21 8zm-2 1h-5V4H5v16h14V9zM8 7h3v2H8V7zm0 4h8v2H8v-2zm0 4h8v2H8v-2z" }),
582
+ "delete-bin-line": /* @__PURE__ */ jsx("path", { d: "M17 6h5v2h-2v13a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V8H2V6h5V3a1 1 0 0 1 1-1h8a1 1 0 0 1 1 1v3zm1 2H6v12h12V8zm-9 3h2v6H9v-6zm4 0h2v6h-2v-6zM9 4v2h6V4H9z" }),
583
+ "edit-line": /* @__PURE__ */ jsx("path", { d: "M6.414 16L16.556 5.858l-1.414-1.414L5 14.586V16h1.414zm.829 2H3v-4.243L14.435 2.322a1 1 0 0 1 1.414 0l2.829 2.829a1 1 0 0 1 0 1.414L7.243 18zM3 20h18v2H3v-2z" }),
584
+ "check-line": /* @__PURE__ */ jsx("path", { d: "M10 15.172l9.192-9.193 1.415 1.414L10 18l-6.364-6.364 1.414-1.414z" }),
585
+ "file-copy-line": /* @__PURE__ */ jsx("path", { d: "M7 6V3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1h-3v3c0 .552-.45 1-1.007 1H4.007A1.001 1.001 0 0 1 3 20l.003-14c0-.552.45-1 1.007-1H7zM5.003 8L5 20h10V8H5.003zM9 6h8v10h2V4H9v2z" }),
586
+ "refresh-line": /* @__PURE__ */ jsx("path", { d: "M5.463 4.433A9.961 9.961 0 0 1 12 2c5.523 0 10 4.477 10 10 0 2.136-.67 4.116-1.81 5.74L17 12h3a8 8 0 0 0-2.46-5.772A7.96 7.96 0 0 0 12 4a7.96 7.96 0 0 0-5.54 2.228l1.414 1.414A5.972 5.972 0 0 1 12 6a5.99 5.99 0 0 1 3.88 1.42l-1.42 1.42A4 4 0 0 0 8.54 12H6V7l.002-.002 1.461 1.435zM18.537 19.567A9.961 9.961 0 0 1 12 22C6.477 22 2 17.523 2 12c0-2.136.67-4.116 1.81-5.74L7 12H4a8 8 0 0 0 2.46 5.772A7.96 7.96 0 0 0 12 20a7.96 7.96 0 0 0 5.54-2.228l-1.414-1.414A5.972 5.972 0 0 1 12 18a5.99 5.99 0 0 1-3.88-1.42l1.42-1.42A4 4 0 0 0 15.46 12H18v5l-.002.002-1.461-1.435z" }),
587
+ "stop-circle-line": /* @__PURE__ */ jsx("path", { d: "M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM9 9h6v6H9V9z" }),
588
+ "settings-3-line": /* @__PURE__ */ jsx("path", { d: "M3.34 17a10.018 10.018 0 0 1-.978-2.326 3 3 0 0 0 .002-5.347A9.99 9.99 0 0 1 4.865 4.99a3 3 0 0 0 4.631-2.674 9.99 9.99 0 0 1 5.007.002 3 3 0 0 0 4.632 2.672 10.018 10.018 0 0 1 2.525 4.337 3 3 0 0 0-.002 5.347 9.99 9.99 0 0 1-2.525 4.337 3 3 0 0 0-4.631 2.674 9.99 9.99 0 0 1-5.007-.002 3 3 0 0 0-4.632-2.672A10.018 10.018 0 0 1 3.34 17zm5.66.196a4.993 4.993 0 0 1 2.25 2.77c.499.047 1 .048 1.499.001A4.993 4.993 0 0 1 15 17.197a4.993 4.993 0 0 1 3.525-.565c.29-.408.54-.843.748-1.298A4.993 4.993 0 0 1 18 12c0-1.26.47-2.437 1.273-3.334a8.126 8.126 0 0 0-.75-1.298A4.993 4.993 0 0 1 15 6.804a4.993 4.993 0 0 1-2.25-2.77c-.499-.047-1-.048-1.499-.001A4.993 4.993 0 0 1 9 6.804a4.993 4.993 0 0 1-3.525.565 7.99 7.99 0 0 0-.748 1.298A4.993 4.993 0 0 1 6 12c0 1.26-.47 2.437-1.273 3.334a8.126 8.126 0 0 0 .75 1.298A4.993 4.993 0 0 1 9 17.196zM12 15a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0-2a1 1 0 1 0 0-2 1 1 0 0 0 0 2z" }),
589
+ "loader-4-line": /* @__PURE__ */ jsx("path", { d: "M12 3a9 9 0 0 1 9 9h-2a7 7 0 0 0-7-7V3z" }),
590
+ "sparkling-line": /* @__PURE__ */ jsx("path", { d: "M14 4.438A2 2 0 0 1 15.438 3l4.62.764a2 2 0 0 1 1.112.529l.152.152a2 2 0 0 1 .529 1.112L22.5 10a2 2 0 0 1-.436 1.559l-.095.11L21 10.5l-3.3-3.3L14 4.438zm-3 1.124l5.5 5.5-9.086 9.086a2 2 0 0 1-2.828 0l-.172-.172a2 2 0 0 1 0-2.828L13.5 8.062 11 5.562zm-8.657 14.95a1 1 0 0 0 1.414 0l9.9-9.9-1.414-1.413-9.9 9.9a1 1 0 0 0 0 1.414z" }),
591
+ "arrow-down-s-line": /* @__PURE__ */ jsx("path", { d: "M12 13.172l4.95-4.95 1.414 1.414L12 16 5.636 9.636 7.05 8.222z" }),
592
+ "arrow-up-s-line": /* @__PURE__ */ jsx("path", { d: "M12 10.828l-4.95 4.95-1.414-1.414L12 8l6.364 6.364-1.414 1.414z" }),
593
+ "double-quotes-l": /* @__PURE__ */ jsx("path", { d: "M4.583 17.321C3.553 16.227 3 15 3 13.011c0-3.5 2.457-6.637 6.03-8.188l.893 1.378c-3.335 1.804-3.987 4.145-4.247 5.621.537-.278 1.24-.375 1.929-.311 1.804.167 3.226 1.648 3.226 3.489a3.5 3.5 0 0 1-3.5 3.5c-1.073 0-2.099-.49-2.748-1.179zm10 0C13.553 16.227 13 15 13 13.011c0-3.5 2.457-6.637 6.03-8.188l.893 1.378c-3.335 1.804-3.987 4.145-4.247 5.621.537-.278 1.24-.375 1.929-.311 1.804.167 3.226 1.648 3.226 3.489a3.5 3.5 0 0 1-3.5 3.5c-1.073 0-2.099-.49-2.748-1.179z" }),
594
+ "robot-line": /* @__PURE__ */ jsx("path", { d: "M13 4.055c4.5.497 8 4.312 8 8.945v9H3v-9c0-4.633 3.5-8.448 8-8.945V1h2v3.055zM5 20h14v-7a7 7 0 1 0-14 0v7zm2-7a5 5 0 0 1 10 0v4H7v-4zm2 0v2h2v-2H9zm4 0v2h2v-2h-2z" }),
595
+ "user-3-line": /* @__PURE__ */ jsx("path", { d: "M20 22h-2v-2a3 3 0 0 0-3-3H9a3 3 0 0 0-3 3v2H4v-2a5 5 0 0 1 5-5h6a5 5 0 0 1 5 5v2zm-8-9a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8z" }),
596
+ "sun-line": /* @__PURE__ */ jsx("path", { d: "M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85l1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z" }),
597
+ "moon-line": /* @__PURE__ */ jsx("path", { d: "M10 7a7 7 0 0 0 12 4.9v.1c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2h.1A6.979 6.979 0 0 0 10 7zm-6 5a8 8 0 0 0 15.062 3.762A9 9 0 0 1 8.238 4.938 7.999 7.999 0 0 0 4 12z" }),
598
+ "key-line": /* @__PURE__ */ jsx("path", { d: "M12.917 13A6.002 6.002 0 0 1 1 12a6 6 0 0 1 11.917-1H23v6h-2v-4h-2v4h-2v-4h-4.083zM7 10a2 2 0 1 0 0 4 2 2 0 0 0 0-4z" }),
599
+ "links-line": /* @__PURE__ */ jsx("path", { d: "M17.657 14.828l-1.414-1.414L17.657 12A4 4 0 1 0 12 6.343l-1.414 1.414-1.414-1.414 1.414-1.414a6 6 0 0 1 8.485 8.485l-1.414 1.414zm-2.829 2.829l-1.414 1.414a6 6 0 1 1-8.485-8.485l1.414-1.414 1.414 1.414L6.343 12A4 4 0 1 0 12 17.657l1.414-1.414 1.414 1.414zm0-9.9l1.415 1.415-7.071 7.07-1.415-1.414 7.071-7.07z" }),
600
+ "external-link-line": /* @__PURE__ */ jsx("path", { d: "M10 6v2H5v11h11v-5h2v6a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h6zm11-3v8h-2V6.413l-7.793 7.794-1.414-1.414L17.585 5H13V3h8z" }),
601
+ "arrow-left-line": /* @__PURE__ */ jsx("path", { d: "M7.828 11H20v2H7.828l5.364 5.364-1.414 1.414L4 12l7.778-7.778 1.414 1.414z" }),
602
+ "arrow-right-line": /* @__PURE__ */ jsx("path", { d: "M16.172 11l-5.364-5.364 1.414-1.414L20 12l-7.778 7.778-1.414-1.414L16.172 13H4v-2z" })
603
+ };
604
+ const iconPath = icons[name];
605
+ return /* @__PURE__ */ jsx(
606
+ "svg",
607
+ {
608
+ className,
609
+ width: sizeValue,
610
+ height: sizeValue,
611
+ viewBox: "0 0 24 24",
612
+ fill: color,
613
+ onClick,
614
+ "aria-label": ariaLabel,
615
+ role: onClick ? "button" : "img",
616
+ tabIndex: onClick ? 0 : void 0,
617
+ style: { display: "inline-block", verticalAlign: "middle", ...style },
618
+ children: iconPath || /* @__PURE__ */ jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z" })
619
+ }
620
+ );
621
+ };
622
+
623
+ // src/react/components/ChatSidebar.tsx
624
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
625
+ var formatDate = (timestamp) => {
626
+ const date = new Date(timestamp);
627
+ const now = /* @__PURE__ */ new Date();
628
+ const diff = now.getTime() - date.getTime();
629
+ const days = Math.floor(diff / (1e3 * 60 * 60 * 24));
630
+ if (days === 0) return "\uC624\uB298";
631
+ if (days === 1) return "\uC5B4\uC81C";
632
+ if (days < 7) return `${days}\uC77C \uC804`;
633
+ return date.toLocaleDateString("ko-KR");
634
+ };
635
+ var ChatSidebar = ({
636
+ sessions,
637
+ currentSessionId,
638
+ onSelectSession,
639
+ onNewSession,
640
+ onDeleteSession,
641
+ isOpen,
642
+ onToggle
643
+ }) => {
644
+ return /* @__PURE__ */ jsx2(
645
+ "aside",
646
+ {
647
+ className: `chatllm-sidebar ${isOpen ? "chatllm-sidebar--open" : "chatllm-sidebar--closed"}`,
648
+ style: {
649
+ width: isOpen ? "288px" : "0",
650
+ flexShrink: 0,
651
+ backgroundColor: "var(--chatllm-sidebar-bg, #ffffff)",
652
+ borderRight: "1px solid var(--chatllm-border, #e5e7eb)",
653
+ transition: "width 0.3s ease",
654
+ overflow: "hidden"
655
+ },
656
+ children: /* @__PURE__ */ jsxs("div", { style: { width: "288px", height: "100%", display: "flex", flexDirection: "column" }, children: [
657
+ /* @__PURE__ */ jsxs(
658
+ "div",
659
+ {
660
+ style: {
661
+ padding: "16px",
662
+ borderBottom: "1px solid var(--chatllm-border-light, #f3f4f6)"
663
+ },
664
+ children: [
665
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px", marginBottom: "16px" }, children: [
666
+ /* @__PURE__ */ jsx2(
667
+ "div",
668
+ {
669
+ style: {
670
+ width: "32px",
671
+ height: "32px",
672
+ borderRadius: "8px",
673
+ background: "linear-gradient(135deg, #3b82f6, #2563eb)",
674
+ display: "flex",
675
+ alignItems: "center",
676
+ justifyContent: "center"
677
+ },
678
+ children: /* @__PURE__ */ jsx2(IconSvg, { name: "chat-1-line", size: 20, color: "#ffffff" })
679
+ }
680
+ ),
681
+ /* @__PURE__ */ jsx2("span", { style: { fontWeight: 600, color: "var(--chatllm-text, #1f2937)" }, children: "ChatLLM" })
682
+ ] }),
683
+ /* @__PURE__ */ jsxs(
684
+ "button",
685
+ {
686
+ onClick: onNewSession,
687
+ style: {
688
+ width: "100%",
689
+ display: "flex",
690
+ alignItems: "center",
691
+ justifyContent: "center",
692
+ gap: "8px",
693
+ padding: "10px 16px",
694
+ backgroundColor: "var(--chatllm-primary, #3b82f6)",
695
+ color: "#ffffff",
696
+ border: "none",
697
+ borderRadius: "8px",
698
+ fontSize: "14px",
699
+ fontWeight: 500,
700
+ cursor: "pointer",
701
+ transition: "background-color 0.2s"
702
+ },
703
+ onMouseOver: (e) => {
704
+ e.currentTarget.style.backgroundColor = "var(--chatllm-primary-hover, #2563eb)";
705
+ },
706
+ onMouseOut: (e) => {
707
+ e.currentTarget.style.backgroundColor = "var(--chatllm-primary, #3b82f6)";
708
+ },
709
+ children: [
710
+ /* @__PURE__ */ jsx2(IconSvg, { name: "add-line", size: 18, color: "#ffffff" }),
711
+ "\uC0C8 \uB300\uD654"
712
+ ]
713
+ }
714
+ )
715
+ ]
716
+ }
717
+ ),
718
+ /* @__PURE__ */ jsx2("div", { style: { flex: 1, overflow: "auto", padding: "8px" }, children: sessions.length === 0 ? /* @__PURE__ */ jsx2(
719
+ "div",
720
+ {
721
+ style: {
722
+ padding: "24px 16px",
723
+ textAlign: "center",
724
+ color: "var(--chatllm-text-muted, #9ca3af)",
725
+ fontSize: "14px"
726
+ },
727
+ children: "\uB300\uD654 \uAE30\uB85D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4"
728
+ }
729
+ ) : sessions.map((session) => /* @__PURE__ */ jsx2(
730
+ "div",
731
+ {
732
+ onClick: () => onSelectSession(session.id),
733
+ style: {
734
+ padding: "12px",
735
+ marginBottom: "4px",
736
+ borderRadius: "8px",
737
+ backgroundColor: session.id === currentSessionId ? "var(--chatllm-bg-active, #eff6ff)" : "transparent",
738
+ cursor: "pointer",
739
+ transition: "background-color 0.2s"
740
+ },
741
+ onMouseOver: (e) => {
742
+ if (session.id !== currentSessionId) {
743
+ e.currentTarget.style.backgroundColor = "var(--chatllm-bg-hover, #f9fafb)";
744
+ }
745
+ },
746
+ onMouseOut: (e) => {
747
+ if (session.id !== currentSessionId) {
748
+ e.currentTarget.style.backgroundColor = "transparent";
749
+ }
750
+ },
751
+ children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-start" }, children: [
752
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
753
+ /* @__PURE__ */ jsx2(
754
+ "div",
755
+ {
756
+ style: {
757
+ fontWeight: session.id === currentSessionId ? 500 : 400,
758
+ color: "var(--chatllm-text, #1f2937)",
759
+ fontSize: "14px",
760
+ overflow: "hidden",
761
+ textOverflow: "ellipsis",
762
+ whiteSpace: "nowrap"
763
+ },
764
+ children: session.title
765
+ }
766
+ ),
767
+ /* @__PURE__ */ jsx2(
768
+ "div",
769
+ {
770
+ style: {
771
+ fontSize: "12px",
772
+ color: "var(--chatllm-text-muted, #9ca3af)",
773
+ marginTop: "4px"
774
+ },
775
+ children: formatDate(session.updatedAt)
776
+ }
777
+ )
778
+ ] }),
779
+ /* @__PURE__ */ jsx2(
780
+ "button",
781
+ {
782
+ onClick: (e) => {
783
+ e.stopPropagation();
784
+ onDeleteSession(session.id);
785
+ },
786
+ style: {
787
+ padding: "4px",
788
+ backgroundColor: "transparent",
789
+ border: "none",
790
+ borderRadius: "4px",
791
+ cursor: "pointer",
792
+ opacity: 0.5,
793
+ transition: "opacity 0.2s"
794
+ },
795
+ onMouseOver: (e) => {
796
+ e.currentTarget.style.opacity = "1";
797
+ },
798
+ onMouseOut: (e) => {
799
+ e.currentTarget.style.opacity = "0.5";
800
+ },
801
+ children: /* @__PURE__ */ jsx2(IconSvg, { name: "delete-bin-line", size: 16, color: "var(--chatllm-text-muted, #9ca3af)" })
802
+ }
803
+ )
804
+ ] })
805
+ },
806
+ session.id
807
+ )) }),
808
+ /* @__PURE__ */ jsx2(
809
+ "button",
810
+ {
811
+ onClick: onToggle,
812
+ style: {
813
+ position: "absolute",
814
+ top: "16px",
815
+ right: "-40px",
816
+ width: "32px",
817
+ height: "32px",
818
+ backgroundColor: "var(--chatllm-bg, #ffffff)",
819
+ border: "1px solid var(--chatllm-border, #e5e7eb)",
820
+ borderRadius: "8px",
821
+ display: "flex",
822
+ alignItems: "center",
823
+ justifyContent: "center",
824
+ cursor: "pointer"
825
+ },
826
+ children: /* @__PURE__ */ jsx2(IconSvg, { name: isOpen ? "arrow-left-line" : "menu-line", size: 18 })
827
+ }
828
+ )
829
+ ] })
830
+ }
831
+ );
832
+ };
833
+
834
+ // src/react/components/ChatHeader.tsx
835
+ import { useState as useState2 } from "react";
836
+ import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
837
+ var ChatHeader = ({
838
+ title,
839
+ model,
840
+ models,
841
+ onModelChange,
842
+ onSettingsOpen,
843
+ onSidebarToggle,
844
+ sidebarOpen
845
+ }) => {
846
+ const [modelDropdownOpen, setModelDropdownOpen] = useState2(false);
847
+ const currentModel = models.find((m) => m.id === model);
848
+ return /* @__PURE__ */ jsxs2(
849
+ "header",
850
+ {
851
+ className: "chatllm-header",
852
+ style: {
853
+ display: "flex",
854
+ alignItems: "center",
855
+ justifyContent: "space-between",
856
+ padding: "12px 24px",
857
+ borderBottom: "1px solid var(--chatllm-border, #e5e7eb)",
858
+ backgroundColor: "var(--chatllm-bg, #ffffff)"
859
+ },
860
+ children: [
861
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: "12px" }, children: [
862
+ /* @__PURE__ */ jsx3(
863
+ "button",
864
+ {
865
+ onClick: onSidebarToggle,
866
+ style: {
867
+ width: "36px",
868
+ height: "36px",
869
+ display: "flex",
870
+ alignItems: "center",
871
+ justifyContent: "center",
872
+ backgroundColor: "transparent",
873
+ border: "none",
874
+ borderRadius: "8px",
875
+ cursor: "pointer",
876
+ transition: "background-color 0.2s"
877
+ },
878
+ onMouseOver: (e) => {
879
+ e.currentTarget.style.backgroundColor = "var(--chatllm-bg-hover, #f3f4f6)";
880
+ },
881
+ onMouseOut: (e) => {
882
+ e.currentTarget.style.backgroundColor = "transparent";
883
+ },
884
+ children: /* @__PURE__ */ jsx3(IconSvg, { name: "menu-line", size: 20, color: "var(--chatllm-text, #1f2937)" })
885
+ }
886
+ ),
887
+ /* @__PURE__ */ jsx3(
888
+ "h1",
889
+ {
890
+ style: {
891
+ fontSize: "16px",
892
+ fontWeight: 500,
893
+ color: "var(--chatllm-text, #1f2937)",
894
+ margin: 0
895
+ },
896
+ children: title
897
+ }
898
+ )
899
+ ] }),
900
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
901
+ /* @__PURE__ */ jsxs2("div", { style: { position: "relative" }, children: [
902
+ /* @__PURE__ */ jsxs2(
903
+ "button",
904
+ {
905
+ onClick: () => setModelDropdownOpen(!modelDropdownOpen),
906
+ style: {
907
+ display: "flex",
908
+ alignItems: "center",
909
+ gap: "8px",
910
+ padding: "8px 12px",
911
+ backgroundColor: "var(--chatllm-bg-secondary, #f9fafb)",
912
+ border: "1px solid var(--chatllm-border, #e5e7eb)",
913
+ borderRadius: "8px",
914
+ cursor: "pointer",
915
+ transition: "all 0.2s"
916
+ },
917
+ onMouseOver: (e) => {
918
+ e.currentTarget.style.borderColor = "var(--chatllm-primary, #3b82f6)";
919
+ },
920
+ onMouseOut: (e) => {
921
+ e.currentTarget.style.borderColor = "var(--chatllm-border, #e5e7eb)";
922
+ },
923
+ children: [
924
+ /* @__PURE__ */ jsx3(IconSvg, { name: "robot-line", size: 16, color: "var(--chatllm-primary, #3b82f6)" }),
925
+ /* @__PURE__ */ jsx3("span", { style: { fontSize: "14px", fontWeight: 500, color: "var(--chatllm-text, #1f2937)" }, children: currentModel?.name || model }),
926
+ /* @__PURE__ */ jsx3(
927
+ IconSvg,
928
+ {
929
+ name: modelDropdownOpen ? "arrow-up-s-line" : "arrow-down-s-line",
930
+ size: 16,
931
+ color: "var(--chatllm-text-muted, #9ca3af)"
932
+ }
933
+ )
934
+ ]
935
+ }
936
+ ),
937
+ modelDropdownOpen && /* @__PURE__ */ jsxs2(Fragment, { children: [
938
+ /* @__PURE__ */ jsx3(
939
+ "div",
940
+ {
941
+ style: {
942
+ position: "fixed",
943
+ top: 0,
944
+ left: 0,
945
+ right: 0,
946
+ bottom: 0,
947
+ zIndex: 99
948
+ },
949
+ onClick: () => setModelDropdownOpen(false)
950
+ }
951
+ ),
952
+ /* @__PURE__ */ jsx3(
953
+ "div",
954
+ {
955
+ style: {
956
+ position: "absolute",
957
+ top: "100%",
958
+ right: 0,
959
+ marginTop: "8px",
960
+ backgroundColor: "var(--chatllm-bg, #ffffff)",
961
+ borderRadius: "12px",
962
+ boxShadow: "0 4px 24px rgba(0, 0, 0, 0.12)",
963
+ border: "1px solid var(--chatllm-border, #e5e7eb)",
964
+ padding: "8px",
965
+ minWidth: "220px",
966
+ zIndex: 100
967
+ },
968
+ children: Array.from(new Set(models.map((m) => m.provider))).map((provider) => /* @__PURE__ */ jsxs2("div", { children: [
969
+ /* @__PURE__ */ jsx3(
970
+ "div",
971
+ {
972
+ style: {
973
+ padding: "8px 12px",
974
+ fontSize: "11px",
975
+ fontWeight: 600,
976
+ color: "var(--chatllm-text-muted, #9ca3af)",
977
+ textTransform: "uppercase",
978
+ letterSpacing: "0.05em"
979
+ },
980
+ children: provider
981
+ }
982
+ ),
983
+ models.filter((m) => m.provider === provider).map((m) => /* @__PURE__ */ jsxs2(
984
+ "button",
985
+ {
986
+ onClick: () => {
987
+ onModelChange(m.id);
988
+ setModelDropdownOpen(false);
989
+ },
990
+ style: {
991
+ width: "100%",
992
+ display: "flex",
993
+ alignItems: "center",
994
+ gap: "10px",
995
+ padding: "10px 12px",
996
+ backgroundColor: m.id === model ? "var(--chatllm-bg-active, #eff6ff)" : "transparent",
997
+ border: "none",
998
+ borderRadius: "8px",
999
+ cursor: "pointer",
1000
+ textAlign: "left",
1001
+ transition: "background-color 0.2s"
1002
+ },
1003
+ onMouseOver: (e) => {
1004
+ if (m.id !== model) {
1005
+ e.currentTarget.style.backgroundColor = "var(--chatllm-bg-hover, #f9fafb)";
1006
+ }
1007
+ },
1008
+ onMouseOut: (e) => {
1009
+ if (m.id !== model) {
1010
+ e.currentTarget.style.backgroundColor = "transparent";
1011
+ }
1012
+ },
1013
+ children: [
1014
+ /* @__PURE__ */ jsx3(
1015
+ "div",
1016
+ {
1017
+ style: {
1018
+ width: "28px",
1019
+ height: "28px",
1020
+ borderRadius: "6px",
1021
+ backgroundColor: m.id === model ? "var(--chatllm-primary, #3b82f6)" : "var(--chatllm-bg-secondary, #f3f4f6)",
1022
+ display: "flex",
1023
+ alignItems: "center",
1024
+ justifyContent: "center"
1025
+ },
1026
+ children: /* @__PURE__ */ jsx3(
1027
+ IconSvg,
1028
+ {
1029
+ name: "robot-line",
1030
+ size: 14,
1031
+ color: m.id === model ? "#ffffff" : "var(--chatllm-text-muted, #9ca3af)"
1032
+ }
1033
+ )
1034
+ }
1035
+ ),
1036
+ /* @__PURE__ */ jsxs2("div", { children: [
1037
+ /* @__PURE__ */ jsx3(
1038
+ "div",
1039
+ {
1040
+ style: {
1041
+ fontSize: "14px",
1042
+ fontWeight: m.id === model ? 500 : 400,
1043
+ color: "var(--chatllm-text, #1f2937)"
1044
+ },
1045
+ children: m.name
1046
+ }
1047
+ ),
1048
+ m.description && /* @__PURE__ */ jsx3("div", { style: { fontSize: "12px", color: "var(--chatllm-text-muted, #9ca3af)" }, children: m.description })
1049
+ ] }),
1050
+ m.id === model && /* @__PURE__ */ jsx3(
1051
+ IconSvg,
1052
+ {
1053
+ name: "check-line",
1054
+ size: 16,
1055
+ color: "var(--chatllm-primary, #3b82f6)",
1056
+ className: "ml-auto"
1057
+ }
1058
+ )
1059
+ ]
1060
+ },
1061
+ m.id
1062
+ ))
1063
+ ] }, provider))
1064
+ }
1065
+ )
1066
+ ] })
1067
+ ] }),
1068
+ /* @__PURE__ */ jsx3(
1069
+ "button",
1070
+ {
1071
+ onClick: onSettingsOpen,
1072
+ style: {
1073
+ width: "36px",
1074
+ height: "36px",
1075
+ display: "flex",
1076
+ alignItems: "center",
1077
+ justifyContent: "center",
1078
+ backgroundColor: "transparent",
1079
+ border: "none",
1080
+ borderRadius: "8px",
1081
+ cursor: "pointer",
1082
+ transition: "background-color 0.2s"
1083
+ },
1084
+ onMouseOver: (e) => {
1085
+ e.currentTarget.style.backgroundColor = "var(--chatllm-bg-hover, #f3f4f6)";
1086
+ },
1087
+ onMouseOut: (e) => {
1088
+ e.currentTarget.style.backgroundColor = "transparent";
1089
+ },
1090
+ children: /* @__PURE__ */ jsx3(IconSvg, { name: "settings-3-line", size: 20, color: "var(--chatllm-text-muted, #6b7280)" })
1091
+ }
1092
+ )
1093
+ ] })
1094
+ ]
1095
+ }
1096
+ );
1097
+ };
1098
+
1099
+ // src/react/components/ChatInput.tsx
1100
+ import { useRef as useRef2, useEffect as useEffect2, useState as useState3 } from "react";
1101
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1102
+ var ChatInput = ({
1103
+ value,
1104
+ onChange,
1105
+ onSubmit,
1106
+ onStop,
1107
+ isLoading,
1108
+ placeholder = "\uBA54\uC2DC\uC9C0\uB97C \uC785\uB825\uD558\uC138\uC694...",
1109
+ quotedText,
1110
+ onClearQuote,
1111
+ selectedAction,
1112
+ onClearAction,
1113
+ onActionSelect,
1114
+ actions = []
1115
+ }) => {
1116
+ const textareaRef = useRef2(null);
1117
+ const [actionMenuOpen, setActionMenuOpen] = useState3(false);
1118
+ useEffect2(() => {
1119
+ if (textareaRef.current) {
1120
+ textareaRef.current.style.height = "auto";
1121
+ textareaRef.current.style.height = `${Math.min(textareaRef.current.scrollHeight, 200)}px`;
1122
+ }
1123
+ }, [value]);
1124
+ const handleKeyDown = (e) => {
1125
+ if (e.key === "Enter" && !e.shiftKey) {
1126
+ e.preventDefault();
1127
+ onSubmit();
1128
+ }
1129
+ };
1130
+ const handleActionSelect = (action) => {
1131
+ onActionSelect?.(action);
1132
+ setActionMenuOpen(false);
1133
+ };
1134
+ return /* @__PURE__ */ jsxs3(
1135
+ "div",
1136
+ {
1137
+ className: "chatllm-input-container",
1138
+ style: {
1139
+ padding: "16px 24px",
1140
+ borderTop: "1px solid var(--chatllm-border, #e5e7eb)",
1141
+ backgroundColor: "var(--chatllm-bg, #ffffff)"
1142
+ },
1143
+ children: [
1144
+ (quotedText || selectedAction) && /* @__PURE__ */ jsxs3("div", { style: { display: "flex", gap: "8px", marginBottom: "12px", flexWrap: "wrap" }, children: [
1145
+ quotedText && /* @__PURE__ */ jsxs3(
1146
+ "div",
1147
+ {
1148
+ style: {
1149
+ display: "flex",
1150
+ alignItems: "center",
1151
+ gap: "8px",
1152
+ padding: "8px 12px",
1153
+ backgroundColor: "var(--chatllm-bg-secondary, #f9fafb)",
1154
+ borderRadius: "8px",
1155
+ borderLeft: "3px solid var(--chatllm-primary, #3b82f6)",
1156
+ maxWidth: "100%"
1157
+ },
1158
+ children: [
1159
+ /* @__PURE__ */ jsx4(IconSvg, { name: "double-quotes-l", size: 14, color: "var(--chatllm-primary, #3b82f6)" }),
1160
+ /* @__PURE__ */ jsx4(
1161
+ "span",
1162
+ {
1163
+ style: {
1164
+ fontSize: "13px",
1165
+ color: "var(--chatllm-text, #1f2937)",
1166
+ overflow: "hidden",
1167
+ textOverflow: "ellipsis",
1168
+ whiteSpace: "nowrap",
1169
+ maxWidth: "300px"
1170
+ },
1171
+ children: quotedText
1172
+ }
1173
+ ),
1174
+ /* @__PURE__ */ jsx4(
1175
+ "button",
1176
+ {
1177
+ onClick: onClearQuote,
1178
+ style: {
1179
+ padding: "2px",
1180
+ backgroundColor: "transparent",
1181
+ border: "none",
1182
+ borderRadius: "4px",
1183
+ cursor: "pointer",
1184
+ display: "flex",
1185
+ alignItems: "center",
1186
+ justifyContent: "center"
1187
+ },
1188
+ children: /* @__PURE__ */ jsx4(IconSvg, { name: "close-line", size: 14, color: "var(--chatllm-text-muted, #9ca3af)" })
1189
+ }
1190
+ )
1191
+ ]
1192
+ }
1193
+ ),
1194
+ selectedAction && /* @__PURE__ */ jsxs3(
1195
+ "div",
1196
+ {
1197
+ style: {
1198
+ display: "flex",
1199
+ alignItems: "center",
1200
+ gap: "6px",
1201
+ padding: "6px 12px",
1202
+ backgroundColor: "var(--chatllm-primary-light, #dbeafe)",
1203
+ borderRadius: "16px",
1204
+ fontSize: "13px",
1205
+ fontWeight: 500,
1206
+ color: "var(--chatllm-primary, #3b82f6)"
1207
+ },
1208
+ children: [
1209
+ /* @__PURE__ */ jsx4(IconSvg, { name: "sparkling-line", size: 14, color: "var(--chatllm-primary, #3b82f6)" }),
1210
+ selectedAction.label,
1211
+ /* @__PURE__ */ jsx4(
1212
+ "button",
1213
+ {
1214
+ onClick: onClearAction,
1215
+ style: {
1216
+ padding: "2px",
1217
+ backgroundColor: "transparent",
1218
+ border: "none",
1219
+ borderRadius: "4px",
1220
+ cursor: "pointer",
1221
+ display: "flex",
1222
+ alignItems: "center",
1223
+ justifyContent: "center"
1224
+ },
1225
+ children: /* @__PURE__ */ jsx4(IconSvg, { name: "close-line", size: 14, color: "var(--chatllm-primary, #3b82f6)" })
1226
+ }
1227
+ )
1228
+ ]
1229
+ }
1230
+ )
1231
+ ] }),
1232
+ /* @__PURE__ */ jsxs3(
1233
+ "div",
1234
+ {
1235
+ style: {
1236
+ display: "flex",
1237
+ alignItems: "flex-end",
1238
+ gap: "12px",
1239
+ padding: "12px 16px",
1240
+ backgroundColor: "var(--chatllm-input-bg, #f9fafb)",
1241
+ borderRadius: "16px",
1242
+ border: "1px solid var(--chatllm-border, #e5e7eb)",
1243
+ transition: "border-color 0.2s, box-shadow 0.2s"
1244
+ },
1245
+ children: [
1246
+ actions.length > 0 && /* @__PURE__ */ jsxs3("div", { style: { position: "relative" }, children: [
1247
+ /* @__PURE__ */ jsx4(
1248
+ "button",
1249
+ {
1250
+ onClick: () => setActionMenuOpen(!actionMenuOpen),
1251
+ style: {
1252
+ width: "36px",
1253
+ height: "36px",
1254
+ display: "flex",
1255
+ alignItems: "center",
1256
+ justifyContent: "center",
1257
+ backgroundColor: "var(--chatllm-bg, #ffffff)",
1258
+ border: "1px solid var(--chatllm-border, #e5e7eb)",
1259
+ borderRadius: "8px",
1260
+ cursor: "pointer",
1261
+ transition: "background-color 0.2s"
1262
+ },
1263
+ onMouseOver: (e) => {
1264
+ e.currentTarget.style.backgroundColor = "var(--chatllm-bg-hover, #f3f4f6)";
1265
+ },
1266
+ onMouseOut: (e) => {
1267
+ e.currentTarget.style.backgroundColor = "var(--chatllm-bg, #ffffff)";
1268
+ },
1269
+ children: /* @__PURE__ */ jsx4(IconSvg, { name: "add-line", size: 20, color: "var(--chatllm-text, #1f2937)" })
1270
+ }
1271
+ ),
1272
+ actionMenuOpen && /* @__PURE__ */ jsx4(
1273
+ "div",
1274
+ {
1275
+ style: {
1276
+ position: "absolute",
1277
+ bottom: "100%",
1278
+ left: 0,
1279
+ marginBottom: "8px",
1280
+ backgroundColor: "var(--chatllm-bg, #ffffff)",
1281
+ borderRadius: "12px",
1282
+ boxShadow: "0 4px 24px rgba(0, 0, 0, 0.12)",
1283
+ border: "1px solid var(--chatllm-border, #e5e7eb)",
1284
+ padding: "8px",
1285
+ minWidth: "200px",
1286
+ zIndex: 100
1287
+ },
1288
+ children: actions.map((action) => /* @__PURE__ */ jsxs3(
1289
+ "button",
1290
+ {
1291
+ onClick: () => handleActionSelect(action),
1292
+ style: {
1293
+ width: "100%",
1294
+ display: "flex",
1295
+ alignItems: "center",
1296
+ gap: "12px",
1297
+ padding: "10px 12px",
1298
+ backgroundColor: "transparent",
1299
+ border: "none",
1300
+ borderRadius: "8px",
1301
+ cursor: "pointer",
1302
+ textAlign: "left",
1303
+ transition: "background-color 0.2s"
1304
+ },
1305
+ onMouseOver: (e) => {
1306
+ e.currentTarget.style.backgroundColor = "var(--chatllm-bg-hover, #f3f4f6)";
1307
+ },
1308
+ onMouseOut: (e) => {
1309
+ e.currentTarget.style.backgroundColor = "transparent";
1310
+ },
1311
+ children: [
1312
+ /* @__PURE__ */ jsx4(
1313
+ "div",
1314
+ {
1315
+ style: {
1316
+ width: "32px",
1317
+ height: "32px",
1318
+ display: "flex",
1319
+ alignItems: "center",
1320
+ justifyContent: "center",
1321
+ backgroundColor: "var(--chatllm-primary-light, #dbeafe)",
1322
+ borderRadius: "8px"
1323
+ },
1324
+ children: /* @__PURE__ */ jsx4(
1325
+ IconSvg,
1326
+ {
1327
+ name: action.icon === "search" ? "search-line" : action.icon === "image" ? "image-line" : action.icon === "code" ? "code-s-slash-line" : "file-text-line",
1328
+ size: 18,
1329
+ color: "var(--chatllm-primary, #3b82f6)"
1330
+ }
1331
+ )
1332
+ }
1333
+ ),
1334
+ /* @__PURE__ */ jsxs3("div", { children: [
1335
+ /* @__PURE__ */ jsx4("div", { style: { fontSize: "14px", fontWeight: 500, color: "var(--chatllm-text, #1f2937)" }, children: action.label }),
1336
+ /* @__PURE__ */ jsx4("div", { style: { fontSize: "12px", color: "var(--chatllm-text-muted, #9ca3af)" }, children: action.description })
1337
+ ] })
1338
+ ]
1339
+ },
1340
+ action.id
1341
+ ))
1342
+ }
1343
+ )
1344
+ ] }),
1345
+ /* @__PURE__ */ jsx4(
1346
+ "textarea",
1347
+ {
1348
+ ref: textareaRef,
1349
+ value,
1350
+ onChange: (e) => onChange(e.target.value),
1351
+ onKeyDown: handleKeyDown,
1352
+ placeholder,
1353
+ rows: 1,
1354
+ style: {
1355
+ flex: 1,
1356
+ minHeight: "24px",
1357
+ maxHeight: "200px",
1358
+ padding: "6px 0",
1359
+ backgroundColor: "transparent",
1360
+ border: "none",
1361
+ outline: "none",
1362
+ fontSize: "15px",
1363
+ lineHeight: "1.5",
1364
+ resize: "none",
1365
+ color: "var(--chatllm-text, #1f2937)"
1366
+ }
1367
+ }
1368
+ ),
1369
+ isLoading ? /* @__PURE__ */ jsx4(
1370
+ "button",
1371
+ {
1372
+ onClick: onStop,
1373
+ style: {
1374
+ width: "36px",
1375
+ height: "36px",
1376
+ display: "flex",
1377
+ alignItems: "center",
1378
+ justifyContent: "center",
1379
+ backgroundColor: "var(--chatllm-error, #ef4444)",
1380
+ border: "none",
1381
+ borderRadius: "8px",
1382
+ cursor: "pointer",
1383
+ transition: "background-color 0.2s"
1384
+ },
1385
+ children: /* @__PURE__ */ jsx4(IconSvg, { name: "stop-circle-line", size: 20, color: "#ffffff" })
1386
+ }
1387
+ ) : /* @__PURE__ */ jsx4(
1388
+ "button",
1389
+ {
1390
+ onClick: onSubmit,
1391
+ disabled: !value.trim(),
1392
+ style: {
1393
+ width: "36px",
1394
+ height: "36px",
1395
+ display: "flex",
1396
+ alignItems: "center",
1397
+ justifyContent: "center",
1398
+ backgroundColor: value.trim() ? "var(--chatllm-primary, #3b82f6)" : "var(--chatllm-bg-disabled, #e5e7eb)",
1399
+ border: "none",
1400
+ borderRadius: "8px",
1401
+ cursor: value.trim() ? "pointer" : "not-allowed",
1402
+ transition: "background-color 0.2s"
1403
+ },
1404
+ children: /* @__PURE__ */ jsx4(IconSvg, { name: "send-plane-fill", size: 18, color: value.trim() ? "#ffffff" : "#9ca3af" })
1405
+ }
1406
+ )
1407
+ ]
1408
+ }
1409
+ )
1410
+ ]
1411
+ }
1412
+ );
1413
+ };
1414
+
1415
+ // src/react/components/MessageList.tsx
1416
+ import { useRef as useRef3, useEffect as useEffect3, useCallback as useCallback2, useState as useState6 } from "react";
1417
+
1418
+ // src/react/components/MessageBubble.tsx
1419
+ import { useState as useState5 } from "react";
1420
+
1421
+ // src/react/components/MarkdownRenderer.tsx
1422
+ import React4, { useMemo } from "react";
1423
+
1424
+ // src/react/components/LinkChip.tsx
1425
+ import { useState as useState4 } from "react";
1426
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1427
+ var getDomain = (url) => {
1428
+ try {
1429
+ const urlObj = new URL(url);
1430
+ return urlObj.hostname.replace("www.", "");
1431
+ } catch {
1432
+ return url;
1433
+ }
1434
+ };
1435
+ var getShortName = (domain) => {
1436
+ const parts = domain.split(".");
1437
+ if (parts.length >= 2) {
1438
+ return parts[parts.length - 2];
1439
+ }
1440
+ return domain;
1441
+ };
1442
+ var getDomainColor = (domain) => {
1443
+ const lowerDomain = domain.toLowerCase();
1444
+ const colorMap = {
1445
+ "google": "#4285f4",
1446
+ "wikipedia": "#000000",
1447
+ "github": "#24292e",
1448
+ "stackoverflow": "#f48024",
1449
+ "medium": "#00ab6c",
1450
+ "youtube": "#ff0000",
1451
+ "twitter": "#1da1f2",
1452
+ "naver": "#03c75a",
1453
+ "namu": "#00a495",
1454
+ "tistory": "#eb531f",
1455
+ "velog": "#20c997",
1456
+ "brave": "#fb542b",
1457
+ "mk": "#0066cc",
1458
+ "ko": "#3366cc"
1459
+ };
1460
+ for (const [key, color] of Object.entries(colorMap)) {
1461
+ if (lowerDomain.includes(key)) {
1462
+ return color;
1463
+ }
1464
+ }
1465
+ let hash = 0;
1466
+ for (let i = 0; i < domain.length; i++) {
1467
+ hash = domain.charCodeAt(i) + ((hash << 5) - hash);
1468
+ }
1469
+ const hue = hash % 360;
1470
+ return `hsl(${hue}, 60%, 45%)`;
1471
+ };
1472
+ var LinkChip = ({
1473
+ text,
1474
+ url,
1475
+ showFavicon = true,
1476
+ index,
1477
+ style
1478
+ }) => {
1479
+ const [isHovered, setIsHovered] = useState4(false);
1480
+ const domain = getDomain(url);
1481
+ const shortName = getShortName(domain);
1482
+ const domainColor = getDomainColor(domain);
1483
+ const parseText = (t) => {
1484
+ const match = t.match(/^(\d+)\.\s*(.+)$/);
1485
+ if (match) {
1486
+ return { number: match[1], label: match[2] };
1487
+ }
1488
+ return { label: t };
1489
+ };
1490
+ const parsed = parseText(text);
1491
+ const displayNumber = index !== void 0 ? String(index + 1) : parsed.number;
1492
+ const displayLabel = parsed.label;
1493
+ return /* @__PURE__ */ jsxs4(
1494
+ "a",
1495
+ {
1496
+ href: url,
1497
+ target: "_blank",
1498
+ rel: "noopener noreferrer",
1499
+ onMouseEnter: () => setIsHovered(true),
1500
+ onMouseLeave: () => setIsHovered(false),
1501
+ style: {
1502
+ display: "inline-flex",
1503
+ alignItems: "center",
1504
+ gap: "6px",
1505
+ padding: "4px 10px",
1506
+ backgroundColor: isHovered ? "var(--chatllm-chip-bg-hover, #e2e8f0)" : "var(--chatllm-chip-bg, #f1f5f9)",
1507
+ border: "1px solid var(--chatllm-chip-border, #e2e8f0)",
1508
+ borderRadius: "16px",
1509
+ textDecoration: "none",
1510
+ fontSize: "13px",
1511
+ fontWeight: 500,
1512
+ color: "var(--chatllm-chip-text, #475569)",
1513
+ transition: "all 0.15s ease",
1514
+ cursor: "pointer",
1515
+ maxWidth: "180px",
1516
+ ...style
1517
+ },
1518
+ title: `${displayLabel} - ${domain}`,
1519
+ children: [
1520
+ displayNumber && /* @__PURE__ */ jsx5(
1521
+ "span",
1522
+ {
1523
+ style: {
1524
+ display: "flex",
1525
+ alignItems: "center",
1526
+ justifyContent: "center",
1527
+ width: "18px",
1528
+ height: "18px",
1529
+ borderRadius: "50%",
1530
+ backgroundColor: domainColor,
1531
+ color: "#ffffff",
1532
+ fontSize: "11px",
1533
+ fontWeight: 600,
1534
+ flexShrink: 0
1535
+ },
1536
+ children: displayNumber
1537
+ }
1538
+ ),
1539
+ showFavicon && !displayNumber && /* @__PURE__ */ jsx5(
1540
+ "span",
1541
+ {
1542
+ style: {
1543
+ width: "16px",
1544
+ height: "16px",
1545
+ display: "flex",
1546
+ alignItems: "center",
1547
+ justifyContent: "center",
1548
+ flexShrink: 0
1549
+ },
1550
+ children: /* @__PURE__ */ jsx5(
1551
+ IconSvg,
1552
+ {
1553
+ name: "links-line",
1554
+ size: 14,
1555
+ color: domainColor
1556
+ }
1557
+ )
1558
+ }
1559
+ ),
1560
+ /* @__PURE__ */ jsx5(
1561
+ "span",
1562
+ {
1563
+ style: {
1564
+ overflow: "hidden",
1565
+ textOverflow: "ellipsis",
1566
+ whiteSpace: "nowrap"
1567
+ },
1568
+ children: displayLabel
1569
+ }
1570
+ ),
1571
+ /* @__PURE__ */ jsx5(
1572
+ IconSvg,
1573
+ {
1574
+ name: "external-link-line",
1575
+ size: 12,
1576
+ color: "var(--chatllm-text-muted, #94a3b8)",
1577
+ style: {
1578
+ opacity: isHovered ? 1 : 0.6,
1579
+ flexShrink: 0
1580
+ }
1581
+ }
1582
+ )
1583
+ ]
1584
+ }
1585
+ );
1586
+ };
1587
+
1588
+ // src/react/components/MarkdownRenderer.tsx
1589
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1590
+ var LINK_REGEX = /\[([^\]]+)\]\(([^)]+)\)/g;
1591
+ var SOURCE_LINKS_REGEX = /(\*{0,2}출처:?\*{0,2}\s*)?((?:\[`?[^\]]+`?\]\([^)]+\)\s*)+)/gi;
1592
+ var CODE_BLOCK_REGEX = /```(\w*)\n?([\s\S]*?)```/g;
1593
+ var INLINE_CODE_REGEX = /`([^`]+)`/g;
1594
+ var BOLD_REGEX = /\*\*([^*]+)\*\*/g;
1595
+ var ITALIC_REGEX = /(?<!\*)\*([^*]+)\*(?!\*)/g;
1596
+ var HR_REGEX = /^---+$/gm;
1597
+ var parseSourceLinks = (text) => {
1598
+ const links = [];
1599
+ let match;
1600
+ const linkRegex = /\[`?([^\]`]+)`?\]\(([^)]+)\)/g;
1601
+ while ((match = linkRegex.exec(text)) !== null) {
1602
+ links.push({
1603
+ text: match[1],
1604
+ url: match[2]
1605
+ });
1606
+ }
1607
+ return links;
1608
+ };
1609
+ var parseInlineElements = (text, key) => {
1610
+ const elements = [];
1611
+ let lastIndex = 0;
1612
+ let currentText = text;
1613
+ currentText = currentText.replace(INLINE_CODE_REGEX, "\xA7CODE\xA7$1\xA7/CODE\xA7");
1614
+ currentText = currentText.replace(BOLD_REGEX, "\xA7BOLD\xA7$1\xA7/BOLD\xA7");
1615
+ currentText = currentText.replace(ITALIC_REGEX, "\xA7ITALIC\xA7$1\xA7/ITALIC\xA7");
1616
+ currentText = currentText.replace(LINK_REGEX, "\xA7LINK\xA7$1\xA7URL\xA7$2\xA7/LINK\xA7");
1617
+ const parts = currentText.split(/(§CODE§.*?§\/CODE§|§BOLD§.*?§\/BOLD§|§ITALIC§.*?§\/ITALIC§|§LINK§.*?§\/LINK§)/);
1618
+ parts.forEach((part, index) => {
1619
+ if (part.startsWith("\xA7CODE\xA7")) {
1620
+ const content = part.replace("\xA7CODE\xA7", "").replace("\xA7/CODE\xA7", "");
1621
+ elements.push(
1622
+ /* @__PURE__ */ jsx6(
1623
+ "code",
1624
+ {
1625
+ style: {
1626
+ backgroundColor: "var(--chatllm-code-bg, #f3f4f6)",
1627
+ padding: "2px 6px",
1628
+ borderRadius: "4px",
1629
+ fontSize: "0.9em",
1630
+ fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
1631
+ color: "var(--chatllm-code-text, #e11d48)"
1632
+ },
1633
+ children: content
1634
+ },
1635
+ `${key}-code-${index}`
1636
+ )
1637
+ );
1638
+ } else if (part.startsWith("\xA7BOLD\xA7")) {
1639
+ const content = part.replace("\xA7BOLD\xA7", "").replace("\xA7/BOLD\xA7", "");
1640
+ elements.push(/* @__PURE__ */ jsx6("strong", { children: content }, `${key}-bold-${index}`));
1641
+ } else if (part.startsWith("\xA7ITALIC\xA7")) {
1642
+ const content = part.replace("\xA7ITALIC\xA7", "").replace("\xA7/ITALIC\xA7", "");
1643
+ elements.push(/* @__PURE__ */ jsx6("em", { children: content }, `${key}-italic-${index}`));
1644
+ } else if (part.startsWith("\xA7LINK\xA7")) {
1645
+ const match = part.match(/§LINK§(.+?)§URL§(.+?)§\/LINK§/);
1646
+ if (match) {
1647
+ elements.push(
1648
+ /* @__PURE__ */ jsx6(
1649
+ "a",
1650
+ {
1651
+ href: match[2],
1652
+ target: "_blank",
1653
+ rel: "noopener noreferrer",
1654
+ style: {
1655
+ color: "var(--chatllm-link, #3b82f6)",
1656
+ textDecoration: "none"
1657
+ },
1658
+ children: match[1]
1659
+ },
1660
+ `${key}-link-${index}`
1661
+ )
1662
+ );
1663
+ }
1664
+ } else if (part) {
1665
+ elements.push(part);
1666
+ }
1667
+ });
1668
+ return elements;
1669
+ };
1670
+ var CodeBlock = ({ language, code }) => {
1671
+ const [copied, setCopied] = React4.useState(false);
1672
+ const handleCopy = async () => {
1673
+ try {
1674
+ await navigator.clipboard.writeText(code);
1675
+ setCopied(true);
1676
+ setTimeout(() => setCopied(false), 2e3);
1677
+ } catch (e) {
1678
+ console.error("Failed to copy");
1679
+ }
1680
+ };
1681
+ return /* @__PURE__ */ jsxs5(
1682
+ "div",
1683
+ {
1684
+ style: {
1685
+ position: "relative",
1686
+ margin: "12px 0",
1687
+ borderRadius: "8px",
1688
+ overflow: "hidden",
1689
+ backgroundColor: "var(--chatllm-code-block-bg, #1f2937)"
1690
+ },
1691
+ children: [
1692
+ /* @__PURE__ */ jsxs5(
1693
+ "div",
1694
+ {
1695
+ style: {
1696
+ display: "flex",
1697
+ justifyContent: "space-between",
1698
+ alignItems: "center",
1699
+ padding: "8px 12px",
1700
+ backgroundColor: "var(--chatllm-code-block-header, #374151)",
1701
+ borderBottom: "1px solid var(--chatllm-code-block-border, #4b5563)"
1702
+ },
1703
+ children: [
1704
+ /* @__PURE__ */ jsx6(
1705
+ "span",
1706
+ {
1707
+ style: {
1708
+ fontSize: "12px",
1709
+ color: "var(--chatllm-code-block-lang, #9ca3af)",
1710
+ textTransform: "lowercase"
1711
+ },
1712
+ children: language || "code"
1713
+ }
1714
+ ),
1715
+ /* @__PURE__ */ jsx6(
1716
+ "button",
1717
+ {
1718
+ onClick: handleCopy,
1719
+ style: {
1720
+ padding: "4px 8px",
1721
+ fontSize: "12px",
1722
+ backgroundColor: "transparent",
1723
+ border: "1px solid var(--chatllm-code-block-border, #4b5563)",
1724
+ borderRadius: "4px",
1725
+ color: "var(--chatllm-code-block-text, #e5e7eb)",
1726
+ cursor: "pointer"
1727
+ },
1728
+ children: copied ? "\uBCF5\uC0AC\uB428!" : "\uBCF5\uC0AC"
1729
+ }
1730
+ )
1731
+ ]
1732
+ }
1733
+ ),
1734
+ /* @__PURE__ */ jsx6(
1735
+ "pre",
1736
+ {
1737
+ style: {
1738
+ margin: 0,
1739
+ padding: "16px",
1740
+ overflow: "auto",
1741
+ fontSize: "13px",
1742
+ lineHeight: "1.6",
1743
+ fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
1744
+ color: "var(--chatllm-code-block-text, #e5e7eb)"
1745
+ },
1746
+ children: /* @__PURE__ */ jsx6("code", { children: code.trim() })
1747
+ }
1748
+ )
1749
+ ]
1750
+ }
1751
+ );
1752
+ };
1753
+ var SourceLinksSection = ({ links, label }) => {
1754
+ return /* @__PURE__ */ jsxs5(
1755
+ "div",
1756
+ {
1757
+ style: {
1758
+ display: "flex",
1759
+ flexWrap: "wrap",
1760
+ alignItems: "center",
1761
+ gap: "8px",
1762
+ margin: "12px 0",
1763
+ padding: "12px",
1764
+ backgroundColor: "var(--chatllm-source-bg, #f8fafc)",
1765
+ borderRadius: "8px",
1766
+ border: "1px solid var(--chatllm-border-light, #e2e8f0)"
1767
+ },
1768
+ children: [
1769
+ label && /* @__PURE__ */ jsx6(
1770
+ "span",
1771
+ {
1772
+ style: {
1773
+ fontSize: "13px",
1774
+ fontWeight: 500,
1775
+ color: "var(--chatllm-text-muted, #64748b)",
1776
+ marginRight: "4px"
1777
+ },
1778
+ children: label
1779
+ }
1780
+ ),
1781
+ links.map((link, index) => /* @__PURE__ */ jsx6(LinkChip, { text: link.text, url: link.url }, index))
1782
+ ]
1783
+ }
1784
+ );
1785
+ };
1786
+ var MarkdownRenderer = ({ content, className }) => {
1787
+ const rendered = useMemo(() => {
1788
+ const elements = [];
1789
+ let processedContent = content;
1790
+ const codeBlocks = [];
1791
+ processedContent = processedContent.replace(CODE_BLOCK_REGEX, (_, lang, code) => {
1792
+ codeBlocks.push({ language: lang || "", code });
1793
+ return `\xA7CODEBLOCK\xA7${codeBlocks.length - 1}\xA7/CODEBLOCK\xA7`;
1794
+ });
1795
+ const sourceSections = [];
1796
+ processedContent = processedContent.replace(SOURCE_LINKS_REGEX, (match, label, linksText) => {
1797
+ const links = parseSourceLinks(linksText);
1798
+ if (links.length > 0) {
1799
+ sourceSections.push({ label: label?.replace(/\*+/g, "").trim() || "\uCD9C\uCC98", links });
1800
+ return `\xA7SOURCES\xA7${sourceSections.length - 1}\xA7/SOURCES\xA7`;
1801
+ }
1802
+ return match;
1803
+ });
1804
+ const lines = processedContent.split("\n");
1805
+ let currentList = null;
1806
+ let blockquoteLines = [];
1807
+ const flushList = () => {
1808
+ if (currentList) {
1809
+ if (currentList.type === "ul") {
1810
+ elements.push(
1811
+ /* @__PURE__ */ jsx6("ul", { style: { margin: "8px 0", paddingLeft: "24px" }, children: currentList.items }, `ul-${elements.length}`)
1812
+ );
1813
+ } else {
1814
+ elements.push(
1815
+ /* @__PURE__ */ jsx6("ol", { style: { margin: "8px 0", paddingLeft: "24px" }, children: currentList.items }, `ol-${elements.length}`)
1816
+ );
1817
+ }
1818
+ currentList = null;
1819
+ }
1820
+ };
1821
+ const flushBlockquote = () => {
1822
+ if (blockquoteLines.length > 0) {
1823
+ elements.push(
1824
+ /* @__PURE__ */ jsx6(
1825
+ "blockquote",
1826
+ {
1827
+ style: {
1828
+ margin: "12px 0",
1829
+ padding: "12px 16px",
1830
+ borderLeft: "4px solid var(--chatllm-primary, #3b82f6)",
1831
+ backgroundColor: "var(--chatllm-bg-secondary, #f9fafb)",
1832
+ borderRadius: "0 8px 8px 0",
1833
+ color: "var(--chatllm-text, #374151)"
1834
+ },
1835
+ children: blockquoteLines.map((line, i) => /* @__PURE__ */ jsxs5(React4.Fragment, { children: [
1836
+ parseInlineElements(line, `bq-line-${i}`),
1837
+ i < blockquoteLines.length - 1 && /* @__PURE__ */ jsx6("br", {})
1838
+ ] }, i))
1839
+ },
1840
+ `bq-${elements.length}`
1841
+ )
1842
+ );
1843
+ blockquoteLines = [];
1844
+ }
1845
+ };
1846
+ lines.forEach((line, lineIndex) => {
1847
+ const codeBlockMatch = line.match(/§CODEBLOCK§(\d+)§\/CODEBLOCK§/);
1848
+ if (codeBlockMatch) {
1849
+ flushList();
1850
+ flushBlockquote();
1851
+ const index = parseInt(codeBlockMatch[1]);
1852
+ elements.push(
1853
+ /* @__PURE__ */ jsx6(CodeBlock, { ...codeBlocks[index] }, `codeblock-${lineIndex}`)
1854
+ );
1855
+ return;
1856
+ }
1857
+ const sourcesMatch = line.match(/§SOURCES§(\d+)§\/SOURCES§/);
1858
+ if (sourcesMatch) {
1859
+ flushList();
1860
+ flushBlockquote();
1861
+ const index = parseInt(sourcesMatch[1]);
1862
+ elements.push(
1863
+ /* @__PURE__ */ jsx6(SourceLinksSection, { ...sourceSections[index] }, `sources-${lineIndex}`)
1864
+ );
1865
+ return;
1866
+ }
1867
+ if (HR_REGEX.test(line)) {
1868
+ flushList();
1869
+ flushBlockquote();
1870
+ elements.push(
1871
+ /* @__PURE__ */ jsx6(
1872
+ "hr",
1873
+ {
1874
+ style: {
1875
+ margin: "16px 0",
1876
+ border: "none",
1877
+ borderTop: "1px solid var(--chatllm-border, #e5e7eb)"
1878
+ }
1879
+ },
1880
+ `hr-${lineIndex}`
1881
+ )
1882
+ );
1883
+ return;
1884
+ }
1885
+ const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
1886
+ if (headingMatch) {
1887
+ flushList();
1888
+ flushBlockquote();
1889
+ const level = headingMatch[1].length;
1890
+ const text = headingMatch[2];
1891
+ const HeadingTag = `h${level}`;
1892
+ const sizes = {
1893
+ 1: "1.5em",
1894
+ 2: "1.3em",
1895
+ 3: "1.15em",
1896
+ 4: "1.05em",
1897
+ 5: "1em",
1898
+ 6: "0.95em"
1899
+ };
1900
+ elements.push(
1901
+ /* @__PURE__ */ jsx6(
1902
+ HeadingTag,
1903
+ {
1904
+ style: {
1905
+ fontSize: sizes[level],
1906
+ fontWeight: level <= 2 ? 600 : 500,
1907
+ margin: "16px 0 8px",
1908
+ color: "var(--chatllm-text, #1f2937)"
1909
+ },
1910
+ children: parseInlineElements(text, `heading-${lineIndex}`)
1911
+ },
1912
+ `heading-${lineIndex}`
1913
+ )
1914
+ );
1915
+ return;
1916
+ }
1917
+ const blockquoteMatch = line.match(/^>\s*(.*)$/);
1918
+ if (blockquoteMatch) {
1919
+ flushList();
1920
+ blockquoteLines.push(blockquoteMatch[1]);
1921
+ return;
1922
+ } else {
1923
+ flushBlockquote();
1924
+ }
1925
+ const ulMatch = line.match(/^[\-\*]\s+(.+)$/);
1926
+ if (ulMatch) {
1927
+ flushBlockquote();
1928
+ if (!currentList || currentList.type !== "ul") {
1929
+ flushList();
1930
+ currentList = { type: "ul", items: [] };
1931
+ }
1932
+ currentList.items.push(
1933
+ /* @__PURE__ */ jsx6("li", { style: { margin: "4px 0" }, children: parseInlineElements(ulMatch[1], `li-${lineIndex}`) }, `li-${lineIndex}`)
1934
+ );
1935
+ return;
1936
+ }
1937
+ const olMatch = line.match(/^(\d+)\.\s+(.+)$/);
1938
+ if (olMatch) {
1939
+ flushBlockquote();
1940
+ if (!currentList || currentList.type !== "ol") {
1941
+ flushList();
1942
+ currentList = { type: "ol", items: [] };
1943
+ }
1944
+ currentList.items.push(
1945
+ /* @__PURE__ */ jsx6("li", { style: { margin: "4px 0" }, children: parseInlineElements(olMatch[2], `li-${lineIndex}`) }, `li-${lineIndex}`)
1946
+ );
1947
+ return;
1948
+ }
1949
+ flushList();
1950
+ if (!line.trim()) {
1951
+ elements.push(/* @__PURE__ */ jsx6("br", {}, `br-${lineIndex}`));
1952
+ return;
1953
+ }
1954
+ elements.push(
1955
+ /* @__PURE__ */ jsx6("p", { style: { margin: "4px 0" }, children: parseInlineElements(line, `p-${lineIndex}`) }, `p-${lineIndex}`)
1956
+ );
1957
+ });
1958
+ flushList();
1959
+ flushBlockquote();
1960
+ return elements;
1961
+ }, [content]);
1962
+ return /* @__PURE__ */ jsx6(
1963
+ "div",
1964
+ {
1965
+ className: `chatllm-markdown ${className || ""}`,
1966
+ style: {
1967
+ fontSize: "15px",
1968
+ lineHeight: "1.7",
1969
+ color: "var(--chatllm-text, #374151)"
1970
+ },
1971
+ children: rendered
1972
+ }
1973
+ );
1974
+ };
1975
+
1976
+ // src/react/components/MessageBubble.tsx
1977
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1978
+ var MessageBubble = ({
1979
+ message,
1980
+ isLoading,
1981
+ isCopied,
1982
+ isEditing,
1983
+ onCopy,
1984
+ onEdit,
1985
+ onRegenerate,
1986
+ onQuote,
1987
+ alternatives,
1988
+ activeAlternativeIndex = 0,
1989
+ onAlternativeChange
1990
+ }) => {
1991
+ const [showActions, setShowActions] = useState5(false);
1992
+ const isUser = message.role === "user";
1993
+ const isAssistant = message.role === "assistant";
1994
+ const displayContent = alternatives && alternatives.length > 0 && activeAlternativeIndex > 0 ? alternatives[activeAlternativeIndex - 1]?.content || message.content : message.content;
1995
+ const displayModel = alternatives && alternatives.length > 0 && activeAlternativeIndex > 0 ? alternatives[activeAlternativeIndex - 1]?.model : message.model;
1996
+ const handleMouseUp = () => {
1997
+ if (!onQuote) return;
1998
+ const selection = window.getSelection();
1999
+ const text = selection?.toString().trim();
2000
+ if (text && text.length > 0) {
2001
+ }
2002
+ };
2003
+ return /* @__PURE__ */ jsxs6(
2004
+ "div",
2005
+ {
2006
+ className: `chatllm-message chatllm-message--${message.role}`,
2007
+ style: {
2008
+ display: "flex",
2009
+ gap: "12px",
2010
+ padding: "16px 24px",
2011
+ backgroundColor: isUser ? "transparent" : "var(--chatllm-bg-secondary, #f9fafb)"
2012
+ },
2013
+ onMouseEnter: () => setShowActions(true),
2014
+ onMouseLeave: () => setShowActions(false),
2015
+ onMouseUp: handleMouseUp,
2016
+ children: [
2017
+ /* @__PURE__ */ jsx7(
2018
+ "div",
2019
+ {
2020
+ style: {
2021
+ width: "32px",
2022
+ height: "32px",
2023
+ borderRadius: "8px",
2024
+ backgroundColor: isUser ? "var(--chatllm-user-avatar, #e5e7eb)" : "var(--chatllm-assistant-avatar, #dbeafe)",
2025
+ display: "flex",
2026
+ alignItems: "center",
2027
+ justifyContent: "center",
2028
+ flexShrink: 0
2029
+ },
2030
+ children: /* @__PURE__ */ jsx7(
2031
+ IconSvg,
2032
+ {
2033
+ name: isUser ? "user-3-line" : "robot-line",
2034
+ size: 18,
2035
+ color: isUser ? "var(--chatllm-text-muted, #6b7280)" : "var(--chatllm-primary, #3b82f6)"
2036
+ }
2037
+ )
2038
+ }
2039
+ ),
2040
+ /* @__PURE__ */ jsxs6("div", { style: { flex: 1, minWidth: 0 }, children: [
2041
+ /* @__PURE__ */ jsxs6(
2042
+ "div",
2043
+ {
2044
+ style: {
2045
+ display: "flex",
2046
+ alignItems: "center",
2047
+ gap: "8px",
2048
+ marginBottom: "8px"
2049
+ },
2050
+ children: [
2051
+ /* @__PURE__ */ jsx7(
2052
+ "span",
2053
+ {
2054
+ style: {
2055
+ fontSize: "14px",
2056
+ fontWeight: 500,
2057
+ color: "var(--chatllm-text, #1f2937)"
2058
+ },
2059
+ children: isUser ? "\uB098" : "AI"
2060
+ }
2061
+ ),
2062
+ displayModel && /* @__PURE__ */ jsx7(
2063
+ "span",
2064
+ {
2065
+ style: {
2066
+ fontSize: "12px",
2067
+ padding: "2px 8px",
2068
+ backgroundColor: "var(--chatllm-bg-tertiary, #f3f4f6)",
2069
+ borderRadius: "4px",
2070
+ color: "var(--chatllm-text-muted, #6b7280)"
2071
+ },
2072
+ children: displayModel
2073
+ }
2074
+ )
2075
+ ]
2076
+ }
2077
+ ),
2078
+ /* @__PURE__ */ jsxs6(
2079
+ "div",
2080
+ {
2081
+ style: {
2082
+ wordBreak: "break-word"
2083
+ },
2084
+ children: [
2085
+ isAssistant ? (
2086
+ // AI 메시지는 마크다운 렌더링
2087
+ /* @__PURE__ */ jsx7(MarkdownRenderer, { content: displayContent })
2088
+ ) : (
2089
+ // 사용자 메시지는 일반 텍스트
2090
+ /* @__PURE__ */ jsx7(
2091
+ "div",
2092
+ {
2093
+ style: {
2094
+ fontSize: "15px",
2095
+ lineHeight: "1.7",
2096
+ color: "var(--chatllm-text, #374151)",
2097
+ whiteSpace: "pre-wrap"
2098
+ },
2099
+ children: displayContent
2100
+ }
2101
+ )
2102
+ ),
2103
+ isLoading && isAssistant && !displayContent && /* @__PURE__ */ jsxs6(
2104
+ "span",
2105
+ {
2106
+ style: {
2107
+ display: "inline-flex",
2108
+ gap: "4px"
2109
+ },
2110
+ children: [
2111
+ /* @__PURE__ */ jsx7("span", { className: "chatllm-typing-dot", style: dotStyle }),
2112
+ /* @__PURE__ */ jsx7("span", { className: "chatllm-typing-dot", style: { ...dotStyle, animationDelay: "0.2s" } }),
2113
+ /* @__PURE__ */ jsx7("span", { className: "chatllm-typing-dot", style: { ...dotStyle, animationDelay: "0.4s" } })
2114
+ ]
2115
+ }
2116
+ )
2117
+ ]
2118
+ }
2119
+ ),
2120
+ alternatives && alternatives.length > 0 && /* @__PURE__ */ jsxs6(
2121
+ "div",
2122
+ {
2123
+ style: {
2124
+ display: "flex",
2125
+ alignItems: "center",
2126
+ gap: "8px",
2127
+ marginTop: "12px",
2128
+ paddingTop: "12px",
2129
+ borderTop: "1px solid var(--chatllm-border-light, #f3f4f6)"
2130
+ },
2131
+ children: [
2132
+ /* @__PURE__ */ jsx7(
2133
+ "button",
2134
+ {
2135
+ onClick: () => onAlternativeChange?.(Math.max(0, activeAlternativeIndex - 1)),
2136
+ disabled: activeAlternativeIndex === 0,
2137
+ style: {
2138
+ ...navButtonStyle,
2139
+ opacity: activeAlternativeIndex === 0 ? 0.5 : 1,
2140
+ cursor: activeAlternativeIndex === 0 ? "not-allowed" : "pointer"
2141
+ },
2142
+ children: /* @__PURE__ */ jsx7(IconSvg, { name: "arrow-left-line", size: 14 })
2143
+ }
2144
+ ),
2145
+ /* @__PURE__ */ jsxs6("span", { style: { fontSize: "12px", color: "var(--chatllm-text-muted, #9ca3af)" }, children: [
2146
+ activeAlternativeIndex + 1,
2147
+ " / ",
2148
+ alternatives.length + 1
2149
+ ] }),
2150
+ /* @__PURE__ */ jsx7(
2151
+ "button",
2152
+ {
2153
+ onClick: () => onAlternativeChange?.(Math.min(alternatives.length, activeAlternativeIndex + 1)),
2154
+ disabled: activeAlternativeIndex === alternatives.length,
2155
+ style: {
2156
+ ...navButtonStyle,
2157
+ opacity: activeAlternativeIndex === alternatives.length ? 0.5 : 1,
2158
+ cursor: activeAlternativeIndex === alternatives.length ? "not-allowed" : "pointer"
2159
+ },
2160
+ children: /* @__PURE__ */ jsx7(IconSvg, { name: "arrow-right-line", size: 14 })
2161
+ }
2162
+ )
2163
+ ]
2164
+ }
2165
+ ),
2166
+ showActions && !isLoading && /* @__PURE__ */ jsxs6(
2167
+ "div",
2168
+ {
2169
+ style: {
2170
+ display: "flex",
2171
+ gap: "4px",
2172
+ marginTop: "12px"
2173
+ },
2174
+ children: [
2175
+ /* @__PURE__ */ jsx7("button", { onClick: onCopy, style: actionButtonStyle, title: "\uBCF5\uC0AC", children: /* @__PURE__ */ jsx7(
2176
+ IconSvg,
2177
+ {
2178
+ name: isCopied ? "check-line" : "file-copy-line",
2179
+ size: 16,
2180
+ color: isCopied ? "var(--chatllm-success, #22c55e)" : "var(--chatllm-text-muted, #9ca3af)"
2181
+ }
2182
+ ) }),
2183
+ isUser && /* @__PURE__ */ jsx7("button", { onClick: onEdit, style: actionButtonStyle, title: "\uC218\uC815", children: /* @__PURE__ */ jsx7(IconSvg, { name: "edit-line", size: 16, color: "var(--chatllm-text-muted, #9ca3af)" }) }),
2184
+ isAssistant && onRegenerate && /* @__PURE__ */ jsx7("button", { onClick: onRegenerate, style: actionButtonStyle, title: "\uB2E4\uC2DC \uC0DD\uC131", children: /* @__PURE__ */ jsx7(IconSvg, { name: "refresh-line", size: 16, color: "var(--chatllm-text-muted, #9ca3af)" }) })
2185
+ ]
2186
+ }
2187
+ )
2188
+ ] })
2189
+ ]
2190
+ }
2191
+ );
2192
+ };
2193
+ var dotStyle = {
2194
+ width: "8px",
2195
+ height: "8px",
2196
+ borderRadius: "50%",
2197
+ backgroundColor: "var(--chatllm-primary, #3b82f6)",
2198
+ animation: "chatllm-typing 1.4s infinite ease-in-out both"
2199
+ };
2200
+ var actionButtonStyle = {
2201
+ padding: "6px",
2202
+ backgroundColor: "transparent",
2203
+ border: "none",
2204
+ borderRadius: "6px",
2205
+ cursor: "pointer",
2206
+ display: "flex",
2207
+ alignItems: "center",
2208
+ justifyContent: "center",
2209
+ transition: "background-color 0.2s"
2210
+ };
2211
+ var navButtonStyle = {
2212
+ width: "24px",
2213
+ height: "24px",
2214
+ display: "flex",
2215
+ alignItems: "center",
2216
+ justifyContent: "center",
2217
+ backgroundColor: "var(--chatllm-bg, #ffffff)",
2218
+ border: "1px solid var(--chatllm-border, #e5e7eb)",
2219
+ borderRadius: "6px"
2220
+ };
2221
+
2222
+ // src/react/components/MessageList.tsx
2223
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
2224
+ var MessageList = ({
2225
+ messages,
2226
+ isLoading,
2227
+ onCopy,
2228
+ onEdit,
2229
+ onRegenerate,
2230
+ onQuote,
2231
+ copiedId,
2232
+ editingId
2233
+ }) => {
2234
+ const messagesEndRef = useRef3(null);
2235
+ const containerRef = useRef3(null);
2236
+ const [selectedText, setSelectedText] = useState6("");
2237
+ const [selectionPosition, setSelectionPosition] = useState6(null);
2238
+ useEffect3(() => {
2239
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
2240
+ }, [messages]);
2241
+ const handleMouseUp = useCallback2(() => {
2242
+ const selection = window.getSelection();
2243
+ const text = selection?.toString().trim();
2244
+ if (text && text.length > 0) {
2245
+ const range = selection?.getRangeAt(0);
2246
+ const rect = range?.getBoundingClientRect();
2247
+ if (rect && containerRef.current) {
2248
+ const containerRect = containerRef.current.getBoundingClientRect();
2249
+ setSelectedText(text);
2250
+ setSelectionPosition({
2251
+ x: rect.left - containerRect.left + rect.width / 2,
2252
+ y: rect.top - containerRect.top - 10
2253
+ });
2254
+ }
2255
+ } else {
2256
+ setTimeout(() => {
2257
+ const currentSelection = window.getSelection()?.toString().trim();
2258
+ if (!currentSelection) {
2259
+ setSelectionPosition(null);
2260
+ }
2261
+ }, 100);
2262
+ }
2263
+ }, []);
2264
+ const handleQuote = () => {
2265
+ if (selectedText && onQuote) {
2266
+ onQuote(selectedText);
2267
+ setSelectionPosition(null);
2268
+ setSelectedText("");
2269
+ window.getSelection()?.removeAllRanges();
2270
+ }
2271
+ };
2272
+ return /* @__PURE__ */ jsxs7(
2273
+ "div",
2274
+ {
2275
+ ref: containerRef,
2276
+ className: "chatllm-message-list",
2277
+ style: {
2278
+ flex: 1,
2279
+ overflow: "auto",
2280
+ position: "relative"
2281
+ },
2282
+ onMouseUp: handleMouseUp,
2283
+ children: [
2284
+ messages.map((message, index) => /* @__PURE__ */ jsx8(
2285
+ MessageBubble,
2286
+ {
2287
+ message,
2288
+ isLoading: isLoading && index === messages.length - 1 && message.role === "assistant",
2289
+ isCopied: copiedId === message.id,
2290
+ isEditing: editingId === message.id,
2291
+ onCopy: () => onCopy(message.content, message.id),
2292
+ onEdit: () => onEdit(message),
2293
+ onRegenerate: message.role === "assistant" ? () => onRegenerate(message.id) : void 0,
2294
+ onQuote,
2295
+ alternatives: message.alternatives
2296
+ },
2297
+ message.id
2298
+ )),
2299
+ selectionPosition && /* @__PURE__ */ jsxs7(
2300
+ "div",
2301
+ {
2302
+ style: {
2303
+ position: "absolute",
2304
+ left: selectionPosition.x,
2305
+ top: selectionPosition.y,
2306
+ transform: "translate(-50%, -100%)",
2307
+ zIndex: 50
2308
+ },
2309
+ children: [
2310
+ /* @__PURE__ */ jsxs7(
2311
+ "button",
2312
+ {
2313
+ onClick: handleQuote,
2314
+ style: {
2315
+ display: "flex",
2316
+ alignItems: "center",
2317
+ gap: "6px",
2318
+ padding: "8px 12px",
2319
+ backgroundColor: "var(--chatllm-text, #1f2937)",
2320
+ color: "#ffffff",
2321
+ border: "none",
2322
+ borderRadius: "8px",
2323
+ fontSize: "13px",
2324
+ fontWeight: 500,
2325
+ cursor: "pointer",
2326
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)"
2327
+ },
2328
+ children: [
2329
+ /* @__PURE__ */ jsx8(IconSvg, { name: "double-quotes-l", size: 14, color: "#ffffff" }),
2330
+ "\uC778\uC6A9\uD558\uAE30"
2331
+ ]
2332
+ }
2333
+ ),
2334
+ /* @__PURE__ */ jsx8(
2335
+ "div",
2336
+ {
2337
+ style: {
2338
+ position: "absolute",
2339
+ left: "50%",
2340
+ bottom: "-6px",
2341
+ transform: "translateX(-50%)",
2342
+ width: 0,
2343
+ height: 0,
2344
+ borderLeft: "6px solid transparent",
2345
+ borderRight: "6px solid transparent",
2346
+ borderTop: "6px solid var(--chatllm-text, #1f2937)"
2347
+ }
2348
+ }
2349
+ )
2350
+ ]
2351
+ }
2352
+ ),
2353
+ /* @__PURE__ */ jsx8("div", { ref: messagesEndRef })
2354
+ ]
2355
+ }
2356
+ );
2357
+ };
2358
+
2359
+ // src/react/components/EmptyState.tsx
2360
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
2361
+ var EmptyState = ({
2362
+ greeting,
2363
+ templates = [],
2364
+ onTemplateClick,
2365
+ actions = [],
2366
+ onActionSelect
2367
+ }) => {
2368
+ const getActionIcon = (icon) => {
2369
+ const iconMap = {
2370
+ search: "search-line",
2371
+ image: "image-line",
2372
+ code: "code-s-slash-line",
2373
+ document: "file-text-line"
2374
+ };
2375
+ return iconMap[icon] || "sparkling-line";
2376
+ };
2377
+ return /* @__PURE__ */ jsxs8(
2378
+ "div",
2379
+ {
2380
+ className: "chatllm-empty-state",
2381
+ style: {
2382
+ flex: 1,
2383
+ display: "flex",
2384
+ flexDirection: "column",
2385
+ alignItems: "center",
2386
+ justifyContent: "center",
2387
+ padding: "48px 24px",
2388
+ textAlign: "center"
2389
+ },
2390
+ children: [
2391
+ /* @__PURE__ */ jsx9(
2392
+ "div",
2393
+ {
2394
+ style: {
2395
+ width: "64px",
2396
+ height: "64px",
2397
+ borderRadius: "16px",
2398
+ background: "linear-gradient(135deg, #3b82f6, #8b5cf6)",
2399
+ display: "flex",
2400
+ alignItems: "center",
2401
+ justifyContent: "center",
2402
+ marginBottom: "24px",
2403
+ boxShadow: "0 8px 32px rgba(59, 130, 246, 0.25)"
2404
+ },
2405
+ children: /* @__PURE__ */ jsx9(IconSvg, { name: "sparkling-line", size: 32, color: "#ffffff" })
2406
+ }
2407
+ ),
2408
+ /* @__PURE__ */ jsx9(
2409
+ "h1",
2410
+ {
2411
+ style: {
2412
+ fontSize: "28px",
2413
+ fontWeight: 600,
2414
+ color: "var(--chatllm-text, #1f2937)",
2415
+ marginBottom: "8px"
2416
+ },
2417
+ children: greeting
2418
+ }
2419
+ ),
2420
+ /* @__PURE__ */ jsx9(
2421
+ "p",
2422
+ {
2423
+ style: {
2424
+ fontSize: "16px",
2425
+ color: "var(--chatllm-text-muted, #6b7280)",
2426
+ marginBottom: "32px"
2427
+ },
2428
+ children: "\uBB34\uC5C7\uC744 \uB3C4\uC640\uB4DC\uB9B4\uAE4C\uC694?"
2429
+ }
2430
+ ),
2431
+ actions.length > 0 && /* @__PURE__ */ jsx9(
2432
+ "div",
2433
+ {
2434
+ style: {
2435
+ display: "grid",
2436
+ gridTemplateColumns: "repeat(auto-fit, minmax(140px, 1fr))",
2437
+ gap: "12px",
2438
+ width: "100%",
2439
+ maxWidth: "600px",
2440
+ marginBottom: "32px"
2441
+ },
2442
+ children: actions.map((action) => /* @__PURE__ */ jsxs8(
2443
+ "button",
2444
+ {
2445
+ onClick: () => onActionSelect?.(action),
2446
+ style: {
2447
+ display: "flex",
2448
+ flexDirection: "column",
2449
+ alignItems: "center",
2450
+ gap: "12px",
2451
+ padding: "20px 16px",
2452
+ backgroundColor: "var(--chatllm-bg, #ffffff)",
2453
+ border: "1px solid var(--chatllm-border, #e5e7eb)",
2454
+ borderRadius: "12px",
2455
+ cursor: "pointer",
2456
+ transition: "all 0.2s"
2457
+ },
2458
+ onMouseOver: (e) => {
2459
+ e.currentTarget.style.borderColor = "var(--chatllm-primary, #3b82f6)";
2460
+ e.currentTarget.style.boxShadow = "0 4px 12px rgba(59, 130, 246, 0.1)";
2461
+ },
2462
+ onMouseOut: (e) => {
2463
+ e.currentTarget.style.borderColor = "var(--chatllm-border, #e5e7eb)";
2464
+ e.currentTarget.style.boxShadow = "none";
2465
+ },
2466
+ children: [
2467
+ /* @__PURE__ */ jsx9(
2468
+ "div",
2469
+ {
2470
+ style: {
2471
+ width: "44px",
2472
+ height: "44px",
2473
+ borderRadius: "12px",
2474
+ backgroundColor: "var(--chatllm-primary-light, #dbeafe)",
2475
+ display: "flex",
2476
+ alignItems: "center",
2477
+ justifyContent: "center"
2478
+ },
2479
+ children: /* @__PURE__ */ jsx9(
2480
+ IconSvg,
2481
+ {
2482
+ name: getActionIcon(action.icon),
2483
+ size: 22,
2484
+ color: "var(--chatllm-primary, #3b82f6)"
2485
+ }
2486
+ )
2487
+ }
2488
+ ),
2489
+ /* @__PURE__ */ jsx9("div", { children: /* @__PURE__ */ jsx9(
2490
+ "div",
2491
+ {
2492
+ style: {
2493
+ fontSize: "14px",
2494
+ fontWeight: 500,
2495
+ color: "var(--chatllm-text, #1f2937)"
2496
+ },
2497
+ children: action.label
2498
+ }
2499
+ ) })
2500
+ ]
2501
+ },
2502
+ action.id
2503
+ ))
2504
+ }
2505
+ ),
2506
+ templates.length > 0 && /* @__PURE__ */ jsxs8("div", { style: { width: "100%", maxWidth: "600px" }, children: [
2507
+ /* @__PURE__ */ jsx9(
2508
+ "h3",
2509
+ {
2510
+ style: {
2511
+ fontSize: "14px",
2512
+ fontWeight: 500,
2513
+ color: "var(--chatllm-text-muted, #6b7280)",
2514
+ marginBottom: "12px",
2515
+ textAlign: "left"
2516
+ },
2517
+ children: "\uCD94\uCC9C \uD504\uB86C\uD504\uD2B8"
2518
+ }
2519
+ ),
2520
+ /* @__PURE__ */ jsx9(
2521
+ "div",
2522
+ {
2523
+ style: {
2524
+ display: "flex",
2525
+ flexDirection: "column",
2526
+ gap: "8px"
2527
+ },
2528
+ children: templates.map((template) => /* @__PURE__ */ jsxs8(
2529
+ "button",
2530
+ {
2531
+ onClick: () => onTemplateClick(template),
2532
+ style: {
2533
+ display: "flex",
2534
+ alignItems: "center",
2535
+ gap: "12px",
2536
+ padding: "14px 16px",
2537
+ backgroundColor: "var(--chatllm-bg, #ffffff)",
2538
+ border: "1px solid var(--chatllm-border, #e5e7eb)",
2539
+ borderRadius: "10px",
2540
+ cursor: "pointer",
2541
+ textAlign: "left",
2542
+ transition: "all 0.2s"
2543
+ },
2544
+ onMouseOver: (e) => {
2545
+ e.currentTarget.style.backgroundColor = "var(--chatllm-bg-hover, #f9fafb)";
2546
+ },
2547
+ onMouseOut: (e) => {
2548
+ e.currentTarget.style.backgroundColor = "var(--chatllm-bg, #ffffff)";
2549
+ },
2550
+ children: [
2551
+ /* @__PURE__ */ jsx9(
2552
+ "div",
2553
+ {
2554
+ style: {
2555
+ width: "36px",
2556
+ height: "36px",
2557
+ borderRadius: "8px",
2558
+ backgroundColor: "var(--chatllm-bg-secondary, #f3f4f6)",
2559
+ display: "flex",
2560
+ alignItems: "center",
2561
+ justifyContent: "center",
2562
+ flexShrink: 0
2563
+ },
2564
+ children: /* @__PURE__ */ jsx9(IconSvg, { name: "file-text-line", size: 18, color: "var(--chatllm-text-muted, #6b7280)" })
2565
+ }
2566
+ ),
2567
+ /* @__PURE__ */ jsxs8("div", { style: { flex: 1, minWidth: 0 }, children: [
2568
+ /* @__PURE__ */ jsx9(
2569
+ "div",
2570
+ {
2571
+ style: {
2572
+ fontSize: "14px",
2573
+ fontWeight: 500,
2574
+ color: "var(--chatllm-text, #1f2937)"
2575
+ },
2576
+ children: template.title
2577
+ }
2578
+ ),
2579
+ /* @__PURE__ */ jsx9(
2580
+ "div",
2581
+ {
2582
+ style: {
2583
+ fontSize: "13px",
2584
+ color: "var(--chatllm-text-muted, #9ca3af)",
2585
+ overflow: "hidden",
2586
+ textOverflow: "ellipsis",
2587
+ whiteSpace: "nowrap"
2588
+ },
2589
+ children: template.description
2590
+ }
2591
+ )
2592
+ ] }),
2593
+ /* @__PURE__ */ jsx9(IconSvg, { name: "arrow-right-line", size: 16, color: "var(--chatllm-text-muted, #9ca3af)" })
2594
+ ]
2595
+ },
2596
+ template.id
2597
+ ))
2598
+ }
2599
+ )
2600
+ ] })
2601
+ ]
2602
+ }
2603
+ );
2604
+ };
2605
+
2606
+ // src/react/components/MemoryPanel.tsx
2607
+ import { useState as useState7 } from "react";
2608
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
2609
+ var categoryLabels = {
2610
+ context: "\uB300\uD654 \uCEE8\uD14D\uC2A4\uD2B8",
2611
+ preference: "\uC0AC\uC6A9\uC790 \uC120\uD638",
2612
+ skill: "\uD559\uC2B5\uB41C \uC2A4\uD0AC",
2613
+ fact: "\uC800\uC7A5\uB41C \uC815\uBCF4"
2614
+ };
2615
+ var categoryColors = {
2616
+ context: "#3b82f6",
2617
+ preference: "#8b5cf6",
2618
+ skill: "#10b981",
2619
+ fact: "#f59e0b"
2620
+ };
2621
+ var MemoryPanel = ({
2622
+ items,
2623
+ contextSummary,
2624
+ onDelete,
2625
+ onClearAll,
2626
+ isOpen,
2627
+ onToggle
2628
+ }) => {
2629
+ const [expandedId, setExpandedId] = useState7(null);
2630
+ const [activeTab, setActiveTab] = useState7("all");
2631
+ const filteredItems = activeTab === "all" ? items : items.filter((item) => item.category === activeTab);
2632
+ const formatDate2 = (timestamp) => {
2633
+ const date = new Date(timestamp);
2634
+ return date.toLocaleDateString("ko-KR", {
2635
+ month: "short",
2636
+ day: "numeric",
2637
+ hour: "2-digit",
2638
+ minute: "2-digit"
2639
+ });
2640
+ };
2641
+ if (!isOpen) {
2642
+ return /* @__PURE__ */ jsx10(
2643
+ "button",
2644
+ {
2645
+ onClick: onToggle,
2646
+ style: {
2647
+ position: "fixed",
2648
+ right: "16px",
2649
+ bottom: "100px",
2650
+ width: "48px",
2651
+ height: "48px",
2652
+ borderRadius: "50%",
2653
+ backgroundColor: "var(--chatllm-primary, #3b82f6)",
2654
+ border: "none",
2655
+ boxShadow: "0 4px 12px rgba(59, 130, 246, 0.3)",
2656
+ cursor: "pointer",
2657
+ display: "flex",
2658
+ alignItems: "center",
2659
+ justifyContent: "center",
2660
+ zIndex: 100
2661
+ },
2662
+ children: /* @__PURE__ */ jsx10(IconSvg, { name: "robot-line", size: 24, color: "#ffffff" })
2663
+ }
2664
+ );
2665
+ }
2666
+ return /* @__PURE__ */ jsxs9(
2667
+ "div",
2668
+ {
2669
+ className: "chatllm-memory-panel",
2670
+ style: {
2671
+ position: "fixed",
2672
+ right: "16px",
2673
+ bottom: "16px",
2674
+ width: "380px",
2675
+ maxHeight: "70vh",
2676
+ backgroundColor: "var(--chatllm-bg, #ffffff)",
2677
+ borderRadius: "16px",
2678
+ boxShadow: "0 8px 32px rgba(0, 0, 0, 0.12)",
2679
+ border: "1px solid var(--chatllm-border, #e5e7eb)",
2680
+ display: "flex",
2681
+ flexDirection: "column",
2682
+ overflow: "hidden",
2683
+ zIndex: 100
2684
+ },
2685
+ children: [
2686
+ /* @__PURE__ */ jsxs9(
2687
+ "div",
2688
+ {
2689
+ style: {
2690
+ display: "flex",
2691
+ alignItems: "center",
2692
+ justifyContent: "space-between",
2693
+ padding: "16px",
2694
+ borderBottom: "1px solid var(--chatllm-border, #e5e7eb)"
2695
+ },
2696
+ children: [
2697
+ /* @__PURE__ */ jsxs9("div", { style: { display: "flex", alignItems: "center", gap: "10px" }, children: [
2698
+ /* @__PURE__ */ jsx10(
2699
+ "div",
2700
+ {
2701
+ style: {
2702
+ width: "32px",
2703
+ height: "32px",
2704
+ borderRadius: "8px",
2705
+ backgroundColor: "var(--chatllm-primary-light, #dbeafe)",
2706
+ display: "flex",
2707
+ alignItems: "center",
2708
+ justifyContent: "center"
2709
+ },
2710
+ children: /* @__PURE__ */ jsx10(IconSvg, { name: "robot-line", size: 18, color: "var(--chatllm-primary, #3b82f6)" })
2711
+ }
2712
+ ),
2713
+ /* @__PURE__ */ jsxs9("div", { children: [
2714
+ /* @__PURE__ */ jsx10("div", { style: { fontSize: "15px", fontWeight: 600, color: "var(--chatllm-text, #1f2937)" }, children: "AI \uBA54\uBAA8\uB9AC" }),
2715
+ /* @__PURE__ */ jsxs9("div", { style: { fontSize: "12px", color: "var(--chatllm-text-muted, #9ca3af)" }, children: [
2716
+ items.length,
2717
+ "\uAC1C \uD56D\uBAA9"
2718
+ ] })
2719
+ ] })
2720
+ ] }),
2721
+ /* @__PURE__ */ jsxs9("div", { style: { display: "flex", gap: "4px" }, children: [
2722
+ onClearAll && items.length > 0 && /* @__PURE__ */ jsx10(
2723
+ "button",
2724
+ {
2725
+ onClick: onClearAll,
2726
+ style: {
2727
+ padding: "8px",
2728
+ backgroundColor: "transparent",
2729
+ border: "none",
2730
+ borderRadius: "8px",
2731
+ cursor: "pointer"
2732
+ },
2733
+ title: "\uC804\uCCB4 \uC0AD\uC81C",
2734
+ children: /* @__PURE__ */ jsx10(IconSvg, { name: "delete-bin-line", size: 18, color: "var(--chatllm-text-muted, #9ca3af)" })
2735
+ }
2736
+ ),
2737
+ /* @__PURE__ */ jsx10(
2738
+ "button",
2739
+ {
2740
+ onClick: onToggle,
2741
+ style: {
2742
+ padding: "8px",
2743
+ backgroundColor: "transparent",
2744
+ border: "none",
2745
+ borderRadius: "8px",
2746
+ cursor: "pointer"
2747
+ },
2748
+ children: /* @__PURE__ */ jsx10(IconSvg, { name: "close-line", size: 18, color: "var(--chatllm-text-muted, #9ca3af)" })
2749
+ }
2750
+ )
2751
+ ] })
2752
+ ]
2753
+ }
2754
+ ),
2755
+ /* @__PURE__ */ jsx10(
2756
+ "div",
2757
+ {
2758
+ style: {
2759
+ display: "flex",
2760
+ gap: "4px",
2761
+ padding: "12px 16px",
2762
+ borderBottom: "1px solid var(--chatllm-border-light, #f3f4f6)",
2763
+ overflowX: "auto"
2764
+ },
2765
+ children: ["all", "context", "preference", "skill", "fact"].map((tab) => /* @__PURE__ */ jsx10(
2766
+ "button",
2767
+ {
2768
+ onClick: () => setActiveTab(tab),
2769
+ style: {
2770
+ padding: "6px 12px",
2771
+ fontSize: "13px",
2772
+ fontWeight: activeTab === tab ? 500 : 400,
2773
+ backgroundColor: activeTab === tab ? "var(--chatllm-primary-light, #dbeafe)" : "transparent",
2774
+ color: activeTab === tab ? "var(--chatllm-primary, #3b82f6)" : "var(--chatllm-text-muted, #6b7280)",
2775
+ border: "none",
2776
+ borderRadius: "6px",
2777
+ cursor: "pointer",
2778
+ whiteSpace: "nowrap"
2779
+ },
2780
+ children: tab === "all" ? "\uC804\uCCB4" : categoryLabels[tab]
2781
+ },
2782
+ tab
2783
+ ))
2784
+ }
2785
+ ),
2786
+ /* @__PURE__ */ jsxs9("div", { style: { flex: 1, overflow: "auto", padding: "12px" }, children: [
2787
+ contextSummary && activeTab === "all" && /* @__PURE__ */ jsxs9(
2788
+ "div",
2789
+ {
2790
+ style: {
2791
+ padding: "12px",
2792
+ marginBottom: "12px",
2793
+ backgroundColor: "var(--chatllm-bg-secondary, #f9fafb)",
2794
+ borderRadius: "10px",
2795
+ borderLeft: "3px solid var(--chatllm-primary, #3b82f6)"
2796
+ },
2797
+ children: [
2798
+ /* @__PURE__ */ jsxs9(
2799
+ "div",
2800
+ {
2801
+ style: {
2802
+ display: "flex",
2803
+ alignItems: "center",
2804
+ gap: "6px",
2805
+ marginBottom: "8px"
2806
+ },
2807
+ children: [
2808
+ /* @__PURE__ */ jsx10(IconSvg, { name: "file-text-line", size: 14, color: "var(--chatllm-primary, #3b82f6)" }),
2809
+ /* @__PURE__ */ jsx10("span", { style: { fontSize: "12px", fontWeight: 500, color: "var(--chatllm-primary, #3b82f6)" }, children: "\uB300\uD654 \uC694\uC57D" })
2810
+ ]
2811
+ }
2812
+ ),
2813
+ /* @__PURE__ */ jsx10(
2814
+ "p",
2815
+ {
2816
+ style: {
2817
+ fontSize: "13px",
2818
+ lineHeight: "1.6",
2819
+ color: "var(--chatllm-text, #374151)",
2820
+ margin: 0
2821
+ },
2822
+ children: contextSummary
2823
+ }
2824
+ )
2825
+ ]
2826
+ }
2827
+ ),
2828
+ filteredItems.length === 0 ? /* @__PURE__ */ jsxs9(
2829
+ "div",
2830
+ {
2831
+ style: {
2832
+ padding: "32px 16px",
2833
+ textAlign: "center",
2834
+ color: "var(--chatllm-text-muted, #9ca3af)"
2835
+ },
2836
+ children: [
2837
+ /* @__PURE__ */ jsx10(IconSvg, { name: "robot-line", size: 32, color: "var(--chatllm-text-muted, #d1d5db)" }),
2838
+ /* @__PURE__ */ jsx10("p", { style: { fontSize: "14px", marginTop: "12px" }, children: "\uC800\uC7A5\uB41C \uBA54\uBAA8\uB9AC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4" })
2839
+ ]
2840
+ }
2841
+ ) : /* @__PURE__ */ jsx10("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: filteredItems.map((item) => /* @__PURE__ */ jsxs9(
2842
+ "div",
2843
+ {
2844
+ style: {
2845
+ padding: "12px",
2846
+ backgroundColor: "var(--chatllm-bg, #ffffff)",
2847
+ border: "1px solid var(--chatllm-border, #e5e7eb)",
2848
+ borderRadius: "10px",
2849
+ cursor: "pointer",
2850
+ transition: "all 0.2s"
2851
+ },
2852
+ onClick: () => setExpandedId(expandedId === item.id ? null : item.id),
2853
+ children: [
2854
+ /* @__PURE__ */ jsxs9("div", { style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between" }, children: [
2855
+ /* @__PURE__ */ jsxs9("div", { style: { flex: 1, minWidth: 0 }, children: [
2856
+ /* @__PURE__ */ jsxs9("div", { style: { display: "flex", alignItems: "center", gap: "8px", marginBottom: "4px" }, children: [
2857
+ item.category && /* @__PURE__ */ jsx10(
2858
+ "span",
2859
+ {
2860
+ style: {
2861
+ fontSize: "11px",
2862
+ fontWeight: 500,
2863
+ padding: "2px 8px",
2864
+ backgroundColor: `${categoryColors[item.category]}15`,
2865
+ color: categoryColors[item.category],
2866
+ borderRadius: "4px"
2867
+ },
2868
+ children: categoryLabels[item.category]
2869
+ }
2870
+ ),
2871
+ /* @__PURE__ */ jsx10("span", { style: { fontSize: "11px", color: "var(--chatllm-text-muted, #9ca3af)" }, children: formatDate2(item.timestamp) })
2872
+ ] }),
2873
+ /* @__PURE__ */ jsx10(
2874
+ "div",
2875
+ {
2876
+ style: {
2877
+ fontSize: "13px",
2878
+ fontWeight: 500,
2879
+ color: "var(--chatllm-text, #1f2937)"
2880
+ },
2881
+ children: item.key
2882
+ }
2883
+ )
2884
+ ] }),
2885
+ /* @__PURE__ */ jsxs9("div", { style: { display: "flex", alignItems: "center", gap: "4px" }, children: [
2886
+ onDelete && /* @__PURE__ */ jsx10(
2887
+ "button",
2888
+ {
2889
+ onClick: (e) => {
2890
+ e.stopPropagation();
2891
+ onDelete(item.id);
2892
+ },
2893
+ style: {
2894
+ padding: "4px",
2895
+ backgroundColor: "transparent",
2896
+ border: "none",
2897
+ borderRadius: "4px",
2898
+ cursor: "pointer",
2899
+ opacity: 0.5
2900
+ },
2901
+ children: /* @__PURE__ */ jsx10(IconSvg, { name: "delete-bin-line", size: 14, color: "var(--chatllm-text-muted, #9ca3af)" })
2902
+ }
2903
+ ),
2904
+ /* @__PURE__ */ jsx10(
2905
+ IconSvg,
2906
+ {
2907
+ name: expandedId === item.id ? "arrow-up-s-line" : "arrow-down-s-line",
2908
+ size: 16,
2909
+ color: "var(--chatllm-text-muted, #9ca3af)"
2910
+ }
2911
+ )
2912
+ ] })
2913
+ ] }),
2914
+ expandedId === item.id && /* @__PURE__ */ jsx10(
2915
+ "div",
2916
+ {
2917
+ style: {
2918
+ marginTop: "12px",
2919
+ paddingTop: "12px",
2920
+ borderTop: "1px solid var(--chatllm-border-light, #f3f4f6)"
2921
+ },
2922
+ children: /* @__PURE__ */ jsx10(
2923
+ "p",
2924
+ {
2925
+ style: {
2926
+ fontSize: "13px",
2927
+ lineHeight: "1.6",
2928
+ color: "var(--chatllm-text, #374151)",
2929
+ margin: 0,
2930
+ whiteSpace: "pre-wrap"
2931
+ },
2932
+ children: item.value
2933
+ }
2934
+ )
2935
+ }
2936
+ )
2937
+ ]
2938
+ },
2939
+ item.id
2940
+ )) })
2941
+ ] })
2942
+ ]
2943
+ }
2944
+ );
2945
+ };
2946
+
2947
+ // src/react/ChatUI.tsx
2948
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
2949
+ var DEFAULT_ACTIONS = [
2950
+ {
2951
+ id: "webSearch",
2952
+ label: "\uC6F9 \uAC80\uC0C9",
2953
+ icon: "search",
2954
+ description: "\uC6F9\uC5D0\uC11C \uC815\uBCF4\uB97C \uAC80\uC0C9\uD569\uB2C8\uB2E4",
2955
+ systemPrompt: "\uC6F9 \uAC80\uC0C9 \uACB0\uACFC\uB97C \uAE30\uBC18\uC73C\uB85C \uB2F5\uBCC0\uD574\uC8FC\uC138\uC694."
2956
+ },
2957
+ {
2958
+ id: "imageGen",
2959
+ label: "\uC774\uBBF8\uC9C0 \uC0DD\uC131",
2960
+ icon: "image",
2961
+ description: "\uD504\uB86C\uD504\uD2B8\uB85C \uC774\uBBF8\uC9C0\uB97C \uC0DD\uC131\uD569\uB2C8\uB2E4",
2962
+ systemPrompt: "\uC774\uBBF8\uC9C0 \uC0DD\uC131 \uC694\uCCAD\uC785\uB2C8\uB2E4."
2963
+ },
2964
+ {
2965
+ id: "codeAnalysis",
2966
+ label: "\uCF54\uB4DC \uBD84\uC11D",
2967
+ icon: "code",
2968
+ description: "\uCF54\uB4DC\uB97C \uBD84\uC11D\uD558\uACE0 \uC124\uBA85\uD569\uB2C8\uB2E4",
2969
+ systemPrompt: "\uCF54\uB4DC\uB97C \uBD84\uC11D\uD558\uACE0 \uC790\uC138\uD788 \uC124\uBA85\uD574\uC8FC\uC138\uC694."
2970
+ },
2971
+ {
2972
+ id: "summarize",
2973
+ label: "\uC694\uC57D",
2974
+ icon: "document",
2975
+ description: "\uAE34 \uD14D\uC2A4\uD2B8\uB97C \uC694\uC57D\uD569\uB2C8\uB2E4",
2976
+ systemPrompt: "\uB2E4\uC74C \uB0B4\uC6A9\uC744 \uD575\uC2EC\uB9CC \uAC04\uACB0\uD558\uAC8C \uC694\uC57D\uD574\uC8FC\uC138\uC694."
2977
+ }
2978
+ ];
2979
+ var DEFAULT_TEMPLATES = [
2980
+ {
2981
+ id: "1",
2982
+ title: "AI \uAE30\uBC18 \uAE00\uC4F0\uAE30",
2983
+ description: "\uBE14\uB85C\uADF8, \uC774\uBA54\uC77C, \uBCF4\uACE0\uC11C \uC791\uC131 \uB3C4\uC6C0",
2984
+ prompt: "\uB2E4\uC74C \uC8FC\uC81C\uC5D0 \uB300\uD574 \uBE14\uB85C\uADF8 \uAE00\uC744 \uC791\uC131\uD574\uC8FC\uC138\uC694: ",
2985
+ category: "\uAE00\uC4F0\uAE30"
2986
+ },
2987
+ {
2988
+ id: "2",
2989
+ title: "\uCF54\uB4DC \uB9AC\uBDF0",
2990
+ description: "\uCF54\uB4DC \uBD84\uC11D \uBC0F \uAC1C\uC120\uC810 \uC81C\uC548",
2991
+ prompt: "\uB2E4\uC74C \uCF54\uB4DC\uB97C \uB9AC\uBDF0\uD558\uACE0 \uAC1C\uC120\uC810\uC744 \uC81C\uC548\uD574\uC8FC\uC138\uC694: ",
2992
+ category: "\uAC1C\uBC1C"
2993
+ },
2994
+ {
2995
+ id: "3",
2996
+ title: "\uBC88\uC5ED \uB3C4\uC6B0\uBBF8",
2997
+ description: "\uC790\uC5F0\uC2A4\uB7EC\uC6B4 \uBC88\uC5ED \uBC0F \uAD50\uC815",
2998
+ prompt: "\uB2E4\uC74C \uD14D\uC2A4\uD2B8\uB97C \uC790\uC5F0\uC2A4\uB7FD\uAC8C \uBC88\uC5ED\uD574\uC8FC\uC138\uC694: ",
2999
+ category: "\uBC88\uC5ED"
3000
+ },
3001
+ {
3002
+ id: "4",
3003
+ title: "\uC694\uC57D \uC815\uB9AC",
3004
+ description: "\uAE34 \uBB38\uC11C\uB97C \uD575\uC2EC\uB9CC \uC694\uC57D",
3005
+ prompt: "\uB2E4\uC74C \uB0B4\uC6A9\uC744 \uD575\uC2EC\uB9CC \uC694\uC57D\uD574\uC8FC\uC138\uC694: ",
3006
+ category: "\uC815\uB9AC"
3007
+ }
3008
+ ];
3009
+ var injectStyles = () => {
3010
+ if (typeof document === "undefined") return;
3011
+ const styleId = "chatllm-styles";
3012
+ if (document.getElementById(styleId)) return;
3013
+ const style = document.createElement("style");
3014
+ style.id = styleId;
3015
+ style.textContent = `
3016
+ @keyframes chatllm-typing {
3017
+ 0%, 80%, 100% {
3018
+ transform: scale(0);
3019
+ opacity: 0.5;
3020
+ }
3021
+ 40% {
3022
+ transform: scale(1);
3023
+ opacity: 1;
3024
+ }
3025
+ }
3026
+
3027
+ .chatllm-root {
3028
+ --chatllm-primary: #3b82f6;
3029
+ --chatllm-primary-hover: #2563eb;
3030
+ --chatllm-primary-light: #dbeafe;
3031
+ --chatllm-bg: #ffffff;
3032
+ --chatllm-bg-secondary: #f9fafb;
3033
+ --chatllm-bg-tertiary: #f3f4f6;
3034
+ --chatllm-bg-hover: #f3f4f6;
3035
+ --chatllm-bg-active: #eff6ff;
3036
+ --chatllm-bg-disabled: #e5e7eb;
3037
+ --chatllm-sidebar-bg: #ffffff;
3038
+ --chatllm-input-bg: #f9fafb;
3039
+ --chatllm-text: #1f2937;
3040
+ --chatllm-text-muted: #6b7280;
3041
+ --chatllm-border: #e5e7eb;
3042
+ --chatllm-border-light: #f3f4f6;
3043
+ --chatllm-error: #ef4444;
3044
+ --chatllm-success: #22c55e;
3045
+ --chatllm-user-avatar: #e5e7eb;
3046
+ --chatllm-assistant-avatar: #dbeafe;
3047
+ }
3048
+
3049
+ .chatllm-root.chatllm-dark {
3050
+ --chatllm-primary: #60a5fa;
3051
+ --chatllm-primary-hover: #3b82f6;
3052
+ --chatllm-primary-light: #1e3a5f;
3053
+ --chatllm-bg: #111827;
3054
+ --chatllm-bg-secondary: #1f2937;
3055
+ --chatllm-bg-tertiary: #374151;
3056
+ --chatllm-bg-hover: #374151;
3057
+ --chatllm-bg-active: #1e3a5f;
3058
+ --chatllm-bg-disabled: #374151;
3059
+ --chatllm-sidebar-bg: #111827;
3060
+ --chatllm-input-bg: #1f2937;
3061
+ --chatllm-text: #f9fafb;
3062
+ --chatllm-text-muted: #9ca3af;
3063
+ --chatllm-border: #374151;
3064
+ --chatllm-border-light: #1f2937;
3065
+ --chatllm-user-avatar: #374151;
3066
+ --chatllm-assistant-avatar: #1e3a5f;
3067
+ }
3068
+
3069
+ .chatllm-root * {
3070
+ box-sizing: border-box;
3071
+ }
3072
+
3073
+ .chatllm-root textarea::placeholder {
3074
+ color: var(--chatllm-text-muted);
3075
+ }
3076
+
3077
+ .chatllm-root button:focus-visible {
3078
+ outline: 2px solid var(--chatllm-primary);
3079
+ outline-offset: 2px;
3080
+ }
3081
+
3082
+ .chatllm-message-list::-webkit-scrollbar {
3083
+ width: 6px;
3084
+ }
3085
+
3086
+ .chatllm-message-list::-webkit-scrollbar-track {
3087
+ background: transparent;
3088
+ }
3089
+
3090
+ .chatllm-message-list::-webkit-scrollbar-thumb {
3091
+ background-color: var(--chatllm-border);
3092
+ border-radius: 3px;
3093
+ }
3094
+ `;
3095
+ document.head.appendChild(style);
3096
+ };
3097
+ var ChatUI = ({
3098
+ models,
3099
+ actions = DEFAULT_ACTIONS,
3100
+ templates = DEFAULT_TEMPLATES,
3101
+ personalization,
3102
+ apiKey,
3103
+ apiEndpoint = "/api/chat",
3104
+ theme,
3105
+ showSidebar = true,
3106
+ showSettings = true,
3107
+ showModelSelector = true,
3108
+ systemPrompt,
3109
+ contextCompressionThreshold = 20,
3110
+ keepRecentMessages = 6,
3111
+ storageKey = "chatllm_sessions",
3112
+ className = "",
3113
+ onSendMessage,
3114
+ onSessionChange,
3115
+ onError
3116
+ }) => {
3117
+ React8.useEffect(() => {
3118
+ injectStyles();
3119
+ }, []);
3120
+ const hookOptions = {
3121
+ models,
3122
+ actions,
3123
+ initialPersonalization: personalization,
3124
+ apiKey,
3125
+ apiEndpoint,
3126
+ initialModel: models[0]?.id,
3127
+ storageKey,
3128
+ contextCompressionThreshold,
3129
+ keepRecentMessages,
3130
+ onSendMessage,
3131
+ onSessionChange,
3132
+ onError
3133
+ };
3134
+ const {
3135
+ sessions,
3136
+ currentSession,
3137
+ currentSessionId,
3138
+ messages,
3139
+ input,
3140
+ isLoading,
3141
+ selectedModel,
3142
+ sidebarOpen,
3143
+ settingsOpen,
3144
+ quotedText,
3145
+ selectedAction,
3146
+ copiedMessageId,
3147
+ editingMessageId,
3148
+ personalization: currentPersonalization,
3149
+ setInput,
3150
+ sendMessage,
3151
+ stopGeneration,
3152
+ newSession,
3153
+ selectSession,
3154
+ deleteSession,
3155
+ setModel,
3156
+ toggleSidebar,
3157
+ openSettings,
3158
+ closeSettings,
3159
+ setQuotedText,
3160
+ setSelectedAction,
3161
+ copyMessage,
3162
+ startEdit,
3163
+ cancelEdit,
3164
+ saveEdit,
3165
+ regenerate,
3166
+ updatePersonalization
3167
+ } = useChatUI(hookOptions);
3168
+ const greeting = currentPersonalization.userProfile.nickname ? `\uC548\uB155\uD558\uC138\uC694, ${currentPersonalization.userProfile.nickname}\uB2D8` : "\uC548\uB155\uD558\uC138\uC694";
3169
+ const handleTemplateClick = (template) => {
3170
+ setInput(template.prompt);
3171
+ };
3172
+ const handleActionSelect = (action) => {
3173
+ setSelectedAction(action);
3174
+ };
3175
+ const handleSubmit = () => {
3176
+ sendMessage();
3177
+ };
3178
+ const [memoryPanelOpen, setMemoryPanelOpen] = useState8(false);
3179
+ const memoryItems = React8.useMemo(() => {
3180
+ const items = [];
3181
+ if (currentSession?.contextSummary) {
3182
+ items.push({
3183
+ id: "context_summary",
3184
+ key: "\uB300\uD654 \uCEE8\uD14D\uC2A4\uD2B8 \uC694\uC57D",
3185
+ value: currentSession.contextSummary,
3186
+ category: "context",
3187
+ timestamp: currentSession.updatedAt
3188
+ });
3189
+ }
3190
+ if (currentPersonalization.userProfile.nickname) {
3191
+ items.push({
3192
+ id: "pref_nickname",
3193
+ key: "\uC0AC\uC6A9\uC790 \uB2C9\uB124\uC784",
3194
+ value: currentPersonalization.userProfile.nickname,
3195
+ category: "preference",
3196
+ timestamp: Date.now()
3197
+ });
3198
+ }
3199
+ if (currentPersonalization.userProfile.occupation) {
3200
+ items.push({
3201
+ id: "pref_occupation",
3202
+ key: "\uC9C1\uC5C5/\uC5ED\uD560",
3203
+ value: currentPersonalization.userProfile.occupation,
3204
+ category: "preference",
3205
+ timestamp: Date.now()
3206
+ });
3207
+ }
3208
+ if (currentPersonalization.userProfile.additionalInfo) {
3209
+ items.push({
3210
+ id: "pref_info",
3211
+ key: "\uCD94\uAC00 \uC815\uBCF4",
3212
+ value: currentPersonalization.userProfile.additionalInfo,
3213
+ category: "preference",
3214
+ timestamp: Date.now()
3215
+ });
3216
+ }
3217
+ return items;
3218
+ }, [currentSession, currentPersonalization]);
3219
+ const themeClass = theme?.mode === "dark" ? "chatllm-dark" : "";
3220
+ return /* @__PURE__ */ jsxs10(
3221
+ "div",
3222
+ {
3223
+ className: `chatllm-root ${themeClass} ${className}`,
3224
+ style: {
3225
+ display: "flex",
3226
+ height: "100%",
3227
+ backgroundColor: "var(--chatllm-bg-secondary)",
3228
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'
3229
+ },
3230
+ children: [
3231
+ showSidebar && /* @__PURE__ */ jsx11(
3232
+ ChatSidebar,
3233
+ {
3234
+ sessions,
3235
+ currentSessionId,
3236
+ onSelectSession: selectSession,
3237
+ onNewSession: newSession,
3238
+ onDeleteSession: deleteSession,
3239
+ isOpen: sidebarOpen,
3240
+ onToggle: toggleSidebar
3241
+ }
3242
+ ),
3243
+ /* @__PURE__ */ jsxs10(
3244
+ "main",
3245
+ {
3246
+ style: {
3247
+ flex: 1,
3248
+ display: "flex",
3249
+ flexDirection: "column",
3250
+ backgroundColor: "var(--chatllm-bg)",
3251
+ minWidth: 0
3252
+ },
3253
+ children: [
3254
+ /* @__PURE__ */ jsx11(
3255
+ ChatHeader,
3256
+ {
3257
+ title: currentSession?.title || "\uC0C8 \uB300\uD654",
3258
+ model: selectedModel,
3259
+ models,
3260
+ onModelChange: setModel,
3261
+ onSettingsOpen: openSettings,
3262
+ onSidebarToggle: toggleSidebar,
3263
+ sidebarOpen
3264
+ }
3265
+ ),
3266
+ messages.length === 0 ? /* @__PURE__ */ jsx11(
3267
+ EmptyState,
3268
+ {
3269
+ greeting,
3270
+ templates,
3271
+ onTemplateClick: handleTemplateClick,
3272
+ actions,
3273
+ onActionSelect: handleActionSelect
3274
+ }
3275
+ ) : /* @__PURE__ */ jsx11(
3276
+ MessageList,
3277
+ {
3278
+ messages,
3279
+ isLoading,
3280
+ onCopy: copyMessage,
3281
+ onEdit: startEdit,
3282
+ onRegenerate: regenerate,
3283
+ onQuote: setQuotedText,
3284
+ copiedId: copiedMessageId,
3285
+ editingId: editingMessageId
3286
+ }
3287
+ ),
3288
+ /* @__PURE__ */ jsx11(
3289
+ ChatInput,
3290
+ {
3291
+ value: input,
3292
+ onChange: setInput,
3293
+ onSubmit: handleSubmit,
3294
+ onStop: stopGeneration,
3295
+ isLoading,
3296
+ placeholder: "\uBA54\uC2DC\uC9C0\uB97C \uC785\uB825\uD558\uC138\uC694...",
3297
+ quotedText,
3298
+ onClearQuote: () => setQuotedText(null),
3299
+ selectedAction,
3300
+ onClearAction: () => setSelectedAction(null),
3301
+ onActionSelect: setSelectedAction,
3302
+ actions
3303
+ }
3304
+ )
3305
+ ]
3306
+ }
3307
+ ),
3308
+ /* @__PURE__ */ jsx11(
3309
+ MemoryPanel,
3310
+ {
3311
+ items: memoryItems,
3312
+ contextSummary: currentSession?.contextSummary,
3313
+ isOpen: memoryPanelOpen,
3314
+ onToggle: () => setMemoryPanelOpen(!memoryPanelOpen)
3315
+ }
3316
+ )
3317
+ ]
3318
+ }
3319
+ );
3320
+ };
3321
+
3322
+ // src/react/components/SettingsModal.tsx
3323
+ import { useState as useState9 } from "react";
3324
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
3325
+ var DEFAULT_PERSONALIZATION2 = {
3326
+ responseStyle: {
3327
+ warmth: "medium",
3328
+ enthusiasm: "medium",
3329
+ emojiUsage: "low",
3330
+ formatting: "default",
3331
+ verbosity: "balanced"
3332
+ },
3333
+ userProfile: {},
3334
+ useMemory: true,
3335
+ language: "auto"
3336
+ };
3337
+ var SettingsModal = ({
3338
+ isOpen,
3339
+ onClose,
3340
+ personalization,
3341
+ onPersonalizationChange,
3342
+ apiKey = "",
3343
+ onApiKeyChange,
3344
+ onClearAllData,
3345
+ apiKeyLabel = "API Key",
3346
+ apiKeyDescription = "Cloud \uBAA8\uB378 \uC0AC\uC6A9\uC5D0 \uD544\uC694\uD569\uB2C8\uB2E4"
3347
+ }) => {
3348
+ const [activeTab, setActiveTab] = useState9("general");
3349
+ const [localApiKey, setLocalApiKey] = useState9(apiKey);
3350
+ if (!isOpen) return null;
3351
+ const updateResponseStyle = (key, value) => {
3352
+ onPersonalizationChange({
3353
+ ...personalization,
3354
+ responseStyle: {
3355
+ ...personalization.responseStyle,
3356
+ [key]: value
3357
+ }
3358
+ });
3359
+ };
3360
+ const updateUserProfile = (key, value) => {
3361
+ onPersonalizationChange({
3362
+ ...personalization,
3363
+ userProfile: {
3364
+ ...personalization.userProfile,
3365
+ [key]: value
3366
+ }
3367
+ });
3368
+ };
3369
+ const handleApiKeyChange = (value) => {
3370
+ setLocalApiKey(value);
3371
+ onApiKeyChange?.(value);
3372
+ };
3373
+ return /* @__PURE__ */ jsx12(
3374
+ "div",
3375
+ {
3376
+ className: "chatllm-settings-overlay",
3377
+ style: {
3378
+ position: "fixed",
3379
+ inset: 0,
3380
+ backgroundColor: "rgba(0, 0, 0, 0.3)",
3381
+ display: "flex",
3382
+ alignItems: "center",
3383
+ justifyContent: "center",
3384
+ zIndex: 1e3
3385
+ },
3386
+ onClick: onClose,
3387
+ children: /* @__PURE__ */ jsxs11(
3388
+ "div",
3389
+ {
3390
+ className: "chatllm-settings-modal",
3391
+ style: {
3392
+ backgroundColor: "var(--chatllm-bg, #ffffff)",
3393
+ borderRadius: "16px",
3394
+ width: "100%",
3395
+ maxWidth: "800px",
3396
+ height: "80vh",
3397
+ maxHeight: "600px",
3398
+ margin: "16px",
3399
+ boxShadow: "0 20px 60px rgba(0, 0, 0, 0.15)",
3400
+ display: "flex",
3401
+ overflow: "hidden"
3402
+ },
3403
+ onClick: (e) => e.stopPropagation(),
3404
+ children: [
3405
+ /* @__PURE__ */ jsxs11(
3406
+ "div",
3407
+ {
3408
+ style: {
3409
+ width: "200px",
3410
+ backgroundColor: "var(--chatllm-bg-secondary, #f9fafb)",
3411
+ borderRight: "1px solid var(--chatllm-border, #e5e7eb)",
3412
+ display: "flex",
3413
+ flexDirection: "column"
3414
+ },
3415
+ children: [
3416
+ /* @__PURE__ */ jsx12("div", { style: { padding: "16px", borderBottom: "1px solid var(--chatllm-border, #e5e7eb)" }, children: /* @__PURE__ */ jsx12(
3417
+ "button",
3418
+ {
3419
+ onClick: onClose,
3420
+ style: {
3421
+ padding: "8px",
3422
+ backgroundColor: "transparent",
3423
+ border: "none",
3424
+ borderRadius: "8px",
3425
+ cursor: "pointer",
3426
+ display: "flex",
3427
+ alignItems: "center",
3428
+ justifyContent: "center"
3429
+ },
3430
+ children: /* @__PURE__ */ jsx12(IconSvg, { name: "close-line", size: 20, color: "var(--chatllm-text-muted, #6b7280)" })
3431
+ }
3432
+ ) }),
3433
+ /* @__PURE__ */ jsxs11("nav", { style: { flex: 1, padding: "8px" }, children: [
3434
+ /* @__PURE__ */ jsx12(
3435
+ TabButton,
3436
+ {
3437
+ active: activeTab === "general",
3438
+ onClick: () => setActiveTab("general"),
3439
+ icon: "settings-3-line",
3440
+ label: "\uC77C\uBC18"
3441
+ }
3442
+ ),
3443
+ /* @__PURE__ */ jsx12(
3444
+ TabButton,
3445
+ {
3446
+ active: activeTab === "personalization",
3447
+ onClick: () => setActiveTab("personalization"),
3448
+ icon: "user-3-line",
3449
+ label: "\uAC1C\uC778 \uB9DE\uCDA4 \uC124\uC815"
3450
+ }
3451
+ ),
3452
+ /* @__PURE__ */ jsx12(
3453
+ TabButton,
3454
+ {
3455
+ active: activeTab === "data",
3456
+ onClick: () => setActiveTab("data"),
3457
+ icon: "delete-bin-line",
3458
+ label: "\uB370\uC774\uD130 \uC81C\uC5B4"
3459
+ }
3460
+ )
3461
+ ] })
3462
+ ]
3463
+ }
3464
+ ),
3465
+ /* @__PURE__ */ jsxs11("div", { style: { flex: 1, overflow: "auto", padding: "24px" }, children: [
3466
+ activeTab === "general" && /* @__PURE__ */ jsxs11("div", { children: [
3467
+ /* @__PURE__ */ jsx12("h2", { style: { fontSize: "20px", fontWeight: 600, marginBottom: "24px", color: "var(--chatllm-text, #1f2937)" }, children: "\uC77C\uBC18" }),
3468
+ /* @__PURE__ */ jsx12(SettingRow, { label: "\uC5B8\uC5B4", children: /* @__PURE__ */ jsxs11(
3469
+ "select",
3470
+ {
3471
+ value: personalization.language,
3472
+ onChange: (e) => onPersonalizationChange({ ...personalization, language: e.target.value }),
3473
+ style: selectStyle,
3474
+ children: [
3475
+ /* @__PURE__ */ jsx12("option", { value: "auto", children: "\uC790\uB3D9 \uD0D0\uC9C0" }),
3476
+ /* @__PURE__ */ jsx12("option", { value: "ko", children: "\uD55C\uAD6D\uC5B4" }),
3477
+ /* @__PURE__ */ jsx12("option", { value: "en", children: "English" }),
3478
+ /* @__PURE__ */ jsx12("option", { value: "ja", children: "\u65E5\u672C\u8A9E" })
3479
+ ]
3480
+ }
3481
+ ) }),
3482
+ /* @__PURE__ */ jsx12(SettingRow, { label: "\uAE30\uBCF8\uAC12\uC73C\uB85C \uCD08\uAE30\uD654", description: "\uBAA8\uB4E0 \uC124\uC815\uC744 \uCD08\uAE30 \uC0C1\uD0DC\uB85C \uB418\uB3CC\uB9BD\uB2C8\uB2E4", children: /* @__PURE__ */ jsx12(
3483
+ "button",
3484
+ {
3485
+ onClick: () => onPersonalizationChange(DEFAULT_PERSONALIZATION2),
3486
+ style: buttonSecondaryStyle,
3487
+ children: "\uCD08\uAE30\uD654"
3488
+ }
3489
+ ) }),
3490
+ onApiKeyChange && /* @__PURE__ */ jsxs11("div", { style: { marginTop: "32px", paddingTop: "24px", borderTop: "1px solid var(--chatllm-border, #e5e7eb)" }, children: [
3491
+ /* @__PURE__ */ jsx12("h3", { style: { fontSize: "16px", fontWeight: 500, marginBottom: "16px", color: "var(--chatllm-text, #1f2937)" }, children: "API \uC124\uC815" }),
3492
+ /* @__PURE__ */ jsxs11("div", { children: [
3493
+ /* @__PURE__ */ jsx12("label", { style: { display: "block", fontSize: "14px", marginBottom: "8px", color: "var(--chatllm-text, #374151)" }, children: apiKeyLabel }),
3494
+ /* @__PURE__ */ jsx12(
3495
+ "input",
3496
+ {
3497
+ type: "password",
3498
+ value: localApiKey,
3499
+ onChange: (e) => handleApiKeyChange(e.target.value),
3500
+ placeholder: "API \uD0A4\uB97C \uC785\uB825\uD558\uC138\uC694",
3501
+ style: inputStyle
3502
+ }
3503
+ ),
3504
+ /* @__PURE__ */ jsx12("p", { style: { fontSize: "12px", color: "var(--chatllm-text-muted, #9ca3af)", marginTop: "4px" }, children: apiKeyDescription })
3505
+ ] })
3506
+ ] })
3507
+ ] }),
3508
+ activeTab === "personalization" && /* @__PURE__ */ jsxs11("div", { children: [
3509
+ /* @__PURE__ */ jsx12("h2", { style: { fontSize: "20px", fontWeight: 600, marginBottom: "24px", color: "var(--chatllm-text, #1f2937)" }, children: "\uAC1C\uC778 \uB9DE\uCDA4 \uC124\uC815" }),
3510
+ /* @__PURE__ */ jsxs11("section", { style: { marginBottom: "32px" }, children: [
3511
+ /* @__PURE__ */ jsx12("h3", { style: { fontSize: "14px", fontWeight: 500, color: "var(--chatllm-text-muted, #6b7280)", marginBottom: "16px" }, children: "\uC0AC\uC6A9\uC790 \uD504\uB85C\uD544" }),
3512
+ /* @__PURE__ */ jsxs11("div", { style: { display: "flex", flexDirection: "column", gap: "12px" }, children: [
3513
+ /* @__PURE__ */ jsxs11("div", { children: [
3514
+ /* @__PURE__ */ jsx12("label", { style: labelStyle, children: "\uB2C9\uB124\uC784" }),
3515
+ /* @__PURE__ */ jsx12(
3516
+ "input",
3517
+ {
3518
+ type: "text",
3519
+ value: personalization.userProfile.nickname || "",
3520
+ onChange: (e) => updateUserProfile("nickname", e.target.value),
3521
+ placeholder: "\uC5B4\uB5BB\uAC8C \uBD88\uB7EC\uB4DC\uB9B4\uAE4C\uC694?",
3522
+ style: inputStyle
3523
+ }
3524
+ )
3525
+ ] }),
3526
+ /* @__PURE__ */ jsxs11("div", { children: [
3527
+ /* @__PURE__ */ jsx12("label", { style: labelStyle, children: "\uC9C1\uC5C5" }),
3528
+ /* @__PURE__ */ jsx12(
3529
+ "input",
3530
+ {
3531
+ type: "text",
3532
+ value: personalization.userProfile.occupation || "",
3533
+ onChange: (e) => updateUserProfile("occupation", e.target.value),
3534
+ placeholder: "\uC608: \uC18C\uD504\uD2B8\uC6E8\uC5B4 \uAC1C\uBC1C\uC790",
3535
+ style: inputStyle
3536
+ }
3537
+ )
3538
+ ] }),
3539
+ /* @__PURE__ */ jsxs11("div", { children: [
3540
+ /* @__PURE__ */ jsx12("label", { style: labelStyle, children: "\uCD94\uAC00 \uC815\uBCF4" }),
3541
+ /* @__PURE__ */ jsx12(
3542
+ "textarea",
3543
+ {
3544
+ value: personalization.userProfile.additionalInfo || "",
3545
+ onChange: (e) => updateUserProfile("additionalInfo", e.target.value),
3546
+ placeholder: "\uAD00\uC2EC\uC0AC, \uC120\uD638 \uC0AC\uD56D \uB4F1",
3547
+ rows: 3,
3548
+ style: { ...inputStyle, resize: "none" }
3549
+ }
3550
+ )
3551
+ ] })
3552
+ ] })
3553
+ ] }),
3554
+ /* @__PURE__ */ jsxs11("section", { children: [
3555
+ /* @__PURE__ */ jsx12("h3", { style: { fontSize: "14px", fontWeight: 500, color: "var(--chatllm-text-muted, #6b7280)", marginBottom: "16px" }, children: "\uC751\uB2F5 \uC2A4\uD0C0\uC77C" }),
3556
+ /* @__PURE__ */ jsx12(SettingRow, { label: "\uB530\uB73B\uD568", children: /* @__PURE__ */ jsxs11(
3557
+ "select",
3558
+ {
3559
+ value: personalization.responseStyle.warmth,
3560
+ onChange: (e) => updateResponseStyle("warmth", e.target.value),
3561
+ style: selectStyle,
3562
+ children: [
3563
+ /* @__PURE__ */ jsx12("option", { value: "high", children: "\uB192\uC74C - \uCE5C\uADFC\uD558\uACE0 \uB530\uB73B\uD558\uAC8C" }),
3564
+ /* @__PURE__ */ jsx12("option", { value: "medium", children: "\uAE30\uBCF8\uAC12" }),
3565
+ /* @__PURE__ */ jsx12("option", { value: "low", children: "\uB0AE\uC74C - \uAC04\uACB0\uD558\uACE0 \uC0AC\uBB34\uC801\uC73C\uB85C" })
3566
+ ]
3567
+ }
3568
+ ) }),
3569
+ /* @__PURE__ */ jsx12(SettingRow, { label: "\uC5F4\uC815\uC801", children: /* @__PURE__ */ jsxs11(
3570
+ "select",
3571
+ {
3572
+ value: personalization.responseStyle.enthusiasm,
3573
+ onChange: (e) => updateResponseStyle("enthusiasm", e.target.value),
3574
+ style: selectStyle,
3575
+ children: [
3576
+ /* @__PURE__ */ jsx12("option", { value: "high", children: "\uB192\uC74C - \uC801\uADF9\uC801\uC774\uACE0 \uD65C\uBC1C\uD558\uAC8C" }),
3577
+ /* @__PURE__ */ jsx12("option", { value: "medium", children: "\uAE30\uBCF8\uAC12" }),
3578
+ /* @__PURE__ */ jsx12("option", { value: "low", children: "\uB0AE\uC74C - \uCC28\uBD84\uD558\uACE0 \uC808\uC81C\uC788\uAC8C" })
3579
+ ]
3580
+ }
3581
+ ) }),
3582
+ /* @__PURE__ */ jsx12(SettingRow, { label: "\uC774\uBAA8\uC9C0 \uC0AC\uC6A9", children: /* @__PURE__ */ jsxs11(
3583
+ "select",
3584
+ {
3585
+ value: personalization.responseStyle.emojiUsage,
3586
+ onChange: (e) => updateResponseStyle("emojiUsage", e.target.value),
3587
+ style: selectStyle,
3588
+ children: [
3589
+ /* @__PURE__ */ jsx12("option", { value: "high", children: "\uB192\uC74C - \uC790\uC8FC \uC0AC\uC6A9" }),
3590
+ /* @__PURE__ */ jsx12("option", { value: "medium", children: "\uAE30\uBCF8\uAC12" }),
3591
+ /* @__PURE__ */ jsx12("option", { value: "low", children: "\uB0AE\uC74C - \uAC70\uC758 \uC0AC\uC6A9 \uC548 \uD568" })
3592
+ ]
3593
+ }
3594
+ ) }),
3595
+ /* @__PURE__ */ jsx12(SettingRow, { label: "\uC751\uB2F5 \uAE38\uC774", children: /* @__PURE__ */ jsxs11(
3596
+ "select",
3597
+ {
3598
+ value: personalization.responseStyle.verbosity,
3599
+ onChange: (e) => updateResponseStyle("verbosity", e.target.value),
3600
+ style: selectStyle,
3601
+ children: [
3602
+ /* @__PURE__ */ jsx12("option", { value: "detailed", children: "\uC0C1\uC138 - \uC790\uC138\uD558\uAC8C \uC124\uBA85" }),
3603
+ /* @__PURE__ */ jsx12("option", { value: "balanced", children: "\uAE30\uBCF8\uAC12" }),
3604
+ /* @__PURE__ */ jsx12("option", { value: "concise", children: "\uAC04\uACB0 - \uD575\uC2EC\uB9CC \uC694\uC57D" })
3605
+ ]
3606
+ }
3607
+ ) })
3608
+ ] })
3609
+ ] }),
3610
+ activeTab === "data" && /* @__PURE__ */ jsxs11("div", { children: [
3611
+ /* @__PURE__ */ jsx12("h2", { style: { fontSize: "20px", fontWeight: 600, marginBottom: "24px", color: "var(--chatllm-text, #1f2937)" }, children: "\uB370\uC774\uD130 \uC81C\uC5B4" }),
3612
+ /* @__PURE__ */ jsx12(SettingRow, { label: "\uBA54\uBAA8\uB9AC \uC0AC\uC6A9", description: "\uB300\uD654 \uCEE8\uD14D\uC2A4\uD2B8\uB97C \uAE30\uC5B5\uD569\uB2C8\uB2E4", children: /* @__PURE__ */ jsx12(
3613
+ "button",
3614
+ {
3615
+ onClick: () => onPersonalizationChange({ ...personalization, useMemory: !personalization.useMemory }),
3616
+ style: {
3617
+ width: "48px",
3618
+ height: "28px",
3619
+ borderRadius: "14px",
3620
+ backgroundColor: personalization.useMemory ? "var(--chatllm-primary, #3b82f6)" : "var(--chatllm-text-muted, #d1d5db)",
3621
+ border: "none",
3622
+ cursor: "pointer",
3623
+ position: "relative",
3624
+ transition: "background-color 0.2s"
3625
+ },
3626
+ children: /* @__PURE__ */ jsx12(
3627
+ "div",
3628
+ {
3629
+ style: {
3630
+ width: "22px",
3631
+ height: "22px",
3632
+ borderRadius: "50%",
3633
+ backgroundColor: "#ffffff",
3634
+ boxShadow: "0 2px 4px rgba(0, 0, 0, 0.1)",
3635
+ position: "absolute",
3636
+ top: "3px",
3637
+ left: personalization.useMemory ? "23px" : "3px",
3638
+ transition: "left 0.2s"
3639
+ }
3640
+ }
3641
+ )
3642
+ }
3643
+ ) }),
3644
+ onClearAllData && /* @__PURE__ */ jsx12(SettingRow, { label: "\uB300\uD654 \uAE30\uB85D \uC0AD\uC81C", description: "\uBAA8\uB4E0 \uB300\uD654 \uAE30\uB85D\uC744 \uC0AD\uC81C\uD569\uB2C8\uB2E4", children: /* @__PURE__ */ jsx12(
3645
+ "button",
3646
+ {
3647
+ onClick: () => {
3648
+ if (window.confirm("\uBAA8\uB4E0 \uB300\uD654 \uAE30\uB85D\uC744 \uC0AD\uC81C\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?")) {
3649
+ onClearAllData();
3650
+ }
3651
+ },
3652
+ style: buttonDangerStyle,
3653
+ children: "\uC0AD\uC81C"
3654
+ }
3655
+ ) })
3656
+ ] })
3657
+ ] })
3658
+ ]
3659
+ }
3660
+ )
3661
+ }
3662
+ );
3663
+ };
3664
+ var TabButton = ({ active, onClick, icon, label }) => /* @__PURE__ */ jsxs11(
3665
+ "button",
3666
+ {
3667
+ onClick,
3668
+ style: {
3669
+ width: "100%",
3670
+ display: "flex",
3671
+ alignItems: "center",
3672
+ gap: "12px",
3673
+ padding: "10px 12px",
3674
+ borderRadius: "10px",
3675
+ border: "none",
3676
+ backgroundColor: active ? "var(--chatllm-bg, #ffffff)" : "transparent",
3677
+ boxShadow: active ? "0 1px 3px rgba(0, 0, 0, 0.08)" : "none",
3678
+ color: active ? "var(--chatllm-primary, #3b82f6)" : "var(--chatllm-text-muted, #6b7280)",
3679
+ fontSize: "14px",
3680
+ cursor: "pointer",
3681
+ textAlign: "left",
3682
+ marginBottom: "4px"
3683
+ },
3684
+ children: [
3685
+ /* @__PURE__ */ jsx12(IconSvg, { name: icon, size: 20 }),
3686
+ label
3687
+ ]
3688
+ }
3689
+ );
3690
+ var SettingRow = ({ label, description, children }) => /* @__PURE__ */ jsxs11(
3691
+ "div",
3692
+ {
3693
+ style: {
3694
+ display: "flex",
3695
+ alignItems: "center",
3696
+ justifyContent: "space-between",
3697
+ padding: "12px 0",
3698
+ borderBottom: "1px solid var(--chatllm-border-light, #f3f4f6)"
3699
+ },
3700
+ children: [
3701
+ /* @__PURE__ */ jsxs11("div", { children: [
3702
+ /* @__PURE__ */ jsx12("span", { style: { fontSize: "14px", color: "var(--chatllm-text, #374151)" }, children: label }),
3703
+ description && /* @__PURE__ */ jsx12("p", { style: { fontSize: "12px", color: "var(--chatllm-text-muted, #9ca3af)", marginTop: "2px" }, children: description })
3704
+ ] }),
3705
+ children
3706
+ ]
3707
+ }
3708
+ );
3709
+ var selectStyle = {
3710
+ padding: "8px 12px",
3711
+ backgroundColor: "var(--chatllm-bg-secondary, #f9fafb)",
3712
+ border: "1px solid var(--chatllm-border, #e5e7eb)",
3713
+ borderRadius: "8px",
3714
+ fontSize: "14px",
3715
+ color: "var(--chatllm-text, #374151)",
3716
+ minWidth: "200px",
3717
+ cursor: "pointer"
3718
+ };
3719
+ var inputStyle = {
3720
+ width: "100%",
3721
+ padding: "10px 12px",
3722
+ backgroundColor: "var(--chatllm-bg-secondary, #f9fafb)",
3723
+ border: "1px solid var(--chatllm-border, #e5e7eb)",
3724
+ borderRadius: "8px",
3725
+ fontSize: "14px",
3726
+ color: "var(--chatllm-text, #374151)"
3727
+ };
3728
+ var labelStyle = {
3729
+ display: "block",
3730
+ fontSize: "12px",
3731
+ color: "var(--chatllm-text-muted, #6b7280)",
3732
+ marginBottom: "6px"
3733
+ };
3734
+ var buttonSecondaryStyle = {
3735
+ padding: "8px 16px",
3736
+ backgroundColor: "var(--chatllm-bg-secondary, #f3f4f6)",
3737
+ border: "none",
3738
+ borderRadius: "8px",
3739
+ fontSize: "14px",
3740
+ color: "var(--chatllm-text, #374151)",
3741
+ cursor: "pointer"
3742
+ };
3743
+ var buttonDangerStyle = {
3744
+ padding: "8px 16px",
3745
+ backgroundColor: "var(--chatllm-danger-bg, #fef2f2)",
3746
+ border: "none",
3747
+ borderRadius: "8px",
3748
+ fontSize: "14px",
3749
+ color: "var(--chatllm-danger, #dc2626)",
3750
+ cursor: "pointer"
3751
+ };
3752
+ export {
3753
+ ChatHeader,
3754
+ ChatInput,
3755
+ ChatSidebar,
3756
+ ChatUI,
3757
+ EmptyState,
3758
+ Icon,
3759
+ IconSvg,
3760
+ LinkChip,
3761
+ MarkdownRenderer,
3762
+ MemoryPanel,
3763
+ MessageBubble,
3764
+ MessageList,
3765
+ SettingsModal,
3766
+ useChatUI
3767
+ };
3768
+ //# sourceMappingURL=index.mjs.map