diff --git a/ACRYLIC_DESIGN_INVENTORY.md b/docs/ACRYLIC_DESIGN_INVENTORY.md similarity index 100% rename from ACRYLIC_DESIGN_INVENTORY.md rename to docs/ACRYLIC_DESIGN_INVENTORY.md diff --git a/docs/status-language.md b/docs/status-language.md new file mode 100644 index 0000000..42903c4 --- /dev/null +++ b/docs/status-language.md @@ -0,0 +1,40 @@ +# 状态语言 + +系统使用五种语义状态。业务模块保留自己的领域枚举,并在展示边界映射到共享 +`StatusTone`,不要让展示文案、接口字符串或 Tailwind 原色类直接决定状态样式。 + +| Tone | 用途 | 典型状态 | +| --- | --- | --- | +| `neutral` | 尚未开始或不再活动 | 预约、草案、待办、取消、中止、跳过、未分级 | +| `info` | 正在发生的过程 | 执行中、提交中、加载中、连接中 | +| `success` | 已完成或健康 | 完成、在线、正常、已批准 | +| `warning` | 需要关注但未达到异常 | 关注、降级、待复核、待复令、待确认 | +| `danger` | 已异常或操作失败 | 异常、失败、离线、高风险、事故、权限拒绝 | + +## 报告风险 + +报告风险固定为三档: + +- `normal` 映射 `success`,显示“正常”。 +- `attention` 映射 `warning`,显示“关注”。 +- `critical` 映射 `danger`,显示“异常”。 + +`info` 只表示过程,不参与风险等级。不得把 `attention` 合并到正常或异常。 + +## 表达规则 + +- 状态标签使用 `StatusBadge`,小型指示使用 `StatusDot`,图标节点使用 `StatusIcon`。 +- 大面积容器只使用柔和背景,禁止实心红色或橙色状态卡片。 +- `warning` 和 `danger` 必须同时提供文字或图标,不能只依赖颜色。 +- 只有进行中的状态可以使用 `live` 或 `loading` 动效。 +- “复令”是提交处置结果的动作,执行完成但尚未提交时显示“待复令”,使用静态 + `warning`;不得继续显示为“执行中”或提前显示为完成态。 +- 动效必须在 `prefers-reduced-motion` 下停止。 +- 蓝色选中态属于交互反馈,不作为业务风险状态。 +- 紫色只表示 Agent 身份,不表示未知或更高风险。 + +## 地图状态 + +地图与面板共享语义,但保留独立色值以适应浅色底图、影像底图和白色 casing。 +新增地图状态时,应先补充 `map-colors.ts`,再通过对比度测试验证,不要在图层定义中 +写入新的颜色字面量。 diff --git a/package.json b/package.json index 6a7dc11..522fea6 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@deck.gl/core": "^9.3.7", "@deck.gl/layers": "^9.3.7", "@deck.gl/mapbox": "^9.3.7", + "@fontsource-variable/noto-sans-sc": "^5.3.0", "@radix-ui/react-accordion": "^1.2.14", "@radix-ui/react-collapsible": "^1.1.14", "@radix-ui/react-dialog": "^1.1.17", @@ -72,10 +73,10 @@ "@types/react": "^19.0.2", "@types/react-dom": "^19.0.2", "@vitejs/plugin-react-swc": "^4.0.2", + "esbuild": "^0.28.0", "eslint": "^9.17.0", "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-refresh": "^0.4.16", - "esbuild": "^0.28.0", "globals": "^15.14.0", "jsdom": "^25.0.1", "msw": "^2.6.8", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f0fe7df..c1523ed 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: '@deck.gl/mapbox': specifier: ^9.3.7 version: 9.3.7(@deck.gl/core@9.3.7)(@luma.gl/core@9.3.6)(@math.gl/web-mercator@4.1.0) + '@fontsource-variable/noto-sans-sc': + specifier: ^5.3.0 + version: 5.3.0 '@radix-ui/react-accordion': specifier: ^1.2.14 version: 1.2.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -681,6 +684,9 @@ packages: '@floating-ui/utils@0.2.12': resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + '@fontsource-variable/noto-sans-sc@5.3.0': + resolution: {integrity: sha512-lNar1dF7Ik/lHNPo/7JWG0TolXY29LtsqYgMvEysooZ5bsO9uH4shJmRrwyJ3PjyTPljhpMJEK0jDuLSU4vJ1w==} + '@humanfs/core@0.19.2': resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} engines: {node: '>=18.18.0'} @@ -4671,6 +4677,8 @@ snapshots: '@floating-ui/utils@0.2.12': {} + '@fontsource-variable/noto-sans-sc@5.3.0': {} + '@humanfs/core@0.19.2': dependencies: '@humanfs/types': 0.15.0 diff --git a/src/app/app.e2e.ts b/src/app/app.e2e.ts index c621b74..d1f634e 100644 --- a/src/app/app.e2e.ts +++ b/src/app/app.e2e.ts @@ -82,7 +82,7 @@ test("preserves compact typography in header and agent controls", async ({ page await expect(approvalControl).toHaveCSS("font-size", "12px"); await expect(approvalControl).toHaveCSS("line-height", "16px"); await expect(suggestion).toHaveCSS("font-size", "12px"); - await expect(suggestion).toHaveCSS("font-weight", "500"); + await expect(suggestion).toHaveCSS("font-weight", "600"); }); test("lets interactive utilities override surface materials", async ({ page }) => { @@ -102,7 +102,7 @@ test("lets interactive utilities override surface materials", async ({ page }) = expect(hoverBackground).not.toBe(restingBackground); }); -test("preserves v3 menu and panel styling through Tailwind v4", async ({ page }) => { +test("keeps menu origin and integrated composer styling through Tailwind v4", async ({ page }) => { await page.setViewportSize({ width: 1440, height: 900 }); await page.goto("/"); @@ -118,6 +118,6 @@ test("preserves v3 menu and panel styling through Tailwind v4", async ({ page }) const commandShadow = await agentCommandControl.evaluate( (element) => window.getComputedStyle(element).boxShadow ); - expect(commandShadow).toContain("rgba(0, 0, 0, 0.05) 0px 1px 2px 0px"); - expect(commandShadow).not.toContain("0px 1px 3px 0px"); + expect(commandShadow).toBe("none"); + await expect(agentCommandControl).toHaveCSS("border-top-width", "0px"); }); diff --git a/src/app/app.tsx b/src/app/app.tsx index 64985c2..609e42e 100644 --- a/src/app/app.tsx +++ b/src/app/app.tsx @@ -1,4 +1,4 @@ -import { MapToaster } from "@/features/map/core/components/notice"; +import { MapToaster } from "@/features/map/core"; import { MapWorkbenchPage } from "@/features/workbench"; import { AppProviders } from "./providers"; diff --git a/src/features/agent/components/agent-collapsed-rail.tsx b/src/features/agent/components/agent-collapsed-rail.tsx index b06774a..f3d3a99 100644 --- a/src/features/agent/components/agent-collapsed-rail.tsx +++ b/src/features/agent/components/agent-collapsed-rail.tsx @@ -1,9 +1,6 @@ -"use client"; - import { ChevronRight } from "lucide-react"; import type { PersonaState } from "@/shared/ai-elements/persona"; -import { cn } from "@/lib/utils"; -import { MAP_MAJOR_PANEL_RADIUS_CLASS_NAME } from "@/features/map/core/components/map-control-styles"; +import { cn } from "@/shared/ui/cn"; import { AgentPersona } from "./agent-persona"; type AgentCollapsedRailProps = { @@ -23,8 +20,7 @@ export function AgentCollapsedRail({