Wave 0.5: 4K 布局与空态修复(老板验收 15 条,视觉规范 v1.3)
This commit is contained in:
@@ -33,13 +33,13 @@ export default function App() {
|
||||
const content = useMemo(() => {
|
||||
switch (page) {
|
||||
case "overview":
|
||||
return <OverviewPage />;
|
||||
return <OverviewPage onNavigate={setPage} />;
|
||||
case "catalog":
|
||||
return <CatalogPage />;
|
||||
case "my-cli":
|
||||
return <MyCliPage />;
|
||||
return <MyCliPage onNavigate={setPage} />;
|
||||
case "config":
|
||||
return <ConfigCenterPage />;
|
||||
return <ConfigCenterPage onNavigate={setPage} />;
|
||||
case "backup":
|
||||
return <BackupPage />;
|
||||
case "settings":
|
||||
@@ -49,7 +49,6 @@ export default function App() {
|
||||
|
||||
return (
|
||||
<div className="app-shell">
|
||||
<div className="bg-grid grid-drift" aria-hidden="true" />
|
||||
<Sidebar current={page} onNavigate={setPage} />
|
||||
<div className="app-main">
|
||||
<Header title={PAGE_META[page].title} warningCount={warningCount} />
|
||||
|
||||
@@ -1,12 +1,46 @@
|
||||
/**
|
||||
* CLI 双字母头像缩写注册表(视觉规范 v1.3 §7:全系统唯一来源)
|
||||
* 规则:① 首两个字母(大写);② 冲突时保留先收录者,后收录方取「首字母 + 辨识辅音」;
|
||||
* ③ 仍冲突升级「首字母 + 末字母」。新收录 CLI 必须先在此注册,不得现场发明。
|
||||
*/
|
||||
const CLI_MONOGRAMS: Record<string, string> = {
|
||||
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 (
|
||||
<span
|
||||
className="monogram"
|
||||
|
||||
@@ -10,24 +10,25 @@ interface HeaderProps {
|
||||
export function Header({ title, warningCount = 0 }: HeaderProps) {
|
||||
return (
|
||||
<header className="header">
|
||||
<h1 className="header-title">{title}</h1>
|
||||
<div className="header-actions">
|
||||
<div className="search-box">
|
||||
<Search size={16} strokeWidth={1.5} className="search-icon" aria-hidden="true" />
|
||||
<input
|
||||
type="search"
|
||||
placeholder="搜索 CLI…"
|
||||
aria-label="搜索 CLI"
|
||||
// 搜索功能属于后续波次,Wave 0 仅为占位
|
||||
/>
|
||||
<div className="header-inner">
|
||||
<h1 className="header-title">{title}</h1>
|
||||
<div className="header-actions">
|
||||
<div className="search-box">
|
||||
<Search size={16} strokeWidth={1.5} className="search-icon" aria-hidden="true" />
|
||||
<input
|
||||
type="search"
|
||||
placeholder="搜索 CLI…"
|
||||
aria-label="搜索 CLI"
|
||||
/>
|
||||
</div>
|
||||
{warningCount > 0 && (
|
||||
<button type="button" className="env-chip">
|
||||
<span className="status-dot warn breathe" aria-hidden="true" />
|
||||
<span>{warningCount} 项环境警告</span>
|
||||
<ChevronDown size={12} strokeWidth={1.5} aria-hidden="true" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{warningCount > 0 && (
|
||||
<button type="button" className="env-chip">
|
||||
<span className="status-dot warn breathe" aria-hidden="true" />
|
||||
<span>{warningCount} 项环境警告</span>
|
||||
<ChevronDown size={12} strokeWidth={1.5} aria-hidden="true" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
|
||||
@@ -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}
|
||||
</div>
|
||||
<div className="kpi-label">{label}</div>
|
||||
<div className="kpi-subtitle">{subtitle}</div>
|
||||
<div className="kpi-subtitle">
|
||||
{subtitle}
|
||||
{action && value === 0 && (
|
||||
<>
|
||||
{" · "}
|
||||
<button type="button" className="link-btn" onClick={action.onClick}>
|
||||
{action.label}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
import { Archive } from "lucide-react";
|
||||
|
||||
/** 备份与迁移(PRD §7):创建备份、查看内容、恢复。Wave 0 占位。 */
|
||||
/** 备份与迁移(PRD §7):创建备份、查看内容、恢复。空态按 v1.3 §3.7。 */
|
||||
export function BackupPage() {
|
||||
return (
|
||||
<div className="placeholder-page">
|
||||
<div className="placeholder-icon">
|
||||
<Archive size={40} strokeWidth={1.5} aria-hidden="true" />
|
||||
</div>
|
||||
<h2 className="placeholder-title">备份与迁移</h2>
|
||||
<h2 className="placeholder-title">还没有可备份的内容</h2>
|
||||
<p className="placeholder-desc">
|
||||
备份功能在 Wave 4 落地。备份将明确标注是否包含敏感信息,密钥永不进备份。
|
||||
安装 CLI 并完成配置后,可以在这里创建备份与迁移。密钥永不进备份。
|
||||
</p>
|
||||
<button type="button" className="btn btn-secondary" disabled>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary"
|
||||
disabled
|
||||
title="安装 CLI 后才能创建备份"
|
||||
>
|
||||
创建备份
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -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) => (
|
||||
<article className="catalog-card" key={entry.id}>
|
||||
<div className="catalog-card-top">
|
||||
<CliMonogram name={entry.name} size={40} />
|
||||
<CliMonogram id={entry.id} name={entry.name} size={40} />
|
||||
<div className="catalog-card-head">
|
||||
<div className="catalog-card-name">{entry.name_zh}</div>
|
||||
<div className="catalog-card-vendor">{entry.vendor}</div>
|
||||
</div>
|
||||
</div>
|
||||
<p className="catalog-card-desc">官方渠道安装 · 适配器随 Wave 1 落地</p>
|
||||
<p className="catalog-card-desc">官方渠道安装 · 全平台支持</p>
|
||||
<div className="catalog-card-bottom">
|
||||
<StatusBadge kind="uninstalled" label="未安装" />
|
||||
<span className="catalog-platforms">Windows · Linux</span>
|
||||
<MonoChip>v1</MonoChip>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
|
||||
@@ -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 (
|
||||
<div className="placeholder-page">
|
||||
<div className="placeholder-icon">
|
||||
<SlidersHorizontal size={40} strokeWidth={1.5} aria-hidden="true" />
|
||||
</div>
|
||||
<h2 className="placeholder-title">配置中心</h2>
|
||||
<h2 className="placeholder-title">还没有可配置的 CLI</h2>
|
||||
<p className="placeholder-desc">
|
||||
中文配置表单在 Wave 2 落地。敏感字段将使用密码输入与系统密钥库。
|
||||
安装 CLI 后,在这里用中文表单管理配置。敏感字段会用密码输入并存入系统密钥库。
|
||||
</p>
|
||||
<button type="button" className="btn btn-secondary" disabled>
|
||||
<button type="button" className="btn btn-secondary" onClick={() => onNavigate("catalog")}>
|
||||
选择 CLI 开始配置
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -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 (
|
||||
<div className="placeholder-page">
|
||||
<div className="placeholder-icon">
|
||||
<SquareTerminal size={40} strokeWidth={1.5} aria-hidden="true" />
|
||||
</div>
|
||||
<h2 className="placeholder-title">暂无已安装 CLI</h2>
|
||||
<h2 className="placeholder-title">还没有安装任何 CLI</h2>
|
||||
<p className="placeholder-desc">
|
||||
安装功能在 Wave 1 开放。届时这里会展示每个 CLI 的版本、路径与授权状态。
|
||||
安装后,这里会展示每个 CLI 的版本、路径与授权状态。
|
||||
</p>
|
||||
<button type="button" className="btn btn-primary" disabled>
|
||||
<button type="button" className="btn btn-primary" onClick={() => onNavigate("catalog")}>
|
||||
去 CLI 目录看看
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -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 (
|
||||
<div className="runtime-item">
|
||||
<span className={dotClass} aria-hidden="true" />
|
||||
<span className={meta.dot} aria-hidden="true" />
|
||||
<span className="runtime-name">{name}</span>
|
||||
{info.version ? (
|
||||
<MonoChip>{info.version}</MonoChip>
|
||||
<span className="runtime-version">
|
||||
<MonoChip>{info.version}</MonoChip>
|
||||
</span>
|
||||
) : (
|
||||
<span className="runtime-missing">{runtimeStatusText(info.status)}</span>
|
||||
<span className="runtime-missing">{meta.missingText}</span>
|
||||
)}
|
||||
{meta.needsGuide && (
|
||||
<button type="button" className="link-btn runtime-action" onClick={() => onNavigate("catalog")}>
|
||||
安装指引
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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 <RuntimeItem key={key} name={key} info={info} />;
|
||||
return <RuntimeItem key={key} name={key} info={info} onNavigate={onNavigate} />;
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
@@ -155,100 +175,222 @@ function EnvPanel() {
|
||||
);
|
||||
}
|
||||
|
||||
/** 总览页(视觉规范 §3.1 + PRD §7):KPI + 最近诊断/快速操作 + CLI 状态网格 + 本机环境 */
|
||||
export function OverviewPage() {
|
||||
/** 首跑空态引导插画(v1.3 §7:等距线框终端 + 集装箱 Dock,1px 线,霓虹点缀) */
|
||||
function OnboardingArt() {
|
||||
return (
|
||||
<svg width="200" height="150" viewBox="0 0 200 150" fill="none" aria-hidden="true">
|
||||
<rect x="24" y="18" width="120" height="86" rx="8" stroke="currentColor" strokeWidth="1" />
|
||||
<line x1="24" y1="40" x2="144" y2="40" stroke="currentColor" strokeWidth="1" />
|
||||
<circle cx="36" cy="29" r="2" style={{ fill: "var(--ad-primary)" }} />
|
||||
<circle cx="46" cy="29" r="2" fill="currentColor" opacity="0.55" />
|
||||
<circle cx="56" cy="29" r="2" fill="currentColor" opacity="0.55" />
|
||||
<rect x="36" y="52" width="58" height="6" rx="3" fill="currentColor" opacity="0.5" />
|
||||
<rect x="36" y="66" width="84" height="6" rx="3" fill="currentColor" opacity="0.32" />
|
||||
<rect x="36" y="80" width="46" height="6" rx="3" style={{ fill: "var(--ad-accent)" }} opacity="0.8" />
|
||||
<rect x="24" y="116" width="36" height="26" rx="4" stroke="currentColor" strokeWidth="1" />
|
||||
<rect x="66" y="116" width="36" height="26" rx="4" stroke="currentColor" strokeWidth="1" />
|
||||
<rect x="108" y="116" width="36" height="26" rx="4" stroke="currentColor" strokeWidth="1" />
|
||||
<line x1="24" y1="142" x2="144" y2="142" stroke="currentColor" strokeWidth="1" opacity="0.4" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
/** 首跑引导区(v1.3 §3.1.5:插画 + 三句式文案 + 主行动按钮) */
|
||||
function Onboarding({ onNavigate }: { onNavigate: (p: PageKey) => void }) {
|
||||
return (
|
||||
<div className="panel onboarding">
|
||||
<div className="onboarding-art">
|
||||
<OnboardingArt />
|
||||
</div>
|
||||
<div className="onboarding-copy">
|
||||
<h2 className="onboarding-title">你的工具箱还是空的</h2>
|
||||
<p className="onboarding-desc">
|
||||
从目录安装第一个 Agent CLI,安装、配置、授权、诊断都帮你盯好。
|
||||
</p>
|
||||
<div className="onboarding-actions">
|
||||
<button type="button" className="btn btn-primary" onClick={() => onNavigate("catalog")}>
|
||||
浏览 CLI 目录
|
||||
</button>
|
||||
<button type="button" className="btn btn-secondary">
|
||||
先诊断本机环境
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/** 最近诊断卡(v1.3 §3.1.2:无记录时展示空态,不渲染占位行与「查看全部」) */
|
||||
function DiagPanel() {
|
||||
// Wave 0.5 尚无诊断引擎数据,恒为空态(语义按 §3.7,不虚构记录)
|
||||
const records: { cli: string; text: string; time: string }[] = [];
|
||||
return (
|
||||
<div className="panel diag-panel">
|
||||
<div className="panel-head">
|
||||
<h2 className="panel-title">最近诊断</h2>
|
||||
</div>
|
||||
{records.length === 0 ? (
|
||||
<div className="diag-empty">
|
||||
<span className="diag-empty-text">暂无诊断记录</span>
|
||||
<button type="button" className="btn btn-secondary">
|
||||
<ScanSearch size={16} strokeWidth={1.5} aria-hidden="true" /> 立即诊断
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<ul className="diag-list">
|
||||
{records.map((r) => (
|
||||
<li className="diag-row" key={r.cli}>
|
||||
<span className="status-dot ok" aria-hidden="true" />
|
||||
<span className="diag-cli">{r.cli}</span>
|
||||
<span className="diag-text">{r.text}</span>
|
||||
<span className="diag-time">{r.time}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/** 快速操作卡(v1.3 §3.1.3:三按钮竖向操作组,创建备份依赖数据置灰) */
|
||||
function QuickActions({
|
||||
firstRun,
|
||||
onNavigate,
|
||||
}: {
|
||||
firstRun: boolean;
|
||||
onNavigate: (p: PageKey) => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="panel quick-card">
|
||||
<h2 className="panel-title">快速操作</h2>
|
||||
<button type="button" className="btn btn-primary" onClick={() => onNavigate("catalog")}>
|
||||
<Plus size={16} strokeWidth={1.5} aria-hidden="true" /> 添加 CLI
|
||||
</button>
|
||||
<button type="button" className="btn btn-secondary">
|
||||
<Activity size={16} strokeWidth={1.5} aria-hidden="true" /> 立即诊断
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary"
|
||||
disabled
|
||||
title={firstRun ? "安装 CLI 后才能创建备份" : "暂无内容可备份"}
|
||||
>
|
||||
<Archive size={16} strokeWidth={1.5} aria-hidden="true" /> 创建备份
|
||||
</button>
|
||||
<div className="recent-installs">
|
||||
<div className="group-label">最近安装</div>
|
||||
<p className="group-empty">暂无记录</p>
|
||||
</div>
|
||||
<p className="safety-note">
|
||||
<Lock size={12} strokeWidth={1.5} aria-hidden="true" />
|
||||
所有操作在本地完成,密钥只存系统保险柜,不上传。
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/** CLI 状态网格块(v1.3 §3.1.4;首跑精简为两行 + 安装入口,§3.1.5) */
|
||||
function CliBlock({
|
||||
entry,
|
||||
compact,
|
||||
onNavigate,
|
||||
}: {
|
||||
entry: CatalogEntry;
|
||||
compact: boolean;
|
||||
onNavigate: (p: PageKey) => void;
|
||||
}) {
|
||||
const installed = entry.status === "installed";
|
||||
return (
|
||||
<div className="cli-block">
|
||||
<div className="cli-block-top">
|
||||
<CliMonogram id={entry.id} name={entry.name} size={32} />
|
||||
<span className="cli-block-name">{entry.name_zh}</span>
|
||||
<StatusBadge kind={installed ? "installed" : "uninstalled"} label={installed ? "已安装" : "未安装"} />
|
||||
</div>
|
||||
<div className="cli-block-version">
|
||||
{installed ? null : <span className="version-missing">未安装</span>}
|
||||
</div>
|
||||
{!compact && (
|
||||
<div className="cli-block-auth">
|
||||
{installed ? (
|
||||
<>
|
||||
<span className="status-dot ok" aria-hidden="true" />
|
||||
<span>已安装</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span className="status-dot unknown" aria-hidden="true" />
|
||||
<span>未检测 · 安装后自动检测</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{compact && (
|
||||
<div className="cli-block-install">
|
||||
<button type="button" className="link-btn" onClick={() => onNavigate("catalog")}>
|
||||
安装
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface OverviewProps {
|
||||
onNavigate: (p: PageKey) => void;
|
||||
}
|
||||
|
||||
/** 总览页(视觉规范 §3.1 + PRD §7):首跑空态 / KPI + 诊断/快速操作 + CLI 状态网格 + 本机环境 */
|
||||
export function OverviewPage({ onNavigate }: OverviewProps) {
|
||||
const { env } = useEnv();
|
||||
const { entries } = useCatalog();
|
||||
|
||||
const installedCount = entries.filter((e) => e.status === "installed").length;
|
||||
const firstRun = installedCount === 0; // 无安装且无诊断记录(当前无诊断引擎)
|
||||
|
||||
const warningCount = env ? runtimeProblems(env).length : 0;
|
||||
const warningNames = env ? runtimeProblems(env).map((p) => p.name) : [];
|
||||
|
||||
return (
|
||||
<div className="overview">
|
||||
{/* ① KPI 一排 4 张 */}
|
||||
<section className="kpi-grid" aria-label="概览指标">
|
||||
<KpiCard label="已安装" value={0} tone="primary" subtitle="暂无已安装 CLI" />
|
||||
<KpiCard label="待授权" value={0} tone="attention" subtitle="暂无待授权项" />
|
||||
<KpiCard label="可更新" value={0} tone="accent" subtitle="暂无可用更新" />
|
||||
<KpiCard
|
||||
label="环境异常"
|
||||
value={warningCount}
|
||||
tone="warning"
|
||||
subtitle={warningCount > 0 ? warningNames.join(" · ") : "环境正常"}
|
||||
/>
|
||||
</section>
|
||||
{/* ① KPI 一排 4 张(首跑空态隐藏,v1.3 §3.1.5) */}
|
||||
{!firstRun && (
|
||||
<section className="kpi-grid" aria-label="概览指标">
|
||||
<KpiCard label="已安装" value={installedCount} tone="primary" subtitle="暂无安装" action={{ label: "去目录看看", onClick: () => onNavigate("catalog") }} />
|
||||
<KpiCard label="待授权" value={0} tone="attention" subtitle="暂无待授权" />
|
||||
<KpiCard label="可更新" value={0} tone="accent" subtitle="全部已是最新" />
|
||||
<KpiCard
|
||||
label="环境异常"
|
||||
value={warningCount}
|
||||
tone="warning"
|
||||
subtitle={warningCount > 0 ? warningNames.join(" · ") : "环境正常"}
|
||||
/>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* ② 中段:左 2/3 最近诊断 + 右 1/3 快速操作 */}
|
||||
{/* ② 中段:诊断/引导 + 快速操作(超宽三栏见 global.css) */}
|
||||
<section className="overview-mid">
|
||||
<div className="panel diag-panel">
|
||||
<div className="panel-head">
|
||||
<h2 className="panel-title">最近诊断</h2>
|
||||
<button type="button" className="link-btn">
|
||||
查看全部 <ChevronRight size={14} strokeWidth={1.5} aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
<ul className="diag-list">
|
||||
<li className="diag-row">
|
||||
<span className="status-dot ok" aria-hidden="true" />
|
||||
<span className="diag-cli">检测引擎</span>
|
||||
<span className="diag-text">诊断功能将在 Wave 1 接入</span>
|
||||
<span className="diag-time">刚刚</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="panel quick-card">
|
||||
<h2 className="panel-title">快速操作</h2>
|
||||
<button type="button" className="btn btn-primary" disabled>
|
||||
<Plus size={16} strokeWidth={1.5} aria-hidden="true" /> 添加 CLI
|
||||
</button>
|
||||
<button type="button" className="btn btn-secondary" disabled>
|
||||
<Activity size={16} strokeWidth={1.5} aria-hidden="true" /> 立即诊断
|
||||
</button>
|
||||
<button type="button" className="btn btn-secondary" disabled>
|
||||
<Archive size={16} strokeWidth={1.5} aria-hidden="true" /> 创建备份
|
||||
</button>
|
||||
<div className="recent-installs">
|
||||
<div className="group-label">最近安装</div>
|
||||
<p className="group-empty">暂无记录</p>
|
||||
</div>
|
||||
<p className="safety-note">
|
||||
<Lock size={12} strokeWidth={1.5} aria-hidden="true" />
|
||||
所有操作在本地完成,密钥只存系统保险柜,不上传。
|
||||
</p>
|
||||
</div>
|
||||
{firstRun ? <Onboarding onNavigate={onNavigate} /> : <DiagPanel />}
|
||||
<QuickActions firstRun={firstRun} onNavigate={onNavigate} />
|
||||
</section>
|
||||
|
||||
{/* ③ 下段:CLI 状态网格通栏 */}
|
||||
{/* ③ 下段:CLI 状态网格通栏(首跑精简:可安装列表 + 安装入口) */}
|
||||
<section className="panel cli-status-panel">
|
||||
<div className="panel-head">
|
||||
<h2 className="panel-title">CLI 状态</h2>
|
||||
<button type="button" className="link-btn">
|
||||
管理全部 {entries.length} 个
|
||||
<ChevronRight size={14} strokeWidth={1.5} aria-hidden="true" />
|
||||
</button>
|
||||
<h2 className="panel-title">
|
||||
{firstRun ? `可安装的 CLI(${entries.length})` : "CLI 状态"}
|
||||
</h2>
|
||||
{/* 「管理全部 N 个」入口:仅当未全量展示时才渲染(v1.3 §3.1.4),本页恒全量展示,故不渲染 */}
|
||||
</div>
|
||||
<div className="cli-status-grid">
|
||||
{entries.map((entry) => (
|
||||
<div className="cli-block" key={entry.id}>
|
||||
<div className="cli-block-top">
|
||||
<CliMonogram name={entry.name} size={32} />
|
||||
<span className="cli-block-name">{entry.name_zh}</span>
|
||||
<StatusBadge kind="uninstalled" label="未安装" />
|
||||
</div>
|
||||
<div className="cli-block-version">
|
||||
<MonoChip>—</MonoChip>
|
||||
</div>
|
||||
<div className="cli-block-auth">
|
||||
<span className="status-dot unknown" aria-hidden="true" />
|
||||
<span>未检测 · 可一键安装</span>
|
||||
</div>
|
||||
</div>
|
||||
<CliBlock key={entry.id} entry={entry} compact={firstRun} onNavigate={onNavigate} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ④ 本机环境(真实检测结果,架构 §5) */}
|
||||
<EnvPanel />
|
||||
{/* ④ 本机环境(真实检测结果,架构 §5;超宽断点上提为第三栏) */}
|
||||
<EnvPanel onNavigate={onNavigate} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,14 +9,14 @@ export function SettingsPage() {
|
||||
<div className="setting-row">
|
||||
<div className="setting-info">
|
||||
<div className="setting-label">下载源策略</div>
|
||||
<div className="setting-hint">选择 CLI 安装时的首选下载渠道(Wave 1 生效)</div>
|
||||
<div className="setting-hint">选择 CLI 安装时的首选下载渠道</div>
|
||||
</div>
|
||||
<span className="setting-value">官方渠道</span>
|
||||
</div>
|
||||
<div className="setting-row">
|
||||
<div className="setting-info">
|
||||
<div className="setting-label">自动检查更新</div>
|
||||
<div className="setting-hint">启动时检查 AgentDock 自身与 CLI 的更新(Wave 4 生效)</div>
|
||||
<div className="setting-hint">启动时检查 AgentDock 自身与 CLI 的更新</div>
|
||||
</div>
|
||||
<span className="setting-value">开</span>
|
||||
</div>
|
||||
@@ -30,7 +30,7 @@ export function SettingsPage() {
|
||||
<div className="setting-row">
|
||||
<div className="setting-info">
|
||||
<div className="setting-label">日志与隐私</div>
|
||||
<div className="setting-hint">日志本地保存,敏感值自动脱敏(Wave 1 生效)</div>
|
||||
<div className="setting-hint">日志本地保存,敏感值自动脱敏</div>
|
||||
</div>
|
||||
<span className="setting-value">默认</span>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* ============================================================
|
||||
* AgentDock 全局样式(Wave 0)
|
||||
* 全部颜色/辉光/动效时长来自 tokens/(视觉规范 v1.2),
|
||||
* AgentDock 全局样式(Wave 0.5)
|
||||
* 全部颜色/辉光/动效时长来自 tokens/(视觉规范 v1.3),
|
||||
* 本文件不出现任何魔法色值;布局尺寸来自规范 §3.0/§6。
|
||||
* ============================================================ */
|
||||
|
||||
@@ -12,18 +12,22 @@
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
#root {
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#root {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--ad-font-ui);
|
||||
font-size: var(--ad-text-m-size);
|
||||
line-height: var(--ad-text-m-height);
|
||||
color: var(--ad-text-1);
|
||||
background: var(--ad-bg-0);
|
||||
overflow: hidden;
|
||||
background: var(--ad-bg-gradient); /* v1.3 §3.0:静态深空渐变,网格已移除 */
|
||||
overflow-y: auto; /* v1.3 §3.0:主滚动容器 = 窗口,滚动条贴右缘 */
|
||||
overflow-x: hidden;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
@@ -31,25 +35,40 @@ button {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
/* ---------- 背景网格(§3.0:5% + 边缘羽化,低档静态) ---------- */
|
||||
.bg-grid {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
background-image:
|
||||
linear-gradient(var(--ad-grid-color) 1px, transparent 1px),
|
||||
linear-gradient(90deg, var(--ad-grid-color) 1px, transparent 1px);
|
||||
background-size: var(--ad-grid-size) var(--ad-grid-size);
|
||||
-webkit-mask-image: var(--ad-grid-mask);
|
||||
mask-image: var(--ad-grid-mask);
|
||||
/* ---------- 自定义滚动条(v1.3 §2.7:10px、灰蓝滑块、透明轨道、不发光) ---------- */
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--ad-scrollbar-thumb);
|
||||
border-radius: 5px;
|
||||
border: 2px solid transparent;
|
||||
background-clip: padding-box;
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--ad-scrollbar-thumb-hover);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
/* 标准属性兜底(Firefox 等) */
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--ad-scrollbar-thumb) transparent;
|
||||
}
|
||||
|
||||
/* ---------- 应用外壳 ---------- */
|
||||
.app-shell {
|
||||
position: relative;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@@ -60,17 +79,29 @@ button {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.app-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
/* 内容区与顶栏内区共用同一宽度体系(v1.3 §3.0:顶栏与内容区右缘对齐) */
|
||||
.app-content,
|
||||
.header-inner {
|
||||
width: 100%;
|
||||
max-width: 1440px; /* §6 宽阔断点锁宽居中 */
|
||||
margin: 0 auto;
|
||||
padding: var(--ad-space-6) var(--ad-space-8);
|
||||
max-width: var(--ad-frame-max);
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding-left: var(--ad-frame-pad-x);
|
||||
padding-right: var(--ad-frame-pad-x);
|
||||
}
|
||||
|
||||
/* ---------- 侧边栏(§3.0) ---------- */
|
||||
.app-content {
|
||||
flex: 1;
|
||||
padding-top: var(--ad-space-6);
|
||||
padding-bottom: var(--ad-space-6);
|
||||
}
|
||||
|
||||
/* ---------- 侧边栏(§3.0:232px 贴左,窗口滚动时保持固定) ---------- */
|
||||
.sidebar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
align-self: flex-start;
|
||||
height: 100vh;
|
||||
width: 232px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
@@ -141,17 +172,23 @@ button {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* ---------- 顶栏(§3.0) ---------- */
|
||||
/* ---------- 顶栏(§3.0:通栏 56px,内区与内容区同一宽度体系,滚动时吸顶) ---------- */
|
||||
.header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
height: 56px;
|
||||
flex-shrink: 0;
|
||||
background: var(--ad-bg-0);
|
||||
border-bottom: 1px solid var(--ad-border);
|
||||
}
|
||||
|
||||
.header-inner {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--ad-space-4);
|
||||
padding: 0 var(--ad-space-8);
|
||||
background: var(--ad-bg-0);
|
||||
border-bottom: 1px solid var(--ad-border);
|
||||
}
|
||||
|
||||
.header-title {
|
||||
@@ -233,6 +270,8 @@ button {
|
||||
justify-content: center;
|
||||
gap: var(--ad-space-2);
|
||||
height: 36px;
|
||||
min-width: 96px; /* v1.3 §2.6.1 */
|
||||
max-width: 360px; /* v1.3 §2.6.1:禁止通栏 */
|
||||
padding: 0 var(--ad-space-4);
|
||||
border: 1px solid transparent;
|
||||
border-radius: var(--ad-radius-s);
|
||||
@@ -245,7 +284,7 @@ button {
|
||||
}
|
||||
|
||||
.btn:disabled {
|
||||
opacity: 0.55;
|
||||
opacity: 0.4; /* v1.3 §3.7:无意义操作置灰 */
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@@ -524,8 +563,12 @@ button {
|
||||
gap: var(--ad-space-3);
|
||||
}
|
||||
|
||||
/* v1.3 §2.6.1:快速操作组定宽(组宽 = min(内容宽, 360px)),左对齐,不通栏 */
|
||||
.quick-card .btn {
|
||||
width: 100%;
|
||||
width: min(100%, 360px);
|
||||
justify-content: flex-start;
|
||||
padding-left: var(--ad-space-4);
|
||||
padding-right: var(--ad-space-4);
|
||||
}
|
||||
|
||||
.recent-installs {
|
||||
@@ -592,10 +635,6 @@ button {
|
||||
color: var(--ad-text-1);
|
||||
}
|
||||
|
||||
.cli-block-version .mono-chip {
|
||||
color: var(--ad-text-3);
|
||||
}
|
||||
|
||||
.cli-block-auth {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -604,6 +643,80 @@ button {
|
||||
font-size: var(--ad-text-xs-size);
|
||||
}
|
||||
|
||||
/* 未安装卡版本位(v1.3 §3.1.4:废止「—」chip,改灰字「未安装」) */
|
||||
.cli-block-version .version-missing {
|
||||
color: var(--ad-text-3);
|
||||
font-size: var(--ad-text-s-size);
|
||||
}
|
||||
|
||||
/* 首跑精简块:安装入口(v1.3 §3.1.5) */
|
||||
.cli-block-install {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
margin-top: var(--ad-space-1);
|
||||
}
|
||||
|
||||
/* ---------- 总览:首跑空态引导区(v1.3 §3.1.5) ---------- */
|
||||
.onboarding {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: var(--ad-space-6);
|
||||
align-items: center;
|
||||
padding: var(--ad-space-6);
|
||||
background: var(--ad-bg-1);
|
||||
border: 1px solid var(--ad-border);
|
||||
border-radius: var(--ad-radius-l);
|
||||
}
|
||||
|
||||
.onboarding-art {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--ad-border-bright);
|
||||
}
|
||||
|
||||
.onboarding-copy {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: var(--ad-space-3);
|
||||
}
|
||||
|
||||
.onboarding-title {
|
||||
font-size: var(--ad-text-xl-size);
|
||||
font-weight: var(--ad-text-xl-weight);
|
||||
color: var(--ad-text-1);
|
||||
}
|
||||
|
||||
.onboarding-desc {
|
||||
max-width: 480px;
|
||||
color: var(--ad-text-2);
|
||||
font-size: var(--ad-text-m-size);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.onboarding-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--ad-space-3);
|
||||
margin-top: var(--ad-space-2);
|
||||
}
|
||||
|
||||
/* 诊断空态(v1.3 §3.1.2 / §3.7:无记录不渲染占位行) */
|
||||
.diag-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: var(--ad-space-3);
|
||||
padding: var(--ad-space-5) var(--ad-space-2);
|
||||
}
|
||||
|
||||
.diag-empty-text {
|
||||
color: var(--ad-text-2);
|
||||
font-size: var(--ad-text-s-size);
|
||||
}
|
||||
|
||||
/* ---------- 总览:本机环境 ---------- */
|
||||
.env-panel {
|
||||
margin-top: var(--ad-space-6);
|
||||
@@ -640,10 +753,11 @@ button {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/* 本机环境运行时项(v1.3 §3.1.6:四元组同构,点—名称—版本—行动,行高 28px) */
|
||||
.runtime-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: var(--ad-space-3);
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: var(--ad-space-4);
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
@@ -651,17 +765,32 @@ button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--ad-space-2);
|
||||
min-height: 28px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.runtime-name {
|
||||
flex: 1;
|
||||
color: var(--ad-text-2);
|
||||
color: var(--ad-text-1);
|
||||
font-size: var(--ad-text-s-size);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.runtime-version {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.runtime-missing {
|
||||
color: var(--ad-warning);
|
||||
color: var(--ad-text-3);
|
||||
font-size: var(--ad-text-xs-size);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.runtime-action {
|
||||
margin-left: auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.path-block {
|
||||
@@ -903,8 +1032,8 @@ button {
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- 响应式(§6 三断点) ---------- */
|
||||
/* 紧凑 960–1199px:导航收起 64px,KPI 2×2,中段上下堆叠,状态网格 2 列 */
|
||||
/* ---------- 响应式(v1.3 §6 四断点) ---------- */
|
||||
/* 紧凑 960–1199px:导航收起 64px,KPI 2×2,中段上下堆叠 */
|
||||
@media (max-width: 1199px) {
|
||||
.sidebar {
|
||||
width: 64px;
|
||||
@@ -942,7 +1071,7 @@ button {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.cli-status-grid {
|
||||
.runtime-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
@@ -983,11 +1112,48 @@ button {
|
||||
}
|
||||
}
|
||||
|
||||
/* 标准 1200–1599px:默认样式(完整导航、KPI 一排 4、诊断+快速 2:1、状态网格 3 列) */
|
||||
/* 宽阔 ≥1600px:内容锁 1440 居中,状态网格 4 列 */
|
||||
@media (min-width: 1600px) {
|
||||
.cli-status-grid {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
/* 标准 1200–1919px:默认(完整导航、KPI 一排 4、诊断+快速 2:1、内容区 max 1440 居中)
|
||||
* 宽阔 1920–2559px:内容区 max 1760 居中(v1.3 §6)
|
||||
* 超宽 ≥2560px:取消居中锁宽,内容区全宽、边距 48px(v1.3 §6) */
|
||||
@media (min-width: 1920px) and (max-width: 2559px) {
|
||||
:root {
|
||||
--ad-frame-max: 1760px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 2560px) {
|
||||
:root {
|
||||
--ad-frame-max: none;
|
||||
--ad-frame-pad-x: var(--ad-space-12);
|
||||
}
|
||||
}
|
||||
|
||||
/* 超宽 ≥2560px:中段三栏(诊断 1/2 + 快速操作 1/4 + 本机环境 1/4),本机环境上提 */
|
||||
@media (min-width: 2560px) {
|
||||
.overview {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr 1fr;
|
||||
column-gap: var(--ad-space-4);
|
||||
row-gap: var(--ad-space-6);
|
||||
}
|
||||
|
||||
.overview .kpi-grid {
|
||||
grid-column: 1 / -1;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.overview .overview-mid {
|
||||
grid-column: 1 / 3;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.overview .cli-status-panel {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.overview .env-panel {
|
||||
grid-column: 3;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
--ad-border: #22304A; /* 常规 1px 分隔线 */
|
||||
--ad-border-bright: #35507A; /* 悬浮/选中的边框 */
|
||||
|
||||
/* ---- 自定义滚动条(v1.3 §2.7:灰蓝滑块、透明轨道、不发光)---- */
|
||||
--ad-scrollbar-thumb: #1A2438; /* = --ad-bg-3 */
|
||||
--ad-scrollbar-thumb-hover: #35507A; /* = --ad-border-bright */
|
||||
|
||||
/* ---- 霓虹强调色(全系统只有这两个霓虹色)---- */
|
||||
--ad-primary: #00E5FF; /* 主操作、链接、聚焦、选中态(青) */
|
||||
--ad-primary-hover: #4DEFFF; /* 主操作悬浮 */
|
||||
|
||||
@@ -30,10 +30,8 @@
|
||||
--ad-btn-accent-glow-hover: 0 0 14px rgba(255, 77, 219, 0.30),
|
||||
0 0 36px rgba(255, 77, 219, 0.12);
|
||||
|
||||
/* ---- 背景网格(§3.0:要么可见要么不做)---- */
|
||||
--ad-grid-color: rgba(0, 229, 255, 0.05); /* 透明度固定 5%,施工员不许再调 */
|
||||
--ad-grid-size: 40px;
|
||||
--ad-grid-mask: radial-gradient(120% 90% at 50% 40%, #000 30%, transparent 75%);
|
||||
/* ---- 背景氛围(v1.3 §3.0:网格移除,降级为静态深空渐变,三档通用)---- */
|
||||
--ad-bg-gradient: radial-gradient(120% 90% at 50% 0%, #0D1420 0%, #070B14 70%);
|
||||
|
||||
/* ---- 弹窗遮罩(§3.6)---- */
|
||||
--ad-modal-mask: rgba(4, 7, 13, 0.7);
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
--ad-space-6: 24px;
|
||||
--ad-space-8: 32px;
|
||||
--ad-space-10: 40px;
|
||||
--ad-space-12: 48px; /* v1.3 §3.0 超宽断点内容区边距 */
|
||||
|
||||
/* ---- 内容区宽度体系(v1.3 §6:标准 1440 / 宽阔 1760 / 超宽全宽)---- */
|
||||
--ad-frame-max: 1440px;
|
||||
--ad-frame-pad-x: var(--ad-space-8);
|
||||
|
||||
/* ---- 圆角 ---- */
|
||||
--ad-radius-s: 4px; /* 标签、小按钮、chip */
|
||||
|
||||
@@ -34,18 +34,6 @@
|
||||
box-shadow: var(--ad-glow-balanced);
|
||||
}
|
||||
|
||||
/* 背景网格漂移(§4.8,60s 一周,仅高档/中档) */
|
||||
[data-fx-tier="low"] .grid-drift {
|
||||
animation: none;
|
||||
}
|
||||
.grid-drift {
|
||||
animation: grid-drift 60s linear infinite;
|
||||
}
|
||||
@keyframes grid-drift {
|
||||
from { background-position: 0 0; }
|
||||
to { background-position: 40px 40px; }
|
||||
}
|
||||
|
||||
/* 状态点呼吸(§4.5:仅警告/错误/授权可能过期呼吸;低档停用) */
|
||||
[data-fx-tier="low"] .status-dot-breathe {
|
||||
animation: none;
|
||||
|
||||
Reference in New Issue
Block a user