From 1ce689ad1eb797f5b631c5718c60a61e8f9ace43 Mon Sep 17 00:00:00 2001 From: xiaopeng <1509442308@qq.com> Date: Wed, 27 May 2026 15:10:45 +0800 Subject: [PATCH] fix: sidebar menu visibility, model selector cleanup, time format - Sidebar: restore DropdownMenu for session actions, fix long titles pushing button off-screen (add w-0 flex-1, w-full on content div) - Model selector: remove SiliconFlow models, DeepSeek only (deepseek-reasoner default, deepseek-chat alternative) - Message timestamp: change from HH:mm to yyyy/M/d HH:mm format - Default model changed to deepseek-reasoner Co-Authored-By: Claude Opus 4.7 --- web/src/components/chat/chat-interface.tsx | 2 +- web/src/components/chat/message-item.tsx | 2 +- web/src/components/chat/model-selector.tsx | 130 +++++--------- web/src/components/chat/sidebar.tsx | 196 ++++++++++----------- 4 files changed, 145 insertions(+), 185 deletions(-) diff --git a/web/src/components/chat/chat-interface.tsx b/web/src/components/chat/chat-interface.tsx index 613c60c..d60aa12 100644 --- a/web/src/components/chat/chat-interface.tsx +++ b/web/src/components/chat/chat-interface.tsx @@ -15,7 +15,7 @@ import { knowledgeBaseAPI } from "@/lib/api"; export default function ChatInterface() { const [inputMessage, setInputMessage] = useState(""); const [isComposing, setIsComposing] = useState(false); - const [selectedModel, setSelectedModel] = useState("deepseek-chat"); + const [selectedModel, setSelectedModel] = useState("deepseek-reasoner"); const [selectedKnowledgeBases, setSelectedKnowledgeBases] = useState([]); const [systemKnowledgeBases, setSystemKnowledgeBases] = useState([]); const [userKnowledgeBases, setUserKnowledgeBases] = useState([]); diff --git a/web/src/components/chat/message-item.tsx b/web/src/components/chat/message-item.tsx index 8fc455f..fb48cb5 100644 --- a/web/src/components/chat/message-item.tsx +++ b/web/src/components/chat/message-item.tsx @@ -253,7 +253,7 @@ export default function MessageItem({ message, selectedModel }: MessageItemProps )} {message.created_at - ? format(new Date(new Date(message.created_at).getTime() + 8 * 60 * 60 * 1000), "HH:mm") + ? format(new Date(message.created_at), "yyyy/M/d HH:mm") : ""} diff --git a/web/src/components/chat/model-selector.tsx b/web/src/components/chat/model-selector.tsx index c566d08..f2560d8 100644 --- a/web/src/components/chat/model-selector.tsx +++ b/web/src/components/chat/model-selector.tsx @@ -8,10 +8,9 @@ import { DropdownMenuItem, DropdownMenuTrigger, DropdownMenuSeparator, - DropdownMenuLabel, } from "@/components/ui/dropdown-menu"; import { Badge } from "@/components/ui/badge"; -import { ChevronDown, Cpu, Zap, Sparkles, Brain } from "lucide-react"; +import { ChevronDown, Cpu, Sparkles, Brain } from "lucide-react"; import { cn } from "@/lib/utils"; export interface ModelOption { @@ -29,55 +28,25 @@ interface ModelSelectorProps { className?: string; } -const modelGroups = [ +const models: ModelOption[] = [ { + id: "deepseek-reasoner", + name: "DeepSeek-R1", + description: "深度推理模型,适合复杂分析任务", provider: "deepseek-official", - label: "DeepSeek 官方", - models: [ - { - id: "deepseek-chat", - name: "DeepSeek-V3", - description: "DeepSeek 最新通用模型,速度快、能力强", - provider: "deepseek-official", - providerLabel: "DeepSeek", - icon: Sparkles, - }, - { - id: "deepseek-reasoner", - name: "DeepSeek-R1", - description: "深度推理模型,适合复杂分析任务", - provider: "deepseek-official", - providerLabel: "DeepSeek", - icon: Brain, - }, - ], + providerLabel: "DeepSeek", + icon: Brain, }, { - provider: "siliconflow", - label: "SiliconFlow(硅基流动)", - models: [ - { - id: "deepseek-ai/DeepSeek-V3", - name: "DeepSeek-V3", - description: "通过硅基流动调用,稳定的推理能力", - provider: "siliconflow", - providerLabel: "硅基流动", - icon: Sparkles, - }, - { - id: "Qwen/QwQ-32B", - name: "QwQ-32B", - description: "Qwen 推理模型,高效准确", - provider: "siliconflow", - providerLabel: "硅基流动", - icon: Zap, - }, - ], + id: "deepseek-chat", + name: "DeepSeek-V3", + description: "通用模型,速度快、能力强", + provider: "deepseek-official", + providerLabel: "DeepSeek", + icon: Sparkles, }, ]; -const allModels = modelGroups.flatMap((g) => g.models); - export default function ModelSelector({ selectedModel, onModelChange, @@ -86,7 +55,7 @@ export default function ModelSelector({ const [isOpen, setIsOpen] = useState(false); const selectedModelData = - allModels.find((model) => model.id === selectedModel) || allModels[0]; + models.find((model) => model.id === selectedModel) || models[0]; const IconComponent = selectedModelData.icon || Cpu; return ( @@ -107,51 +76,44 @@ export default function ModelSelector({ - +
选择AI模型
- {modelGroups.map((group) => ( -
- - {group.label} - - {group.models.map((model) => { - const ModelIcon = model.icon || Cpu; - const isSelected = model.id === selectedModel; + {models.map((model) => { + const ModelIcon = model.icon || Cpu; + const isSelected = model.id === selectedModel; - return ( - { - onModelChange(model.id); - setIsOpen(false); - }} - className={cn( - "flex items-start space-x-3 p-3 cursor-pointer", - isSelected && "bg-muted/50" + return ( + { + onModelChange(model.id); + setIsOpen(false); + }} + className={cn( + "flex items-start space-x-3 p-3 cursor-pointer", + isSelected && "bg-muted/50" + )} + > + +
+
+ {model.name} + {isSelected && ( + + 已选择 + )} - > - -
-
- {model.name} - {isSelected && ( - - 已选择 - - )} -
-

- {model.description} -

-
- - ); - })} -
- ))} +
+

+ {model.description} +

+
+ + ); + })}
); diff --git a/web/src/components/chat/sidebar.tsx b/web/src/components/chat/sidebar.tsx index c3a12eb..9fcdc6f 100644 --- a/web/src/components/chat/sidebar.tsx +++ b/web/src/components/chat/sidebar.tsx @@ -12,7 +12,14 @@ import { Trash2, Download, Search, + MoreVertical, } from "lucide-react"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; import { Dialog, DialogContent, @@ -109,106 +116,97 @@ export default function Sidebar({ onClose }: SidebarProps) { const groups = groupSessionsByDate(filteredSessions()); return ( - <> -
- {/* Header */} -
-
- 对话 - -
-
- - setSearchQuery(e.target.value)} - className="pl-8 h-8 text-sm bg-muted/30 border border-border/20 focus-visible:ring-1 focus-visible:border-primary/30" - /> -
-
- - {/* Session list with date groups */} - -
- {groups.map(group => ( -
-
- {group.label} -
-
- {group.sessions.map(session => ( -
handleSelectSession(session.id)} - > - {/* Title row */} -
- - {session.title} -
- - {/* Action buttons — absolute positioned, always visible */} -
e.stopPropagation()} - > - - {}}> - - - -
-
- ))} -
-
- ))} - - {sessions.length === 0 && ( -
- -

开始新的对话

-
- )} -
-
- - {/* Footer — new chat button with distinct background */} -
-
+
+ + setSearchQuery(e.target.value)} + className="pl-8 h-8 text-sm bg-muted/30 border border-border/20 focus-visible:ring-1 focus-visible:border-primary/30" + /> +
+
+ + {/* Session list */} + +
+ {groups.map(group => ( +
+
+ {group.label} +
+
+ {group.sessions.map(session => ( +
handleSelectSession(session.id)} + > + + {session.title} + + {/* Dropdown menu — visible on hover */} +
e.stopPropagation()}> + + + + + e.stopPropagation()}> + { setEditingSession(session.id); setEditTitle(session.title); }}> + + 重命名 + + + e.preventDefault()}> + + 导出 + + + setDeleteSessionId(session.id)} className="text-destructive"> + + 删除 + + + +
+
+ ))} +
+
+ ))} + + {sessions.length === 0 && ( +
+ +

开始新的对话

+
+ )} +
+
+ + {/* Footer */} +
+
{/* Rename dialog */} @@ -222,7 +220,7 @@ export default function Sidebar({ onClose }: SidebarProps) { value={editTitle} onChange={(e) => setEditTitle(e.target.value)} placeholder="对话名称" - onKeyDown={(e) => { if (e.key === 'Enter' && editTitle.trim()) handleSaveRename(); }} + onKeyDown={(e) => { if (e.key === "Enter" && editTitle.trim()) handleSaveRename(); }} autoFocus /> @@ -246,6 +244,6 @@ export default function Sidebar({ onClose }: SidebarProps) { - + ); }