@gendive/chatllm 0.17.1 → 0.17.3

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.
@@ -2353,7 +2353,8 @@ ${newConversation}
2353
2353
  }, []);
2354
2354
  const sendMessage = (0, import_react5.useCallback)(async (content, options2) => {
2355
2355
  const messageContent = content || input;
2356
- if (!messageContent.trim() || isLoading) return;
2356
+ if (!messageContent.trim() && attachments.length === 0 || isLoading) return;
2357
+ setIsLoading(true);
2357
2358
  let sessionId = currentSessionId;
2358
2359
  if (!sessionId) {
2359
2360
  if (useExternalStorage && onCreateSessionRef.current) {
@@ -2528,7 +2529,6 @@ ${finalContent}`;
2528
2529
  setIsLoading(false);
2529
2530
  return;
2530
2531
  }
2531
- setIsLoading(true);
2532
2532
  abortControllerRef.current = new AbortController();
2533
2533
  try {
2534
2534
  let messagesToSend = [...existingMessages, userMessage];
@@ -2588,6 +2588,7 @@ ${currentContextSummary}` },
2588
2588
  chatMessages = messagesToSend.map((m) => ({ role: m.role, content: m.content }));
2589
2589
  }
2590
2590
  if (attachmentResults.length > 0 && shouldContinueAfterAttachment) {
2591
+ skipNextSkillParsingRef.current = true;
2591
2592
  const attachmentContext = attachmentResults.filter((part) => part.type === "tool_result").map((part) => `[${part.label || part.toolName} \uACB0\uACFC]
2592
2593
  ${part.result.content}`).join("\n\n");
2593
2594
  if (attachmentContext) {
@@ -2608,8 +2609,8 @@ ${attachmentContext}
2608
2609
  const modelConfig = models.find((m) => m.id === selectedModel);
2609
2610
  const provider = modelConfig?.provider || "ollama";
2610
2611
  let response;
2611
- if (onSendMessage) {
2612
- const result = await onSendMessage({
2612
+ if (onSendMessageRef.current) {
2613
+ const result = await onSendMessageRef.current({
2613
2614
  messages: messagesForApi,
2614
2615
  model: selectedModel,
2615
2616
  provider,
@@ -3034,14 +3035,12 @@ ${result.content}
3034
3035
  keepRecentMessages,
3035
3036
  buildSystemPrompt,
3036
3037
  compressContext,
3037
- onSendMessage,
3038
- onError,
3039
- generateTitleCallback,
3040
- onTitleChange,
3041
3038
  useExternalStorage,
3042
- onSaveMessages,
3043
3039
  handleSkillCall,
3044
- resolvedSkills
3040
+ resolvedSkills,
3041
+ /** @Todo vibecode - attachments, continueAfterToolResult를 deps에 추가하여 stale closure 방지 */
3042
+ attachments,
3043
+ continueAfterToolResult
3045
3044
  ]);
3046
3045
  const handlePollSubmit = (0, import_react5.useCallback)(
3047
3046
  (messageId, responses) => {
@@ -3309,8 +3308,8 @@ ${currentSession.contextSummary}` },
3309
3308
  const provider = modelConfig?.provider || "ollama";
3310
3309
  let responseContent = "";
3311
3310
  let responseSources;
3312
- if (onSendMessage) {
3313
- const result = await onSendMessage({
3311
+ if (onSendMessageRef.current) {
3312
+ const result = await onSendMessageRef.current({
3314
3313
  messages: messagesForApi,
3315
3314
  model: targetModel,
3316
3315
  provider,
@@ -8651,9 +8650,26 @@ var MessageList = ({
8651
8650
  const containerRef = (0, import_react17.useRef)(null);
8652
8651
  const [selectedText, setSelectedText] = (0, import_react17.useState)("");
8653
8652
  const [selectionPosition, setSelectionPosition] = (0, import_react17.useState)(null);
8653
+ const [showScrollButton, setShowScrollButton] = (0, import_react17.useState)(false);
8654
+ const isUserScrolledUpRef = (0, import_react17.useRef)(false);
8655
+ const SCROLL_THRESHOLD = 100;
8656
+ const handleScroll = (0, import_react17.useCallback)(() => {
8657
+ if (!containerRef.current) return;
8658
+ const { scrollTop, scrollHeight, clientHeight } = containerRef.current;
8659
+ const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
8660
+ const isNearBottom = distanceFromBottom < SCROLL_THRESHOLD;
8661
+ isUserScrolledUpRef.current = !isNearBottom;
8662
+ setShowScrollButton(!isNearBottom);
8663
+ }, []);
8654
8664
  (0, import_react17.useEffect)(() => {
8665
+ if (isUserScrolledUpRef.current) return;
8655
8666
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
8656
8667
  }, [messages]);
8668
+ const scrollToBottom = (0, import_react17.useCallback)(() => {
8669
+ isUserScrolledUpRef.current = false;
8670
+ setShowScrollButton(false);
8671
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
8672
+ }, []);
8657
8673
  const handleMouseUp = (0, import_react17.useCallback)(() => {
8658
8674
  const selection = typeof window !== "undefined" ? window.getSelection() : null;
8659
8675
  const text = selection?.toString().trim();
@@ -8698,6 +8714,7 @@ var MessageList = ({
8698
8714
  overflow: "auto",
8699
8715
  position: "relative"
8700
8716
  },
8717
+ onScroll: handleScroll,
8701
8718
  onMouseUp: handleMouseUp,
8702
8719
  children: [
8703
8720
  /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
@@ -8750,6 +8767,32 @@ var MessageList = ({
8750
8767
  ]
8751
8768
  }
8752
8769
  ),
8770
+ showScrollButton && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8771
+ "button",
8772
+ {
8773
+ onClick: scrollToBottom,
8774
+ "aria-label": "\uB9E8 \uC544\uB798\uB85C \uC2A4\uD06C\uB864",
8775
+ style: {
8776
+ position: "sticky",
8777
+ bottom: "260px",
8778
+ left: "50%",
8779
+ transform: "translateX(-50%)",
8780
+ width: "40px",
8781
+ height: "40px",
8782
+ borderRadius: "50%",
8783
+ backgroundColor: "var(--chatllm-content-bg)",
8784
+ border: "1px solid var(--chatllm-border)",
8785
+ boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
8786
+ cursor: "pointer",
8787
+ display: "flex",
8788
+ alignItems: "center",
8789
+ justifyContent: "center",
8790
+ zIndex: 10,
8791
+ transition: "opacity 0.2s"
8792
+ },
8793
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(IconSvg, { name: "arrow-down-s-line", size: 24, color: "var(--chatllm-text-secondary)" })
8794
+ }
8795
+ ),
8753
8796
  selectionPosition && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
8754
8797
  "div",
8755
8798
  {