diff --git a/apps/desktop/src/App.tsx b/apps/desktop/src/App.tsx
index 7bff421..bf7a808 100644
--- a/apps/desktop/src/App.tsx
+++ b/apps/desktop/src/App.tsx
@@ -33,13 +33,13 @@ export default function App() {
const content = useMemo(() => {
switch (page) {
case "overview":
- return ;
+ return ;
case "catalog":
return ;
case "my-cli":
- return ;
+ return ;
case "config":
- return ;
+ return ;
case "backup":
return ;
case "settings":
@@ -49,7 +49,6 @@ export default function App() {
return (
-
diff --git a/apps/desktop/src/components/CliMonogram.tsx b/apps/desktop/src/components/CliMonogram.tsx
index b72d908..5b68a63 100644
--- a/apps/desktop/src/components/CliMonogram.tsx
+++ b/apps/desktop/src/components/CliMonogram.tsx
@@ -1,12 +1,46 @@
+/**
+ * CLI 双字母头像缩写注册表(视觉规范 v1.3 §7:全系统唯一来源)
+ * 规则:① 首两个字母(大写);② 冲突时保留先收录者,后收录方取「首字母 + 辨识辅音」;
+ * ③ 仍冲突升级「首字母 + 末字母」。新收录 CLI 必须先在此注册,不得现场发明。
+ */
+const CLI_MONOGRAMS: Record
= {
+ codex: "CO",
+ "claude-code": "CL",
+ gemini: "GE",
+ copilot: "CP",
+ kimi: "KI",
+ qwen: "QW",
+ codebuddy: "CB",
+ opencode: "OC",
+ crush: "CR",
+ goose: "GO",
+ aider: "AI",
+ cursor: "CU",
+ cline: "CN",
+ warp: "WA",
+ /* 第二批 4 个预注册(暂缓接入,登记防碰撞) */
+ grok: "GR",
+ amp: "AM",
+ "deepseek-dsh": "DS",
+ plandex: "PL",
+};
+
+/** 未注册时的兜底:取主名单词首两个字母(大写),仅用于未来新增且尚未登记的过渡态 */
+function fallbackAbbr(name: string): string {
+ return name.replace(/\s+/g, "").slice(0, 2).toUpperCase();
+}
+
/** CLI 单色字母头像(视觉规范 §7:双字母单色,--ad-bg-2 底 + 1px --ad-border) */
export function CliMonogram({
+ id,
name,
size = 32,
}: {
+ id: string;
name: string;
size?: number;
}) {
- const chars = name.replace(/\s+/g, "").slice(0, 2).toUpperCase();
+ const chars = CLI_MONOGRAMS[id] ?? fallbackAbbr(name);
return (
- {title}
-
-
-
-
+
+
{title}
+
+
+
+
+
+ {warningCount > 0 && (
+
+ )}
- {warningCount > 0 && (
-
- )}
);
diff --git a/apps/desktop/src/components/KpiCard.tsx b/apps/desktop/src/components/KpiCard.tsx
index acdfaf3..a66be7e 100644
--- a/apps/desktop/src/components/KpiCard.tsx
+++ b/apps/desktop/src/components/KpiCard.tsx
@@ -11,17 +11,19 @@ interface KpiCardProps {
label: string;
value: number;
tone: KpiTone;
- /** 副标题:对象点名句式(视觉规范 §3.1.1) */
+ /** 副标题:对象点名句式(视觉规范 §3.1.1);0 值用「确认句 + 行动」短句 */
subtitle: string;
+ /** 0 值行动后缀(如「去目录看看」),仅当存在对应页面动作时出现 */
+ action?: { label: string; onClick: () => void };
}
/**
* KPI 卡(视觉规范 §3.1.1):
* - 径向渐变玻璃板 + 上缘高光 + --ad-shadow-panel
* - 顶部色条贯通整宽;数值为 0 时色条降灰、数字回 --ad-text-1
- * - 数字随状态色贯穿
+ * - 数字随状态色贯穿;0 值副文案 = 确认句 + 青字行动后缀
*/
-export function KpiCard({ label, value, tone, subtitle }: KpiCardProps) {
+export function KpiCard({ label, value, tone, subtitle, action }: KpiCardProps) {
const color = value > 0 ? TONE_COLOR[tone] : "var(--ad-border)";
const valueColor = value > 0 ? TONE_COLOR[tone] : "var(--ad-text-1)";
return (
@@ -32,7 +34,17 @@ export function KpiCard({ label, value, tone, subtitle }: KpiCardProps) {
{value}
{label}
-
{subtitle}
+
+ {subtitle}
+ {action && value === 0 && (
+ <>
+ {" · "}
+
+ >
+ )}
+
);
diff --git a/apps/desktop/src/pages/Backup.tsx b/apps/desktop/src/pages/Backup.tsx
index 0f799c9..a012af3 100644
--- a/apps/desktop/src/pages/Backup.tsx
+++ b/apps/desktop/src/pages/Backup.tsx
@@ -1,17 +1,22 @@
import { Archive } from "lucide-react";
-/** 备份与迁移(PRD §7):创建备份、查看内容、恢复。Wave 0 占位。 */
+/** 备份与迁移(PRD §7):创建备份、查看内容、恢复。空态按 v1.3 §3.7。 */
export function BackupPage() {
return (
-
备份与迁移
+
还没有可备份的内容
- 备份功能在 Wave 4 落地。备份将明确标注是否包含敏感信息,密钥永不进备份。
+ 安装 CLI 并完成配置后,可以在这里创建备份与迁移。密钥永不进备份。
-
diff --git a/apps/desktop/src/pages/Catalog.tsx b/apps/desktop/src/pages/Catalog.tsx
index dafab11..eaadc5c 100644
--- a/apps/desktop/src/pages/Catalog.tsx
+++ b/apps/desktop/src/pages/Catalog.tsx
@@ -2,7 +2,6 @@ import { useMemo, useState } from "react";
import { Search } from "lucide-react";
import { useCatalog } from "../hooks/useCatalog";
import { StatusBadge } from "../components/StatusBadge";
-import { MonoChip } from "../components/MonoChip";
import { CliMonogram } from "../components/CliMonogram";
type StatusFilter = "all" | "installed" | "uninstalled" | "update";
@@ -90,17 +89,16 @@ export function CatalogPage() {
{filtered.map((entry) => (
-
+
{entry.name_zh}
{entry.vendor}
- 官方渠道安装 · 适配器随 Wave 1 落地
+ 官方渠道安装 · 全平台支持
Windows · Linux
- v1
))}
diff --git a/apps/desktop/src/pages/ConfigCenter.tsx b/apps/desktop/src/pages/ConfigCenter.tsx
index 778aeb7..ad213c8 100644
--- a/apps/desktop/src/pages/ConfigCenter.tsx
+++ b/apps/desktop/src/pages/ConfigCenter.tsx
@@ -1,17 +1,18 @@
import { SlidersHorizontal } from "lucide-react";
+import type { PageKey } from "../components/Sidebar";
-/** 配置中心(PRD §7):按 CLI 展示中文配置表单。Wave 0 占位。 */
-export function ConfigCenterPage() {
+/** 配置中心(PRD §7):按 CLI 展示中文配置表单。空态按 v1.3 §3.7。 */
+export function ConfigCenterPage({ onNavigate }: { onNavigate: (p: PageKey) => void }) {
return (
-
配置中心
+
还没有可配置的 CLI
- 中文配置表单在 Wave 2 落地。敏感字段将使用密码输入与系统密钥库。
+ 安装 CLI 后,在这里用中文表单管理配置。敏感字段会用密码输入并存入系统密钥库。
-
+ onNavigate("catalog")}>
选择 CLI 开始配置
diff --git a/apps/desktop/src/pages/MyCli.tsx b/apps/desktop/src/pages/MyCli.tsx
index b423456..d96e926 100644
--- a/apps/desktop/src/pages/MyCli.tsx
+++ b/apps/desktop/src/pages/MyCli.tsx
@@ -1,17 +1,18 @@
import { SquareTerminal } from "lucide-react";
+import type { PageKey } from "../components/Sidebar";
-/** 我的 CLI(PRD §7):版本 / 路径 / 授权状态。Wave 0 尚无已安装 CLI,展示空态。 */
-export function MyCliPage() {
+/** 我的 CLI(PRD §7):版本 / 路径 / 授权状态。空态按 v1.3 §3.7。 */
+export function MyCliPage({ onNavigate }: { onNavigate: (p: PageKey) => void }) {
return (
-
暂无已安装 CLI
+
还没有安装任何 CLI
- 安装功能在 Wave 1 开放。届时这里会展示每个 CLI 的版本、路径与授权状态。
+ 安装后,这里会展示每个 CLI 的版本、路径与授权状态。
-
+ onNavigate("catalog")}>
去 CLI 目录看看
diff --git a/apps/desktop/src/pages/Overview.tsx b/apps/desktop/src/pages/Overview.tsx
index 0aca283..13677ba 100644
--- a/apps/desktop/src/pages/Overview.tsx
+++ b/apps/desktop/src/pages/Overview.tsx
@@ -1,12 +1,13 @@
import { useState } from "react";
-import { Activity, Archive, ChevronRight, Lock, Plus } from "lucide-react";
+import { Activity, Archive, Lock, Plus, ScanSearch } from "lucide-react";
import { useEnv } from "../hooks/useEnv";
import { useCatalog } from "../hooks/useCatalog";
import { KpiCard } from "../components/KpiCard";
import { MonoChip } from "../components/MonoChip";
import { StatusBadge } from "../components/StatusBadge";
import { CliMonogram } from "../components/CliMonogram";
-import type { PlatformEnv, RuntimeInfo } from "../ipc/types";
+import type { PlatformEnv, RuntimeInfo, RuntimeStatus, CatalogEntry } from "../ipc/types";
+import type { PageKey } from "../components/Sidebar";
/** 平台相关运行时集合(Windows 不看 apt,Linux 不看 winget) */
const WINDOWS_RUNTIMES = ["node", "npm", "python", "uv", "git", "winget"] as const;
@@ -24,42 +25,61 @@ function runtimeProblems(env: PlatformEnv): { name: string; info: RuntimeInfo }[
return problems;
}
-function runtimeStatusText(status: string): string {
+/** 本机环境运行时项四元组(v1.3 §3.1.6):状态点 + 名称 + 版本位 + 行动位 */
+function runtimeMeta(status: RuntimeStatus): {
+ dot: string;
+ missingText: string | null;
+ needsGuide: boolean;
+} {
switch (status) {
- case "not_in_path":
- return "不在 PATH";
+ case "installed":
+ return { dot: "status-dot ok", missingText: null, needsGuide: false };
case "not_installed":
- return "未安装";
+ return { dot: "status-dot unknown", missingText: "未检测到", needsGuide: true };
+ case "not_in_path":
+ return { dot: "status-dot warn breathe", missingText: "不在 PATH", needsGuide: false };
+ case "permission_denied":
+ return { dot: "status-dot warn breathe", missingText: "无访问权限", needsGuide: false };
case "exec_failed":
- return "执行失败";
+ return { dot: "status-dot warn breathe", missingText: "执行失败", needsGuide: false };
case "version_unparseable":
- return "版本未知";
+ return { dot: "status-dot warn breathe", missingText: "版本未知", needsGuide: false };
default:
- return "未知";
+ return { dot: "status-dot unknown", missingText: "未知", needsGuide: false };
}
}
-function RuntimeItem({ name, info }: { name: string; info: RuntimeInfo }) {
- const dotClass =
- info.status === "installed"
- ? "status-dot ok"
- : info.status === "exec_failed"
- ? "status-dot error"
- : "status-dot warn";
+function RuntimeItem({
+ name,
+ info,
+ onNavigate,
+}: {
+ name: string;
+ info: RuntimeInfo;
+ onNavigate: (p: PageKey) => void;
+}) {
+ const meta = runtimeMeta(info.status);
return (
-
+
{name}
{info.version ? (
- {info.version}
+
+ {info.version}
+
) : (
- {runtimeStatusText(info.status)}
+ {meta.missingText}
+ )}
+ {meta.needsGuide && (
+ onNavigate("catalog")}>
+ 安装指引
+
)}
);
}
-function EnvPanel() {
+function EnvPanel({ onNavigate }: { onNavigate: (p: PageKey) => void }) {
const { env, loading, error } = useEnv();
const [showAllPath, setShowAllPath] = useState(false);
@@ -122,7 +142,7 @@ function EnvPanel() {
(key) => {
const info = env.runtimes[key];
if (!info) return null;
- return
;
+ return
;
},
)}
@@ -155,100 +175,222 @@ function EnvPanel() {
);
}
-/** 总览页(视觉规范 §3.1 + PRD §7):KPI + 最近诊断/快速操作 + CLI 状态网格 + 本机环境 */
-export function OverviewPage() {
+/** 首跑空态引导插画(v1.3 §7:等距线框终端 + 集装箱 Dock,1px 线,霓虹点缀) */
+function OnboardingArt() {
+ return (
+