Просмотр исходного кода

feat(ui): 模型选择器移至输入框右侧,新增leftControls/rightControls API,拖拽条移入圆角容器内

Mochocyang 3 месяцев назад
Родитель
Сommit
19f5a6fd42

+ 45 - 37
src/components/chat/chat-input.tsx

@@ -35,13 +35,19 @@ interface ChatInputProps {
   onStop: () => void
   isStreaming: boolean
   placeholder?: string
+  leftControls?: ReactNode
+  rightControls?: ReactNode
+  /** @deprecated 请使用 leftControls 代替 */
   leadingControls?: ReactNode
+  /** @deprecated 请使用 leftControls 代替 */
   footerControls?: ReactNode
   value?: string
   onChange?: (value: string) => void
 }
 
-export function ChatInput({ onSend, onStop, isStreaming, placeholder, leadingControls, footerControls, value: controlledValue, onChange }: ChatInputProps) {
+export function ChatInput({ onSend, onStop, isStreaming, placeholder, leftControls, rightControls, leadingControls, footerControls, value: controlledValue, onChange }: ChatInputProps) {
+  const leftToolbarContent = leftControls ?? leadingControls ?? footerControls
+  const rightToolbarContent = rightControls
   const activeConversationId = useChatStore((state) => state.activeConversationId)
   const setConversationInputDraft = useChatStore((state) => state.setConversationInputDraft)
   const conversation = useChatStore((state) =>
@@ -297,18 +303,18 @@ export function ChatInput({ onSend, onStop, isStreaming, placeholder, leadingCon
 
   return (
     <div ref={rootRef} className="border-t p-3">
-      <div
-        role="separator"
-        aria-label="拖动调整输入框高度"
-        title={resizeTitle}
-        className="flex h-2 cursor-ns-resize items-center justify-center transition-colors"
-        onPointerDown={handleResizePointerDown}
-        onDoubleClick={handleDoubleClick}
-      >
-        <span className={`h-0.5 w-10 rounded-full transition-colors ${handleBarColor}`} />
-      </div>
       <div className="rounded-2xl border bg-background shadow-sm overflow-hidden flex flex-col">
-        <div className="px-4 pt-3">
+        <div
+          role="separator"
+          aria-label="拖动调整输入框高度"
+          title={resizeTitle}
+          className="flex h-2 cursor-ns-resize items-center justify-center transition-colors"
+          onPointerDown={handleResizePointerDown}
+          onDoubleClick={handleDoubleClick}
+        >
+          <span className={`h-0.5 w-10 rounded-full transition-colors ${handleBarColor}`} />
+        </div>
+        <div className="px-4 pt-2">
           <textarea
             ref={textareaRef}
             value={value}
@@ -323,35 +329,37 @@ export function ChatInput({ onSend, onStop, isStreaming, placeholder, leadingCon
           />
         </div>
         <div className="flex items-center justify-between border-t px-3 py-2.5 gap-2">
-          {(leadingControls || footerControls) ? (
+          {leftToolbarContent ? (
             <div className="flex items-center gap-2 min-w-0 overflow-x-auto">
-              {leadingControls}
-              {footerControls}
+              {leftToolbarContent}
             </div>
-          ) : null}
-          {isStreaming ? (
-            <Button
-              variant="destructive"
-              size="icon"
-              onClick={onStop}
-              className="shrink-0"
-              title="停止生成"
-              aria-label="停止生成"
-            >
-              <Square className="h-4 w-4" />
-            </Button>
           ) : (
-            <Button
-              size="icon"
-              onClick={handleSend}
-              disabled={!value.trim()}
-              className="shrink-0"
-              title="发送消息"
-              aria-label="发送消息"
-            >
-              <ArrowUp className="h-4 w-4" />
-            </Button>
+            <div className="flex-1" />
           )}
+          <div className="flex items-center gap-2 shrink-0">
+            {rightToolbarContent}
+            {isStreaming ? (
+              <Button
+                variant="destructive"
+                size="icon"
+                onClick={onStop}
+                title="停止生成"
+                aria-label="停止生成"
+              >
+                <Square className="h-4 w-4" />
+              </Button>
+            ) : (
+              <Button
+                size="icon"
+                onClick={handleSend}
+                disabled={!value.trim()}
+                title="发送消息"
+                aria-label="发送消息"
+              >
+                <ArrowUp className="h-4 w-4" />
+              </Button>
+            )}
+          </div>
         </div>
       </div>
     </div>

+ 10 - 8
src/components/chat/chat-panel.tsx

@@ -1507,7 +1507,7 @@ export function ChatPanel() {
             onSend={handleSend}
             onStop={handleStop}
             isStreaming={isStreaming}
-            footerControls={
+            leftControls={
               <TooltipProvider delay={200}>
                 <div className="flex items-center gap-2 flex-nowrap overflow-x-auto">
                   <ChatDockControls />
@@ -1588,16 +1588,18 @@ export function ChatPanel() {
                       </Tooltip>
                     </>
                   )}
-                  <ChatModelSelector
-                    value={aiChatModel}
-                    onChange={(model) => {
-                      setAiChatModel(model)
-                      void saveAiChatModel(model)
-                    }}
-                  />
                 </div>
               </TooltipProvider>
             }
+            rightControls={
+              <ChatModelSelector
+                value={aiChatModel}
+                onChange={(model) => {
+                  setAiChatModel(model)
+                  void saveAiChatModel(model)
+                }}
+              />
+            }
             placeholder={
               mode === "ingest"
                 ? t(novelMode ? "novel.chat.ingestPlaceholder" : "chat.ingestPlaceholder")

+ 17 - 15
src/components/sources/outline-chat-panel.tsx

@@ -656,27 +656,29 @@ export function OutlineChatPanel({ onClose }: { onClose: () => void }) {
         placeholder="输入关于大纲的问题..."
         value={inputValue}
         onChange={setInputValue}
-        footerControls={
+        leftControls={
           <TooltipProvider delay={200}>
             <div className="flex items-center gap-2 flex-nowrap overflow-x-auto">
               <ChatDockControls />
-              {hasAvailableModels ? (
-                <ChatModelSelector
-                  value={localModelId}
-                  onChange={(value) => {
-                    setLocalModelId(value)
-                    if (activeConversationId) {
-                      setConversationModel(activeConversationId, value)
-                    }
-                  }}
-                  disabled={isStreaming}
-                />
-              ) : (
-                <p className="text-xs text-destructive">请先在「设置 → 大语言模型」中添加并启用一个模型。</p>
-              )}
             </div>
           </TooltipProvider>
         }
+        rightControls={
+          hasAvailableModels ? (
+            <ChatModelSelector
+              value={localModelId}
+              onChange={(value) => {
+                setLocalModelId(value)
+                if (activeConversationId) {
+                  setConversationModel(activeConversationId, value)
+                }
+              }}
+              disabled={isStreaming}
+            />
+          ) : (
+            <p className="text-xs text-destructive">请先在「设置 → 大语言模型」中添加并启用一个模型。</p>
+          )
+        }
       />
     </div>
   )