@gendive/chatllm 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -246,6 +246,13 @@ interface ChatUIProps {
246
246
  onSessionChange?: (session: ChatSession | null) => void;
247
247
  /** 에러 핸들러 */
248
248
  onError?: (error: Error) => void;
249
+ /** @Todo vibecode - 세션 제목 변경 핸들러 */
250
+ onTitleChange?: (sessionId: string, newTitle: string) => void;
251
+ /**
252
+ * @description 첫 메시지 전송 시 LLM으로 제목 생성 콜백
253
+ * @Todo vibecode - 자동 제목 생성 기능
254
+ */
255
+ generateTitle?: (firstMessage: string) => Promise<string> | string;
249
256
  /** 재압축 임계값 - 새 메시지 수 (기본: 10) */
250
257
  recompressionThreshold?: number;
251
258
  /** 토큰 한도 (기본: 8000) */
@@ -300,6 +307,8 @@ interface SidebarProps {
300
307
  onSelectSession: (id: string) => void;
301
308
  onNewSession: () => void;
302
309
  onDeleteSession: (id: string) => void;
310
+ /** @Todo vibecode - 세션 제목 변경 핸들러 */
311
+ onRenameSession?: (id: string, newTitle: string) => void;
303
312
  isOpen: boolean;
304
313
  onToggle: () => void;
305
314
  }
@@ -404,6 +413,8 @@ interface UseChatUIReturn {
404
413
  newSession: () => void;
405
414
  selectSession: (id: string) => void;
406
415
  deleteSession: (id: string) => void;
416
+ /** @Todo vibecode - 세션 제목 변경 함수 */
417
+ renameSession: (id: string, newTitle: string) => void;
407
418
  setModel: (model: string) => void;
408
419
  toggleSidebar: () => void;
409
420
  openSettings: () => void;
@@ -465,6 +476,13 @@ interface UseChatUIOptions {
465
476
  onSessionChange?: (session: ChatSession | null) => void;
466
477
  /** 에러 핸들러 */
467
478
  onError?: (error: Error) => void;
479
+ /** @Todo vibecode - 세션 제목 변경 핸들러 */
480
+ onTitleChange?: (sessionId: string, newTitle: string) => void;
481
+ /**
482
+ * @description 첫 메시지 전송 시 LLM으로 제목 생성 콜백
483
+ * @Todo vibecode - 자동 제목 생성 기능
484
+ */
485
+ generateTitle?: (firstMessage: string) => Promise<string> | string;
468
486
  /** 글로벌 메모리 사용 여부 (기본: true) */
469
487
  useGlobalMemoryEnabled?: boolean;
470
488
  /** 글로벌 메모리 설정 */
@@ -246,6 +246,13 @@ interface ChatUIProps {
246
246
  onSessionChange?: (session: ChatSession | null) => void;
247
247
  /** 에러 핸들러 */
248
248
  onError?: (error: Error) => void;
249
+ /** @Todo vibecode - 세션 제목 변경 핸들러 */
250
+ onTitleChange?: (sessionId: string, newTitle: string) => void;
251
+ /**
252
+ * @description 첫 메시지 전송 시 LLM으로 제목 생성 콜백
253
+ * @Todo vibecode - 자동 제목 생성 기능
254
+ */
255
+ generateTitle?: (firstMessage: string) => Promise<string> | string;
249
256
  /** 재압축 임계값 - 새 메시지 수 (기본: 10) */
250
257
  recompressionThreshold?: number;
251
258
  /** 토큰 한도 (기본: 8000) */
@@ -300,6 +307,8 @@ interface SidebarProps {
300
307
  onSelectSession: (id: string) => void;
301
308
  onNewSession: () => void;
302
309
  onDeleteSession: (id: string) => void;
310
+ /** @Todo vibecode - 세션 제목 변경 핸들러 */
311
+ onRenameSession?: (id: string, newTitle: string) => void;
303
312
  isOpen: boolean;
304
313
  onToggle: () => void;
305
314
  }
@@ -404,6 +413,8 @@ interface UseChatUIReturn {
404
413
  newSession: () => void;
405
414
  selectSession: (id: string) => void;
406
415
  deleteSession: (id: string) => void;
416
+ /** @Todo vibecode - 세션 제목 변경 함수 */
417
+ renameSession: (id: string, newTitle: string) => void;
407
418
  setModel: (model: string) => void;
408
419
  toggleSidebar: () => void;
409
420
  openSettings: () => void;
@@ -465,6 +476,13 @@ interface UseChatUIOptions {
465
476
  onSessionChange?: (session: ChatSession | null) => void;
466
477
  /** 에러 핸들러 */
467
478
  onError?: (error: Error) => void;
479
+ /** @Todo vibecode - 세션 제목 변경 핸들러 */
480
+ onTitleChange?: (sessionId: string, newTitle: string) => void;
481
+ /**
482
+ * @description 첫 메시지 전송 시 LLM으로 제목 생성 콜백
483
+ * @Todo vibecode - 자동 제목 생성 기능
484
+ */
485
+ generateTitle?: (firstMessage: string) => Promise<string> | string;
468
486
  /** 글로벌 메모리 사용 여부 (기본: true) */
469
487
  useGlobalMemoryEnabled?: boolean;
470
488
  /** 글로벌 메모리 설정 */
@@ -48,7 +48,7 @@ __export(index_exports, {
48
48
  module.exports = __toCommonJS(index_exports);
49
49
 
50
50
  // src/react/ChatUI.tsx
51
- var import_react12 = __toESM(require("react"));
51
+ var import_react13 = __toESM(require("react"));
52
52
 
53
53
  // src/react/hooks/useChatUI.ts
54
54
  var import_react3 = require("react");
@@ -563,6 +563,8 @@ var useChatUI = (options) => {
563
563
  onSendMessage,
564
564
  onSessionChange,
565
565
  onError,
566
+ onTitleChange,
567
+ generateTitle: generateTitleCallback,
566
568
  // Memory options
567
569
  useGlobalMemoryEnabled = true,
568
570
  globalMemoryConfig,
@@ -817,6 +819,15 @@ ${newConversation}
817
819
  return filtered;
818
820
  });
819
821
  }, [currentSessionId, storageKey]);
822
+ const renameSession = (0, import_react3.useCallback)((id, newTitle) => {
823
+ if (!newTitle.trim()) return;
824
+ setSessions(
825
+ (prev) => prev.map(
826
+ (s) => s.id === id ? { ...s, title: newTitle.trim(), updatedAt: Date.now() } : s
827
+ )
828
+ );
829
+ onTitleChange?.(id, newTitle.trim());
830
+ }, [onTitleChange]);
820
831
  const setModel = (0, import_react3.useCallback)((model) => {
821
832
  setSelectedModel(model);
822
833
  if (currentSessionId) {
@@ -896,6 +907,7 @@ ${finalContent}`;
896
907
  setQuotedText(null);
897
908
  setSelectedAction(null);
898
909
  const capturedSessionId = sessionId;
910
+ const isFirstMessage = !sessions.find((s) => s.id === capturedSessionId)?.messages.length;
899
911
  setSessions(
900
912
  (prev) => prev.map((s) => {
901
913
  if (s.id === capturedSessionId) {
@@ -910,6 +922,19 @@ ${finalContent}`;
910
922
  return s;
911
923
  })
912
924
  );
925
+ if (isFirstMessage && generateTitleCallback) {
926
+ Promise.resolve(generateTitleCallback(finalContent)).then((generatedTitle) => {
927
+ if (generatedTitle && generatedTitle.trim()) {
928
+ setSessions(
929
+ (prev) => prev.map(
930
+ (s) => s.id === capturedSessionId ? { ...s, title: generatedTitle.trim(), updatedAt: Date.now() } : s
931
+ )
932
+ );
933
+ onTitleChange?.(capturedSessionId, generatedTitle.trim());
934
+ }
935
+ }).catch(() => {
936
+ });
937
+ }
913
938
  setIsLoading(true);
914
939
  abortControllerRef.current = new AbortController();
915
940
  try {
@@ -1109,7 +1134,9 @@ ${contextSummary}` },
1109
1134
  buildSystemPrompt,
1110
1135
  compressContext,
1111
1136
  onSendMessage,
1112
- onError
1137
+ onError,
1138
+ generateTitleCallback,
1139
+ onTitleChange
1113
1140
  ]);
1114
1141
  const saveEdit = (0, import_react3.useCallback)(async (content) => {
1115
1142
  if (!editingMessageId || !currentSession || !currentSessionId) return;
@@ -1335,6 +1362,7 @@ ${currentSession.contextSummary}` },
1335
1362
  newSession,
1336
1363
  selectSession,
1337
1364
  deleteSession,
1365
+ renameSession,
1338
1366
  setModel,
1339
1367
  toggleSidebar,
1340
1368
  openSettings,
@@ -1365,6 +1393,9 @@ ${currentSession.contextSummary}` },
1365
1393
  };
1366
1394
  };
1367
1395
 
1396
+ // src/react/components/ChatSidebar.tsx
1397
+ var import_react4 = require("react");
1398
+
1368
1399
  // src/react/components/Icon.tsx
1369
1400
  var import_jsx_runtime = require("react/jsx-runtime");
1370
1401
  var Icon = ({
@@ -1475,9 +1506,44 @@ var ChatSidebar = ({
1475
1506
  onSelectSession,
1476
1507
  onNewSession,
1477
1508
  onDeleteSession,
1509
+ onRenameSession,
1478
1510
  isOpen,
1479
1511
  onToggle
1480
1512
  }) => {
1513
+ const [editingId, setEditingId] = (0, import_react4.useState)(null);
1514
+ const [editingTitle, setEditingTitle] = (0, import_react4.useState)("");
1515
+ const inputRef = (0, import_react4.useRef)(null);
1516
+ (0, import_react4.useEffect)(() => {
1517
+ if (editingId && inputRef.current) {
1518
+ inputRef.current.focus();
1519
+ inputRef.current.select();
1520
+ }
1521
+ }, [editingId]);
1522
+ const handleStartEdit = (session, e) => {
1523
+ e.stopPropagation();
1524
+ setEditingId(session.id);
1525
+ setEditingTitle(session.title);
1526
+ };
1527
+ const handleSaveEdit = () => {
1528
+ if (editingId && editingTitle.trim() && onRenameSession) {
1529
+ onRenameSession(editingId, editingTitle.trim());
1530
+ }
1531
+ setEditingId(null);
1532
+ setEditingTitle("");
1533
+ };
1534
+ const handleCancelEdit = () => {
1535
+ setEditingId(null);
1536
+ setEditingTitle("");
1537
+ };
1538
+ const handleKeyDown = (e) => {
1539
+ if (e.key === "Enter") {
1540
+ e.preventDefault();
1541
+ handleSaveEdit();
1542
+ } else if (e.key === "Escape") {
1543
+ e.preventDefault();
1544
+ handleCancelEdit();
1545
+ }
1546
+ };
1481
1547
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1482
1548
  "aside",
1483
1549
  {
@@ -1587,7 +1653,30 @@ var ChatSidebar = ({
1587
1653
  },
1588
1654
  children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-start" }, children: [
1589
1655
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
1590
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1656
+ editingId === session.id ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1657
+ "input",
1658
+ {
1659
+ ref: inputRef,
1660
+ type: "text",
1661
+ value: editingTitle,
1662
+ onChange: (e) => setEditingTitle(e.target.value),
1663
+ onKeyDown: handleKeyDown,
1664
+ onBlur: handleSaveEdit,
1665
+ onClick: (e) => e.stopPropagation(),
1666
+ "aria-label": "\uC138\uC158 \uC81C\uBAA9 \uD3B8\uC9D1",
1667
+ style: {
1668
+ width: "100%",
1669
+ padding: "4px 8px",
1670
+ fontSize: "14px",
1671
+ fontWeight: session.id === currentSessionId ? 500 : 400,
1672
+ color: "var(--chatllm-text, #1f2937)",
1673
+ backgroundColor: "var(--chatllm-bg, #ffffff)",
1674
+ border: "1px solid var(--chatllm-primary, #3b82f6)",
1675
+ borderRadius: "4px",
1676
+ outline: "none"
1677
+ }
1678
+ }
1679
+ ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1591
1680
  "div",
1592
1681
  {
1593
1682
  style: {
@@ -1613,31 +1702,57 @@ var ChatSidebar = ({
1613
1702
  }
1614
1703
  )
1615
1704
  ] }),
1616
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1617
- "button",
1618
- {
1619
- onClick: (e) => {
1620
- e.stopPropagation();
1621
- onDeleteSession(session.id);
1622
- },
1623
- style: {
1624
- padding: "4px",
1625
- backgroundColor: "transparent",
1626
- border: "none",
1627
- borderRadius: "4px",
1628
- cursor: "pointer",
1629
- opacity: 0.5,
1630
- transition: "opacity 0.2s"
1631
- },
1632
- onMouseOver: (e) => {
1633
- e.currentTarget.style.opacity = "1";
1634
- },
1635
- onMouseOut: (e) => {
1636
- e.currentTarget.style.opacity = "0.5";
1637
- },
1638
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(IconSvg, { name: "delete-bin-line", size: 16, color: "var(--chatllm-text-muted, #9ca3af)" })
1639
- }
1640
- )
1705
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", gap: "2px" }, children: [
1706
+ onRenameSession && editingId !== session.id && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1707
+ "button",
1708
+ {
1709
+ onClick: (e) => handleStartEdit(session, e),
1710
+ "aria-label": "\uC81C\uBAA9 \uD3B8\uC9D1",
1711
+ style: {
1712
+ padding: "4px",
1713
+ backgroundColor: "transparent",
1714
+ border: "none",
1715
+ borderRadius: "4px",
1716
+ cursor: "pointer",
1717
+ opacity: 0.5,
1718
+ transition: "opacity 0.2s"
1719
+ },
1720
+ onMouseOver: (e) => {
1721
+ e.currentTarget.style.opacity = "1";
1722
+ },
1723
+ onMouseOut: (e) => {
1724
+ e.currentTarget.style.opacity = "0.5";
1725
+ },
1726
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(IconSvg, { name: "pencil-line", size: 16, color: "var(--chatllm-text-muted, #9ca3af)" })
1727
+ }
1728
+ ),
1729
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1730
+ "button",
1731
+ {
1732
+ onClick: (e) => {
1733
+ e.stopPropagation();
1734
+ onDeleteSession(session.id);
1735
+ },
1736
+ "aria-label": "\uC138\uC158 \uC0AD\uC81C",
1737
+ style: {
1738
+ padding: "4px",
1739
+ backgroundColor: "transparent",
1740
+ border: "none",
1741
+ borderRadius: "4px",
1742
+ cursor: "pointer",
1743
+ opacity: 0.5,
1744
+ transition: "opacity 0.2s"
1745
+ },
1746
+ onMouseOver: (e) => {
1747
+ e.currentTarget.style.opacity = "1";
1748
+ },
1749
+ onMouseOut: (e) => {
1750
+ e.currentTarget.style.opacity = "0.5";
1751
+ },
1752
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(IconSvg, { name: "delete-bin-line", size: 16, color: "var(--chatllm-text-muted, #9ca3af)" })
1753
+ }
1754
+ )
1755
+ ] })
1641
1756
  ] })
1642
1757
  },
1643
1758
  session.id
@@ -1669,7 +1784,7 @@ var ChatSidebar = ({
1669
1784
  };
1670
1785
 
1671
1786
  // src/react/components/ChatHeader.tsx
1672
- var import_react4 = require("react");
1787
+ var import_react5 = require("react");
1673
1788
  var import_jsx_runtime3 = require("react/jsx-runtime");
1674
1789
  var ChatHeader = ({
1675
1790
  title,
@@ -1682,7 +1797,7 @@ var ChatHeader = ({
1682
1797
  showModelSelector = true,
1683
1798
  showSettings = true
1684
1799
  }) => {
1685
- const [modelDropdownOpen, setModelDropdownOpen] = (0, import_react4.useState)(false);
1800
+ const [modelDropdownOpen, setModelDropdownOpen] = (0, import_react5.useState)(false);
1686
1801
  const currentModel = models.find((m) => m.id === model);
1687
1802
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1688
1803
  "header",
@@ -1936,7 +2051,7 @@ var ChatHeader = ({
1936
2051
  };
1937
2052
 
1938
2053
  // src/react/components/ChatInput.tsx
1939
- var import_react5 = require("react");
2054
+ var import_react6 = require("react");
1940
2055
  var import_jsx_runtime4 = require("react/jsx-runtime");
1941
2056
  var ChatInput = ({
1942
2057
  value,
@@ -1952,9 +2067,9 @@ var ChatInput = ({
1952
2067
  onActionSelect,
1953
2068
  actions = []
1954
2069
  }) => {
1955
- const textareaRef = (0, import_react5.useRef)(null);
1956
- const [actionMenuOpen, setActionMenuOpen] = (0, import_react5.useState)(false);
1957
- (0, import_react5.useEffect)(() => {
2070
+ const textareaRef = (0, import_react6.useRef)(null);
2071
+ const [actionMenuOpen, setActionMenuOpen] = (0, import_react6.useState)(false);
2072
+ (0, import_react6.useEffect)(() => {
1958
2073
  if (textareaRef.current) {
1959
2074
  textareaRef.current.style.height = "auto";
1960
2075
  textareaRef.current.style.height = `${Math.min(textareaRef.current.scrollHeight, 200)}px`;
@@ -2252,16 +2367,16 @@ var ChatInput = ({
2252
2367
  };
2253
2368
 
2254
2369
  // src/react/components/MessageList.tsx
2255
- var import_react9 = require("react");
2370
+ var import_react10 = require("react");
2256
2371
 
2257
2372
  // src/react/components/MessageBubble.tsx
2258
- var import_react8 = require("react");
2373
+ var import_react9 = require("react");
2259
2374
 
2260
2375
  // src/react/components/MarkdownRenderer.tsx
2261
- var import_react7 = __toESM(require("react"));
2376
+ var import_react8 = __toESM(require("react"));
2262
2377
 
2263
2378
  // src/react/components/LinkChip.tsx
2264
- var import_react6 = require("react");
2379
+ var import_react7 = require("react");
2265
2380
  var import_jsx_runtime5 = require("react/jsx-runtime");
2266
2381
  var getDomain = (url) => {
2267
2382
  try {
@@ -2315,7 +2430,7 @@ var LinkChip = ({
2315
2430
  index,
2316
2431
  style
2317
2432
  }) => {
2318
- const [isHovered, setIsHovered] = (0, import_react6.useState)(false);
2433
+ const [isHovered, setIsHovered] = (0, import_react7.useState)(false);
2319
2434
  const domain = getDomain(url);
2320
2435
  const shortName = getShortName(domain);
2321
2436
  const domainColor = getDomainColor(domain);
@@ -2610,7 +2725,7 @@ var MarkdownTable = ({ data }) => {
2610
2725
  );
2611
2726
  };
2612
2727
  var CodeBlock = ({ language, code }) => {
2613
- const [copied, setCopied] = import_react7.default.useState(false);
2728
+ const [copied, setCopied] = import_react8.default.useState(false);
2614
2729
  const handleCopy = async () => {
2615
2730
  try {
2616
2731
  await navigator.clipboard.writeText(code);
@@ -2693,9 +2808,9 @@ var CodeBlock = ({ language, code }) => {
2693
2808
  );
2694
2809
  };
2695
2810
  var ImageWithCopyButton = ({ src, alt, imageKey }) => {
2696
- const [isHovered, setIsHovered] = import_react7.default.useState(false);
2697
- const [copyState, setCopyState] = import_react7.default.useState("idle");
2698
- const imgRef = import_react7.default.useRef(null);
2811
+ const [isHovered, setIsHovered] = import_react8.default.useState(false);
2812
+ const [copyState, setCopyState] = import_react8.default.useState("idle");
2813
+ const imgRef = import_react8.default.useRef(null);
2699
2814
  const getImageBlob = async () => {
2700
2815
  const img = imgRef.current;
2701
2816
  if (img && img.complete && img.naturalWidth > 0) {
@@ -2911,7 +3026,7 @@ var ImageWithCopyButton = ({ src, alt, imageKey }) => {
2911
3026
  );
2912
3027
  };
2913
3028
  var ChoiceButtons = ({ choices, title, onChoiceClick }) => {
2914
- const [hoveredIndex, setHoveredIndex] = import_react7.default.useState(null);
3029
+ const [hoveredIndex, setHoveredIndex] = import_react8.default.useState(null);
2915
3030
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2916
3031
  "div",
2917
3032
  {
@@ -3034,7 +3149,7 @@ var SourceLinksSection = ({ links, label }) => {
3034
3149
  );
3035
3150
  };
3036
3151
  var MarkdownRenderer = ({ content, className, onChoiceClick }) => {
3037
- const rendered = (0, import_react7.useMemo)(() => {
3152
+ const rendered = (0, import_react8.useMemo)(() => {
3038
3153
  const elements = [];
3039
3154
  let processedContent = content;
3040
3155
  const codeBlocks = [];
@@ -3132,7 +3247,7 @@ var MarkdownRenderer = ({ content, className, onChoiceClick }) => {
3132
3247
  borderRadius: "0 8px 8px 0",
3133
3248
  color: "var(--chatllm-text, #374151)"
3134
3249
  },
3135
- children: blockquoteLines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react7.default.Fragment, { children: [
3250
+ children: blockquoteLines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react8.default.Fragment, { children: [
3136
3251
  parseInlineElements(line, `bq-line-${i}`),
3137
3252
  i < blockquoteLines.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("br", {})
3138
3253
  ] }, i))
@@ -3285,9 +3400,16 @@ var MarkdownRenderer = ({ content, className, onChoiceClick }) => {
3285
3400
  return;
3286
3401
  }
3287
3402
  }
3288
- elements.push(
3289
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: { margin: "4px 0" }, children: parseInlineElements(line, `p-${lineIndex}`) }, `p-${lineIndex}`)
3290
- );
3403
+ const hasImage = IMAGE_REGEX.test(line);
3404
+ if (hasImage) {
3405
+ elements.push(
3406
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { margin: "4px 0" }, children: parseInlineElements(line, `p-${lineIndex}`) }, `p-${lineIndex}`)
3407
+ );
3408
+ } else {
3409
+ elements.push(
3410
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: { margin: "4px 0" }, children: parseInlineElements(line, `p-${lineIndex}`) }, `p-${lineIndex}`)
3411
+ );
3412
+ }
3291
3413
  });
3292
3414
  flushList();
3293
3415
  flushBlockquote();
@@ -3327,8 +3449,8 @@ var MessageBubble = ({
3327
3449
  nextAssistantMessage,
3328
3450
  onChoiceClick
3329
3451
  }) => {
3330
- const [showActions, setShowActions] = (0, import_react8.useState)(false);
3331
- const [showModelMenu, setShowModelMenu] = (0, import_react8.useState)(false);
3452
+ const [showActions, setShowActions] = (0, import_react9.useState)(false);
3453
+ const [showModelMenu, setShowModelMenu] = (0, import_react9.useState)(false);
3332
3454
  const isUser = message.role === "user";
3333
3455
  const isAssistant = message.role === "assistant";
3334
3456
  const relevantAlternatives = isUser ? alternatives : message.alternatives;
@@ -3721,14 +3843,14 @@ var MessageList = ({
3721
3843
  editingId,
3722
3844
  onChoiceClick
3723
3845
  }) => {
3724
- const messagesEndRef = (0, import_react9.useRef)(null);
3725
- const containerRef = (0, import_react9.useRef)(null);
3726
- const [selectedText, setSelectedText] = (0, import_react9.useState)("");
3727
- const [selectionPosition, setSelectionPosition] = (0, import_react9.useState)(null);
3728
- (0, import_react9.useEffect)(() => {
3846
+ const messagesEndRef = (0, import_react10.useRef)(null);
3847
+ const containerRef = (0, import_react10.useRef)(null);
3848
+ const [selectedText, setSelectedText] = (0, import_react10.useState)("");
3849
+ const [selectionPosition, setSelectionPosition] = (0, import_react10.useState)(null);
3850
+ (0, import_react10.useEffect)(() => {
3729
3851
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
3730
3852
  }, [messages]);
3731
- const handleMouseUp = (0, import_react9.useCallback)(() => {
3853
+ const handleMouseUp = (0, import_react10.useCallback)(() => {
3732
3854
  const selection = window.getSelection();
3733
3855
  const text = selection?.toString().trim();
3734
3856
  if (text && text.length > 0) {
@@ -4105,7 +4227,7 @@ var EmptyState = ({
4105
4227
  };
4106
4228
 
4107
4229
  // src/react/components/MemoryPanel.tsx
4108
- var import_react10 = require("react");
4230
+ var import_react11 = require("react");
4109
4231
  var import_jsx_runtime10 = require("react/jsx-runtime");
4110
4232
  var categoryLabels = {
4111
4233
  context: "\uB300\uD654 \uCEE8\uD14D\uC2A4\uD2B8",
@@ -4127,8 +4249,8 @@ var MemoryPanel = ({
4127
4249
  isOpen,
4128
4250
  onToggle
4129
4251
  }) => {
4130
- const [expandedId, setExpandedId] = (0, import_react10.useState)(null);
4131
- const [activeTab, setActiveTab] = (0, import_react10.useState)("all");
4252
+ const [expandedId, setExpandedId] = (0, import_react11.useState)(null);
4253
+ const [activeTab, setActiveTab] = (0, import_react11.useState)("all");
4132
4254
  const filteredItems = activeTab === "all" ? items : items.filter((item) => item.category === activeTab);
4133
4255
  const formatDate2 = (timestamp) => {
4134
4256
  const date = new Date(timestamp);
@@ -4446,7 +4568,7 @@ var MemoryPanel = ({
4446
4568
  };
4447
4569
 
4448
4570
  // src/react/components/SettingsModal.tsx
4449
- var import_react11 = require("react");
4571
+ var import_react12 = require("react");
4450
4572
  var import_jsx_runtime11 = require("react/jsx-runtime");
4451
4573
  var DEFAULT_PERSONALIZATION2 = {
4452
4574
  responseStyle: {
@@ -4471,8 +4593,8 @@ var SettingsModal = ({
4471
4593
  apiKeyLabel = "API Key",
4472
4594
  apiKeyDescription = "Cloud \uBAA8\uB378 \uC0AC\uC6A9\uC5D0 \uD544\uC694\uD569\uB2C8\uB2E4"
4473
4595
  }) => {
4474
- const [activeTab, setActiveTab] = (0, import_react11.useState)("general");
4475
- const [localApiKey, setLocalApiKey] = (0, import_react11.useState)(apiKey);
4596
+ const [activeTab, setActiveTab] = (0, import_react12.useState)("general");
4597
+ const [localApiKey, setLocalApiKey] = (0, import_react12.useState)(apiKey);
4476
4598
  if (!isOpen) return null;
4477
4599
  const updateResponseStyle = (key, value) => {
4478
4600
  onPersonalizationChange({
@@ -5078,9 +5200,11 @@ var ChatUI = ({
5078
5200
  className = "",
5079
5201
  onSendMessage,
5080
5202
  onSessionChange,
5081
- onError
5203
+ onError,
5204
+ onTitleChange,
5205
+ generateTitle: generateTitle2
5082
5206
  }) => {
5083
- import_react12.default.useEffect(() => {
5207
+ import_react13.default.useEffect(() => {
5084
5208
  injectStyles();
5085
5209
  }, []);
5086
5210
  const hookOptions = {
@@ -5095,7 +5219,9 @@ var ChatUI = ({
5095
5219
  keepRecentMessages,
5096
5220
  onSendMessage,
5097
5221
  onSessionChange,
5098
- onError
5222
+ onError,
5223
+ onTitleChange,
5224
+ generateTitle: generateTitle2
5099
5225
  };
5100
5226
  const {
5101
5227
  sessions,
@@ -5118,6 +5244,7 @@ var ChatUI = ({
5118
5244
  newSession,
5119
5245
  selectSession,
5120
5246
  deleteSession,
5247
+ renameSession,
5121
5248
  setModel,
5122
5249
  toggleSidebar,
5123
5250
  openSettings,
@@ -5148,8 +5275,8 @@ var ChatUI = ({
5148
5275
  const handleChoiceClick = (choice) => {
5149
5276
  setInput(choice.text);
5150
5277
  };
5151
- const [memoryPanelOpen, setMemoryPanelOpen] = (0, import_react12.useState)(false);
5152
- const memoryItems = import_react12.default.useMemo(() => {
5278
+ const [memoryPanelOpen, setMemoryPanelOpen] = (0, import_react13.useState)(false);
5279
+ const memoryItems = import_react13.default.useMemo(() => {
5153
5280
  const items = [];
5154
5281
  if (currentSession?.contextSummary) {
5155
5282
  items.push({
@@ -5209,6 +5336,7 @@ var ChatUI = ({
5209
5336
  onSelectSession: selectSession,
5210
5337
  onNewSession: newSession,
5211
5338
  onDeleteSession: deleteSession,
5339
+ onRenameSession: renameSession,
5212
5340
  isOpen: sidebarOpen,
5213
5341
  onToggle: toggleSidebar
5214
5342
  }