diff --git a/src/components/ai/TopBar.vue b/src/components/ai/TopBar.vue index b973383..d0824a6 100644 --- a/src/components/ai/TopBar.vue +++ b/src/components/ai/TopBar.vue @@ -151,6 +151,42 @@ + +
+ + + +
+ +
+
系统提示
+
+
+ 目标({{ contextInfo.goals.length }}) + {{ contextInfo.goals.join('; ') || '无' }} +
+
+ 环境 + {{ contextInfo.osInfo }} +
+
+ 压缩摘要 + {{ contextInfo.summaryCount }} 条 +
+
+
+ +
+
增强上下文
+
+
+ @项目 + {{ contextInfo.projectNames || '无' }} +
+
+
+
+
@@ -324,6 +360,9 @@ function onBottomToolsClickOutside(e: MouseEvent) { if (summaryExpanded.value && !goalsRef.value.contains(e.target as Node)) { summaryExpanded.value = false } + if (contextExpanded.value && !goalsRef.value.contains(e.target as Node)) { + contextExpanded.value = false + } } // ── 📄 上下文摘要(压缩/归档的系统消息) ── interface SummaryItem { id: string; text: string } @@ -337,12 +376,44 @@ const summaryMsgs = computed(() => { }) const summaryExpanded = ref(false) +// ── L3 完整上下文 ── +interface ContextInfo { + goals: string[] + summaryCount: number + osInfo: string + projectNames: string +} +const contextExpanded = ref(false) + +const contextInfo = computed(() => { + const convId = store.state.activeConversationId + const conv = convId ? store.state.conversations.find(c => c.id === convId) : undefined + // 从所有 user 消息中收集 mentionSpans(kind === 'project'),去重 + const projectLabels = new Set() + for (const m of store.state.messages) { + if (m.role === 'user' && m.mentionSpans?.length) { + for (const sp of m.mentionSpans) { + if (sp.kind === 'project' && sp.label) { + projectLabels.add(sp.label) + } + } + } + } + return { + goals: conv?.pinned_goals ?? [], + summaryCount: summaryMsgs.value.length, + osInfo: 'Windows 11 / PowerShell', + projectNames: Array.from(projectLabels).join(', ') || '无', + } +}) + /** 按 Escape 关闭所有下拉 */ function onBottomToolsKeydown(e: KeyboardEvent) { if (e.key === 'Escape') { goalsExpanded.value = false historyExpanded.value = false summaryExpanded.value = false + contextExpanded.value = false } } onMounted(() => { @@ -718,6 +789,56 @@ onBeforeUnmount(() => { overflow: hidden; } +/* ═══ L3 完整上下文(底部) ═══ */ +.ai-context-inline { + position: relative; + display: inline-flex; + align-items: center; +} +.ai-context-inline-list { + position: absolute; + left: auto; right: 0; + top: 100%; + z-index: 100; + min-width: 280px; + max-width: 420px; + background: var(--df-bg); + border: 0.5px solid var(--df-border); + border-radius: var(--df-radius-sm); + box-shadow: 0 2px 8px rgba(0,0,0,0.1); + margin-top: 2px; +} +.ai-context-section { + padding: 6px 10px; + border-bottom: 0.5px solid var(--df-border); +} +.ai-context-section:last-child { border-bottom: none; } +.ai-context-section-title { + font-size: 10px; + font-weight: 600; + color: var(--df-accent); + margin-bottom: 4px; +} +.ai-context-section-body { + display: flex; + flex-direction: column; + gap: 3px; +} +.ai-context-row { + display: flex; + gap: 6px; + font-size: 11px; +} +.ai-context-label { + flex-shrink: 0; + color: var(--df-text-dim); + min-width: 56px; +} +.ai-context-value { + color: var(--df-text-secondary); + word-break: break-all; +} + /* header 中间区:provider + 模型选择器并排 */ .ai-header-center { display: flex;