diff --git a/backend/app/api/v1/chat.py b/backend/app/api/v1/chat.py
index c354fc1..a474ab1 100644
--- a/backend/app/api/v1/chat.py
+++ b/backend/app/api/v1/chat.py
@@ -268,7 +268,9 @@ def _compact_cited_refs(answer: str, retrieved_docs):
"citation_id": new_id,
"file_path": file_path,
"anchor_text": doc.get("anchor_text") or "",
- "excerpt": doc.get("excerpt") or doc.get("content") or doc.get("anchor_text") or "",
+ # 优先使用命中文档的上下文片段(命中点前后扩展),避免分块边界
+ # 的短文本(如文档末尾残余块)导致引用预览内容过少
+ "excerpt": doc.get("content") or doc.get("excerpt") or doc.get("anchor_text") or "",
})
old_to_new[old_id] = new_id
diff --git a/frontend/src/pages/Chat/Chat.css b/frontend/src/pages/Chat/Chat.css
index f1b3a99..7f7088a 100644
--- a/frontend/src/pages/Chat/Chat.css
+++ b/frontend/src/pages/Chat/Chat.css
@@ -410,7 +410,8 @@
vertical-align: super;
color: #1677ff;
font-weight: 600;
- margin: 0 1px;
+ margin: 0 3px;
+ padding: 0 1px;
cursor: pointer;
}
@@ -578,7 +579,7 @@
.chat-citation-preview {
display: flex;
flex-direction: column;
- gap: 8px;
+ gap: 10px;
max-width: 460px;
}
@@ -592,7 +593,7 @@
overflow: auto;
word-break: break-word;
line-height: 1.65;
- padding: 10px;
+ padding: 12px 14px;
border: 1px solid var(--border-color);
border-radius: 8px;
background: color-mix(in srgb, var(--border-color) 10%, transparent);
@@ -646,13 +647,11 @@
}
.chat-shell .chat-composer-shell {
- margin: 0 24px 24px;
+ margin: 0 24px 12px;
}
.chat-new-composer {
- min-height: 150px;
- padding-top: 10px;
- padding-bottom: 10px;
+ margin-bottom: 12px;
}
.chat-composer-input {
@@ -697,18 +696,18 @@
}
.chat-composer-plus {
- width: 44px;
- height: 44px;
- min-width: 44px;
+ width: 32px;
+ height: 32px;
+ min-width: 32px;
border: 1px solid #f0f0f0;
border-radius: 50%;
background: #fff;
- box-shadow: 0 6px 18px rgba(15, 23, 42, 0.08);
+ box-shadow: 0 2px 8px rgba(15, 23, 42, 0.06);
color: var(--text-color-secondary);
display: inline-flex;
align-items: center;
justify-content: center;
- font-size: 24px;
+ font-size: 16px;
cursor: pointer;
flex-shrink: 0;
}
@@ -778,21 +777,19 @@
}
.chat-send-button {
- width: 40px !important;
- height: 40px !important;
- min-width: 40px !important;
+ width: 32px !important;
+ height: 32px !important;
+ min-width: 32px !important;
border: 1px solid #bebebe !important;
background: #d1d1d1 !important;
color: #fff !important;
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.28);
- font-size: 24px !important;
+ font-size: 14px !important;
line-height: 1 !important;
- transform: scale(1.08);
- transform-origin: center;
}
.chat-send-button .anticon {
- font-size: 22px;
+ font-size: 15px;
}
.chat-send-button:not(:disabled):hover {
@@ -885,10 +882,9 @@ body.dark .chat-search-highlight {
}
.chat-send-button {
- width: 48px !important;
- height: 48px !important;
- min-width: 48px !important;
- font-size: 24px !important;
- transform: scale(1.08);
+ width: 40px !important;
+ height: 40px !important;
+ min-width: 40px !important;
+ font-size: 18px !important;
}
}
diff --git a/frontend/src/pages/Chat/Chat.jsx b/frontend/src/pages/Chat/Chat.jsx
index 7e28c16..436eb30 100644
--- a/frontend/src/pages/Chat/Chat.jsx
+++ b/frontend/src/pages/Chat/Chat.jsx
@@ -38,6 +38,7 @@ import 'highlight.js/styles/github.css'
import { createChatSession, deleteChatMessage, deleteChatSession, getChatMessages, getChatSessions, searchChatMessages, sendChatMessageStream, updateChatSessionTitle } from '@/api/chat'
import { getMyProjects } from '@/api/project'
import { getLLMModelConfigs } from '@/api/llmModelConfigs'
+import useUserStore from '@/stores/userStore'
import './Chat.css'
const { TextArea } = Input
@@ -208,6 +209,7 @@ function MessageActions({ content, onCopy, onDelete }) {
function Chat() {
const navigate = useNavigate()
const location = useLocation()
+ const { user: currentUser } = useUserStore()
const [searchParams, setSearchParams] = useSearchParams()
const [sessions, setSessions] = useState([])
const [projects, setProjects] = useState([])
@@ -232,6 +234,7 @@ function Chat() {
const [renameSession, setRenameSession] = useState(null)
const [renameTitle, setRenameTitle] = useState('')
const messageRefs = useRef(new Map())
+ const messagesEndRef = useRef(null)
const skipNextOpenSessionRef = useRef(null)
const searchRequestIdRef = useRef(0)
const newMode = location.pathname === '/chat/new'
@@ -252,6 +255,19 @@ function Chat() {
const canCreateSession = Boolean(newQuestion.trim() && currentProject && currentModel)
const canSendMessage = Boolean(inputValue.trim() && currentSession)
+ // 与全站侧边栏/个人资料一致的用户头像:avatar 字段是相对路径时转换为 API 端点
+ const userAvatarUrl = useMemo(() => {
+ const avatar = currentUser?.avatar
+ if (!avatar) return null
+ if (avatar.startsWith('http')) return avatar
+ const parts = avatar.split('/')
+ if (parts.length >= 3) {
+ return `/api/v1/auth/avatar/${parts[0]}/${parts[2]}`
+ }
+ return null
+ }, [currentUser])
+ const userDisplayName = currentUser?.nickname || currentUser?.username
+
const groupedSessions = useMemo(() => {
const groups = []
const groupMap = new Map()
@@ -353,6 +369,11 @@ function Chat() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [sessionId, sessions.length, newMode])
+ // 新会话打开或消息生成/更新时,自动滚动到对话底部
+ useEffect(() => {
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' })
+ }, [messages, currentSession])
+
useEffect(() => {
const target = searchParams.get('message_id')
if (!target) return
@@ -874,11 +895,16 @@ function Chat() {
/>
)}
- {isUser &&