From e1143d40bce641fc2a39b738e8ad337bdf13009e Mon Sep 17 00:00:00 2001 From: balibabu Date: Wed, 21 Jan 2026 15:39:18 +0800 Subject: [PATCH] Feat: Add a think button to the chat box. #12742 (#12743) ### What problem does this PR solve? Feat: Add a think button to the chat box. #12742 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/components/floating-chat-widget.tsx | 12 ++- web/src/components/message-input/next.tsx | 71 ++++++++----- web/src/interfaces/database/chat.ts | 1 + .../agent/chat/use-send-agent-message.ts | 49 ++++----- web/src/pages/agent/debug-content/index.tsx | 4 +- .../chat/app-settings/chat-prompt-engine.tsx | 5 - .../chat/chat-box/multiple-chat-box.tsx | 1 + .../chat/chat-box/single-chat-box.tsx | 1 + .../next-chats/hooks/use-send-chat-message.ts | 85 ++++++++-------- .../hooks/use-send-multiple-message.ts | 99 ++++++++++--------- .../hooks/use-send-shared-message.ts | 10 +- web/src/pages/next-chats/share/index.tsx | 1 + 12 files changed, 190 insertions(+), 149 deletions(-) diff --git a/web/src/components/floating-chat-widget.tsx b/web/src/components/floating-chat-widget.tsx index c02d28f0a..c63695031 100644 --- a/web/src/components/floating-chat-widget.tsx +++ b/web/src/components/floating-chat-widget.tsx @@ -68,8 +68,9 @@ const FloatingChatWidget = () => { // Play sound when opening const playNotificationSound = useCallback(() => { try { - const audioContext = new (window.AudioContext || - (window as any).webkitAudioContext)(); + const audioContext = new ( + window.AudioContext || (window as any).webkitAudioContext + )(); const oscillator = audioContext.createOscillator(); const gainNode = audioContext.createGain(); @@ -95,8 +96,9 @@ const FloatingChatWidget = () => { // Play sound for AI responses (Intercom-style) const playResponseSound = useCallback(() => { try { - const audioContext = new (window.AudioContext || - (window as any).webkitAudioContext)(); + const audioContext = new ( + window.AudioContext || (window as any).webkitAudioContext + )(); const oscillator = audioContext.createOscillator(); const gainNode = audioContext.createGain(); @@ -266,7 +268,7 @@ const FloatingChatWidget = () => { // Wait for state to update, then send setTimeout(() => { - handlePressEnter([]); + handlePressEnter({ enableThinking: false }); // Clear our local input after sending setInputValue(''); }, 50); diff --git a/web/src/components/message-input/next.tsx b/web/src/components/message-input/next.tsx index 55eceaea9..20f8e88db 100644 --- a/web/src/components/message-input/next.tsx +++ b/web/src/components/message-input/next.tsx @@ -18,11 +18,11 @@ import { cn } from '@/lib/utils'; import { t } from 'i18next'; import { CircleStop, Paperclip, Send, Upload, X } from 'lucide-react'; import * as React from 'react'; -import { useEffect } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { toast } from 'sonner'; import { AudioButton } from '../ui/audio-button'; -interface IProps { +interface NextMessageInputProps { disabled: boolean; value: string; sendDisabled: boolean; @@ -32,14 +32,19 @@ interface IProps { isShared?: boolean; showUploadIcon?: boolean; isUploading?: boolean; - onPressEnter(...prams: any[]): void; + onPressEnter({ enableThinking }: { enableThinking: boolean }): void; onInputChange: React.ChangeEventHandler; createConversationBeforeUploadDocument?(message: string): Promise; stopOutputMessage?(): void; onUpload?: NonNullable; removeFile?(file: File): void; + showReasoning?: boolean; } +export type NextMessageInputOnPressEnterParameter = Parameters< + NextMessageInputProps['onPressEnter'] +>; + export function NextMessageInput({ isUploading = false, value, @@ -52,12 +57,19 @@ export function NextMessageInput({ stopOutputMessage, onPressEnter, removeFile, -}: IProps) { + showReasoning = false, +}: NextMessageInputProps) { const [files, setFiles] = React.useState([]); const [audioInputValue, setAudioInputValue] = React.useState( null, ); + const [enableThinking, setEnableThinking] = useState(false); + + const handleThinkingToggle = useCallback(() => { + setEnableThinking((prev) => !prev); + }, []); + useEffect(() => { if (audioInputValue !== null) { onInputChange({ @@ -65,11 +77,11 @@ export function NextMessageInput({ } as React.ChangeEvent); setTimeout(() => { - onPressEnter(); + onPressEnter({ enableThinking }); setAudioInputValue(null); }, 0); } - }, [audioInputValue, onInputChange, onPressEnter]); + }, [audioInputValue, onInputChange, onPressEnter, enableThinking]); const onFileReject = React.useCallback((file: File, message: string) => { toast(message, { @@ -79,9 +91,9 @@ export function NextMessageInput({ const submit = React.useCallback(() => { if (isUploading) return; - onPressEnter(); + onPressEnter({ enableThinking }); setFiles([]); - }, [isUploading, onPressEnter]); + }, [isUploading, onPressEnter, enableThinking]); const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey) { @@ -165,38 +177,49 @@ export function NextMessageInput({ disabled={isUploading || disabled || sendLoading} onKeyDown={handleKeyDown} /> -
- {showUploadIcon && ( - +
+
+ {showUploadIcon && ( + + + + )} + {showReasoning && ( - - )} + )} +
{sendLoading ? ( ) : (
- {/*
*/} { setAudioInputValue(value); }} /> - {/*
*/}