@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.
@@ -2287,7 +2287,8 @@ ${newConversation}
2287
2287
  }, []);
2288
2288
  const sendMessage = useCallback5(async (content, options2) => {
2289
2289
  const messageContent = content || input;
2290
- if (!messageContent.trim() || isLoading) return;
2290
+ if (!messageContent.trim() && attachments.length === 0 || isLoading) return;
2291
+ setIsLoading(true);
2291
2292
  let sessionId = currentSessionId;
2292
2293
  if (!sessionId) {
2293
2294
  if (useExternalStorage && onCreateSessionRef.current) {
@@ -2462,7 +2463,6 @@ ${finalContent}`;
2462
2463
  setIsLoading(false);
2463
2464
  return;
2464
2465
  }
2465
- setIsLoading(true);
2466
2466
  abortControllerRef.current = new AbortController();
2467
2467
  try {
2468
2468
  let messagesToSend = [...existingMessages, userMessage];
@@ -2522,6 +2522,7 @@ ${currentContextSummary}` },
2522
2522
  chatMessages = messagesToSend.map((m) => ({ role: m.role, content: m.content }));
2523
2523
  }
2524
2524
  if (attachmentResults.length > 0 && shouldContinueAfterAttachment) {
2525
+ skipNextSkillParsingRef.current = true;
2525
2526
  const attachmentContext = attachmentResults.filter((part) => part.type === "tool_result").map((part) => `[${part.label || part.toolName} \uACB0\uACFC]
2526
2527
  ${part.result.content}`).join("\n\n");
2527
2528
  if (attachmentContext) {
@@ -2542,8 +2543,8 @@ ${attachmentContext}
2542
2543
  const modelConfig = models.find((m) => m.id === selectedModel);
2543
2544
  const provider = modelConfig?.provider || "ollama";
2544
2545
  let response;
2545
- if (onSendMessage) {
2546
- const result = await onSendMessage({
2546
+ if (onSendMessageRef.current) {
2547
+ const result = await onSendMessageRef.current({
2547
2548
  messages: messagesForApi,
2548
2549
  model: selectedModel,
2549
2550
  provider,
@@ -2968,14 +2969,12 @@ ${result.content}
2968
2969
  keepRecentMessages,
2969
2970
  buildSystemPrompt,
2970
2971
  compressContext,
2971
- onSendMessage,
2972
- onError,
2973
- generateTitleCallback,
2974
- onTitleChange,
2975
2972
  useExternalStorage,
2976
- onSaveMessages,
2977
2973
  handleSkillCall,
2978
- resolvedSkills
2974
+ resolvedSkills,
2975
+ /** @Todo vibecode - attachments, continueAfterToolResult를 deps에 추가하여 stale closure 방지 */
2976
+ attachments,
2977
+ continueAfterToolResult
2979
2978
  ]);
2980
2979
  const handlePollSubmit = useCallback5(
2981
2980
  (messageId, responses) => {
@@ -3243,8 +3242,8 @@ ${currentSession.contextSummary}` },
3243
3242
  const provider = modelConfig?.provider || "ollama";
3244
3243
  let responseContent = "";
3245
3244
  let responseSources;
3246
- if (onSendMessage) {
3247
- const result = await onSendMessage({
3245
+ if (onSendMessageRef.current) {
3246
+ const result = await onSendMessageRef.current({
3248
3247
  messages: messagesForApi,
3249
3248
  model: targetModel,
3250
3249
  provider,
@@ -8585,9 +8584,26 @@ var MessageList = ({
8585
8584
  const containerRef = useRef8(null);
8586
8585
  const [selectedText, setSelectedText] = useState15("");
8587
8586
  const [selectionPosition, setSelectionPosition] = useState15(null);
8587
+ const [showScrollButton, setShowScrollButton] = useState15(false);
8588
+ const isUserScrolledUpRef = useRef8(false);
8589
+ const SCROLL_THRESHOLD = 100;
8590
+ const handleScroll = useCallback8(() => {
8591
+ if (!containerRef.current) return;
8592
+ const { scrollTop, scrollHeight, clientHeight } = containerRef.current;
8593
+ const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
8594
+ const isNearBottom = distanceFromBottom < SCROLL_THRESHOLD;
8595
+ isUserScrolledUpRef.current = !isNearBottom;
8596
+ setShowScrollButton(!isNearBottom);
8597
+ }, []);
8588
8598
  useEffect8(() => {
8599
+ if (isUserScrolledUpRef.current) return;
8589
8600
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
8590
8601
  }, [messages]);
8602
+ const scrollToBottom = useCallback8(() => {
8603
+ isUserScrolledUpRef.current = false;
8604
+ setShowScrollButton(false);
8605
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
8606
+ }, []);
8591
8607
  const handleMouseUp = useCallback8(() => {
8592
8608
  const selection = typeof window !== "undefined" ? window.getSelection() : null;
8593
8609
  const text = selection?.toString().trim();
@@ -8632,6 +8648,7 @@ var MessageList = ({
8632
8648
  overflow: "auto",
8633
8649
  position: "relative"
8634
8650
  },
8651
+ onScroll: handleScroll,
8635
8652
  onMouseUp: handleMouseUp,
8636
8653
  children: [
8637
8654
  /* @__PURE__ */ jsxs15(
@@ -8684,6 +8701,32 @@ var MessageList = ({
8684
8701
  ]
8685
8702
  }
8686
8703
  ),
8704
+ showScrollButton && /* @__PURE__ */ jsx16(
8705
+ "button",
8706
+ {
8707
+ onClick: scrollToBottom,
8708
+ "aria-label": "\uB9E8 \uC544\uB798\uB85C \uC2A4\uD06C\uB864",
8709
+ style: {
8710
+ position: "sticky",
8711
+ bottom: "260px",
8712
+ left: "50%",
8713
+ transform: "translateX(-50%)",
8714
+ width: "40px",
8715
+ height: "40px",
8716
+ borderRadius: "50%",
8717
+ backgroundColor: "var(--chatllm-content-bg)",
8718
+ border: "1px solid var(--chatllm-border)",
8719
+ boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
8720
+ cursor: "pointer",
8721
+ display: "flex",
8722
+ alignItems: "center",
8723
+ justifyContent: "center",
8724
+ zIndex: 10,
8725
+ transition: "opacity 0.2s"
8726
+ },
8727
+ children: /* @__PURE__ */ jsx16(IconSvg, { name: "arrow-down-s-line", size: 24, color: "var(--chatllm-text-secondary)" })
8728
+ }
8729
+ ),
8687
8730
  selectionPosition && /* @__PURE__ */ jsxs15(
8688
8731
  "div",
8689
8732
  {