diff --git a/docs/superpowers/specs/2026-08-07-frontend-context-popover-theme-fix-design.md b/docs/superpowers/specs/2026-08-07-frontend-context-popover-theme-fix-design.md new file mode 100644 index 0000000..8e6764b --- /dev/null +++ b/docs/superpowers/specs/2026-08-07-frontend-context-popover-theme-fix-design.md @@ -0,0 +1,59 @@ +# Frontend 上下文窗口 popover 主题适配 — Design Spec + +**日期:** 2026-08-07 +**状态:** Approved +**范围:** Bug 修复(CSS 变量 / Element Plus Teleport) + +## 背景 + +`src/views/frontend/components/InputArea.vue` 中的"上下文窗口"指示器(`.context-indicator`)点击后弹出 `el-popover`,其中显示 token 使用量、阈值百分比等。当前问题是:该 popover 没有适配 frontend 的深 / 浅色主题。 + +## 根因 + +Element Plus 的 `el-popover` 默认 `teleported: true`(已确认,源码 `element-plus/es/components/tooltip/src/content.mjs`),弹出层被 Teleport 到 body 之外。 + +`body[data-fe-theme="light"]` 只覆盖了 `--fe-*` 系列变量,未覆盖 Element Plus 内部用到的 `--el-bg-color-overlay`、`--el-border-color-lighter`、`--el-text-color-regular` 等。结果是: + +- popover 容器(`.el-popper`)回退到 Element Plus 默认值(浅色背景 + 浅色文本) +- 在深色主题下,与 frontend 整体深色风格不一致 +- 而 `.context-popover` 内部的 `--fe-*` 变量虽然继承自 `:root`,但 popover 容器本身的背景 / 边框 / 文字色来自 `--el-*`,这些没被覆盖 + +## 方案 + +**给 `el-popover` 设置 `:teleported="false"`**,让 popover 容器保留在 `.frontend-container` DOM 树内,从而继承 `.frontend-container` 下的所有 `--el-*` / `--fe-*` 变量映射。 + +### 改动点 + +`src/views/frontend/components/InputArea.vue` 第 81-85 行的 `el-popover` 标签,新增 `:teleported="false"`: + +```vue + +``` + +`.context-popover` 现有的 scoped 样式(`.popover-title` / `.popover-body` / `.popover-footer` / `.token-count`)保持不变,因为它们使用 `--fe-*` 变量,本来就能工作。 + +### 不需要改动 + +- `theme.scss`:`.frontend-container` 已经把 `--el-bg-color-overlay`、`--el-text-color-regular`、`--el-color-success`、`--el-color-danger` 等都映射到 `--fe-*` 变量,只要 popover 在 `.frontend-container` 内就能自动生效 +- 浅色覆盖:`.frontend-container[data-theme="light"]` 同样完整覆盖了 `--el-*` 系列 +- `.context-popover` 的 scoped 样式:因为 `.context-popover` 本身在 InputArea 模板中,scoped 的 `data-v-xxx` 仍会附加到元素上,popover 留在组件树内后所有选择器自然匹配 + +## 验证 + +1. 切到深色主题,点击输入框右下角的"XX%"指示器 → popover 应为深色背景(`#252526` / `--fe-bg-elevated`)、浅色文字 +2. 切到浅色主题,点击同一指示器 → popover 应为白色(`#ffffff` / `--el-bg-color`)、深色文字 +3. 百分比 `el-tag`:深色主题下 `success` 是青色(`#4ec9b0`),`danger` 是珊瑚红(`#f48771`);浅色主题下 `success` 是蓝色(`#1890ff`),`danger` 是红色(`#f56c6c`) +4. `token-count` 文字:深色下青色,浅色下蓝色 +5. 重复点击 / 多次切换主题,popover 位置不漂移、不被聊天内容遮挡 + +## 非目标 + +- 不改 `el-popover` 的其它属性(placement、width、trigger) +- 不改 `theme.scss` +- 不改 `.context-popover` 现有样式(变量已经够用) +- 不动 `el-tooltip`(左下角 `@` 按钮的 tooltip,独立组件,独立处理) \ No newline at end of file diff --git a/image.png b/image.png deleted file mode 100644 index 703ae6b..0000000 Binary files a/image.png and /dev/null differ diff --git a/src/assets/images/login-background.jpg b/src/assets/images/login-background.jpg index 8a89eb8..7183dc2 100644 Binary files a/src/assets/images/login-background.jpg and b/src/assets/images/login-background.jpg differ diff --git a/src/views/frontend/components/ChatHeader.vue b/src/views/frontend/components/ChatHeader.vue index 27b10af..48d29cb 100644 --- a/src/views/frontend/components/ChatHeader.vue +++ b/src/views/frontend/components/ChatHeader.vue @@ -1,40 +1,49 @@ @@ -78,8 +97,25 @@ function handleModelSelect(model) { color: var(--fe-text-primary); .header-left { - max-width: 400px; - overflow: hidden; + display: flex; + align-items: center; + gap: 10px; + max-width: 480px; + + .agent-avatar { + width: 32px; + height: 32px; + border-radius: 50%; + background: var(--fe-accent); + color: var(--fe-text-inverse); + display: flex; + align-items: center; + justify-content: center; + font-size: 14px; + font-weight: 600; + flex-shrink: 0; + } + .conversation-name { font-size: 16px; font-weight: 600; @@ -93,6 +129,43 @@ function handleModelSelect(model) { .header-right { display: flex; gap: 8px; + align-items: center; + + .icon-btn { + padding: 4px 8px; + background: transparent; + border: none; + color: var(--fe-text-secondary); + height: 32px; + width: 32px; + cursor: pointer; + border-radius: var(--fe-radius-md); + transition: all 0.2s; + + &:hover { + background: var(--fe-bg-hover); + color: var(--fe-text-primary); + } + + &--danger:hover { + color: var(--fe-danger); + } + + .el-icon { + font-size: 18px; + } + } + + .model-select { + width: 180px; + :deep(.el-select__wrapper) { + background: var(--fe-bg-elevated); + box-shadow: 0 0 0 1px var(--fe-border) inset; + border-radius: var(--fe-radius-md); + } + :deep(.el-select__placeholder) { color: var(--fe-text-muted); } + :deep(.el-select__selected-item) { color: var(--fe-text-primary); } + } } } diff --git a/src/views/frontend/components/ChatWindow.vue b/src/views/frontend/components/ChatWindow.vue index 5ec2fc3..79d1a00 100644 --- a/src/views/frontend/components/ChatWindow.vue +++ b/src/views/frontend/components/ChatWindow.vue @@ -445,7 +445,7 @@ defineExpose({ scrollToBottom, forceScrollToBottom }) .chat-window { position: relative; flex: 1; - padding: 16px 20px; + padding: 12px 16px; overflow: hidden; display: flex; flex-direction: column; @@ -491,7 +491,7 @@ defineExpose({ scrollToBottom, forceScrollToBottom }) } .message-group { - margin-bottom: 12px; + margin-bottom: 10px; &.user-group { display: flex; @@ -501,7 +501,7 @@ defineExpose({ scrollToBottom, forceScrollToBottom }) .message-bubble { background: var(--fe-bg-elevated); color: var(--fe-text-primary); - border-radius: 16px 16px 4px 16px; + border-radius: 10px 10px 4px 10px; box-shadow: var(--fe-shadow-sm); max-width: 70%; @@ -530,15 +530,16 @@ defineExpose({ scrollToBottom, forceScrollToBottom }) .message-bubble { background: var(--fe-bg-elevated); color: var(--fe-text-primary); - border-radius: 16px 16px 16px 4px; + border-radius: 10px 10px 10px 4px; box-shadow: var(--fe-shadow-sm); max-width: 70%; } } .message-bubble { - padding: 10px 14px; + padding: 9px 13px; line-height: 1.6; + border-radius: 10px; .message-text { margin: 0; diff --git a/src/views/frontend/components/ConversationSidebar.vue b/src/views/frontend/components/ConversationSidebar.vue index 867f343..2ff9de3 100644 --- a/src/views/frontend/components/ConversationSidebar.vue +++ b/src/views/frontend/components/ConversationSidebar.vue @@ -24,25 +24,46 @@ +
加载中...
-
-
-
- {{ session.title || session.name }} - {{ formatTimeAgo(session.updateTime) }} -
-
-
+ @@ -67,9 +88,9 @@