+
+
+
+
+ mdi-reply
+ 引用
+
+
@@ -311,7 +325,7 @@ export default {
default: false
}
},
- emits: ['openImagePreview', 'replyMessage'],
+ emits: ['openImagePreview', 'replyMessage', 'replyWithText'],
setup() {
const { t } = useI18n();
const { tm } = useModuleI18n('features/chat');
@@ -332,6 +346,12 @@ export default {
expandedToolCalls: new Set(), // Track which tool call cards are expanded
elapsedTimeTimer: null, // Timer for updating elapsed time
currentTime: Date.now() / 1000, // Current time for elapsed time calculation
+ // 选中文本相关状态
+ selectedText: {
+ content: '',
+ messageIndex: null,
+ position: { top: 0, left: 0 }
+ }
};
},
mounted() {
@@ -349,6 +369,86 @@ export default {
}
},
methods: {
+ // 处理文本选择
+ handleTextSelection() {
+ const selection = window.getSelection();
+ const selectedText = selection.toString();
+
+ if (!selectedText.trim()) {
+ // 清除选中状态
+ this.selectedText.content = '';
+ this.selectedText.messageIndex = null;
+ return;
+ }
+
+ // 获取被选中的元素,找到对应的message-item
+ const range = selection.getRangeAt(0);
+ const startContainer = range.startContainer;
+ let messageItem = null;
+ let node = startContainer.parentElement;
+
+ // 遍历DOM树向上查找message-item
+ while (node && !node.classList.contains('message-item')) {
+ node = node.parentElement;
+ }
+
+ messageItem = node;
+
+ if (!messageItem) {
+ this.selectedText.content = '';
+ this.selectedText.messageIndex = null;
+ return;
+ }
+
+ // 获取message-item在messages数组中的索引
+ const messageItems = this.$refs.messageContainer?.querySelectorAll('.message-item');
+ let messageIndex = -1;
+ if (messageItems) {
+ for (let i = 0; i < messageItems.length; i++) {
+ if (messageItems[i] === messageItem) {
+ messageIndex = i;
+ break;
+ }
+ }
+ }
+
+ if (messageIndex === -1) {
+ this.selectedText.content = '';
+ this.selectedText.messageIndex = null;
+ return;
+ }
+
+ // 获取选中文本的位置(相对于viewport)
+ const rect = selection.getRangeAt(0).getBoundingClientRect();
+
+ this.selectedText.content = selectedText;
+ this.selectedText.messageIndex = messageIndex;
+ this.selectedText.position = {
+ top: Math.max(0, rect.bottom + 5),
+ left: Math.max(0, (rect.left + rect.right) / 2)
+ };
+ },
+
+ // 处理引用选中的文本
+ handleQuoteSelected() {
+ if (this.selectedText.messageIndex === null) return;
+
+ const msg = this.messages[this.selectedText.messageIndex];
+ if (!msg || !msg.id) return;
+
+ // 触发replyWithText事件,传递选中的文本内容
+ this.$emit('replyWithText', {
+ messageId: msg.id,
+ selectedText: this.selectedText.content,
+ messageIndex: this.selectedText.messageIndex
+ });
+
+ // 清除选中状态
+ this.selectedText.content = '';
+ this.selectedText.messageIndex = null;
+ window.getSelection().removeAllRanges();
+ },
+
// 检查 message 中是否有音频
hasAudio(messageParts) {
if (!Array.isArray(messageParts)) return false;
@@ -805,6 +905,23 @@ export default {
gap: 8px;
}
+:deep(code.bg-secondary) {
+ background-color: #ececec !important;
+ color: #0d0d0d !important;
+}
+
+:deep(code.rounded) {
+ border-radius: 6px !important;
+}
+
+.messages-container.is-dark :deep(code.bg-secondary) {
+ background-color: #424242 !important;
+ color: #ffffff !important;
+}
+
+.messages-container.is-dark :deep(.code-block-container) {
+ background-color: #1f1f1f !important;
+}
/* 基础动画 */
@keyframes fadeIn {
@@ -1293,11 +1410,25 @@ export default {
margin-top: 6px;
}
+.tool-calls-label {
+ font-size: 13px;
+ font-weight: 500;
+ color: var(--v-theme-secondaryText);
+ opacity: 0.7;
+ margin-bottom: 4px;
+}
+
.tool-call-card {
border-radius: 8px;
overflow: hidden;
background-color: #eff3f6;
margin: 8px 0px;
+ max-width: 300px;
+ transition: max-width 0.1s ease;
+}
+
+.tool-call-card.expanded {
+ max-width: 100%;
}
.tool-call-header {
@@ -1374,6 +1505,36 @@ export default {
font-size: 14px;
}
+/* 浮动引用按钮样式 */
+.selection-quote-button {
+ position: fixed;
+ z-index: 1000;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ pointer-events: all;
+}
+
+
+.quote-btn {
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ font-size: 14px;
+ padding: 4px 24px;
+ background-color: #f6f4fa !important;
+ color: #333333 !important;
+}
+
+.quote-btn:hover {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
+ background-color: #f6f4fa !important;
+}
+
+/* 深色主题 */
+.quote-btn.dark-mode {
+ background-color: #2d2d2d !important;
+ color: #ffffff !important;
+}
+
.tool-call-status .status-icon.spinning {
animation: spin 1s linear infinite;
}
diff --git a/dashboard/src/composables/useMessages.ts b/dashboard/src/composables/useMessages.ts
index 47910a984..58bfbfc39 100644
--- a/dashboard/src/composables/useMessages.ts
+++ b/dashboard/src/composables/useMessages.ts
@@ -45,13 +45,13 @@ export interface MessagePart {
// embedded fields - 加载后填充
embedded_url?: string; // blob URL for image, record
embedded_file?: FileInfo; // for file (保留 attachment_id 用于按需下载)
- reply_content?: string; // for reply - 被引用消息的内容
+ selected_text?: string; // for reply - 被引用消息的内容
}
// 引用信息 (用于发送消息时)
export interface ReplyInfo {
messageId: number;
- messageContent: string;
+ selectedText?: string; // 选中的文本内容(可选)
}
// 简化的消息内容结构
@@ -216,11 +216,12 @@ export function useMessages(
const userMessageParts: MessagePart[] = [];
// 添加引用消息段
+ console.log('ReplyTo in sendMessage:', replyTo);
if (replyTo) {
userMessageParts.push({
type: 'reply',
message_id: replyTo.messageId,
- reply_content: replyTo.messageContent
+ selected_text: replyTo.selectedText
});
}
@@ -295,7 +296,8 @@ export function useMessages(
if (replyTo) {
parts.push({
type: 'reply',
- message_id: replyTo.messageId
+ message_id: replyTo.messageId,
+ selected_text: replyTo.selectedText
});
}
diff --git a/dashboard/src/i18n/locales/en-US/features/chat.json b/dashboard/src/i18n/locales/en-US/features/chat.json
index 5cc14c0dd..640f814d9 100644
--- a/dashboard/src/i18n/locales/en-US/features/chat.json
+++ b/dashboard/src/i18n/locales/en-US/features/chat.json
@@ -42,7 +42,8 @@
"fullscreen": "Fullscreen Mode",
"exitFullscreen": "Exit Fullscreen",
"reply": "Reply",
- "providerConfig": "AI Configuration"
+ "providerConfig": "AI Configuration",
+ "toolsUsed": "Tool Used"
},
"conversation": {
"newConversation": "New Conversation",
diff --git a/dashboard/src/i18n/locales/zh-CN/features/chat.json b/dashboard/src/i18n/locales/zh-CN/features/chat.json
index b5a9172c3..b77512595 100644
--- a/dashboard/src/i18n/locales/zh-CN/features/chat.json
+++ b/dashboard/src/i18n/locales/zh-CN/features/chat.json
@@ -42,7 +42,8 @@
"fullscreen": "全屏模式",
"exitFullscreen": "退出全屏",
"reply": "引用回复",
- "providerConfig": "AI 配置"
+ "providerConfig": "AI 配置",
+ "toolsUsed": "已使用工具"
},
"conversation": {
"newConversation": "新的聊天",