From 96d9950c6b278a192603455e40998234046c8b3d Mon Sep 17 00:00:00 2001 From: yinjianm Date: Tue, 21 Apr 2026 05:15:18 +0800 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20=E9=87=8D=E6=9E=84=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0=E8=B0=83=E8=AF=95=E4=BB=A3=E7=90=86=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 `packages/frontend` 的 Vite 开发代理改为可通过环境变量 切换目标地址,并补充本地联调所需的默认主题与样式更新。 同时同步更新前后端默认 UI 主题定义,便于在本地直接验证 远端接口、WebSocket 与视觉效果。 --- .helloagents/CHANGELOG.md | 3 + .../.status.json | 1 + .../proposal.md | 139 +++++++++ .../tasks.md | 56 ++++ .helloagents/archive/_index.md | 1 + .helloagents/modules/frontend.md | 5 + nvidia/DESIGN.md | 293 ++++++++++++++++++ packages/backend/src/config/default-themes.ts | 58 ++-- .../style-customizer/StyleCustomizerUiTab.vue | 106 +++++-- .../appearance/config/default-themes.ts | 58 ++-- packages/frontend/src/style.css | 89 +++--- packages/frontend/vite.config.ts | 64 ++-- 12 files changed, 715 insertions(+), 158 deletions(-) create mode 100644 .helloagents/archive/2026-04/202604210440_frontend-dev-api-theme-verification/.status.json create mode 100644 .helloagents/archive/2026-04/202604210440_frontend-dev-api-theme-verification/proposal.md create mode 100644 .helloagents/archive/2026-04/202604210440_frontend-dev-api-theme-verification/tasks.md create mode 100644 nvidia/DESIGN.md diff --git a/.helloagents/CHANGELOG.md b/.helloagents/CHANGELOG.md index 7151cfd..26e1a53 100644 --- a/.helloagents/CHANGELOG.md +++ b/.helloagents/CHANGELOG.md @@ -2,6 +2,9 @@ ## [Unreleased] +- **[frontend]**: 将 `packages/frontend` 的 Vite 开发代理改为支持通过 `VITE_DEV_PROXY_TARGET`、`VITE_DEV_WS_PROXY_TARGET` 与 `VITE_API_BASE_URL` 切换远端联调目标,并验证 `focus-switcher-sequence`、登录链路与默认白色主题可在本地前端联调时正常工作 — by yinjianm + - 方案: [202604210440_frontend-dev-api-theme-verification](archive/2026-04/202604210440_frontend-dev-api-theme-verification/) + - **[frontend]**: 为 SSH 顶部服务器标签与服务器内终端标签补充 `%` 命令运行中提示,并基于前端发送链路与 shell prompt 输出派生运行态 — by yinjianm - 方案: [202604192106_terminal-running-indicator](archive/2026-04/202604192106_terminal-running-indicator/) - 决策: terminal-running-indicator#D001(运行态继续作为前端派生状态实现), terminal-running-indicator#D002(采用发送置位加 prompt 清除的混合检测策略) diff --git a/.helloagents/archive/2026-04/202604210440_frontend-dev-api-theme-verification/.status.json b/.helloagents/archive/2026-04/202604210440_frontend-dev-api-theme-verification/.status.json new file mode 100644 index 0000000..1fcce80 --- /dev/null +++ b/.helloagents/archive/2026-04/202604210440_frontend-dev-api-theme-verification/.status.json @@ -0,0 +1 @@ +{"status":"completed","completed":6,"failed":0,"pending":0,"total":6,"done":6,"percent":100,"current":"任务完成,等待归档","updated_at":"2026-04-21 04:53:00"} diff --git a/.helloagents/archive/2026-04/202604210440_frontend-dev-api-theme-verification/proposal.md b/.helloagents/archive/2026-04/202604210440_frontend-dev-api-theme-verification/proposal.md new file mode 100644 index 0000000..9c5aef6 --- /dev/null +++ b/.helloagents/archive/2026-04/202604210440_frontend-dev-api-theme-verification/proposal.md @@ -0,0 +1,139 @@ +# 变更提案: frontend-dev-api-theme-verification + +## 元信息 +```yaml +类型: 优化 +方案类型: implementation +优先级: P1 +状态: 已完成 +创建: 2026-04-21 +``` + +--- + +## 1. 需求 + +### 背景 +用户要求将本地前端联调请求切到 `https://ssh.deiban.com`,重点验证 +`/api/v1/settings/focus-switcher-sequence` 这类设置接口是否能通过本地前端正确访问, +并使用 `admin / @Micah.666` 登录后确认此前“默认前端白色主题”改动是否真实生效。 + +### 目标 +- 仅调整本地开发联调入口,不改动前端生产默认 API 地址。 +- 让本地 `packages/frontend` 在开发模式下统一代理远端 `https://ssh.deiban.com`。 +- 完成一次真实浏览器登录与页面视觉验收,确认白色主题是否生效,并给出结论。 + +### 约束条件 +```yaml +时间约束: 当前回合内完成代码改动与联调验证 +性能约束: 不引入额外运行时开销,保持 Vite 代理方案 +兼容性约束: 现有相对路径 API 调用保持不变,本地切换后仍可回退到 localhost:3001 +业务约束: 仅作用于本地开发联调,不覆盖共享生产配置 +``` + +### 验收标准 +- [√] 本地前端开发环境可将 `/api`、`/uploads`、`/ws` 请求代理到 `ssh.deiban.com` +- [√] 使用 `admin / @Micah.666` 可从本地前端完成登录并访问登录后页面 +- [√] 浏览器实测能判断白色主题是否生效,并能给出成功或失败的证据 + +--- + +## 2. 方案 + +### 技术方案 +保留前端现有的相对路径请求方式,不改 `apiClient` 和 store 内部接口地址。 +将 `packages/frontend/vite.config.ts` 改为支持从本地开发环境变量读取 dev proxy 目标, +默认仍指向 `localhost:3001`。同时新增忽略提交的 +`packages/frontend/.env.development.local`,把本地开发代理目标切换到 +`https://ssh.deiban.com` / `wss://ssh.deiban.com`,并显式设置 `VITE_API_BASE_URL` +供页面背景等直接拼接后端 URL 的逻辑使用。完成后启动本地 Vite,并用浏览器执行登录和主题验收。 + +### 影响范围 +```yaml +涉及模块: + - frontend: 调整本地开发代理配置与运行时联调环境变量 +预计变更文件: 4 +``` + +### 风险评估 +| 风险 | 等级 | 应对 | +|------|------|------| +| 远端站点 WebSocket 目标与本地代理协议不兼容 | 中 | 同步提供 `VITE_DEV_WS_PROXY_TARGET`,保持与 HTTP 代理解耦 | +| 远端管理员账号存在用户级外观配置,导致白色主题判断受后端数据影响 | 中 | 同时检查登录页和登录后页面样式来源,区分默认主题与后端覆盖 | +| 本地环境变量文件被忽略后不在 Git 记录中 | 低 | 在最终结果中明确说明本地文件路径与用途 | + +--- + +## 3. 技术设计(可选) + +> 本次不涉及架构、API 或数据模型变更。 + +### 架构设计 +N/A + +### API设计 +N/A + +### 数据模型 +N/A + +--- + +## 4. 核心场景 + +> 执行完成后同步到对应模块文档 + +### 场景: 本地前端联调远端测试后端 +**模块**: frontend +**条件**: 开发者在 `packages/frontend` 启动本地 Vite 开发服务器 +**行为**: 前端通过本地代理将 `/api`、`/uploads`、`/ws` 请求转发到 `ssh.deiban.com` +**结果**: 无需改动业务代码中的相对路径接口即可完成远端联调 + +### 场景: 登录后验收默认白色主题 +**模块**: frontend +**条件**: 使用管理员账号登录本地前端 +**行为**: 检查登录页与登录后页面的背景、文字和主容器配色 +**结果**: 能明确判断白色主题是否生效,以及是否被后端持久化外观配置覆盖 + +--- + +## 5. 技术决策 + +> 本方案涉及的技术决策,归档后成为决策的唯一完整记录 + +### frontend-dev-api-theme-verification#D001: 使用可配置的 dev proxy 而非写死远端 API +**日期**: 2026-04-21 +**状态**: ✅采纳 +**背景**: 需要将本地前端切到远端测试后端验证主题效果,但不应把共享开发默认值永久改成远端地址。 +**选项分析**: +| 选项 | 优点 | 缺点 | +|------|------|------| +| A: 直接把 `vite.config.ts` 写死到 `ssh.deiban.com` | 改动最少,立刻可用 | 污染团队默认开发配置,回切需要再次改代码 | +| B: 让 `vite.config.ts` 读取环境变量,再用 `.env.development.local` 本地覆盖 | 保持默认本地后端联调,同时支持当前远端测试 | 需要新增一个本地环境变量文件 | +**决策**: 选择方案 B +**理由**: 既满足当前本地调试诉求,又不破坏仓库共享默认值,后续切回本地后端只需改本地环境变量。 +**影响**: 影响 `packages/frontend/vite.config.ts` 的开发代理读取方式,以及本地开发环境变量文件组织方式 + +--- + +## 6. 成果设计 + +> 含视觉产出的任务由 DESIGN Phase2 填充。非视觉任务整节标注"N/A"。 + +本任务不新增视觉设计,仅验证既有白色主题是否已经生效。 + +### 设计方向 +- **美学基调**: N/A +- **记忆点**: N/A +- **参考**: 现有前端默认白色主题实现 + +### 视觉要素 +- **配色**: N/A +- **字体**: N/A +- **布局**: N/A +- **动效**: N/A +- **氛围**: N/A + +### 技术约束 +- **可访问性**: 保持现有主题变量与文本对比度,不在本任务中新增样式覆盖 +- **响应式**: 仅做桌面浏览器联调验证 diff --git a/.helloagents/archive/2026-04/202604210440_frontend-dev-api-theme-verification/tasks.md b/.helloagents/archive/2026-04/202604210440_frontend-dev-api-theme-verification/tasks.md new file mode 100644 index 0000000..3eccf28 --- /dev/null +++ b/.helloagents/archive/2026-04/202604210440_frontend-dev-api-theme-verification/tasks.md @@ -0,0 +1,56 @@ +# 任务清单: frontend-dev-api-theme-verification + +> **@status:** completed | 2026-04-21 04:55 + +```yaml +@feature: frontend-dev-api-theme-verification +@created: 2026-04-21 +@status: completed +@mode: R2 +``` + +## 进度概览 + +| 完成 | 失败 | 跳过 | 总数 | +|------|------|------|------| +| 6 | 0 | 0 | 6 | + +--- + +## 任务列表 + +### 1. 方案与配置 + +- [√] 1.1 完成本地前端联调方案包填充,明确仅影响开发代理与本地环境变量 | depends_on: [] +- [√] 1.2 在 `packages/frontend/src` 与 `vite.config.ts` 相关上下文中确认 API、背景资源和 WebSocket 的开发联调入口 | depends_on: [1.1] + +### 2. 前端开发联调改动 + +- [√] 2.1 在 `packages/frontend/vite.config.ts` 中改造开发代理,支持通过环境变量切换 HTTP 与 WebSocket 目标 | depends_on: [1.2] +- [√] 2.2 新增 `packages/frontend/.env.development.local`,将本地开发联调目标设置为 `ssh.deiban.com` 并声明 `VITE_API_BASE_URL` | depends_on: [2.1] + +### 3. 浏览器验收 + +- [√] 3.1 启动本地前端,使用 `admin / @Micah.666` 完成登录并验证关键接口可用 | depends_on: [2.2] +- [√] 3.2 检查登录页与登录后页面的主题表现,确认默认白色主题是否成功生效并记录证据 | depends_on: [3.1] + +--- + +## 执行日志 + +| 时间 | 任务 | 状态 | 备注 | +|------|------|------|------| +| 2026-04-21 04:40 | 方案包创建 | completed | `create_package.py` 已生成 proposal.md 与 tasks.md 模板 | +| 2026-04-21 04:44 | 2.x | completed | `vite.config.ts` 已支持基于环境变量切换 dev proxy,本地 `.env.development.local` 已指向 `ssh.deiban.com` | +| 2026-04-21 04:45 | 构建验证 | completed | `npm run build` 通过,仅保留既有 chunk size 警告 | +| 2026-04-21 04:53 | 3.x | completed | 本地前端以 `admin / @Micah.666` 登录成功,`/api/v1/settings/focus-switcher-sequence` 登录后返回 200,登录页与仪表盘 `body`/主容器为浅色主题 | + +--- + +## 执行备注 + +> 记录执行过程中的重要说明、决策变更、风险提示等 + +- 本任务采用 R2 简化流程,不做多方案对比。 +- 本地联调参数优先放入忽略提交的 `.env.development.local`,避免污染共享默认配置。 +- 浏览器实测结果: 登录页 `body` 背景为 `rgb(245, 245, 247)`,仪表盘主容器背景为 `rgb(245, 245, 247)`,统计卡片背景为 `rgb(255, 255, 255)`,标题文字为 `rgb(29, 29, 31)`,默认白色主题验证通过。 diff --git a/.helloagents/archive/_index.md b/.helloagents/archive/_index.md index 1dfa513..543c07d 100644 --- a/.helloagents/archive/_index.md +++ b/.helloagents/archive/_index.md @@ -7,6 +7,7 @@ | 时间戳 | 名称 | 类型 | 涉及模块 | 决策 | 结果 | |--------|------|------|---------|------|------| +| 202604210440 | frontend-dev-api-theme-verification | - | - | - | ✅完成 | | 202604192106 | terminal-running-indicator | - | - | - | ✅完成 | | 202604190520 | status-monitor-cpu-summary-modal | - | - | - | ✅完成 | | 202604190351 | status-monitor-cpu-total-and-per-core | implementation | frontend, backend | status-monitor-cpu-total-and-per-core#D001 | ✅完成 | diff --git a/.helloagents/modules/frontend.md b/.helloagents/modules/frontend.md index a1c0e98..30681d6 100644 --- a/.helloagents/modules/frontend.md +++ b/.helloagents/modules/frontend.md @@ -34,6 +34,11 @@ **行为**: `router.beforeEach` 根据 `needsSetup` 与 `isAuthenticated` 决定是否重定向到 `/setup`、`/login` 或首页。 **结果**: 初始化设置和登录约束由统一路由守卫执行。 +### 本地开发联调代理 +**条件**: 开发者通过 `packages/frontend` 启动本地 Vite 开发服务器,并在本地环境变量中提供 `VITE_DEV_PROXY_TARGET`、`VITE_DEV_WS_PROXY_TARGET` 或 `VITE_API_BASE_URL`。 +**行为**: `vite.config.ts` 会按环境变量为 `/api`、`/uploads` 与 `/ws` 配置开发代理;业务代码继续保留相对路径请求,`appearance.store.ts` 和 `LayoutRenderer.vue` 中需要拼接后端资源地址的逻辑则读取 `VITE_API_BASE_URL`。 +**结果**: 前端可以在不改业务接口路径的前提下切换到远端测试后端联调,本地验证时仍可通过移除或修改本地环境变量回退到默认 `localhost:3001`。 + ### 工作区交互 **条件**: 用户进入 `/workspace` 或相关管理页面。 **行为**: 通过组件、Pinia 与 composable 协同管理终端、文件管理、命令历史、布局配置、主题和状态监控;当前 `/workspace` 默认主布局为“左侧 Workbench、中央终端、右侧状态监控”,其中 Workbench 继续整合快捷指令、命令历史、文件管理和编辑器四个面板,导航入口保持为纯图标按钮,但已调整为位于 `Workbench` 标题区上方的横向 icon rail,四个入口自左向右排列、默认仅显示图标并通过 tooltip 暴露名称,默认激活快捷指令。`CommandInputBar.vue` 当前已将底部命令框升级为支持会话级草稿保留的多行 `textarea`:普通 `Enter` 插入换行,`Ctrl+Shift+Enter` 发送当前命令,输入框会按内容自动增高至约 6 行,超出后在输入框内部滚动,并继续兼容快捷指令/命令历史同步与选中发送逻辑。围绕这条发送链路,`session` 派生状态现已补上 `isCommandRunning` 与 `terminalInputBuffer`,用于在发送非空命令后标记运行中,并在收到常见 shell prompt、`Ctrl+C`、断连或错误时清理运行态。应用根组件 `App.vue` 现在还新增了全局服务器快捷检索:已登录页面按下 `Ctrl+Shift+F` 会打开 `GlobalConnectionQuickSearch.vue`,通过 `utils/connectionSearch.ts` 对连接名称、主机、用户名、类型和标签做本地模糊排序,并直接复用 `sessionStore.handleConnectRequest()` 触发 SSH 工作区跳转或 RDP / VNC 弹窗连接;该检索弹层现在还会复用 `tags.store.ts` 读取标签名称映射,在结果卡片内补充显示每台服务器的标签 chips,便于快速区分同名或近似主机。快捷指令相关能力目前由 `AddEditQuickCommandForm.vue`、`QuickCommandsView.vue` 与新增的 `utils/quickCommandTemplate.ts` 协同实现:编辑弹窗左侧既可维护自定义 `${变量名}`,也提供 `${{date}}`、`${{time}}`、`${{timestamp}}`、`${{week}}`、`${{uuid}}`、`${{random:8}}`、`${{clipboard}}`、`${{password}}` 等动态变量的一键插入;实际执行时会统一走共享解析器,覆盖编辑弹窗执行、列表直接执行、粘贴到命令输入框和发送到全部服务器等链路,并对未定义变量、无法读取的剪贴板或不可用密码给出非阻断告警。`QuickCommandsView.vue` 内的新增按钮、空状态按钮和列表操作按钮统一复用 `bg-button`、`text-button-text`、`hover:bg-button-hover`、`hover:bg-border` 等主题变量类,避免写死黑白 hover 色值;该视图当前还支持命令项右键菜单,并已修正为实底卡片式上下文菜单,提供立即执行、粘贴到命令输入框(不自动发送)、复制命令、发送到全部服务器、编辑和删除等动作。快捷命令列表的鼠标主交互当前已从“单击立即执行”收紧为“单击仅更新选中态、双击才执行”,从而继续兼容键盘 `Enter` 的选中执行路径并降低误触风险;每条命令项同时会把完整 `command` 文本挂到浏览器原生 tooltip 上,便于在名称或命令被截断时直接 hover 核对完整内容。`Terminal.vue` 现在会跟踪 xterm 相对底部的视口偏移与贴底状态,在终端标签切换、重新激活和 `fit()` 后按原滚动意图恢复;当隐藏标签在后台持续追加日志时,重新激活会基于“距底部偏移”而不是过期的绝对行号恢复 viewport,避免用户继续向下滚动时无法回到底部。组件同时继续在渲染层为带 `xterm-fg-*` class 或内联 `style.color` 的显式前景色字符打标记,让终端文字描边/阴影仅作用于默认前景文本,不覆盖 ANSI 彩色输出;`session.store` 当前会为同一 SSH 连接下的新终端分配递增的 `terminalIndex`。当前顶部 `TerminalTabBar.vue` 已改为服务器级入口:SSH 项只负责在不同服务器之间切换,全局 `+` 继续负责选择其他服务器;同一服务器下的多个终端则下沉到 `LayoutRenderer.vue` 的终端面板内部,以次级标签条承载切换、关闭和新增,从而让“进入服务器后再管理该服务器的多个终端”成为主要交互模型。服务器组头现在除主点击切换外,还额外提供了一个 hover 后出现的 `X` 按钮,点击后会复用既有 `session:close` 事件逐个关闭该 `connectionId` 下的全部终端;如果该服务器下任一终端仍在执行命令,组头会显示一个琥珀色 `%` 提示。当前服务器内部终端标签同样会在对应终端运行命令时显示 `%`,从而同时暴露“服务器级有活跃命令”和“具体哪个终端仍在运行”两层信号。当前终端标签右键菜单继续复用 `WorkspaceView.vue` 中转的会话关闭链路,除关闭当前、关闭其他、关闭左右侧外,也支持直接触发“关闭全部”来清空当前工作区中的全部终端标签。连接新增弹窗中的脚本模式则继续由 `useAddConnectionForm.ts` 统一清洗输入:会先剔除空行、Markdown 代码围栏行,再按单引号/双引号感知切分参数,并去掉成对包裹值的外层引号,避免像 `-p '$Moka1998A'` 这样的输入把 `'` 一并保存。`ConnectionsView.vue` 已升级为“左侧范围树 + 顶部搜索工具条 + 右侧结果列表”的双栏管理台,当前左侧进一步支持基于标签名路径分隔符推导的多级标签树、树节点展开状态持久化、分组 scope 恢复,以及树工具栏中的展开全部、收起全部和重置范围控制;近期又补上了独立的左侧树搜索、命中节点及祖先路径过滤、命中链路自动展开、节点计数高亮,以及更接近资源管理器的树头部布局;本轮继续为树节点加入 hover 工具按钮、资源管理器式分隔标题行与拖拽重排占位反馈;右侧结果列表则同时支持顶部排序控件、列头点击排序,并将行内操作整理为“连接”主按钮加“更多”菜单,其中 SSH 连接卡片默认进一步提升为“连接 / 测试 / 更多”三按钮结构,复用既有单连接测试状态,编辑/克隆/删除等次级操作保留在更多菜单中;连接页顶部工具条当前又补上了独立“标签管理”入口,打开 `ManageConnectionTagsModal.vue` 后可按标签名搜索、多选、批量删除标签,并通过显式危险开关决定删除标签时是否连带删除命中的连接;`tags.store.ts` 在该链路里会统一刷新标签与连接缓存,而 `ConnectionsView.vue` 会在当前 scope 指向已删标签或分组时自动回退到 `all`。`FileManager.vue` 当前已进一步收敛为固定 `/` 根节点的单栏资源管理器树,组件加载时会优先拉取 `/` 目录,树中按“目录在前、文件在后”同时显示目录和文件节点,点击目录只展开与聚焦,点击文件则沿用现有工作区文件打开链路;文件右键菜单链路则已补齐图标化菜单结构、危险态删除项、终端子菜单(执行 `cd` 命令到终端 / 新建终端到当前目录)、复制文件名与复制绝对路径等动作,并继续复用现有下载、权限、新建、上传和删除逻辑,同时又新增了独立“上传文件夹”入口:前端会先将本地目录打包为 zip,再复用现有 `sftp:upload` 链路上传,并在上传成功后自动调用远端解压、尝试清理临时压缩包;外部拖拽文件或目录上传时,则会按鼠标当前悬停的目录作为目标路径,其中目录同样走“先压缩再上传”的路径,从而显著降低小文件很多时的扫描与上传耗时;本轮又补上了拖拽上传前的目标路径确认、桌面端右键子菜单点击展开,以及目录删除时“仅删空目录 / 强制递归删除”的显式二选一;当前右键菜单的关闭职责已经收敛到 `FileManagerContextMenu.vue` 组件层处理,`useFileManagerContextMenu.ts` 不再额外注册捕获阶段的全局点击关闭监听,以避免“终端 / 上传 / 压缩”等带子菜单项在展开或点击前被提前关闭;同时 `useSftpActions.ts` 会在删除目录后自动回退当前或待加载的失效路径,避免文件树持续对已删除目录刷出 `No such file`。样式编辑器中的终端文字描边/阴影默认开关也已与新的黑绿终端风格保持默认开启。 diff --git a/nvidia/DESIGN.md b/nvidia/DESIGN.md new file mode 100644 index 0000000..4dfcaac --- /dev/null +++ b/nvidia/DESIGN.md @@ -0,0 +1,293 @@ +# Design System Inspired by NVIDIA + +## 1. Visual Theme & Atmosphere + +NVIDIA's website is a high-contrast, technology-forward experience that communicates raw computational power through design restraint. The page is built on a stark black (`#000000`) and white (`#ffffff`) foundation, punctuated by NVIDIA's signature green (`#76b900`) -- a color so specific it functions as a brand fingerprint. This is not the lush green of nature; it's the electric, lime-shifted green of GPU-rendered light, a color that sits between chartreuse and kelly green and immediately signals "NVIDIA" to anyone in technology. + +The custom NVIDIA-EMEA font family (with Arial and Helvetica fallbacks) creates a clean, industrial typographic voice. Headings at 36px bold with tight 1.25 line-height create dense, authoritative blocks of text. The font lacks the geometric playfulness of Silicon Valley sans-serifs -- it's European, pragmatic, and engineering-focused. Body text runs at 15-16px, comfortable for reading but not generous, maintaining the sense that screen real estate is optimized like GPU memory. + +What distinguishes NVIDIA's design from other dark-background tech sites is the disciplined use of the green accent. The `#76b900` appears in borders (`2px solid #76b900`), link underlines (`underline 2px rgb(118, 185, 0)`), and CTAs -- but never as backgrounds or large surface areas on the main content. The green is a signal, not a surface. Combined with a deep shadow system (`rgba(0, 0, 0, 0.3) 0px 0px 5px`) and minimal border radius (1-2px), the overall effect is of precision engineering hardware rendered in pixels. + +**Key Characteristics:** +- NVIDIA Green (`#76b900`) as pure accent -- borders, underlines, and interactive highlights only +- Black (`#000000`) dominant background with white (`#ffffff`) text on dark sections +- NVIDIA-EMEA custom font with Arial/Helvetica fallback -- industrial, European, clean +- Tight line-heights (1.25 for headings) creating dense, authoritative text blocks +- Minimal border radius (1-2px) -- sharp, engineered corners throughout +- Green-bordered buttons (`2px solid #76b900`) as primary interactive pattern +- Font Awesome 6 Pro/Sharp icon system at weight 900 for sharp iconography +- Multi-framework architecture (PrimeReact, Fluent UI, Element Plus) enabling rich interactive components + +## 2. Color Palette & Roles + +### Primary Brand +- **NVIDIA Green** (`#76b900`): The signature -- borders, link underlines, CTA outlines, active indicators. Never used as large surface fills. +- **True Black** (`#000000`): Primary page background, text on light surfaces, dominant tone. +- **Pure White** (`#ffffff`): Text on dark backgrounds, light section backgrounds, card surfaces. + +### Extended Brand Palette +- **NVIDIA Green Light** (`#bff230`): Bright lime accent for highlights and hover states. +- **Orange 400** (`#df6500`): Warm accent for alerts, featured badges, or energy-related contexts. +- **Yellow 300** (`#ef9100`): Secondary warm accent, product category highlights. +- **Yellow 050** (`#feeeb2`): Light warm surface for callout backgrounds. + +### Status & Semantic +- **Red 500** (`#e52020`): Error states, destructive actions, critical alerts. +- **Red 800** (`#650b0b`): Deep red for severe warning backgrounds. +- **Green 500** (`#3f8500`): Success states, positive indicators (darker than brand green). +- **Blue 700** (`#0046a4`): Informational accents, link hover alternative. + +### Decorative +- **Purple 800** (`#4d1368`): Deep purple for gradient ends, premium/AI contexts. +- **Purple 100** (`#f9d4ff`): Light purple surface tint. +- **Fuchsia 700** (`#8c1c55`): Rich accent for special promotions or featured content. + +### Neutral Scale +- **Gray 300** (`#a7a7a7`): Muted text, disabled labels. +- **Gray 400** (`#898989`): Secondary text, metadata. +- **Gray 500** (`#757575`): Tertiary text, placeholders, footers. +- **Gray Border** (`#5e5e5e`): Subtle borders, divider lines. +- **Near Black** (`#1a1a1a`): Dark surfaces, card backgrounds on black pages. + +### Interactive States +- **Link Default (dark bg)** (`#ffffff`): White links on dark backgrounds. +- **Link Default (light bg)** (`#000000`): Black links with green underline on light backgrounds. +- **Link Hover** (`#3860be`): Blue shift on hover across all link variants. +- **Button Hover** (`#1eaedb`): Teal highlight for button hover states. +- **Button Active** (`#007fff`): Bright blue for active/pressed button states. +- **Focus Ring** (`#000000 solid 2px`): Black outline for keyboard focus. + +### Shadows & Depth +- **Card Shadow** (`rgba(0, 0, 0, 0.3) 0px 0px 5px 0px`): Subtle ambient shadow for elevated cards. + +## 3. Typography Rules + +### Font Family +- **Primary**: `NVIDIA-EMEA`, with fallbacks: `Arial, Helvetica, sans-serif` +- **Icon Font**: `Font Awesome 6 Pro` (weight 900 for solid icons, 700 for regular) +- **Icon Sharp**: `Font Awesome 6 Sharp` (weight 300 for light icons, 400 for regular) + +### Hierarchy + +| Role | Font | Size | Weight | Line Height | Letter Spacing | Notes | +|------|------|------|--------|-------------|----------------|-------| +| Display Hero | NVIDIA-EMEA | 36px (2.25rem) | 700 | 1.25 (tight) | normal | Maximum impact headlines | +| Section Heading | NVIDIA-EMEA | 24px (1.50rem) | 700 | 1.25 (tight) | normal | Section titles, card headings | +| Sub-heading | NVIDIA-EMEA | 22px (1.38rem) | 400 | 1.75 (relaxed) | normal | Feature descriptions, subtitles | +| Card Title | NVIDIA-EMEA | 20px (1.25rem) | 700 | 1.25 (tight) | normal | Card and module headings | +| Body Large | NVIDIA-EMEA | 18px (1.13rem) | 700 | 1.67 (relaxed) | normal | Emphasized body, lead paragraphs | +| Body | NVIDIA-EMEA | 16px (1.00rem) | 400 | 1.50 | normal | Standard reading text | +| Body Bold | NVIDIA-EMEA | 16px (1.00rem) | 700 | 1.50 | normal | Strong labels, nav items | +| Body Small | NVIDIA-EMEA | 15px (0.94rem) | 400 | 1.67 (relaxed) | normal | Secondary content, descriptions | +| Body Small Bold | NVIDIA-EMEA | 15px (0.94rem) | 700 | 1.50 | normal | Emphasized secondary content | +| Button Large | NVIDIA-EMEA | 18px (1.13rem) | 700 | 1.25 (tight) | normal | Primary CTA buttons | +| Button | NVIDIA-EMEA | 16px (1.00rem) | 700 | 1.25 (tight) | normal | Standard buttons | +| Button Compact | NVIDIA-EMEA | 14.4px (0.90rem) | 700 | 1.00 (tight) | 0.144px | Small/compact buttons | +| Link | NVIDIA-EMEA | 14px (0.88rem) | 700 | 1.43 | normal | Navigation links | +| Link Uppercase | NVIDIA-EMEA | 14px (0.88rem) | 700 | 1.43 | normal | `text-transform: uppercase`, nav labels | +| Caption | NVIDIA-EMEA | 14px (0.88rem) | 600 | 1.50 | normal | Metadata, timestamps | +| Caption Small | NVIDIA-EMEA | 12px (0.75rem) | 400 | 1.25 (tight) | normal | Fine print, legal | +| Micro Label | NVIDIA-EMEA | 10px (0.63rem) | 700 | 1.50 | normal | `text-transform: uppercase`, tiny badges | +| Micro | NVIDIA-EMEA | 11px (0.69rem) | 700 | 1.00 (tight) | normal | Smallest UI text | + +### Principles +- **Bold as the default voice**: NVIDIA leans heavily on weight 700 for headings, buttons, links, and labels. The 400 weight is reserved for body text and descriptions -- everything else is bold, projecting confidence and authority. +- **Tight headings, relaxed body**: Heading line-height is consistently 1.25 (tight), while body text relaxes to 1.50-1.67. This contrast creates visual density at the top of content blocks and comfortable readability in paragraphs. +- **Uppercase for navigation**: Link labels use `text-transform: uppercase` with weight 700, creating a navigation voice that reads like hardware specification labels. +- **No decorative tracking**: Letter-spacing is normal throughout, except for compact buttons (0.144px). The font itself carries the industrial character without manipulation. + +## 4. Component Stylings + +### Buttons + +**Primary (Green Border)** +- Background: `transparent` +- Text: `#000000` +- Padding: 11px 13px +- Border: `2px solid #76b900` +- Radius: 2px +- Font: 16px weight 700 +- Hover: background `#1eaedb`, text `#ffffff` +- Active: background `#007fff`, text `#ffffff`, border `1px solid #003eff`, scale(1) +- Focus: background `#1eaedb`, text `#ffffff`, outline `#000000 solid 2px`, opacity 0.9 +- Use: Primary CTA ("Learn More", "Explore Solutions") + +**Secondary (Green Border Thin)** +- Background: transparent +- Border: `1px solid #76b900` +- Radius: 2px +- Use: Secondary actions, alternative CTAs + +**Compact / Inline** +- Font: 14.4px weight 700 +- Letter-spacing: 0.144px +- Line-height: 1.00 +- Use: Inline CTAs, compact navigation + +### Cards & Containers +- Background: `#ffffff` (light) or `#1a1a1a` (dark sections) +- Border: none (clean edges) or `1px solid #5e5e5e` +- Radius: 2px +- Shadow: `rgba(0, 0, 0, 0.3) 0px 0px 5px 0px` for elevated cards +- Hover: shadow intensification +- Padding: 16-24px internal + +### Links +- **On Dark Background**: `#ffffff`, no underline, hover shifts to `#3860be` +- **On Light Background**: `#000000` or `#1a1a1a`, underline `2px solid #76b900`, hover shifts to `#3860be`, underline removed +- **Green Links**: `#76b900`, hover shifts to `#3860be` +- **Muted Links**: `#666666`, hover shifts to `#3860be` + +### Navigation +- Dark black background (`#000000`) +- Logo left-aligned, prominent NVIDIA wordmark +- Links: NVIDIA-EMEA 14px weight 700 uppercase, `#ffffff` +- Hover: color shift, no underline change +- Mega-menu dropdowns for product categories +- Sticky on scroll with backdrop + +### Image Treatment +- Product/GPU renders as hero images, often full-width +- Screenshot images with subtle shadow for depth +- Green gradient overlays on dark hero sections +- Circular avatar containers with 50% radius + +### Distinctive Components + +**Product Cards** +- Clean white or dark card with minimal radius (2px) +- Green accent border or underline on title +- Bold heading + lighter description pattern +- CTA with green border at bottom + +**Tech Spec Tables** +- Industrial grid layouts +- Alternating row backgrounds (subtle gray shift) +- Bold labels, regular values +- Green highlights for key metrics + +**Cookie/Consent Banner** +- Fixed bottom positioning +- Rounded buttons (2px radius) +- Gray border treatments + +## 5. Layout Principles + +### Spacing System +- Base unit: 8px +- Scale: 1px, 2px, 3px, 4px, 5px, 6px, 7px, 8px, 9px, 10px, 11px, 12px, 13px, 15px +- Primary padding values: 8px, 11px, 13px, 16px, 24px, 32px +- Section spacing: 48-80px vertical padding + +### Grid & Container +- Max content width: approximately 1200px (contained) +- Full-width hero sections with contained text +- Feature sections: 2-3 column grids for product cards +- Single-column for article/blog content +- Sidebar layouts for documentation + +### Whitespace Philosophy +- **Purposeful density**: NVIDIA uses tighter spacing than typical SaaS sites, reflecting the density of technical content. White space exists to separate concepts, not to create luxury emptiness. +- **Section rhythm**: Dark sections alternate with white sections, using background color (not just spacing) to separate content blocks. +- **Card density**: Product cards sit close together with 16-20px gaps, creating a catalog feel rather than a gallery feel. + +### Border Radius Scale +- Micro (1px): Inline spans, tiny elements +- Standard (2px): Buttons, cards, containers, inputs -- the default for nearly everything +- Circle (50%): Avatar images, circular tab indicators + +## 6. Depth & Elevation + +| Level | Treatment | Use | +|-------|-----------|-----| +| Flat (Level 0) | No shadow | Page backgrounds, inline text | +| Subtle (Level 1) | `rgba(0,0,0,0.3) 0px 0px 5px 0px` | Standard cards, modals | +| Border (Level 1b) | `1px solid #5e5e5e` | Content dividers, section borders | +| Green accent (Level 2) | `2px solid #76b900` | Active elements, CTAs, selected items | +| Focus (Accessibility) | `2px solid #000000` outline | Keyboard focus ring | + +**Shadow Philosophy**: NVIDIA's depth system is minimal and utilitarian. There is essentially one shadow value -- a 5px ambient blur at 30% opacity -- used sparingly for cards and modals. The primary depth signal is not shadow but _color contrast_: black backgrounds next to white sections, green borders on black surfaces. This creates hardware-like visual layering where depth comes from material difference, not simulated light. + +### Decorative Depth +- Green gradient washes behind hero content +- Dark-to-darker gradients (black to near-black) for section transitions +- No glassmorphism or blur effects -- clarity over atmosphere + +## 7. Responsive Behavior + +### Breakpoints +| Name | Width | Key Changes | +|------|-------|-------------| +| Mobile Small | <375px | Compact single column, reduced padding | +| Mobile | 375-425px | Standard mobile layout | +| Mobile Large | 425-600px | Wider mobile, some 2-col hints | +| Tablet Small | 600-768px | 2-column grids begin | +| Tablet | 768-1024px | Full card grids, expanded nav | +| Desktop | 1024-1350px | Standard desktop layout | +| Large Desktop | >1350px | Maximum content width, generous margins | + +### Touch Targets +- Buttons use 11px 13px padding for comfortable tap targets +- Navigation links at 14px uppercase with adequate spacing +- Green-bordered buttons provide high-contrast touch targets on dark backgrounds +- Mobile: hamburger menu collapse with full-screen overlay + +### Collapsing Strategy +- Hero: 36px heading scales down proportionally +- Navigation: full horizontal nav collapses to hamburger menu at ~1024px +- Product cards: 3-column to 2-column to single column stacked +- Footer: multi-column grid collapses to single stacked column +- Section spacing: 64-80px reduces to 32-48px on mobile +- Images: maintain aspect ratio, scale to container width + +### Image Behavior +- GPU/product renders maintain high resolution at all sizes +- Hero images scale proportionally with viewport +- Card images use consistent aspect ratios +- Full-bleed dark sections maintain edge-to-edge treatment + +## 8. Responsive Behavior (Extended) + +### Typography Scaling +- Display 36px scales to ~24px on mobile +- Section headings 24px scale to ~20px on mobile +- Body text maintains 15-16px across all breakpoints +- Button text maintains 16px for consistent tap targets + +### Dark/Light Section Strategy +- Dark sections (black bg, white text) alternate with light sections (white bg, black text) +- The green accent remains consistent across both surface types +- On dark: links are white, underlines are green +- On light: links are black, underlines are green +- This alternation creates natural scroll rhythm and content grouping + +## 9. Agent Prompt Guide + +### Quick Color Reference +- Primary accent: NVIDIA Green (`#76b900`) +- Background dark: True Black (`#000000`) +- Background light: Pure White (`#ffffff`) +- Heading text (dark bg): White (`#ffffff`) +- Heading text (light bg): Black (`#000000`) +- Body text (light bg): Black (`#000000`) or Near Black (`#1a1a1a`) +- Body text (dark bg): White (`#ffffff`) or Gray 300 (`#a7a7a7`) +- Link hover: Blue (`#3860be`) +- Border accent: `2px solid #76b900` +- Button hover: Teal (`#1eaedb`) + +### Example Component Prompts +- "Create a hero section on black background. Headline at 36px NVIDIA-EMEA weight 700, line-height 1.25, color #ffffff. Subtitle at 18px weight 400, line-height 1.67, color #a7a7a7. CTA button with transparent background, 2px solid #76b900 border, 2px radius, 11px 13px padding, text #ffffff. Hover: background #1eaedb, text white." +- "Design a product card: white background, 2px border-radius, box-shadow rgba(0,0,0,0.3) 0px 0px 5px. Title at 20px NVIDIA-EMEA weight 700, line-height 1.25, color #000000. Body at 15px weight 400, line-height 1.67, color #757575. Green underline accent on title: border-bottom 2px solid #76b900." +- "Build a navigation bar: #000000 background, sticky top. NVIDIA logo left-aligned. Links at 14px NVIDIA-EMEA weight 700 uppercase, color #ffffff. Hover: color #3860be. Green-bordered CTA button right-aligned." +- "Create a dark feature section: #000000 background. Section label at 14px weight 700 uppercase, color #76b900. Heading at 24px weight 700, color #ffffff. Description at 16px weight 400, color #a7a7a7. Three product cards in a row with 20px gap." +- "Design a footer: #000000 background. Multi-column layout with link groups. Links at 14px weight 400, color #a7a7a7. Hover: color #76b900. Bottom bar with legal text at 12px, color #757575." + +### Iteration Guide +1. Always use `#76b900` as accent, never as a background fill -- it's a signal color for borders, underlines, and highlights +2. Buttons are transparent with green borders by default -- filled backgrounds appear only on hover/active states +3. Weight 700 is the dominant voice for all interactive and heading elements; 400 is only for body paragraphs +4. Border radius is 2px for everything -- this sharp, minimal rounding is core to the industrial aesthetic +5. Dark sections use white text; light sections use black text -- green accent works identically on both +6. Link hover is always `#3860be` (blue) regardless of the link's default color +7. Line-height 1.25 for headings, 1.50-1.67 for body text -- maintain this contrast for visual hierarchy +8. Navigation uses uppercase 14px bold -- this hardware-label typography is part of the brand voice diff --git a/packages/backend/src/config/default-themes.ts b/packages/backend/src/config/default-themes.ts index 39bebfb..3ef891e 100644 --- a/packages/backend/src/config/default-themes.ts +++ b/packages/backend/src/config/default-themes.ts @@ -25,40 +25,40 @@ export const defaultXtermTheme: ITheme = { brightWhite: '#f3fff0' }; -// 默认 UI 主题 (CSS 变量) — Apple Design System Light +// 默认 UI 主题 (CSS 变量) — NVIDIA Design System Dark // (与 frontend/src/features/appearance/config/default-themes.ts 保持一致) export const defaultUiTheme: Record = { - '--app-bg-color': '#f5f5f7', // Apple Light Gray - '--text-color': '#1d1d1f', // Near Black - '--text-color-secondary': 'rgba(0, 0, 0, 0.8)', // Black 80% - '--text-color-tertiary': 'rgba(0, 0, 0, 0.48)', // Black 48% - '--border-color': 'rgba(0, 0, 0, 0.08)', // Ultra-subtle border - '--link-color': '#0066cc', // Link Blue - '--link-hover-color': '#0071e3', // Apple Blue - '--link-active-color': '#0071e3', // Apple Blue - '--link-active-bg-color': 'rgba(0, 113, 227, 0.08)', // Blue 8% + '--app-bg-color': '#000000', // True Black + '--text-color': '#ffffff', // White on dark + '--text-color-secondary': '#a7a7a7', // Gray 300 + '--text-color-tertiary': '#757575', // Gray 500 + '--border-color': '#5e5e5e', // Gray Border + '--link-color': '#ffffff', // White links on dark + '--link-hover-color': '#3860be', // Blue hover + '--link-active-color': '#76b900', // NVIDIA Green + '--link-active-bg-color': 'rgba(118, 185, 0, 0.12)',// Green 12% '--nav-item-active-bg-color': 'var(--link-active-bg-color)', - '--header-bg-color': '#e8e8ed', - '--footer-bg-color': '#f5f5f7', - '--card-bg-color': '#ffffff', - '--button-bg-color': '#0071e3', // Apple Blue CTA - '--button-text-color': '#ffffff', - '--button-hover-bg-color': '#0077ed', - '--button-secondary-bg-color': '#e8e8ed', - '--icon-color': 'rgba(0, 0, 0, 0.48)', - '--icon-hover-color': '#0071e3', - '--split-line-color': 'rgba(0, 0, 0, 0.08)', - '--split-line-hover-color': 'rgba(0, 0, 0, 0.16)', - '--input-focus-border-color': '#0071e3', - '--input-focus-glow': '#0071e3', - '--overlay-bg-color': 'rgba(0, 0, 0, 0.4)', - '--color-success': '#30d158', - '--color-error': '#ff453a', - '--color-warning': '#ff9f0a', + '--header-bg-color': '#000000', + '--footer-bg-color': '#000000', + '--card-bg-color': '#1a1a1a', // Near Black cards + '--button-bg-color': '#76b900', // NVIDIA Green CTA + '--button-text-color': '#000000', // Black on green + '--button-hover-bg-color': '#1eaedb', // Teal hover + '--button-secondary-bg-color': '#1a1a1a', + '--icon-color': '#757575', + '--icon-hover-color': '#76b900', + '--split-line-color': '#5e5e5e', + '--split-line-hover-color': '#76b900', + '--input-focus-border-color': '#76b900', + '--input-focus-glow': '#76b900', + '--overlay-bg-color': 'rgba(0, 0, 0, 0.85)', + '--color-success': '#3f8500', + '--color-error': '#e52020', + '--color-warning': '#ef9100', '--color-success-text': '#ffffff', '--color-error-text': '#ffffff', - '--color-warning-text': '#1d1d1f', - '--font-family-sans-serif': "-apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', 'Helvetica Neue', Helvetica, Arial, sans-serif", + '--color-warning-text': '#1a1a1a', + '--font-family-sans-serif': "Arial, Helvetica, sans-serif", '--base-padding': '1rem', '--base-margin': '0.5rem', }; diff --git a/packages/frontend/src/components/style-customizer/StyleCustomizerUiTab.vue b/packages/frontend/src/components/style-customizer/StyleCustomizerUiTab.vue index 57e2118..c9b862a 100644 --- a/packages/frontend/src/components/style-customizer/StyleCustomizerUiTab.vue +++ b/packages/frontend/src/components/style-customizer/StyleCustomizerUiTab.vue @@ -16,37 +16,80 @@ const editableUiTheme = ref>({}); const editableUiThemeString = ref(''); const themeParseError = ref(null); -// 定义黑暗模式主题变量 +// NVIDIA Design System — Dark Theme const darkModeTheme = { - '--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)', + '--app-bg-color': '#000000', // True Black + '--text-color': '#ffffff', // White on dark + '--text-color-secondary': '#a7a7a7', // Gray 300 + '--text-color-tertiary': '#757575', // Gray 500 + '--border-color': '#5e5e5e', // Gray Border + '--link-color': '#ffffff', // White links on dark + '--link-hover-color': '#3860be', // Blue hover + '--link-active-color': '#76b900', // NVIDIA Green + '--link-active-bg-color': 'rgba(118, 185, 0, 0.12)',// Green 12% '--nav-item-active-bg-color': 'var(--link-active-bg-color)', - '--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': '#3b6045', - '--input-focus-border-color': 'var(--link-active-color)', - '--input-focus-glow': 'var(--link-active-color)', - '--overlay-bg-color': 'rgba(0, 0, 0, 0.84)', - '--color-success': '#3fdc78', - '--color-error': '#d86a4d', - '--color-warning': '#d1a445', - '--font-family-sans-serif': 'sans-serif', + '--header-bg-color': '#000000', // True Black + '--footer-bg-color': '#000000', + '--card-bg-color': '#1a1a1a', // Near Black cards + '--button-bg-color': '#76b900', // NVIDIA Green CTA + '--button-text-color': '#000000', // Black on green + '--button-hover-bg-color': '#1eaedb', // Teal hover + '--button-secondary-bg-color': '#1a1a1a', // Near Black secondary + '--icon-color': '#757575', // Gray 500 + '--icon-hover-color': '#76b900', // NVIDIA Green + '--split-line-color': '#5e5e5e', + '--split-line-hover-color': '#76b900', + '--input-focus-border-color': '#76b900', // Green focus ring + '--input-focus-glow': '#76b900', + '--overlay-bg-color': 'rgba(0, 0, 0, 0.85)', + '--color-success': '#3f8500', // NVIDIA Green 500 + '--color-error': '#e52020', // NVIDIA Red 500 + '--color-warning': '#ef9100', // NVIDIA Yellow 300 + '--color-success-text': '#ffffff', + '--color-error-text': '#ffffff', + '--color-warning-text': '#1a1a1a', + '--font-family-sans-serif': "Arial, Helvetica, sans-serif", '--base-padding': '1rem', '--base-margin': '0.5rem' }; +// Apple Design System — Light Theme +const lightModeTheme = { + '--app-bg-color': '#f5f5f7', // Apple Light Gray + '--text-color': '#1d1d1f', // Near Black + '--text-color-secondary': 'rgba(0, 0, 0, 0.8)', // Black 80% + '--text-color-tertiary': 'rgba(0, 0, 0, 0.48)', // Black 48% + '--border-color': 'rgba(0, 0, 0, 0.08)', // Ultra-subtle border + '--link-color': '#0066cc', // Link Blue + '--link-hover-color': '#0071e3', // Apple Blue + '--link-active-color': '#0071e3', // Apple Blue + '--link-active-bg-color': 'rgba(0, 113, 227, 0.08)', // Blue 8% + '--nav-item-active-bg-color': 'var(--link-active-bg-color)', + '--header-bg-color': '#e8e8ed', + '--footer-bg-color': '#f5f5f7', + '--card-bg-color': '#ffffff', + '--button-bg-color': '#0071e3', // Apple Blue CTA + '--button-text-color': '#ffffff', + '--button-hover-bg-color': '#0077ed', + '--button-secondary-bg-color': '#e8e8ed', + '--icon-color': 'rgba(0, 0, 0, 0.48)', + '--icon-hover-color': '#0071e3', + '--split-line-color': 'rgba(0, 0, 0, 0.08)', + '--split-line-hover-color': 'rgba(0, 0, 0, 0.16)', + '--input-focus-border-color': '#0071e3', + '--input-focus-glow': '#0071e3', + '--overlay-bg-color': 'rgba(0, 0, 0, 0.4)', + '--color-success': '#30d158', + '--color-error': '#ff453a', + '--color-warning': '#ff9f0a', + '--color-success-text': '#ffffff', + '--color-error-text': '#ffffff', + '--color-warning-text': '#1d1d1f', + '--font-family-sans-serif': "-apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', 'Helvetica Neue', Helvetica, Arial, sans-serif", + '--base-padding': '1rem', + '--base-margin': '0.5rem', +}; + const initializeEditableState = () => { const userThemeJson = appearanceSettings.value.customUiTheme; const userTheme = safeJsonParse(userThemeJson, {}); @@ -106,6 +149,17 @@ const applyDarkMode = async () => { } }; +const applyLightMode = async () => { + try { + editableUiTheme.value = JSON.parse(JSON.stringify(lightModeTheme)); + await appearanceStore.saveCustomUiTheme(editableUiTheme.value); + notificationsStore.addNotification({ type: 'success', message: t('styleCustomizer.lightModeApplied', '白天模式已应用') }); + } catch (error: any) { + console.error("应用白天模式失败:", error); + notificationsStore.addNotification({ type: 'error', message: t('styleCustomizer.lightModeApplyFailed', { message: error.message || '未知错误' }) }); + } +}; + const formattedEditableUiThemeJson = computed(() => { try { const themeObject = editableUiTheme.value; @@ -217,7 +271,7 @@ defineExpose({
- +

{{ t('styleCustomizer.uiDescription') }}

diff --git a/packages/frontend/src/features/appearance/config/default-themes.ts b/packages/frontend/src/features/appearance/config/default-themes.ts index 47ace91..c8f9759 100644 --- a/packages/frontend/src/features/appearance/config/default-themes.ts +++ b/packages/frontend/src/features/appearance/config/default-themes.ts @@ -25,40 +25,40 @@ export const defaultXtermTheme: ITheme = { brightWhite: '#f3fff0' }; -// 默认 UI 主题 (CSS 变量) — Apple Design System Light +// 默认 UI 主题 (CSS 变量) — NVIDIA Design System Dark // (与 backend/src/config/default-themes.ts 中的定义保持一致) export const defaultUiTheme: Record = { - '--app-bg-color': '#f5f5f7', // Apple Light Gray - '--text-color': '#1d1d1f', // Near Black - '--text-color-secondary': 'rgba(0, 0, 0, 0.8)', // Black 80% - '--text-color-tertiary': 'rgba(0, 0, 0, 0.48)', // Black 48% - '--border-color': 'rgba(0, 0, 0, 0.08)', // Ultra-subtle border - '--link-color': '#0066cc', // Link Blue - '--link-hover-color': '#0071e3', // Apple Blue - '--link-active-color': '#0071e3', // Apple Blue - '--link-active-bg-color': 'rgba(0, 113, 227, 0.08)', // Blue 8% + '--app-bg-color': '#000000', // True Black + '--text-color': '#ffffff', // White on dark + '--text-color-secondary': '#a7a7a7', // Gray 300 + '--text-color-tertiary': '#757575', // Gray 500 + '--border-color': '#5e5e5e', // Gray Border + '--link-color': '#ffffff', // White links on dark + '--link-hover-color': '#3860be', // Blue hover + '--link-active-color': '#76b900', // NVIDIA Green + '--link-active-bg-color': 'rgba(118, 185, 0, 0.12)',// Green 12% '--nav-item-active-bg-color': 'var(--link-active-bg-color)', - '--header-bg-color': '#e8e8ed', - '--footer-bg-color': '#f5f5f7', - '--card-bg-color': '#ffffff', - '--button-bg-color': '#0071e3', // Apple Blue CTA - '--button-text-color': '#ffffff', - '--button-hover-bg-color': '#0077ed', - '--button-secondary-bg-color': '#e8e8ed', - '--icon-color': 'rgba(0, 0, 0, 0.48)', - '--icon-hover-color': '#0071e3', - '--split-line-color': 'rgba(0, 0, 0, 0.08)', - '--split-line-hover-color': 'rgba(0, 0, 0, 0.16)', - '--input-focus-border-color': '#0071e3', - '--input-focus-glow': '#0071e3', - '--overlay-bg-color': 'rgba(0, 0, 0, 0.4)', - '--color-success': '#30d158', - '--color-error': '#ff453a', - '--color-warning': '#ff9f0a', + '--header-bg-color': '#000000', + '--footer-bg-color': '#000000', + '--card-bg-color': '#1a1a1a', // Near Black cards + '--button-bg-color': '#76b900', // NVIDIA Green CTA + '--button-text-color': '#000000', // Black on green + '--button-hover-bg-color': '#1eaedb', // Teal hover + '--button-secondary-bg-color': '#1a1a1a', + '--icon-color': '#757575', + '--icon-hover-color': '#76b900', + '--split-line-color': '#5e5e5e', + '--split-line-hover-color': '#76b900', + '--input-focus-border-color': '#76b900', + '--input-focus-glow': '#76b900', + '--overlay-bg-color': 'rgba(0, 0, 0, 0.85)', + '--color-success': '#3f8500', + '--color-error': '#e52020', + '--color-warning': '#ef9100', '--color-success-text': '#ffffff', '--color-error-text': '#ffffff', - '--color-warning-text': '#1d1d1f', - '--font-family-sans-serif': "-apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', 'Helvetica Neue', Helvetica, Arial, sans-serif", + '--color-warning-text': '#1a1a1a', + '--font-family-sans-serif': "Arial, Helvetica, sans-serif", '--base-padding': '1rem', '--base-margin': '0.5rem', }; diff --git a/packages/frontend/src/style.css b/packages/frontend/src/style.css index d7326f1..ea69931 100644 --- a/packages/frontend/src/style.css +++ b/packages/frontend/src/style.css @@ -1,6 +1,6 @@ @import "tailwindcss"; -/* Tailwind Theme Variables Mapping — Apple Design System */ +/* Tailwind Theme Variables Mapping — NVIDIA Design System */ @theme inline { /* Base Colors */ --color-background: var(--app-bg-color); @@ -46,53 +46,53 @@ --radius-large: 12px; --radius-pill: 980px; - /* Shadow (Apple) */ - --shadow-card: rgba(0, 0, 0, 0.22) 3px 5px 30px 0px; - --shadow-subtle: rgba(0, 0, 0, 0.04) 0px 1px 3px 0px; + /* Shadow (NVIDIA) */ + --shadow-card: rgba(0, 0, 0, 0.3) 0px 0px 5px 0px; + --shadow-subtle: rgba(0, 0, 0, 0.2) 0px 1px 3px 0px; } /* ───────────────────────────────────────────── - Apple Design System — Light Theme Variables + NVIDIA Design System — Dark Theme (Default) ───────────────────────────────────────────── */ :root { - /* Base Colors (Apple Light) */ - --app-bg-color: #f5f5f7; /* Apple Light Gray — main canvas */ - --text-color: #1d1d1f; /* Near Black — primary text */ - --text-color-secondary: rgba(0, 0, 0, 0.8); /* Black 80% — secondary text */ - --text-color-tertiary: rgba(0, 0, 0, 0.48); /* Black 48% — tertiary, disabled */ - --border-color: rgba(0, 0, 0, 0.08); /* Ultra-subtle border */ - --link-color: #0066cc; /* Link Blue on light bg */ - --link-hover-color: #0071e3; /* Apple Blue — hover */ - --link-active-color: #0071e3; /* Apple Blue — primary accent */ - --link-active-bg-color: rgba(0, 113, 227, 0.08); /* Blue 8% — active bg */ + /* Base Colors (NVIDIA Dark) */ + --app-bg-color: #000000; /* True Black */ + --text-color: #ffffff; /* White on dark */ + --text-color-secondary: #a7a7a7; /* Gray 300 */ + --text-color-tertiary: #757575; /* Gray 500 */ + --border-color: #5e5e5e; /* Gray Border */ + --link-color: #ffffff; /* White links on dark */ + --link-hover-color: #3860be; /* Blue hover */ + --link-active-color: #76b900; /* NVIDIA Green */ + --link-active-bg-color: rgba(118, 185, 0, 0.12); /* Green 12% */ --nav-item-active-bg-color: var(--link-active-bg-color); - /* Component Colors (Apple Light) */ - --header-bg-color: #e8e8ed; /* Slightly darker than canvas */ - --footer-bg-color: #f5f5f7; - --card-bg-color: #ffffff; /* White cards on gray canvas */ - --button-bg-color: #0071e3; /* Apple Blue CTA */ - --button-text-color: #ffffff; - --button-hover-bg-color: #0077ed; /* Brighter Blue on hover */ - --button-secondary-bg-color: #e8e8ed; /* Light secondary button */ - --icon-color: rgba(0, 0, 0, 0.48); /* Tertiary icons */ - --icon-hover-color: #0071e3; /* Apple Blue on hover */ - --split-line-color: rgba(0, 0, 0, 0.08); - --split-line-hover-color: rgba(0, 0, 0, 0.16); - --input-focus-border-color: #0071e3; /* Apple Blue focus ring */ - --input-focus-glow: #0071e3; - --overlay-bg-color: rgba(0, 0, 0, 0.4); /* Lighter overlay for light theme */ + /* Component Colors (NVIDIA Dark) */ + --header-bg-color: #000000; + --footer-bg-color: #000000; + --card-bg-color: #1a1a1a; /* Near Black cards */ + --button-bg-color: #76b900; /* NVIDIA Green CTA */ + --button-text-color: #000000; /* Black on green */ + --button-hover-bg-color: #1eaedb; /* Teal hover */ + --button-secondary-bg-color: #1a1a1a; + --icon-color: #757575; /* Gray 500 */ + --icon-hover-color: #76b900; /* NVIDIA Green */ + --split-line-color: #5e5e5e; + --split-line-hover-color: #76b900; + --input-focus-border-color: #76b900; /* Green focus ring */ + --input-focus-glow: #76b900; + --overlay-bg-color: rgba(0, 0, 0, 0.85); - /* Status Colors (Apple-inspired) */ - --color-success: #30d158; /* Apple Green */ - --color-warning: #ff9f0a; /* Apple Orange */ - --color-error: #ff453a; /* Apple Red */ + /* Status Colors (NVIDIA) */ + --color-success: #3f8500; + --color-warning: #ef9100; + --color-error: #e52020; --color-success-text: #ffffff; - --color-warning-text: #1d1d1f; + --color-warning-text: #1a1a1a; --color-error-text: #ffffff; - /* Typography (Apple SF Pro System Stack) */ - --font-family-sans-serif: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', 'Helvetica Neue', Helvetica, Arial, sans-serif; + /* Typography */ + --font-family-sans-serif: Arial, Helvetica, sans-serif; /* Spacing */ --base-padding: 1rem; @@ -105,8 +105,7 @@ body { font-family: var(--font-family-sans-serif); background-color: var(--app-bg-color); color: var(--text-color); - line-height: 1.47; /* Apple body line-height */ - letter-spacing: -0.022em; /* Apple body tracking */ + line-height: 1.5; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } @@ -187,31 +186,31 @@ hr { border-bottom: 1px solid var(--border-color); } -/* Style scrollbars — Apple minimal */ +/* Style scrollbars — dark theme */ ::-webkit-scrollbar { width: 6px; height: 6px; } ::-webkit-scrollbar-track { - background: transparent; + background: #1a1a1a; border-radius: 3px; } ::-webkit-scrollbar-thumb { - background-color: rgba(0, 0, 0, 0.15); + background-color: #5e5e5e; border-radius: 3px; } ::-webkit-scrollbar-thumb:hover { - background-color: rgba(0, 0, 0, 0.3); + background-color: #76b900; } -/* Input focus styles — Apple Blue ring */ +/* Input focus styles — NVIDIA Green ring */ input:focus, textarea:focus, select:focus { border-color: var(--input-focus-border-color) !important; outline: 0; - box-shadow: 0 0 0 3px rgba(0, 113, 227, 0.2) !important; + box-shadow: 0 0 0 2px rgba(118, 185, 0, 0.25) !important; } /* Ensure icons inside primary buttons are white */ diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts index b98e9b9..9f9c46a 100644 --- a/packages/frontend/vite.config.ts +++ b/packages/frontend/vite.config.ts @@ -1,35 +1,41 @@ -import { defineConfig } from 'vite'; +import { fileURLToPath } from 'node:url'; +import { dirname } from 'node:path'; +import { defineConfig, loadEnv } from 'vite'; import vue from '@vitejs/plugin-vue'; import monacoEditorPlugin from 'vite-plugin-monaco-editor'; import tailwindcss from '@tailwindcss/vite' + +const frontendRoot = dirname(fileURLToPath(import.meta.url)); + // https://vitejs.dev/config/ -export default defineConfig({ - plugins: [ - vue(), - tailwindcss(), - // @ts-ignore because the plugin type might not perfectly match Vite's expected PluginOption type - (monacoEditorPlugin as any).default({}) - ], - server: { - proxy: { - // 将所有 /api 开头的请求代理到后端服务器 - '/api': { - target: 'http://localhost:3001', // 后端服务器地址 - changeOrigin: true, // 需要虚拟主机站点 - // 可选:如果后端 API 路径没有 /api 前缀,可以在这里重写路径 - // rewrite: (path) => path.replace(/^\/api/, '') - }, - // 将所有 /uploads 开头的请求也代理到后端服务器 - '/uploads': { - target: 'http://localhost:3001', // 后端服务器地址 - changeOrigin: true, // 对于静态资源通常也建议开启 - // 通常不需要重写静态资源的路径 - }, - '/ws': { - target: 'ws://localhost:3001', // 后端 WebSocket 服务器地址 - ws: true, - changeOrigin: true, +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, frontendRoot, ''); + const proxyTarget = env.VITE_DEV_PROXY_TARGET || 'http://localhost:3001'; + const wsProxyTarget = env.VITE_DEV_WS_PROXY_TARGET || 'ws://localhost:3001'; + + return { + plugins: [ + vue(), + tailwindcss(), + // @ts-ignore because the plugin type might not perfectly match Vite's expected PluginOption type + (monacoEditorPlugin as any).default({}) + ], + server: { + proxy: { + '/api': { + target: proxyTarget, + changeOrigin: true, + }, + '/uploads': { + target: proxyTarget, + changeOrigin: true, + }, + '/ws': { + target: wsProxyTarget, + ws: true, + changeOrigin: true, + } } } - } -}) + }; +});