From 4e6eb32929f1aa822a6db85b5cb1b3935b0c901c Mon Sep 17 00:00:00 2001
From: jkooe <62494645+jkooe@users.noreply.github.com>
Date: Sun, 11 Oct 2026 14:29:50 +0800
Subject: [PATCH] =?UTF-8?q?style(console):=20=E9=85=8D=E8=89=B2=E6=94=B6?=
=?UTF-8?q?=E6=95=9B=E4=B8=BA=E3=80=8C=E7=AE=80=E7=BA=A6=E7=99=BD=20/=20?=
=?UTF-8?q?=E9=AB=98=E7=BA=A7=E7=81=B0=E3=80=8D=E4=B8=A4=E5=A5=97=E4=B8=AD?=
=?UTF-8?q?=E6=80=A7=E7=81=B0=E9=98=B6=E4=B8=BB=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
原配色是深色 + 品牌青绿(#63e2b7)+ naive-ui 默认绿主色 —— 颜色种类多。
改为两套**只用中性灰阶**的主题,侧栏底部一行切换,选择存 localStorage:
简约白 页底 #f4f4f5 / 侧栏 #fafafa / 卡片 #ffffff
高级灰 页底 #1d1f22 / 侧栏 #181a1d / 卡片 #24262a
几处刻意的选择:
1. 白主题页底**不是纯白**。卡片本身是白的,两者同色就只能靠 1px 分线撑层次,
整页发「糊」;压一档浅灰后层次为「页底 < 侧栏 < 卡片」,且仍是白为主。
2. 主色位(naive-ui 的 primary)改**中性灰** —— 主按钮、开关激活态、进度条、
勾选框全吃这一个值。原来它们是绿色/青绿,正是「配色不简单」的主因。
3. 语义色(成功/警告/错误/信息)**降饱和而非去掉**。这是运维工具,
「运行中 / 出错」必须一眼可辨,全部灰化会导致误读。故它们只出现在徽章与
告警这类**小面积**上,底色观感仍是「白 + 灰」。
4. **色值只定义一份**:src/theme/index.ts 的 Palette 既生成 naive-ui 的
themeOverrides,又由 App.vue 运行期注入成 --na-* CSS 变量。
若「TS 调色板」与「CSS 写死色值」各存一份,换主题时会**一半新一半旧、
不报错、类型也抓不到** —— 正是本轮要消灭的失效模式。
为此清掉散在 8 个文件里的 17 处写死色值(日志三流着色、代码块底色、
节点选中边框、历史悬停…),复扫后自定义样式里硬编码色为 0。
5. 主题**只存 localStorage**,不落 Rust 侧 settings:那套 Settings 是给
「仅服务模式」(无窗口、常驻托盘)也生效的偏好用的;主题是纯图形偏好,
没有窗口时毫无意义 —— 为一件纯前端的事去动 Rust 命令面与契约守卫不划算。
6. index.html 内联一小段脚本做**首帧防闪**(否则深色用户每次启动闪一帧白)。
此处**刻意重复 2 个底色值**,已在 index.html 与 theme/index.ts 两处注明
「改底色时同步」—— 因为在它执行之前没有任何模块可依赖;
除此之外全应用再无第二份色值。
验证:vue-tsc 0 错 | 契约守卫 6/6 | 单测 280/280 | .app 重打通过(92.22 MiB)|
两套主题均以真实采集数据回填渲染核对 —— 截图脚本顺带读回 --na-bg 与 body 背景,
确认主题真的切过去了,而不是「看起来像」。
---
CHANGELOG.md | 25 +
apps/console/index.html | 15 +
apps/console/src/App.vue | 41 +-
apps/console/src/components/AppShell.vue | 58 ++-
apps/console/src/stores/theme.ts | 53 +++
apps/console/src/theme/index.ts | 478 ++++++++++++++++++++
apps/console/src/views/FilesView.vue | 2 +-
apps/console/src/views/LocalServiceView.vue | 4 +-
apps/console/src/views/LogsView.vue | 6 +-
apps/console/src/views/NodesView.vue | 6 +-
apps/console/src/views/OverviewView.vue | 6 +-
apps/console/src/views/SoftwareView.vue | 2 +-
apps/console/src/views/TerminalView.vue | 2 +-
docs/console-plan.md | 50 ++
14 files changed, 719 insertions(+), 29 deletions(-)
create mode 100644 apps/console/src/stores/theme.ts
create mode 100644 apps/console/src/theme/index.ts
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 336df0a..565dd40 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,9 @@
> 主题:**控制台「去命令行化」** —— 双击应用即用,启停/重启/更新/日志/托盘常驻/开机自启全部在界面里完成。
> 目标原文:「不再打开命令行窗口,运行应用之后,有启动、停止、重启、检查更新等功能,
> 有日志功能,可以后台运行和开机自启等等功能」。
+>
+> 末段另含两轮纯界面调整:**一律不使用图标**(emoji 在部分渲染器里退化成豆腐块)、
+> **配色收敛为「简约白 / 高级灰」两套中性灰阶主题**。
### 新增 —— 本机被控端托管(`apps/console/src-tauri/src/agent.rs`,约 560 行)
@@ -109,6 +112,28 @@
> 它们是**程序输出内容**,会原样出现在「服务日志」页里,但不是界面元素;
> ③ 控件的结构性部件(下拉箭头、折叠箭头、输入框清除钮、表格排序箭头)。
+### 变更 —— 配色收敛为两套中性灰阶主题
+
+- **两套可切换主题:`简约白`(浅)与 `高级灰`(深)**,侧栏底部一行切换,选择存 `localStorage`。
+ 两套都**只用中性灰阶**:去掉原先的青色品牌字(`#63e2b7`)与 naive-ui 默认的绿色主色,
+ 主按钮 / 开关激活态 / 进度条 / 勾选框统一改吃「主色位 = 中性灰」这一个值。
+ - 白主题的**页底刻意不是 `#ffffff`**(`#f4f4f5`):卡片本身是白的,两者同色就只能靠 1px 分线撑层次,
+ 整页发「糊」;压一档浅灰后层次为「页底 < 侧栏 < 卡片」,且仍是白为主。
+- **语义色不是去掉,而是降饱和**(灰绿 / 灰琥珀 / 灰红 / 灰蓝)。
+ 这是运维工具,`运行中 / 出错` 必须一眼可辨,全部灰化会导致误读;故它们只出现在徽章与告警这类小面积上,
+ 底色观感仍是「白 + 灰」。严重程度在关闭图标后本就更依赖 `type` 的底色/边框。
+- **色值只有一份定义**(`src/theme/index.ts` 的 `Palette`):既生成 naive-ui 的 `themeOverrides`,
+ 又由 `App.vue` 在运行期注入成 `--na-*` CSS 变量供自定义样式使用。
+ 原因是若「TS 调色板」与「CSS 写死的色值」各存一份,换主题时会**一半新一半旧且不报错、类型也抓不到**
+ —— 这正是本轮要消灭的失效模式。
+- **清掉 17 处写死的色值**(散在 8 个文件:日志三流着色、代码块/预览底色、节点选中边框、历史悬停等),
+ 全部改为 `var(--na-*)`。复扫后自定义样式里的硬编码色为 **0**。
+- **主题偏好只存 `localStorage`,不落 Rust 侧 settings**:那是给「仅服务模式」(无窗口、常驻托盘)
+ 也要生效的偏好用的,而主题是纯图形偏好 —— 没有窗口时毫无意义。为一件纯前端的事去动 Rust 命令面与契约守卫不划算。
+- **首帧防闪**:`index.html` 内联一段极小脚本,在 Vue 接管前先把上次主题的底色刷上,
+ 否则「高级灰」用户每次启动都会看到一帧刺眼的白。此处**刻意重复了 2 个底色值**(真值在 `theme/index.ts`),
+ 因为在此之前没有任何模块可依赖;已在两处都注明「改底色时同步」。
+
### 说明 —— 本轮**刻意不升版本号**
本轮新增的全部是**控制台**功能,**被控端能力集合未变**(未增删任何 `system.*` / `fs.*` / `app.*` 能力)。
diff --git a/apps/console/index.html b/apps/console/index.html
index 1571290..74e813b 100644
--- a/apps/console/index.html
+++ b/apps/console/index.html
@@ -4,6 +4,21 @@
nodeagent 控制台
+
diff --git a/apps/console/src/App.vue b/apps/console/src/App.vue
index b358847..061bce6 100644
--- a/apps/console/src/App.vue
+++ b/apps/console/src/App.vue
@@ -1,17 +1,40 @@
-
+
diff --git a/apps/console/src/components/AppShell.vue b/apps/console/src/components/AppShell.vue
index 6ed013f..98033f9 100644
--- a/apps/console/src/components/AppShell.vue
+++ b/apps/console/src/components/AppShell.vue
@@ -12,16 +12,25 @@ import {
NSpace,
NText,
NTooltip,
+ NRadioGroup,
+ NRadioButton,
type MenuOption,
} from "naive-ui";
import { navRoutes, type NavMeta } from "../router";
import { useConnStore } from "../stores/conn";
import { useAgentStore } from "../stores/agent";
+import { useThemeStore } from "../stores/theme";
+import { THEME_LIST } from "../theme";
const route = useRoute();
const router = useRouter();
const conn = useConnStore();
const agent = useAgentStore();
+const theme = useThemeStore();
+const themeName = computed({
+ get: () => theme.name,
+ set: (v) => theme.set(v),
+});
onMounted(() => {
conn.bind();
@@ -68,6 +77,15 @@ async function quickToggle() {
diff --git a/apps/console/src/views/SoftwareView.vue b/apps/console/src/views/SoftwareView.vue
index 95277da..998c8e0 100644
--- a/apps/console/src/views/SoftwareView.vue
+++ b/apps/console/src/views/SoftwareView.vue
@@ -175,7 +175,7 @@ onMounted(() => {
margin: 12px 0 0;
padding: 10px;
border-radius: 8px;
- background: rgba(255, 255, 255, 0.05);
+ background: var(--na-sunken);
font-size: 12px;
max-height: 200px;
overflow: auto;
diff --git a/apps/console/src/views/TerminalView.vue b/apps/console/src/views/TerminalView.vue
index 823bffc..38ee6e6 100644
--- a/apps/console/src/views/TerminalView.vue
+++ b/apps/console/src/views/TerminalView.vue
@@ -161,6 +161,6 @@ function reuse(c: string) {
opacity: 0.85;
}
.hist:hover code {
- color: #63e2b7;
+ color: var(--na-accent);
}
diff --git a/docs/console-plan.md b/docs/console-plan.md
index c08ca56..fd15fd7 100644
--- a/docs/console-plan.md
+++ b/docs/console-plan.md
@@ -572,6 +572,56 @@ node 去跑打包内的 sidecar,并走一次真实 JSON-RPC 握手 —— 源
> 该测试**已注入故障实测会红**(改 `agent_start` → `agent_startX`、`na-agent-state` → `na-agent-statee`
> 当场 2 条失败),非空转守卫。
+## 界面观感约定(2026-10-11)
+
+两轮纯界面调整,方向都是「少即是多」。理由记下来,免得日后被当成"没做完"再补回去。
+
+### 一、一律不使用图标
+
+触发原因是一次真实的渲染事故:侧栏的 emoji 图标在非系统字体栈的渲染器里退化成**豆腐块**。
+试过三法,分别无效或更糟 —— 给图标指定 `Apple Color Emoji`、全局字体栈加 `!important`、
+乃至 `font-variant-emoji: emoji`(最后一招把 ASCII 数字也拆成了 emoji 呈现)。
+「哪套字体栈一定带 emoji」跨平台并不成立 —— **与其赌字形,不如不用**。
+
+去掉的范围比"看得见的图标"宽得多(下面这三类都算):
+
+| 类别 | 处数 | 关法 |
+|---|---|---|
+| 侧栏 emoji | 9 | 路由表 `meta.icon` 字段**整体删除**,`NavMeta` 接口同步收窄 |
+| `n-empty` 空态**插画** | **16** | `:show-icon="false"` —— 插画比图标更「图形」,散在 9 个页面,**最易漏** |
+| `n-alert` 类型图标 | 7 | naive-ui 的 `showIcon` **默认就是 `true`**,不显式关它就一直都在 |
+| `n-popconfirm` 询问图标 | 3 | 同上 |
+| 文案里的符号 | 1 | 终端页「⚠ 高权限操作,请谨慎」保留纯文字 |
+
+顺带清掉 `public/{vite,tauri}.svg` 与 `src/assets/vue.svg`:`public/` 下的文件会被**整体复制进产物**,
+此前等于把 Vite / Tauri 的 logo 一并发了出去(且 `index.html` 的 favicon 正指向它)。
+`index.html` 的 `` 还是模板默认的「Tauri + Vue + Typescript App」,也一并改齐
+(原生窗口标题取自 `tauri.conf.json`,故此前只是 dev 态标签页不一致)。
+
+**刻意未动**:① 托盘图标 —— 菜单栏没图标就点不到,关窗后应用无法寻回,属**功能必需**;
+② 被控端 CLI 自己打印的文本标记(`⚠️`/`ℹ️`/`✅`)—— 是**程序输出内容**,会原样出现在「服务日志」页里,
+但不是界面元素;③ 控件的结构性部件(下拉/折叠箭头、输入框清除钮、表格排序箭头)。
+
+### 二、配色收敛为两套中性灰阶主题
+
+`简约白`(浅)/`高级灰`(深),侧栏底部一行切换,选择存 `localStorage`。
+
+| 决定 | 理由 |
+|---|---|
+| **主色位改中性灰** | naive-ui 默认主色是**绿色**、原设计的品牌字是**青绿 `#63e2b7`** —— 都是"颜色",与「简单些」相悖。主按钮/开关激活/进度条/勾选框统一吃这一个值 |
+| 白主题**页底不是纯白**(`#f4f4f5`) | 卡片本身是白的,两者同色就只能靠 1px 分线撑层次,整页发「糊」。压一档浅灰后层次为「页底 < 侧栏 < 卡片」,且仍是白为主 |
+| 语义色**降饱和**而非去掉 | 这是运维工具,「运行中 / 出错」必须一眼可辨,全部灰化会导致误读。故它们只出现在徽章与告警这类**小面积**上,底色观感仍是「白 + 灰」 |
+| **色值只定义一份** | `src/theme/index.ts` 的 `Palette` 同时喂 naive-ui 的 `themeOverrides` 与 `--na-*` CSS 变量(`App.vue` 运行期注入)。两处各写一份,换主题时会**一半新一半旧且不报错、类型也抓不到** |
+| 主题**只存 `localStorage`**,不落 Rust settings | Rust 那套 `Settings` 是给「仅服务模式」(无窗口、常驻托盘)也要生效的偏好用的;主题是纯图形偏好,**没有窗口时毫无意义**。为一件纯前端的事去动 Rust 命令面与契约守卫不划算 |
+
+清掉散在 **8 个文件**里的 **17 处写死色值**(日志三流着色、代码块/预览底色、节点选中边框、历史悬停…),
+全部改为 `var(--na-*)`。复扫后自定义样式里的硬编码色为 **0**。
+
+**首帧防闪**:`index.html` 内联一小段脚本,在 Vue 接管前先把上次主题的底色刷上
+(否则「高级灰」用户每次启动都会看到一帧刺眼的白)。
+此处**刻意重复了 2 个底色值**,已在 `index.html` 与 `theme/index.ts` 两处注明「改底色时同步」——
+因为在它执行之前没有任何模块可依赖。除此之外全应用再无第二份色值。
+
### 本轮验证记录
| 条目 | 结果 |