diff --git a/AGENTS.md b/AGENTS.md index 286cfdd..0973a7d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -96,6 +96,21 @@ - 思考内容当前默认隐藏,只显示“思考中 / 思考完成”标题;相关折叠代码保留,后续可继续修。 - 不要在未获得用户要求的情况下运行交互式 TUI 压测或长时间模拟输入,以免刷屏占满上下文。 +## 5.5) 前端设计风格统一规范(强制) + +> 适用范围:所有前端 UI(以 `static/src` Web 为主;`cli/src` 在视觉可类比处同样适用;以及未来新增的任何界面)。 +> 约束级别:写新 UI 或改动 UI 时**必须遵守**;遇到存量违规应在最小改动允许范围内顺手修正。 +> 配色基础设施:三模式 CSS 变量定义在 `static/src/styles/base/_tokens.scss`(`:root[data-theme='classic'|'light'|'dark']`),切换逻辑见 `static/src/utils/theme.ts`。 + +1. **禁止边缘光晕**:不使用任何 glow / 外发光 / 彩色光晕效果(大范围彩色 `box-shadow` 扩散、`filter: drop-shadow` 光圈、`::before/::after` 模糊光晕等)。已有的要移除。允许的只是中性、克制的投影——沿用 `--claude-shadow` / `--theme-shadow-*` 既有 token,不自造发光阴影。 +2. **禁止原生浏览器组件**:不直接使用浏览器默认外观的 ``、`alert/confirm/prompt`、原生右键菜单、原生 tooltip 等,一律换成与项目风格统一的自定义组件(下拉 / 开关 / 单选 / 滑块 / 模态 / Toast)。 +3. **禁止圆角套娃**:不允许「圆角矩形套圆角矩形套圆角矩形」的多层嵌套卡片——典型垃圾审美,且极大占据有效显示面积。容器层级要扁平,优先用分隔线 / 留白 / 底色区分,不要多包一层带边框圆角的盒子。 +4. **图标外框对齐**:同一组图标按钮的外框(点击热区 / 容器)必须统一对齐,不能一会居中、一会左对齐、一会右对齐;同组固定相同尺寸与对齐方式。 +5. **图标视觉对齐而非理论对齐**:按图标视觉重心对齐而非几何边界盒;对重心偏移的图标(三角形 / 播放键 / 放大镜等)做微调,使其「看起来居中」。 +6. **颜色遵循三模式切换**:所有颜色走原生三模式(经典 `classic` / 明亮 `light` / 夜间 `dark`),禁止写死颜色字面量,一律用 CSS 变量(`--claude-*` / `--theme-*`)。新增颜色必须在三套主题里都补齐。 +7. **带文字容器固定高度**:所有含内部文字的选项 / 按钮 / 标签 / 容器固定高度,禁止因文字多少被撑大撑高;过长文字用省略号或内部滚动处理。 +8. **窗口设最大尺寸 + 内部滚动**:所有弹窗 / 面板 / 列表设置 `max-height` / `max-width`,超出由内部容器滚动,不顶大整个窗口;滚动条要么隐藏,要么做样式适配,不暴露原生粗滚动条。 + ## 6) Git 工作流(开发 + Review) ### 6.1 核心原则 diff --git a/CLAUDE.md b/CLAUDE.md index 6546d8c..6e81997 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -237,6 +237,27 @@ terminal.search_conversations(query="bug fix") terminal.delete_conversation(conversation_id) ``` +## 前端设计风格统一规范(强制) + +> 适用范围:所有前端 UI(以 `static/src` Web 为主;`cli/src` 在视觉可类比处同样适用;以及未来新增的任何界面)。 +> 约束级别:写新 UI 或改动 UI 时**必须遵守**;遇到存量违规应在最小改动允许范围内顺手修正。 + +1. **禁止边缘光晕**:不使用任何 glow / 外发光 / 彩色光晕效果(大范围彩色 `box-shadow` 扩散、用 `filter: drop-shadow` 做光圈、`::before/::after` 模糊光晕等)。已有的要移除。允许的只是中性、克制的投影——沿用 `--claude-shadow` / `--theme-shadow-*` 这类已有 token,不要自造发光阴影。 + +2. **禁止原生浏览器组件**:不直接使用浏览器默认外观的 ``、`alert/confirm/prompt`、原生右键菜单、原生 tooltip 等。一律替换为与项目风格统一的自定义组件(下拉、开关、单选、滑块、模态、Toast 等)。 + +3. **禁止圆角套娃**:不允许「圆角矩形里再套圆角矩形再套圆角矩形」的多层嵌套卡片——这是典型的垃圾审美,且会极大占据有效显示面积。容器层级要扁平:能用分隔线 / 留白 / 底色区分的,就不要再多包一层带边框圆角的盒子。 + +4. **图标外框对齐**:同一组图标按钮的外框(点击热区 / 容器)必须对齐统一,不能一个居中、一个左对齐、一个右对齐。同组按钮固定相同尺寸与对齐方式。 + +5. **图标视觉对齐而非理论对齐**:按图标的视觉重心对齐,而不是几何边界盒。对视觉重心偏移的图标(三角形 / 播放键 / 放大镜等)做微调(micro-nudge),让它「看起来居中」而非「数值上居中」。 + +6. **颜色遵循三模式切换**:所有颜色必须走原生三模式(经典 `classic` / 明亮 `light` / 夜间 `dark`)。禁止写死颜色字面量,一律使用 CSS 变量(`--claude-*` / `--theme-*`,定义在 `static/src/styles/base/_tokens.scss`,由 `:root[data-theme='classic'|'light'|'dark']` 提供;切换逻辑见 `static/src/utils/theme.ts`)。新增颜色必须在三套主题里都补齐。 + +7. **带文字容器固定高度**:所有含内部文字的选项 / 按钮 / 标签 / 容器必须固定高度,禁止因文字多少被撑大撑高。文字过长用省略号或内部滚动处理,保持行高与块高稳定。 + +8. **窗口设最大尺寸 + 内部滚动**:所有弹窗 / 面板 / 列表必须设置 `max-height` / `max-width`,超出时由内部容器滚动,而不是把整个窗口顶大。滚动条要么隐藏,要么做样式适配(贴合整体设计,不暴露原生粗滚动条)。 + ## Security Considerations - 所有用户操作在独立容器中执行,与宿主机隔离 diff --git a/core/web_terminal.py b/core/web_terminal.py index a522e36..341239b 100644 --- a/core/web_terminal.py +++ b/core/web_terminal.py @@ -273,10 +273,10 @@ class WebTerminal(MainTerminal): "message": f"加载对话异常: {e}" } - def get_conversations_list(self, limit: int = 20, offset: int = 0) -> Dict: + def get_conversations_list(self, limit: int = 20, offset: int = 0, non_empty: bool = False) -> Dict: """获取对话列表(Web版本)""" try: - result = self.context_manager.get_conversation_list(limit=limit, offset=offset) + result = self.context_manager.get_conversation_list(limit=limit, offset=offset, non_empty=non_empty) return { "success": True, "data": result diff --git a/server/conversation.py b/server/conversation.py index 1ed1a79..9cc9429 100644 --- a/server/conversation.py +++ b/server/conversation.py @@ -421,12 +421,13 @@ def get_conversations(terminal: WebTerminal, workspace: UserWorkspace, username: # 获取查询参数 limit = request.args.get('limit', 20, type=int) offset = request.args.get('offset', 0, type=int) - + non_empty = request.args.get('non_empty', '0') in ('1', 'true', 'True') + # 限制参数范围 limit = max(1, min(limit, 10000)) # 限制在1-10000之间 offset = max(0, offset) - - result = terminal.get_conversations_list(limit=limit, offset=offset) + + result = terminal.get_conversations_list(limit=limit, offset=offset, non_empty=non_empty) if result["success"]: return jsonify({ diff --git a/static/src/App.vue b/static/src/App.vue index 902e05b..427b0ee 100644 --- a/static/src/App.vue +++ b/static/src/App.vue @@ -419,12 +419,12 @@ @@ -469,6 +470,7 @@ :restoring="versioningRestoring" :restore-mode="versioningRestoreMode" :workspace-matched="versioningWorkspaceMatched" + :icon-style="iconStyle" @close="versioningDialogOpen = false" @refresh="refreshVersioningDialog" @toggle-enabled="toggleConversationVersioning" diff --git a/static/src/app/methods/ui.ts b/static/src/app/methods/ui.ts index 5b61cd7..8e53847 100644 --- a/static/src/app/methods/ui.ts +++ b/static/src/app/methods/ui.ts @@ -2182,25 +2182,70 @@ export const uiMethods = { }); return; } - if (!this.conversations.length && !this.conversationsLoading) { - this.loadConversationsList(); - } - const fallback = this.conversations.find((c) => c.id !== this.currentConversationId); - if (!fallback) { - this.uiPushToast({ - title: '暂无可用对话', - message: '没有可供回顾的其他对话记录', - type: 'info' - }); - return; - } - this.reviewSelectedConversationId = fallback.id; this.reviewDialogOpen = true; + this.reviewSelectedConversationId = null; this.reviewPreviewLines = []; this.reviewPreviewError = null; this.reviewGeneratedPath = null; - this.loadReviewPreview(fallback.id); this.closeQuickMenu(); + // 弹窗使用独立列表(仅含有内容的对话),加载完成后自动选中首个可用项 + this.loadReviewConversations(); + }, + + async loadReviewConversations() { + if (this.reviewListLoading) return; + this.reviewListLoading = true; + this.reviewListOffset = 0; + try { + const limit = this.reviewListLimit; + const resp = await fetch(`/api/conversations?limit=${limit}&offset=0&non_empty=1`); + const data = await resp.json().catch(() => ({})); + if (data?.success) { + this.reviewConversations = data.data.conversations || []; + this.reviewListHasMore = Boolean(data.data.has_more); + this.autoSelectReviewConversation(); + } else { + this.reviewConversations = []; + this.reviewListHasMore = false; + } + } catch (error) { + console.error('加载回顾对话列表异常:', error); + this.reviewConversations = []; + this.reviewListHasMore = false; + } finally { + this.reviewListLoading = false; + } + }, + + async loadMoreReviewConversations() { + if (this.reviewListLoadingMore || !this.reviewListHasMore) return; + this.reviewListLoadingMore = true; + try { + const limit = this.reviewListLimit; + const offset = this.reviewListOffset + limit; + const resp = await fetch(`/api/conversations?limit=${limit}&offset=${offset}&non_empty=1`); + const data = await resp.json().catch(() => ({})); + if (data?.success) { + // 追加到现有列表,不整体替换,避免滚动位置复位 + this.reviewConversations.push(...(data.data.conversations || [])); + this.reviewListHasMore = Boolean(data.data.has_more); + this.reviewListOffset = offset; + this.autoSelectReviewConversation(); + } + } catch (error) { + console.error('加载更多回顾对话异常:', error); + } finally { + this.reviewListLoadingMore = false; + } + }, + + autoSelectReviewConversation() { + if (this.reviewSelectedConversationId) return; + const fallback = this.reviewConversations.find((c) => c.id !== this.currentConversationId); + if (fallback && fallback.id) { + this.reviewSelectedConversationId = fallback.id; + this.loadReviewPreview(fallback.id); + } }, closeHeaderMenu() { diff --git a/static/src/app/state.ts b/static/src/app/state.ts index 2ae643d..71e43f3 100644 --- a/static/src/app/state.ts +++ b/static/src/app/state.ts @@ -229,6 +229,13 @@ export function dataState() { reviewPreviewLimit: 20, reviewSendToModel: true, reviewGeneratedPath: null, + // 回顾弹窗独立的对话列表(只含有内容的对话,不污染侧边栏) + reviewConversations: [], + reviewListLoading: false, + reviewListLoadingMore: false, + reviewListHasMore: false, + reviewListOffset: 0, + reviewListLimit: 20, // 新手教程首次引导弹窗 tutorialPromptVisible: false, diff --git a/static/src/components/overlay/ConversationReviewDialog.vue b/static/src/components/overlay/ConversationReviewDialog.vue index 61155e1..0f1a247 100644 --- a/static/src/components/overlay/ConversationReviewDialog.vue +++ b/static/src/components/overlay/ConversationReviewDialog.vue @@ -2,25 +2,36 @@
-
+
对话回顾
选择要生成回顾文件的对话
-
已生成 {{ generatedPath }}
-
-
- 对话列表 - 共 {{ conversations.length }} 条 +
+ 对话列表 + + 共 {{ conversations.length }} 条 +
-
+
正在加载...
暂无对话
-