From cfc815afe23fc3b9f01016a2cecdec1b5772d4db Mon Sep 17 00:00:00 2001
From: linshen <32978552+linshenkx@users.noreply.github.com>
Date: Wed, 25 Jun 2025 22:05:46 +0800
Subject: [PATCH] =?UTF-8?q?refactor(UI):=20=E9=87=8D=E6=9E=84=20OutputDisp?=
=?UTF-8?q?lay=20=E7=BB=84=E4=BB=B6=E8=87=B3=20V2=20=E6=9E=B6=E6=9E=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
本次提交对 OutputDisplay 组件系统进行了全面的 V2 架构重构,以提升可维护性和用户体验。
- **核心重构**: 引入 `OutputDisplayCore` 作为核心引擎,实现了更清晰的组件职责分离。
- **UI/UX 优化**:
- 使用固定的顶部工具栏替换了旧的悬浮工具栏,显著改善了核心功能的可发现性。
- 将“思考过程”面板独立为可折叠区域,优化了布局。
- **样式修复与文档整合**:
- 修复了多项自定义主题下的样式冲突问题。
- 将所有相关设计文档合并为一份单一的权威文档。
---
docs/experience.md | 42 ++
docs/output-display-component-design.md | 203 ---------
docs/output-display-v2-design.md | 238 ++++++++++
.../ui/src/components/InputWithSelect.vue | 2 +-
.../ui/src/components/MarkdownRenderer.vue | 2 +-
packages/ui/src/components/Modal.vue | 1 -
packages/ui/src/components/ModelManager.vue | 2 +-
.../ui/src/components/OutputDisplayCore.vue | 429 ++++++------------
.../components/OutputDisplayFullscreen.vue | 9 +-
packages/ui/src/components/OutputPanel.vue | 2 +-
packages/ui/src/components/PromptPanel.vue | 2 +-
packages/ui/src/i18n/locales/en-US.ts | 7 +-
packages/ui/src/i18n/locales/zh-CN.ts | 7 +-
packages/ui/src/styles/theme.css | 170 ++++++-
packages/ui/tests/unit/OutputDisplay.test.ts | 387 +++-------------
15 files changed, 650 insertions(+), 853 deletions(-)
delete mode 100644 docs/output-display-component-design.md
create mode 100644 docs/output-display-v2-design.md
diff --git a/docs/experience.md b/docs/experience.md
index 9c226519..411d07b0 100644
--- a/docs/experience.md
+++ b/docs/experience.md
@@ -339,3 +339,45 @@ if (updatedTemplate && contentChanged) {
---
**记住**:好的经验文档应该能让团队成员快速找到解决方案,而不是重新踩坑。
+
+## 7. UI 主题系统与第三方库样式冲突处理
+
+**问题场景**: 在开发多主题功能(特别是紫色、绿色等自定义深色主题)时,发现集成了 Tailwind Typography (`prose`) 插件的 Markdown 渲染组件,其背景和文本颜色无法正确应用主题色,而是被覆盖为不协调的亮色样式(如白色背景)。
+
+**根本原因分析**:
+
+问题的核心在于项目自定义的、基于 `data-theme` 属性的颜色主题系统,与 Tailwind Typography (`prose`) 插件预设的、自成体系的颜色方案发生了直接冲突。
+
+1. **`prose` 的强主张**: `@tailwindcss/typography` 插件不仅仅是一个布局工具,它会为 HTML 内容注入一套完整的视觉方案,其中**包含了固定的颜色、字体、背景等样式**。
+2. **默认亮色偏好**: `prose` 的默认配置(如 `prose-stone`)是为亮色背景设计的,它会强制设定深色的文本颜色。
+3. **`dark:` 模式的局限性**: `prose` 的颜色反转机制 (`dark:prose-invert`) 强依赖于 `` 标签上的 `dark` 类。我们自定义的深色主题(如 `data-theme="purple"`)虽然视觉上是深色的,但并未触发 Tailwind 的 `dark` 模式,因此 `prose` 依然应用其默认的亮色样式,导致了颜色覆盖。
+
+**解决方案与最佳实践**:
+
+面对这种强样式主张的第三方库,必须采取**彻底隔离**的策略,不能试图"混合"使用。
+
+1. **禁止部分应用**: 实践证明,试图通过 `@apply prose-sm` 等方式只"借用" `prose` 的布局功能是行不通的。这依然会引入我们不希望的颜色样式,导致不可预测的覆盖问题。
+
+2. **手动重建布局**: 最稳健的解决方案是,在需要应用自定义主题的组件中,**完全移除** `@apply prose` 或其任何变体。然后,参考 `prose` 的文档或默认样式,**手动为各个 Markdown 元素 (`h1`, `p`, `ul` 等) 添加纯粹的、不包含颜色的布局和间距样式**。
+
+3. **控制权归还**: 通过手动重建布局,我们将样式的控制权完全收归到自己的主题系统中。这样,我们在各个主题下为元素定义的颜色、背景、边框等样式才能不受干扰地、正确地应用。
+
+**示例 - 手动重建的 Markdown 布局**:
+
+```css
+/* 在全局 theme.css 中定义,不属于任何特定主题 */
+.theme-markdown-content {
+ @apply max-w-none;
+}
+
+.theme-markdown-content > :first-child { @apply mt-0; }
+.theme-markdown-content > :last-child { @apply mb-0; }
+.theme-markdown-content h1 { @apply text-2xl font-bold my-4; }
+.theme-markdown-content h2 { @apply text-xl font-semibold my-3; }
+.theme-markdown-content p { @apply my-3 leading-relaxed; }
+.theme-markdown-content ul,
+.theme-markdown-content ol { @apply my-3 pl-6 space-y-2; }
+.theme-markdown-content pre { @apply my-4 p-4 rounded-lg text-sm; }
+/* ... etc ... */
+```
+通过这种方式,我们既保留了优美的排版,又确保了自定义主题的颜色能够正确渲染。
diff --git a/docs/output-display-component-design.md b/docs/output-display-component-design.md
deleted file mode 100644
index 3b7d7313..00000000
--- a/docs/output-display-component-design.md
+++ /dev/null
@@ -1,203 +0,0 @@
-# OutputDisplay 组件体系设计文档
-
-## 1. 概述
-
-**OutputDisplay** 是一个通用的、模块化的内容输出组件体系,旨在统一管理系统中所有内容输出区域的显示和交互。它由一组分工明确的组件构成,共同解决样式不统一、交互分散、维护困难等问题。
-
-### 组件体系构成
-
-- **`OutputDisplay` (外观组件)**:作为整个体系的统一入口,供外部调用。它封装了核心显示逻辑和全屏功能,并向上传递事件。
-- **`OutputDisplayCore` (核心引擎)**:实现所有核心的显示逻辑,包括只读/编辑/对比模式切换、推理内容管理、工具栏交互等。
-- **`OutputDisplayFullscreen` (全屏封装)**:一个轻量级封装,结合 `FullscreenDialog` 和 `OutputDisplayCore` 提供沉浸式的全屏查看体验。
-
-## 2. 设计目标
-
-- **统一性**:提供一致的UI和交互体验。
-- **复用性**:一个组件体系适配多种使用场景。
-- **可维护性**:集中管理核心逻辑,降低耦合度。
-- **扩展性**:通过配置支持不同功能组合。
-
-## 3. 组件接口设计 (`OutputDisplayCore`)
-
-核心引擎 `OutputDisplayCore` 是功能的主要提供者。
-
-### Props 属性
-
-```typescript
-type ActionName = 'fullscreen' | 'diff' | 'copy' | 'edit' | 'reasoning';
-
-interface OutputDisplayCoreProps {
- // === 内容相关 ===
- content?: string; // 主要内容(或对比模式下的新内容)
- originalContent?: string; // 对比模式下的原始(旧)内容。**这是启用内置对比(diff)模式的前提**
- reasoning?: string; // 推理内容
-
- // === 显示模式 ===
- mode: 'readonly' | 'editable'; // 'readonly': 渲染Markdown; 'editable': 提供可编辑的textarea
- reasoningMode: 'show' | 'hide' | 'auto'; // 推理内容显示模式: 'show'-常驻, 'hide'-隐藏, 'auto'-有内容时自动显示
-
- // === 功能开关 ===
- // 通过此数组控制悬浮工具栏中显示哪些功能按钮。
- // 例如,当数组中包含 'diff' 且 `originalContent` prop 也被提供时,对比按钮才会出现。
- enabledActions?: ActionName[]; // e.g., ['copy', 'fullscreen', 'edit']
-
- // === 样式配置 ===
- height?: string | number; // 内容区域高度, e.g., '100%' or 500
- placeholder?: string; // 内容为空时的占位符文本
-
- // === 状态 ===
- loading?: boolean; // 加载状态,显示遮罩
- streaming?: boolean; // 流式输出状态,显示流式指示器
-}
-```
-
-### Events 事件
-
-```typescript
-interface OutputDisplayCoreEvents {
- 'update:content': (content: string) => void; // 在编辑模式下,内容发生变更时触发
- 'update:reasoning': (reasoning: string) => void; // 推理内容更新时触发
- 'copy': (content: string, type: 'content' | 'reasoning' | 'all') => void; // 点击复制按钮时触发
- 'fullscreen': () => void; // 点击全屏按钮时触发
- 'edit-start': () => void; // 进入编辑模式时触发
- 'edit-end': () => void; // 退出编辑模式时触发
- 'reasoning-toggle': (expanded: boolean) => void; // 推理内容区域展开/折叠时触发
- 'view-change': (mode: 'base' | 'diff') => void; // 在内置的常规视图和对比视图之间切换时触发
-}
-```
-
-## 4. 组件结构设计
-
-### 4.1. 组件体系关系
-
-`OutputDisplay` 外观组件是实际应用中应直接使用的组件。它内部管理 `OutputDisplayCore` 和 `OutputDisplayFullscreen` 的实例化和通信。
-
-```mermaid
-graph TD
- A[应用 App.vue] --> B(OutputDisplay);
- B -- "实例化并传递Props" --> C(OutputDisplayCore);
- B -- "监听 'fullscreen' 事件" --> D{显示/隐藏};
- D --> E(OutputDisplayFullscreen);
- E -- "内部实例化" --> C;
- C -- "通过 emit 触发事件" --> B;
- B -- "继续向上传递事件" --> A;
-
- subgraph "组件体系"
- B; C; D; E;
- end
-```
-
-### 4.2. `OutputDisplayCore` 内部结构
-
-核心组件根据不同的模式和状态,动态渲染其内部视图。
-
-```
-OutputDisplayCore
-├── FloatingToolbar (悬浮工具栏, v-show="isHovering")
-│ ├── ReasoningToggle (推理过程按钮)
-│ ├── ViewModeToggle (文本/视图切换)
-│ ├── DiffButton (对比按钮)
-│ ├── CopyButton (复制按钮)
-│ └── FullscreenButton (全屏按钮)
-├── ReasoningSection (v-if="shouldShowReasoning")
-│ └── MarkdownRenderer (渲染推理内容)
-└── MainContent (flex: 1)
- ├── TextDiffUI (v-if="internalViewMode === 'diff'")
- ├── ReadonlyView (v-else-if="mode === 'readonly'")
- │ └── MarkdownRenderer (渲染主要内容)
- └── EditableView (v-else)
- ├── textarea (v-if="isEditing")
- └── MarkdownRenderer (v-else, 点击可编辑)
-```
-
-## 5. 功能特性
-
-### 5.1. 推理内容管理 (`reasoning`)
-- **智能显示**: 根据 `reasoningMode` 自动判断是否显示。
-- **流式指示**: 在 `streaming` 状态下,若只有推理内容,会显示"生成中..."指示器。
-- **独立复制**: 推理内容有独立的复制按钮。
-
-### 5.2. 多视图模式
-- **只读 (`readonly`)**: 使用 `MarkdownRenderer` 展示内容,支持代码高亮等富文本格式。
-- **可编辑 (`editable`)**:
- - 默认显示渲染后的 `Markdown`。
- - 点击内容区域或切换按钮可进入 `textarea` 编辑模式。
- - 编辑内容通过 `update:content` 事件实时同步。
-- **内置对比 (`diff`)**:
- - **激活机制**: 此模式并非由独立的prop控制,而是通过组合条件隐式激活。当`originalContent` prop被提供,且`enabledActions`数组包含`'diff'`时,用户界面上才会出现进入对比模式的按钮。
- - **功能**: 激活后,组件内部使用`TextDiffUI`来展示`content`与`originalContent`之间的差异。
-
-### 5.3. 悬浮工具栏
-- **按需显示**: `enabledActions` prop 控制显示哪些功能按钮。
-- **情景可见**: 仅在鼠标悬浮在组件上时显示,保持界面整洁。
-- **独立控制**: 每个按钮的可见性都与相关状态(如是否有内容可复制)绑定。
-
-## 6. 使用场景与模式
-
-### 6.1. 场景:优化结果展示与编辑
-
-这是最完整的用例,展示了可编辑、可对比、带推理过程的优化结果。
-
-```vue
-