diff --git a/.helloagents/CHANGELOG.md b/.helloagents/CHANGELOG.md index e842e9f..0f43ffa 100644 --- a/.helloagents/CHANGELOG.md +++ b/.helloagents/CHANGELOG.md @@ -12,3 +12,7 @@ - 方案: [202603250532_quickcommands-theme-alignment](archive/2026-03/202603250532_quickcommands-theme-alignment/) - **[frontend]**: 修复终端标签切换后的视口恢复逻辑,贴底终端重新激活后自动贴底,上翻终端保留历史位置 — by yinjianm - 方案: [202603250547_terminal-tab-scroll-restore](archive/2026-03/202603250547_terminal-tab-scroll-restore/) + +### 新增 +- **[frontend]**: 将“黑暗模式”预设与终端默认主题统一调整为黑绿夜间风格 — by yinjianm + - 方案: [202603250603_dark-green-night-theme](archive/2026-03/202603250603_dark-green-night-theme/) diff --git a/.helloagents/INDEX.md b/.helloagents/INDEX.md index 664456c..860839a 100644 --- a/.helloagents/INDEX.md +++ b/.helloagents/INDEX.md @@ -31,7 +31,7 @@ ```yaml kb_version: 2.3.7 -最后更新: 2026-03-25 05:52 +最后更新: 2026-03-25 06:06 模块数量: 4 待执行方案: 0 ``` diff --git a/.helloagents/archive/2026-03/202603250603_dark-green-night-theme/.status.json b/.helloagents/archive/2026-03/202603250603_dark-green-night-theme/.status.json new file mode 100644 index 0000000..aff3ef4 --- /dev/null +++ b/.helloagents/archive/2026-03/202603250603_dark-green-night-theme/.status.json @@ -0,0 +1 @@ +{"status":"completed","completed":5,"failed":0,"pending":0,"total":5,"done":5,"percent":100,"current":"已完成黑绿夜间主题预设调整与验证,等待归档","updated_at":"2026-03-25 06:06:54"} diff --git a/.helloagents/archive/2026-03/202603250603_dark-green-night-theme/proposal.md b/.helloagents/archive/2026-03/202603250603_dark-green-night-theme/proposal.md new file mode 100644 index 0000000..cfba563 --- /dev/null +++ b/.helloagents/archive/2026-03/202603250603_dark-green-night-theme/proposal.md @@ -0,0 +1,66 @@ +# 变更提案: dark-green-night-theme + +## 元信息 +```yaml +类型: 功能增强 +方案类型: implementation +优先级: P2 +状态: 已完成 +状态说明: 已完成黑绿夜间主题预设调整与前后端构建验证,待归档 +创建: 2026-03-25 +``` + +--- + +## 1. 需求 + +### 背景 +当前外观里的“黑暗模式”仍偏通用深色,终端默认主题也没有完全贴近你提供的参考图,整体缺少黑绿监控/终端风格的一致性。 + +### 目标 +- 将界面“黑暗模式”预设改成参考图这种黑绿夜间风格。 +- 同步调整终端默认主题,使终端、工作区和状态面板的视觉方向一致。 +- 保持浅色默认主题不变,只改黑暗模式预设与终端默认主题。 + +### 约束条件 +```yaml +范围约束: 仅调整主题预设与默认主题定义,不改业务交互结构 +一致性约束: 前后端默认主题定义保持一致,避免重置或回退后前后端风格漂移 +体验约束: 保持终端文本可读性,避免纯荧光绿导致长时间阅读疲劳 +验证约束: 以前端构建通过为基础,运行态视觉以本地手验为主 +``` + +### 验收标准 +- [ ] “黑暗模式”按钮应用后,界面主色调变为黑绿夜间风格 +- [ ] 终端默认主题与黑暗模式预设在氛围上保持一致 +- [ ] 不影响浅色默认主题和现有主题编辑功能 +- [ ] 前端构建通过 + +--- + +## 2. 方案 + +### 技术方案 +保留现有主题系统结构,只调整预设值。前端 `StyleCustomizerUiTab.vue` 中的 `darkModeTheme` 改为参考图对应的黑绿 UI 配色;前端 `default-themes.ts` 中的 `defaultXtermTheme` 改为黑底、绿系高亮、青绿辅助色的终端主题;后端 `config/default-themes.ts` 同步更新相同默认定义,确保初始化和回退逻辑一致。 + +### 影响范围 +```yaml +涉及模块: + - frontend: 黑暗模式预设、终端默认主题 + - backend: 默认主题定义镜像副本 +预计变更文件: 3 +``` + +### 风险评估 +| 风险 | 等级 | 应对 | +|------|------|------| +| 绿色过亮导致正文和状态数字刺眼 | 低 | 正文采用浅灰绿,强调色才使用亮绿 | +| UI 黑绿预设与终端主题气质不一致 | 低 | 统一使用深炭黑背景 + 绿色强调 + 青绿辅助色 | +| 只改前端导致后端初始化后的默认值不一致 | 低 | 同步更新 backend 对应默认主题文件 | + +### 实施结果 +- `StyleCustomizerUiTab.vue` 的 `darkModeTheme` 已改为黑绿夜间风格,主背景、边框、按钮、图标 hover 和状态色全部切到深炭黑 + 绿色强调体系。 +- 前后端 `defaultXtermTheme` 均改为黑底、浅灰绿正文、绿系高亮与青绿辅助色的终端默认主题。 +- 浅色默认 `defaultUiTheme` 保持不变,仍保留当前默认浅色体系。 +- `npm run build --workspace @nexus-terminal/frontend` 与 `npm run build --workspace @nexus-terminal/backend` 均通过。 +- 受本地 `/appearance` 保存接口现场限制,本轮未在浏览器里直接点击“黑暗模式”做最终视觉手验。 diff --git a/.helloagents/archive/2026-03/202603250603_dark-green-night-theme/tasks.md b/.helloagents/archive/2026-03/202603250603_dark-green-night-theme/tasks.md new file mode 100644 index 0000000..7dfa2bd --- /dev/null +++ b/.helloagents/archive/2026-03/202603250603_dark-green-night-theme/tasks.md @@ -0,0 +1,51 @@ +# 任务清单: dark-green-night-theme + +```yaml +@feature: dark-green-night-theme +@created: 2026-03-25 +@status: completed +@mode: R2 +``` + +## 进度概览 + +| 完成 | 失败 | 跳过 | 总数 | +|------|------|------|------| +| 5 | 0 | 0 | 5 | + +--- + +## 任务列表 + +### 1. 方案与范围确认 + +- [√] 1.1 创建黑绿夜间主题方案包并锁定前后端默认主题定义范围 | depends_on: [] + +### 2. 主题预设调整 + +- [√] 2.1 统一黑暗模式预设的 UI 配色变量为黑绿夜间风格 | depends_on: [1.1] +- [√] 2.2 统一前后端终端默认主题为黑绿终端风格 | depends_on: [2.1] + +### 3. 验证与同步 + +- [√] 3.1 运行前后端最小验证并记录结果 | depends_on: [2.2] +- [√] 3.2 更新 `.helloagents` 文档与变更记录 | depends_on: [3.1] + +--- + +## 执行日志 + +| 时间 | 任务 | 状态 | 备注 | +|------|------|------|------| +| 2026-03-25 06:03 | 1.1 | 完成 | 创建 implementation 方案包,范围锁定为黑暗模式预设与终端默认主题 | +| 2026-03-25 06:04 | 2.1 / 2.2 | 完成 | 统一黑暗模式预设与前后端默认终端主题为黑绿夜间风格 | +| 2026-03-25 06:05 | 3.1 | 完成 | 前端与后端构建均通过 | +| 2026-03-25 06:06 | 3.2 | 完成 | 更新 frontend/backend 模块文档并准备归档 | + +--- + +## 执行备注 + +- 本次不改浅色默认主题,也不改主题编辑器交互。 +- 后端默认主题定义当前主要用于初始化与一致性镜像,即使前端直接消费自己的默认值,也需要同步。 +- 真实视觉观感仍建议由你在已登录环境里点击一次“黑暗模式”做最终目视确认。 diff --git a/.helloagents/archive/_index.md b/.helloagents/archive/_index.md index 09194f5..b89f98c 100644 --- a/.helloagents/archive/_index.md +++ b/.helloagents/archive/_index.md @@ -10,6 +10,7 @@ | 202603250317 | ghcr-docker-publish | implementation | workspace-root | ghcr-docker-publish#D001 | ✅完成 | | 202603250532 | quickcommands-theme-alignment | implementation | frontend | - | ✅完成 | | 202603250547 | terminal-tab-scroll-restore | implementation | frontend | - | ✅完成 | +| 202603250603 | dark-green-night-theme | implementation | frontend, backend | - | ✅完成 | | 202603251200 | workspace-workbench-monitor | implementation | frontend, backend | workspace-workbench-monitor#D001 | ✅完成 | ## 按月归档 @@ -18,6 +19,7 @@ - [202603250317_ghcr-docker-publish](./2026-03/202603250317_ghcr-docker-publish/) - 新增 GHCR 镜像发布 workflow 并切换 compose 镜像来源 - [202603250532_quickcommands-theme-alignment](./2026-03/202603250532_quickcommands-theme-alignment/) - 统一快捷指令视图按钮主题适配,移除残留硬编码 hover 色值 - [202603250547_terminal-tab-scroll-restore](./2026-03/202603250547_terminal-tab-scroll-restore/) - 修复终端标签切换后的贴底/历史滚动恢复逻辑 +- [202603250603_dark-green-night-theme](./2026-03/202603250603_dark-green-night-theme/) - 将黑暗模式预设与终端默认主题统一调整为黑绿夜间风格 - [202603251200_workspace-workbench-monitor](./2026-03/202603251200_workspace-workbench-monitor/) - `/workspace` 改为三栏 Workbench 布局,并新增开机累计流量监控 ## 结果状态说明 diff --git a/.helloagents/modules/backend.md b/.helloagents/modules/backend.md index b227d6a..9d1b1b1 100644 --- a/.helloagents/modules/backend.md +++ b/.helloagents/modules/backend.md @@ -39,6 +39,11 @@ **行为**: 按 `controller/service/repository/routes` 的分层模式组织连接、通知、设置、快速命令、主题等功能。 **结果**: 新增后端能力时应优先延续现有业务域目录结构,而不是在入口文件堆叠逻辑。 +### 外观默认值 +**条件**: 数据库初始化、外观设置重置或前后端默认主题定义调整。 +**行为**: `appearance.repository.ts` 负责写入默认 UI 外观设置,`config/default-themes.ts` 保持与前端同名默认主题定义一致,作为默认外观与终端主题的镜像基线。 +**结果**: 前后端在默认主题和回退值上保持一致,避免前端回退与后端初始化出现风格漂移。 + ### 状态监控 **条件**: 前端工作区通过 WebSocket 订阅服务器状态。 **行为**: `StatusMonitorService` 通过 SSH 读取 `free`、`df`、`/proc/stat` 与 `/proc/net/dev`,同时计算瞬时网速与默认网卡自开机以来的累计上下行字节数。 diff --git a/.helloagents/modules/frontend.md b/.helloagents/modules/frontend.md index e5aba48..4c9cda0 100644 --- a/.helloagents/modules/frontend.md +++ b/.helloagents/modules/frontend.md @@ -36,8 +36,8 @@ ### 工作区交互 **条件**: 用户进入 `/workspace` 或相关管理页面。 -**行为**: 通过组件、Pinia 与 composable 协同管理终端、文件管理、命令历史、布局配置、主题和状态监控;当前 `/workspace` 默认主布局为“左侧 Workbench、中央终端、右侧状态监控”,其中 Workbench 以 tab 容器整合快捷指令、命令历史、文件管理和编辑器,默认激活快捷指令。`QuickCommandsView.vue` 内的新增按钮、空状态按钮和列表操作按钮统一复用 `bg-button`、`text-button-text`、`hover:bg-button-hover`、`hover:bg-border` 等主题变量类,避免写死黑白 hover 色值;`Terminal.vue` 会跟踪 xterm 的视口行号与贴底状态,在终端标签切换、重新激活和 `fit()` 后按原滚动意图恢复。 -**结果**: 页面逻辑分散在 `views/`、`components/`、`stores/` 与 `composables/`,其中布局与交互微调优先落在 `layout.store.ts`、`LayoutRenderer.vue`、`WorkspaceWorkbench.vue`、`QuickCommandsView.vue` 和 `Terminal.vue`。 +**行为**: 通过组件、Pinia 与 composable 协同管理终端、文件管理、命令历史、布局配置、主题和状态监控;当前 `/workspace` 默认主布局为“左侧 Workbench、中央终端、右侧状态监控”,其中 Workbench 以 tab 容器整合快捷指令、命令历史、文件管理和编辑器,默认激活快捷指令。`QuickCommandsView.vue` 内的新增按钮、空状态按钮和列表操作按钮统一复用 `bg-button`、`text-button-text`、`hover:bg-button-hover`、`hover:bg-border` 等主题变量类,避免写死黑白 hover 色值;`Terminal.vue` 会跟踪 xterm 的视口行号与贴底状态,在终端标签切换、重新激活和 `fit()` 后按原滚动意图恢复;样式编辑器中的“黑暗模式”预设与 `defaultXtermTheme` 已统一为黑绿夜间风格。 +**结果**: 页面逻辑分散在 `views/`、`components/`、`stores/` 与 `composables/`,其中布局与交互微调优先落在 `layout.store.ts`、`LayoutRenderer.vue`、`WorkspaceWorkbench.vue`、`QuickCommandsView.vue`、`Terminal.vue`、`StyleCustomizerUiTab.vue` 和 `features/appearance/config/default-themes.ts`。 ## 依赖关系 diff --git a/.helloagents/plan/202603250614_terminal-ansi-color-effects/.status.json b/.helloagents/plan/202603250614_terminal-ansi-color-effects/.status.json new file mode 100644 index 0000000..479d650 --- /dev/null +++ b/.helloagents/plan/202603250614_terminal-ansi-color-effects/.status.json @@ -0,0 +1 @@ +{"status":"in_progress","completed":1,"failed":0,"pending":4,"total":5,"done":1,"percent":20,"current":"分析 xterm 前景色输出规则并准备修复 Terminal.vue 的 ANSI 彩色文字效果","updated_at":"2026-03-25 06:14:00"} diff --git a/.helloagents/plan/202603250614_terminal-ansi-color-effects/proposal.md b/.helloagents/plan/202603250614_terminal-ansi-color-effects/proposal.md new file mode 100644 index 0000000..5198f2c --- /dev/null +++ b/.helloagents/plan/202603250614_terminal-ansi-color-effects/proposal.md @@ -0,0 +1,60 @@ +# 变更提案: terminal-ansi-color-effects + +## 元信息 +```yaml +类型: 缺陷修复 +方案类型: implementation +优先级: P1 +状态: 进行中 +状态说明: 已确认 ANSI 彩色字符需绕过描边/阴影,同时将终端文字效果默认开关改为开启 +创建: 2026-03-25 +``` + +--- + +## 1. 需求 + +### 背景 +当前终端在启用文字描边或阴影后,ANSI 彩色输出视觉上会被统一的描边/阴影效果“压平”,看起来像所有字体都接近同一种颜色。用户希望保留黑绿主题下默认前景文字的装饰效果,但不能破坏终端原本的 ANSI 彩色语义。 + +### 目标 +- 修复终端 ANSI 彩色字符被统一描边/阴影覆盖的问题。 +- 仅让默认前景文字继续保留描边/阴影效果。 +- 将相关终端文字效果的默认开关改为开启。 + +### 约束条件 +```yaml +范围约束: 优先限制在前端终端组件与外观默认值配置,不改 SSH 数据流和终端主题结构 +实现约束: 不改变 ANSI 颜色来源,只调整文字效果的应用范围 +兼容约束: 兼容 xterm DOM renderer 当前的 class/style 输出方式 +数据约束: 默认值变更只影响未保存该设置的新环境,不强行覆盖已有用户配置 +``` + +### 验收标准 +- [ ] 启用终端文字描边/阴影后,ANSI 彩色字符仍保留原有颜色层次 +- [ ] 默认前景色文本仍可保留描边/阴影效果 +- [ ] 终端文字效果默认开关改为开启 +- [ ] 前端构建通过 + +--- + +## 2. 方案 + +### 技术方案 +在 `Terminal.vue` 中收窄文字描边/阴影 CSS 选择器,不再对整行容器统一施加样式,而是仅对“未显式设置 ANSI 前景色”的字符片段应用样式。识别规则基于 xterm DOM renderer 的输出特征:ANSI 调色板颜色会产生 `xterm-fg-*` class,RGB 颜色会在字符节点上带 `style="color:..."`。同时在后端外观默认配置和前端缺省回退值中,把终端文字描边/阴影开关默认值改为开启。 + +### 影响范围 +```yaml +涉及模块: + - frontend: Terminal.vue 终端文字效果应用逻辑 + - frontend: appearance.store.ts 前端外观缺省回退 + - backend: appearance.repository.ts 外观默认值 +预计变更文件: 3 +``` + +### 风险评估 +| 风险 | 等级 | 应对 | +|------|------|------| +| xterm 默认前景与 ANSI 颜色识别条件不完整 | 中 | 依据 xterm DOM renderer 实际 class/style 规则设计选择器,避免整行样式覆盖 | +| 移除行级样式后默认文本效果范围缩小 | 低 | 仅保留到字符 span 级别,确保默认文本仍有描边/阴影 | +| 默认开关改为开启影响旧用户认知 | 低 | 仅修改默认值与前端 fallback,不覆盖数据库中已有显式设置 | diff --git a/.helloagents/plan/202603250614_terminal-ansi-color-effects/tasks.md b/.helloagents/plan/202603250614_terminal-ansi-color-effects/tasks.md new file mode 100644 index 0000000..d55142d --- /dev/null +++ b/.helloagents/plan/202603250614_terminal-ansi-color-effects/tasks.md @@ -0,0 +1,47 @@ +# 任务清单: terminal-ansi-color-effects + +```yaml +@feature: terminal-ansi-color-effects +@created: 2026-03-25 +@status: in_progress +@mode: R2 +``` + +## 进度概览 + +| 完成 | 失败 | 跳过 | 总数 | +|------|------|------|------| +| 1 | 0 | 0 | 5 | + +--- + +## 任务列表 + +### 1. 方案与范围确认 + +- [√] 1.1 创建 ANSI 彩色字符文字效果修复方案包并锁定终端组件/外观默认值范围 | depends_on: [] + +### 2. 终端文字效果修复 + +- [ ] 2.1 盘点 xterm DOM renderer 的前景色输出规则并收窄 `Terminal.vue` 的文字效果选择器 | depends_on: [1.1] +- [ ] 2.2 在 `Terminal.vue` 中实现“ANSI 彩色字符跳过描边/阴影、默认前景文字保留效果”的渲染规则 | depends_on: [2.1] + +### 3. 默认值与验证 + +- [ ] 3.1 调整前后端终端文字效果默认开关为开启 | depends_on: [2.2] +- [ ] 3.2 运行前端最小验证并同步 `.helloagents` 文档与变更记录 | depends_on: [3.1] + +--- + +## 执行日志 + +| 时间 | 任务 | 状态 | 备注 | +|------|------|------|------| +| 2026-03-25 06:14 | 1.1 | 完成 | 创建 implementation 方案包,范围锁定为 Terminal.vue 的 ANSI 彩色文字效果修复与外观默认值调整 | + +--- + +## 执行备注 + +- 目标不是关闭终端文字效果,而是把效果限定在默认前景文字上。 +- 默认开关改为开启仅影响未保存该项的新环境;已有显式设置保持原值。 diff --git a/packages/backend/src/appearance/appearance.repository.ts b/packages/backend/src/appearance/appearance.repository.ts index 3bfe530..094175e 100644 --- a/packages/backend/src/appearance/appearance.repository.ts +++ b/packages/backend/src/appearance/appearance.repository.ts @@ -185,12 +185,12 @@ const getDefaultAppearanceSettings = (): Omit => { terminal_custom_html: '', // 默认自定义 HTML 为空字符串 remoteHtmlPresetsUrl: null, // 默认远程 HTML 预设 URL 为 null // 终端文本描边设置默认值 - terminalTextStrokeEnabled: false, + terminalTextStrokeEnabled: true, terminalTextStrokeWidth: 1, terminalTextStrokeColor: '#000000', // 终端文本阴影设置默认值 - terminalTextShadowEnabled: false, + terminalTextShadowEnabled: true, terminalTextShadowOffsetX: 2, terminalTextShadowOffsetY: 2, terminalTextShadowBlur: 0, diff --git a/packages/backend/src/config/default-themes.ts b/packages/backend/src/config/default-themes.ts index 15a75a4..737e044 100644 --- a/packages/backend/src/config/default-themes.ts +++ b/packages/backend/src/config/default-themes.ts @@ -3,26 +3,26 @@ import type { ITheme } from 'xterm'; // 默认 xterm 主题 // (与 backend/src/config/default-themes.ts 中的定义保持一致) export const defaultXtermTheme: ITheme = { - background: '#1e1e1e', - foreground: '#d4d4d4', - cursor: '#d4d4d4', - selectionBackground: '#264f78', - black: '#000000', - red: '#cd3131', - green: '#0dbc79', - yellow: '#e5e510', - blue: '#2472c8', - magenta: '#bc3fbc', - cyan: '#11a8cd', - white: '#e5e5e5', - brightBlack: '#666666', - brightRed: '#f14c4c', - brightGreen: '#23d18b', - brightYellow: '#f5f543', - brightBlue: '#3b8eea', - brightMagenta: '#d670d6', - brightCyan: '#29b8db', - brightWhite: '#e5e5e5' + background: '#111411', + foreground: '#d8e6d2', + cursor: '#8ff7a7', + selectionBackground: '#21462b', + black: '#111411', + red: '#d96c5f', + green: '#59d971', + yellow: '#d8ba52', + blue: '#4ca89f', + magenta: '#6f8f8a', + cyan: '#4ec9b0', + white: '#d8e6d2', + brightBlack: '#5d685b', + brightRed: '#f07d6c', + brightGreen: '#7df79b', + brightYellow: '#ead17a', + brightBlue: '#76d0c8', + brightMagenta: '#92b8b2', + brightCyan: '#7ae7d6', + brightWhite: '#f3fff0' }; // 默认 UI 主题 (CSS 变量) diff --git a/packages/frontend/src/components/Terminal.vue b/packages/frontend/src/components/Terminal.vue index ed52ccf..3b2aa0e 100644 --- a/packages/frontend/src/components/Terminal.vue +++ b/packages/frontend/src/components/Terminal.vue @@ -37,6 +37,7 @@ let observedElement: HTMLElement | null = null; // +++ Store the observed elemen let debounceTimer: number | null = null; // 用于防抖的计时器 ID let selectionListenerDisposable: IDisposable | null = null; // +++ 提升声明并添加类型 +++ let scrollListenerDisposable: IDisposable | null = null; +let renderListenerDisposable: IDisposable | null = null; let lastResizeObserverWidth = 0; let lastResizeObserverHeight = 0; const RESIZE_THRESHOLD = 0.5; // px @@ -132,6 +133,25 @@ const restoreViewportSnapshot = (term: Terminal, snapshot?: TerminalViewportSnap syncViewportTracking(term); }; +const markExplicitForegroundSpans = () => { + const hostElement = terminalRef.value; + if (!hostElement) { + return; + } + + const rowSpans = hostElement.querySelectorAll('.xterm-rows span'); + rowSpans.forEach((span) => { + const hasExplicitForeground = + span.className.includes('xterm-fg-') || span.style.color !== ''; + + if (hasExplicitForeground) { + span.dataset.explicitForeground = 'true'; + } else { + delete span.dataset.explicitForeground; + } + }); +}; + // 防抖处理由 ResizeObserver 触发的 resize 事件 const debouncedEmitResize = debounce((term: Terminal) => { if (term && props.isActive) { // 仅当标签仍处于活动状态时才发送防抖后的 resize @@ -311,6 +331,7 @@ onMounted(() => { // terminal.open() 同步执行完毕后,可以认为 Xterm 已尝试附加到 DOM isTerminalDomReady.value = true; // +++ 直接在此处设置 DOM 准备就绪状态 +++ console.log(`[Terminal ${props.sessionId}] Xterm open() called, considering DOM ready for initial style checks.`); + markExplicitForegroundSpans(); // 适应容器大小 fitAndEmitResizeNow(terminal); @@ -320,6 +341,10 @@ onMounted(() => { emitWorkspaceEvent('terminal:input', { sessionId: props.sessionId, data }); }); + renderListenerDisposable = terminal.onRender(() => { + markExplicitForegroundSpans(); + }); + scrollListenerDisposable = terminal.onScroll(() => { if (terminal && props.isActive) { syncViewportTracking(terminal); @@ -665,6 +690,10 @@ onBeforeUnmount(() => { scrollListenerDisposable.dispose(); } + if (renderListenerDisposable) { + renderListenerDisposable.dispose(); + } + // 确保在卸载时移除右键监听器 removeContextMenuListener(); @@ -739,6 +768,7 @@ const applyTerminalTextStyles = () => { } else { hostElement.style.removeProperty('--terminal-shadow'); } + markExplicitForegroundSpans(); // console.log('[Terminal] Applied text styles. Stroke enabled:', terminalTextStrokeEnabled.value, 'Shadow enabled:', terminalTextShadowEnabled.value); } }; @@ -814,9 +844,7 @@ watchEffect(() => { } /* 文字描边和阴影样式 */ -.terminal-inner-container.has-text-stroke :deep(.xterm-rows span), -.terminal-inner-container.has-text-stroke :deep(.xterm-rows div > span), /* 更具体地针对嵌套 span */ -.terminal-inner-container.has-text-stroke :deep(.xterm-rows div) { /* 针对直接包含文本的 div */ +.terminal-inner-container.has-text-stroke :deep(.xterm-rows span:not([data-explicit-foreground="true"])) { -webkit-text-stroke-width: var(--terminal-stroke-width); -webkit-text-stroke-color: var(--terminal-stroke-color); text-stroke-width: var(--terminal-stroke-width); @@ -826,9 +854,7 @@ watchEffect(() => { -webkit-paint-order: stroke fill; /* 兼容 WebKit */ } -.terminal-inner-container.has-text-shadow :deep(.xterm-rows span), -.terminal-inner-container.has-text-shadow :deep(.xterm-rows div > span), -.terminal-inner-container.has-text-shadow :deep(.xterm-rows div) { +.terminal-inner-container.has-text-shadow :deep(.xterm-rows span:not([data-explicit-foreground="true"])) { text-shadow: var(--terminal-shadow); } diff --git a/packages/frontend/src/components/style-customizer/StyleCustomizerTerminalTab.vue b/packages/frontend/src/components/style-customizer/StyleCustomizerTerminalTab.vue index ff5fd42..9816dd2 100644 --- a/packages/frontend/src/components/style-customizer/StyleCustomizerTerminalTab.vue +++ b/packages/frontend/src/components/style-customizer/StyleCustomizerTerminalTab.vue @@ -41,11 +41,11 @@ const { const editableTerminalFontFamily = ref(''); const editableTerminalFontSize = ref(14); -const editableTerminalTextStrokeEnabled = ref(false); +const editableTerminalTextStrokeEnabled = ref(true); const editableTerminalTextStrokeWidth = ref(1); const editableTerminalTextStrokeColor = ref('#000000'); -const editableTerminalTextShadowEnabled = ref(false); +const editableTerminalTextShadowEnabled = ref(true); const editableTerminalTextShadowOffsetX = ref(0); const editableTerminalTextShadowOffsetY = ref(0); const editableTerminalTextShadowBlur = ref(0); @@ -725,4 +725,4 @@ watch(() => props.isEditingTheme, (isEditing) => { - \ No newline at end of file + diff --git a/packages/frontend/src/components/style-customizer/StyleCustomizerUiTab.vue b/packages/frontend/src/components/style-customizer/StyleCustomizerUiTab.vue index 82fd747..57e2118 100644 --- a/packages/frontend/src/components/style-customizer/StyleCustomizerUiTab.vue +++ b/packages/frontend/src/components/style-customizer/StyleCustomizerUiTab.vue @@ -18,30 +18,30 @@ const themeParseError = ref(null); // 定义黑暗模式主题变量 const darkModeTheme = { - '--app-bg-color': '#212529', - '--text-color': '#e9ecef', - '--text-color-secondary': '#adb5bd', - '--border-color': '#495057', - '--link-color': '#BB86FC', - '--link-hover-color': '#D1A9FF', - '--link-active-color': '#A06CD5', - '--link-active-bg-color': 'rgba(160, 108, 213, 0.2)', + '--app-bg-color': '#161816', + '--text-color': '#d8e6d2', + '--text-color-secondary': '#8d9887', + '--border-color': '#2b332c', + '--link-color': '#37c66a', + '--link-hover-color': '#62e38e', + '--link-active-color': '#45d978', + '--link-active-bg-color': 'rgba(69, 217, 120, 0.14)', '--nav-item-active-bg-color': 'var(--link-active-bg-color)', - '--header-bg-color': '#343a40', - '--footer-bg-color': '#343a40', - '--button-bg-color': 'var(--link-active-color)', - '--button-text-color': '#ffffff', - '--button-hover-bg-color': '#8E44AD', + '--header-bg-color': '#1b1f1b', + '--footer-bg-color': '#1b1f1b', + '--button-bg-color': '#203126', + '--button-text-color': '#9aefad', + '--button-hover-bg-color': '#294232', '--icon-color': 'var(--text-color-secondary)', '--icon-hover-color': 'var(--link-hover-color)', '--split-line-color': 'var(--border-color)', - '--split-line-hover-color': 'var(--border-color)', + '--split-line-hover-color': '#3b6045', '--input-focus-border-color': 'var(--link-active-color)', '--input-focus-glow': 'var(--link-active-color)', - '--overlay-bg-color': 'rgba(0, 0, 0, 0.8)', - '--color-success': '#5cb85c', - '--color-error': '#d9534f', - '--color-warning': '#f0ad4e', + '--overlay-bg-color': 'rgba(0, 0, 0, 0.84)', + '--color-success': '#3fdc78', + '--color-error': '#d86a4d', + '--color-warning': '#d1a445', '--font-family-sans-serif': 'sans-serif', '--base-padding': '1rem', '--base-margin': '0.5rem' @@ -265,4 +265,4 @@ defineExpose({

{{ themeParseError }}

- \ No newline at end of file + diff --git a/packages/frontend/src/features/appearance/config/default-themes.ts b/packages/frontend/src/features/appearance/config/default-themes.ts index d3b676e..c68b358 100644 --- a/packages/frontend/src/features/appearance/config/default-themes.ts +++ b/packages/frontend/src/features/appearance/config/default-themes.ts @@ -3,26 +3,26 @@ import type { ITheme } from 'xterm'; // 默认 xterm 主题 // (与 backend/src/config/default-themes.ts 中的定义保持一致) export const defaultXtermTheme: ITheme = { - background: '#1e1e1e', - foreground: '#d4d4d4', - cursor: '#d4d4d4', - selectionBackground: '#264f78', // 使用 selectionBackground - black: '#000000', - red: '#cd3131', - green: '#0dbc79', - yellow: '#e5e510', - blue: '#2472c8', - magenta: '#bc3fbc', - cyan: '#11a8cd', - white: '#e5e5e5', - brightBlack: '#666666', - brightRed: '#f14c4c', - brightGreen: '#23d18b', - brightYellow: '#f5f543', - brightBlue: '#3b8eea', - brightMagenta: '#d670d6', - brightCyan: '#29b8db', - brightWhite: '#e5e5e5' + background: '#111411', + foreground: '#d8e6d2', + cursor: '#8ff7a7', + selectionBackground: '#21462b', + black: '#111411', + red: '#d96c5f', + green: '#59d971', + yellow: '#d8ba52', + blue: '#4ca89f', + magenta: '#6f8f8a', + cyan: '#4ec9b0', + white: '#d8e6d2', + brightBlack: '#5d685b', + brightRed: '#f07d6c', + brightGreen: '#7df79b', + brightYellow: '#ead17a', + brightBlue: '#76d0c8', + brightMagenta: '#92b8b2', + brightCyan: '#7ae7d6', + brightWhite: '#f3fff0' }; // 默认 UI 主题 (CSS 变量) diff --git a/packages/frontend/src/stores/appearance.store.ts b/packages/frontend/src/stores/appearance.store.ts index b100764..666252f 100644 --- a/packages/frontend/src/stores/appearance.store.ts +++ b/packages/frontend/src/stores/appearance.store.ts @@ -154,7 +154,7 @@ export const useAppearanceStore = defineStore('appearance', () => { // 文字描边设置 const terminalTextStrokeEnabled = computed(() => { - return appearanceSettings.value.terminalTextStrokeEnabled ?? false; + return appearanceSettings.value.terminalTextStrokeEnabled ?? true; }); const terminalTextStrokeWidth = computed(() => { return appearanceSettings.value.terminalTextStrokeWidth ?? 1; @@ -165,7 +165,7 @@ export const useAppearanceStore = defineStore('appearance', () => { // 文字阴影设置 const terminalTextShadowEnabled = computed(() => { - return appearanceSettings.value.terminalTextShadowEnabled ?? false; + return appearanceSettings.value.terminalTextShadowEnabled ?? true; }); const terminalTextShadowOffsetX = computed(() => { return appearanceSettings.value.terminalTextShadowOffsetX ?? 0;