diff --git a/apps/desktop/src/renderer/components/settings/usage/UsageBreakdownTables.tsx b/apps/desktop/src/renderer/components/settings/usage/UsageBreakdownTables.tsx index 951da9ac46a..909791bd758 100644 --- a/apps/desktop/src/renderer/components/settings/usage/UsageBreakdownTables.tsx +++ b/apps/desktop/src/renderer/components/settings/usage/UsageBreakdownTables.tsx @@ -1,7 +1,7 @@ /** * UsageBreakdownTables — 用量历史页的两张明细表: 按 agent / harness 与按模型。 * - * 两张表共用同一套表格样式与 rank 配色 (usagePalette), 因此放在同一个文件里, + * 两张表共用同一套表格样式与分类配色 (usageHistoryColors), 因此放在同一个文件里, * 避免为了共享 5 行 class 再拆一层。 * * 缓存命中率与 shared/turnUsageDetails.ts 的逐轮口径一致 (见 usageHistoryStats.cacheHitRate), @@ -14,19 +14,12 @@ import { useTranslation } from 'react-i18next'; import { cn } from '@/lib/utils'; import { formatCompactTokens, formatModelShort } from '@/lib/usageFormat'; -import { usageRankColor, usageRankOf } from '@/components/new-chat/usagePalette'; -import { type AgentTokenRow, type ModelTokenRow, type UsageAgentKind } from './usageHistoryStats'; -import { usageHistoryModelColor } from './usageHistoryColors'; +import { usageRankOf } from '@/components/new-chat/usagePalette'; +import { type AgentTokenRow, type ModelTokenRow } from './usageHistoryStats'; +import { usageHistoryAgentColor, usageHistoryModelColor } from './usageHistoryColors'; import { formatUsagePercent } from './formatUsagePercent'; const UNKNOWN_VALUE = '—'; -/** 与 usagePalette 的 rank 阶梯对齐: Agent 标记保持独立的中性配色,不借用模型类别色。 */ -const AGENT_RANK: Record = { - 'claude-code': 0, - codex: 1, - pi: 2, -}; - const TH_CLASS = 'whitespace-nowrap border-b border-[var(--border-default)] pb-2 pl-3 text-right text-12 font-medium text-[var(--text-secondary)]'; const TD_CLASS = @@ -40,13 +33,7 @@ const TD_CLASS = */ const FIRST_COL_CLASS = 'w-full max-w-0 pl-0'; -function Swatch({ - rank, - color = usageRankColor(rank), -}: { - rank: number; - color?: string; -}): React.JSX.Element { +function Swatch({ color }: { color: string }): React.JSX.Element { return ; } @@ -54,15 +41,7 @@ function HitRateCell({ value }: { value: number | null }): React.JSX.Element { return {value === null ? UNKNOWN_VALUE : formatUsagePercent(value)}; } -function ShareCell({ - share, - rank, - color = usageRankColor(rank), -}: { - share: number; - rank: number; - color?: string; -}): React.JSX.Element { +function ShareCell({ share, color }: { share: number; color: string }): React.JSX.Element { return ( - {/* 占比条: 让"谁占大头"在读表之前就成立。 - 8px + 2px 圆角 = DESIGN.md §5 登记的「非交互 ≤8px 状态微格」豁免档。 */} + {/* 占比条与各行共用 harness 身份色;尺寸与既有布局一致。 */}
{rows.map((row) => (
0 ? (row.tokens / total) * 100 : 0}%`, - backgroundColor: usageRankColor(AGENT_RANK[row.agentKind] ?? 3), + backgroundColor: usageHistoryAgentColor(row.agentKind), }} /> ))} @@ -127,19 +105,19 @@ export function UsageAgentTable({ {rows.map((row) => { - const rank = AGENT_RANK[row.agentKind] ?? 3; + const color = usageHistoryAgentColor(row.agentKind); return ( - + {row.agentKind} {formatCompactTokens(row.tokens)} - + {!hideToday ? ( {row.todayTokens > 0 ? formatCompactTokens(row.todayTokens) : UNKNOWN_VALUE} @@ -195,7 +173,7 @@ export function UsageModelTable({ - + {formatModelShort(row.model)} @@ -206,7 +184,7 @@ export function UsageModelTable({ {formatCompactTokens(row.tokens)} - + {formatCompactTokens(row.inputTokens)} {formatCompactTokens(row.outputTokens)} {formatCompactTokens(row.cacheReadTokens)} diff --git a/apps/desktop/src/renderer/components/settings/usage/UsageHistorySection.tsx b/apps/desktop/src/renderer/components/settings/usage/UsageHistorySection.tsx index 2cdc1db4ca5..f508346b338 100644 --- a/apps/desktop/src/renderer/components/settings/usage/UsageHistorySection.tsx +++ b/apps/desktop/src/renderer/components/settings/usage/UsageHistorySection.tsx @@ -14,7 +14,7 @@ * - 每日柱图固定展示近 30 天, 与筛选范围无关 * * 首页的 HomeUsageDashboard 是金额口径的姊妹实现, 本页不复用它的外壳组件 - * (见 UsageTokenBars 的注释), 但共享同一条聚合链路与配色。 + * (见 UsageTokenBars 的注释), 但共享同一条聚合链路。配色仅在用量历史页内统一。 */ import './usageCharts.css'; @@ -28,7 +28,7 @@ import { Spinner } from '@/components/ui/spinner'; import { useAuth } from '@/contexts/AuthContext'; import { useUsageHistory } from '@/hooks/useUsageHistory'; import { UsageHeatmap } from '@/components/new-chat/UsageHeatmap'; -import { USAGE_TOP_MODELS, usageModelKey } from '@/components/new-chat/usagePalette'; +import { usageModelKey } from '@/components/new-chat/usagePalette'; import { UsageStatRow } from './UsageStatRow'; import { UsageTokenBars } from './UsageTokenBars'; import { UsageAgentTable, UsageModelTable } from './UsageBreakdownTables'; @@ -115,15 +115,11 @@ export function UsageHistorySection(): React.JSX.Element { const summary = useMemo(() => buildSummary(filteredHistory), [filteredHistory]); const modelRows = useMemo(() => buildModelRows(filteredHistory), [filteredHistory]); const agentRows = useMemo(() => buildAgentRows(filteredHistory), [filteredHistory]); - // 配色顺序必须来自 modelRows 而不是 history.models: 后者由 main 按**可比金额**降序排 - // (usageHistory.ts 的 comparable), 本页只讲 token —— 直接用它会让同一个模型在柱图与 - // 模型表里配到不同颜色, 还会让"金额高但 token 很少"的模型挤掉真正的 token 前 N 名。 + // 完整历史按 token 排序建立所有模型的配色,不截断前五名,也不随日期筛选重排。 + // 表格与柱图共用此顺序;history.models 的金额排序不适用于这里。 const colorOrder = useMemo( - () => - buildModelRows(chartHistory) - .slice(0, USAGE_TOP_MODELS) - .map((m) => usageModelKey(m.agentKind, m.model)), - [chartHistory], + () => buildModelRows(history).map((m) => usageModelKey(m.agentKind, m.model)), + [history], ); // 任务行与用量聚合是两条数据源: 聚合里有 token, 本地任务却可能一条都没有 diff --git a/apps/desktop/src/renderer/components/settings/usage/UsageTokenBars.tsx b/apps/desktop/src/renderer/components/settings/usage/UsageTokenBars.tsx index 8515afbdc20..c5f8118946e 100644 --- a/apps/desktop/src/renderer/components/settings/usage/UsageTokenBars.tsx +++ b/apps/desktop/src/renderer/components/settings/usage/UsageTokenBars.tsx @@ -19,7 +19,7 @@ import { useTranslation } from 'react-i18next'; import { formatCompactTokens } from '@/lib/usageFormat'; import type { UsageHistoryModelDay } from '@/hooks/useUsageHistory'; -import { usageModelKey, usageRankOf } from '@/components/new-chat/usagePalette'; +import { usageModelKey } from '@/components/new-chat/usagePalette'; import { usageHistoryModelColor } from './usageHistoryColors'; @@ -72,7 +72,7 @@ export function UsageTokenBars({ onDayClick, }: { modelDaily: UsageHistoryModelDay[]; - /** 前 N 名模型 key (payload.models 排序), 决定分段与图例配色。 */ + /** 完整历史的模型 key(按 token 排序),决定分段与表格配色。 */ colorOrder: string[]; todayKey: string; selectedDay?: string | null; @@ -92,10 +92,11 @@ export function UsageTokenBars({ ); const bars = useMemo(() => { + const ranks = new Map(colorOrder.map((key, rank) => [key, rank])); const segsByDay = new Map>(); for (const row of modelDaily) { if (row.tokens <= 0) continue; - const rank = usageRankOf(colorOrder, usageModelKey(row.agentKind, row.model)); + const rank = ranks.get(usageModelKey(row.agentKind, row.model)) ?? colorOrder.length; let daySegs = segsByDay.get(row.day); if (!daySegs) { daySegs = new Map(); @@ -104,7 +105,7 @@ export function UsageTokenBars({ const seg = daySegs.get(rank); if (seg) { seg.tokens += row.tokens; - // 尾部档 (rank === colorOrder.length) 会把所有非前 N 名模型并进同一分段, + // 防御性兜底:调用方未登记的模型合并到尾部档 (rank === colorOrder.length), // 保留首个模型名会把合计错误地挂到它头上 —— 改标「其它」, 与图例同义。 if (rank >= colorOrder.length) seg.label = t('usageDashboard.othersLegend'); } else { diff --git a/apps/desktop/src/renderer/components/settings/usage/__tests__/usageHistoryColors.test.tsx b/apps/desktop/src/renderer/components/settings/usage/__tests__/usageHistoryColors.test.tsx index 23e7785e7e2..5a730083e79 100644 --- a/apps/desktop/src/renderer/components/settings/usage/__tests__/usageHistoryColors.test.tsx +++ b/apps/desktop/src/renderer/components/settings/usage/__tests__/usageHistoryColors.test.tsx @@ -2,8 +2,8 @@ import React from 'react'; import { cleanup, render } from '@testing-library/react'; import { afterEach, describe, expect, it, vi } from 'vitest'; -import { usageHistoryModelColor } from '../usageHistoryColors'; -import { UsageModelTable } from '../UsageBreakdownTables'; +import { usageHistoryAgentColor, usageHistoryModelColor } from '../usageHistoryColors'; +import { UsageAgentTable, UsageModelTable } from '../UsageBreakdownTables'; import { UsageTokenBars } from '../UsageTokenBars'; import type { ModelTokenRow } from '../usageHistoryStats'; @@ -13,13 +13,111 @@ vi.mock('react-i18next', () => ({ afterEach(cleanup); describe('Usage History model colors', () => { - it('reserves five distinct colors and keeps unlisted models neutral even in short lists', () => { - expect(new Set(Array.from({ length: 5 }, (_, i) => usageHistoryModelColor(i, 5))).size).toBe(5); + it('colors arbitrarily long model lists while retaining the first five theme colors', () => { + const colors = Array.from({ length: 1000 }, (_, i) => usageHistoryModelColor(i, 1000)); + expect(new Set(colors).size).toBe(1000); + expect(colors.every((color) => color.includes('var(--usage-model-'))).toBe(true); + expect(colors.slice(0, 5)).toEqual([1, 2, 3, 4, 5].map((i) => `var(--usage-model-${i})`)); + expect(usageHistoryModelColor(34, 35)).toBe(usageHistoryModelColor(34, 1000)); + // Only an actually missing identity gets the neutral fallback, not rank >= 5. expect(usageHistoryModelColor(2, 2)).toBe('var(--text-tertiary)'); expect(usageHistoryModelColor(0, 0)).toBe('var(--text-tertiary)'); expect(usageHistoryModelColor(5, 5)).toBe('var(--text-tertiary)'); }); + it('matches all three Agent marks and keeps identity colors when rankings reverse', () => { + expect( + ['claude-code', 'codex', 'pi'].map((kind) => + usageHistoryAgentColor(kind as 'claude-code' | 'codex' | 'pi'), + ), + ).toEqual(['var(--engine-badge-cc)', 'var(--engine-badge-codex)', 'var(--usage-model-1)']); + const rows = (['claude-code', 'codex', 'pi'] as const).map((agentKind, i) => ({ + agentKind, + tokens: (3 - i) * 100, + todayTokens: 0, + share: (3 - i) / 6, + cacheHitRate: 0.8, + modelCount: 12, + })); + const view = render(); + const checkColors = (order: typeof rows) => { + const segments = view.container.querySelectorAll('div[title]'); + order.forEach((row, i) => { + const color = usageHistoryAgentColor(row.agentKind); + const swatch = view.getByText(row.agentKind).previousElementSibling as HTMLElement; + const share = swatch.closest('tr')!.querySelector('.inline-block')!; + expect(swatch.style.backgroundColor).toBe(color); + expect(share.style.backgroundColor).toBe(color); + expect(segments[i].style.backgroundColor).toBe(color); + }); + }; + checkColors(rows); + view.rerender(); + checkColors([...rows].reverse()); + }); + + it('keeps 35 model segments separate and matched to filtered table rows', () => { + const rows: ModelTokenRow[] = Array.from({ length: 35 }, (_, i) => ({ + key: `claude-code model-${i}`, + agentKind: 'claude-code', + model: `model-${i}`, + tokens: i + 1, + inputTokens: i + 1, + outputTokens: 0, + cacheReadTokens: 0, + cacheCreateTokens: 0, + share: (i + 1) / 630, + cacheHitRate: 0, + })); + const colorOrder = rows.map((row) => row.key); + const money = { + amount: 0, + currency: 'USD' as const, + approximate: false, + kind: 'actual-cost' as const, + }; + const props = { + todayKey: '2026-09-09', + colorOrder, + modelDaily: rows.map((row) => ({ + ...row, + day: '2026-09-09', + money, + apiMoney: money, + subscriptionEstimateMoney: money, + })), + }; + const view = render( + <> + + + , + ); + const mark = view.container.querySelector( + 'button[title^="2026-09-09"] [data-usage-mark]', + )!; + expect(mark.children).toHaveLength(35); + expect(mark.style.height).toBe('96px'); + const segments = [...mark.children].reverse() as HTMLElement[]; + rows.forEach((row, i) => { + const swatch = view.getByText(row.model).previousElementSibling as HTMLElement; + expect(swatch.style.backgroundColor).not.toBe(''); + expect(swatch.style.backgroundColor).not.toBe('var(--text-tertiary)'); + expect(swatch.style.backgroundColor).toBe(segments[i].style.backgroundColor); + expect(parseFloat(segments[i].style.height)).toBeCloseTo((row.tokens / 630) * 100); + }); + const lastColor = segments[34].style.backgroundColor; + view.rerender( + <> + + + , + ); + expect( + (view.getByText('model-34').previousElementSibling as HTMLElement).style.backgroundColor, + ).toBe(lastColor); + }); + it('keeps a model swatch matched to its bar segment after filtering and reordering rows', () => { const colorOrder = ['claude-code first', 'claude-code second']; const money = { diff --git a/apps/desktop/src/renderer/components/settings/usage/__tests__/usageHistoryDateSelection.test.tsx b/apps/desktop/src/renderer/components/settings/usage/__tests__/usageHistoryDateSelection.test.tsx index fcce751dfdb..6d05d3278d9 100644 --- a/apps/desktop/src/renderer/components/settings/usage/__tests__/usageHistoryDateSelection.test.tsx +++ b/apps/desktop/src/renderer/components/settings/usage/__tests__/usageHistoryDateSelection.test.tsx @@ -82,6 +82,33 @@ function history(): UsageHistoryPayload { } describe('Usage history selection behavior', () => { + it('registers every historical model even when absent from the 30-day chart window', () => { + state.history = history(); + state.history.models.push( + ...Array.from({ length: 35 }, (_, i) => ({ + ...state.history!.models[0], + model: `old-model-${i}`, + inputTokens: i + 1, + })), + ); + state.history.days.unshift({ day: '2026-05-01', money, tokens: 1 }); + state.history.modelDaily.push({ + ...state.history.modelDaily[0], + day: '2026-05-01', + model: 'old-model-34', + tokens: 35, + inputTokens: 35, + }); + const view = render(); + const colors = () => JSON.parse(view.getByTestId('models').textContent!).colorOrder; + const before = colors(); + expect(before).toHaveLength(36); + expect(before).toContain('claude-code old-model-34'); + fireEvent.click(view.getAllByRole('button', { name: /May 1, 2026/ })[0]); + expect(colors()).toEqual(before); + expect(JSON.parse(view.getByTestId('models').textContent!).rows[0].model).toBe('old-model-34'); + }); + it('keeps both chart filters equivalent and preserves default, repeated and outside clicks', () => { state.history = history(); const view = render(); diff --git a/apps/desktop/src/renderer/components/settings/usage/usageHistoryColors.ts b/apps/desktop/src/renderer/components/settings/usage/usageHistoryColors.ts index f86390f3e20..18af16f8340 100644 --- a/apps/desktop/src/renderer/components/settings/usage/usageHistoryColors.ts +++ b/apps/desktop/src/renderer/components/settings/usage/usageHistoryColors.ts @@ -1,4 +1,6 @@ -/** Model colors are scoped to Usage History; money/agent/task palettes stay neutral. */ +import type { UsageAgentKind } from './usageHistoryStats'; + +/** Usage History category colors only; homepage money and task palettes stay neutral. */ const MODEL_COLORS = [ 'var(--usage-model-1)', 'var(--usage-model-2)', @@ -7,9 +9,27 @@ const MODEL_COLORS = [ 'var(--usage-model-5)', ] as const; -/** An unlisted model is always neutral, even when fewer than five models exist. */ +/** + * Keep the approved first five hues. Later groups rotate those themed hues by the + * golden angle, retaining their lightness/chroma instead of fading toward gray. + * CSS resolves from the active theme, including live Light/Dark switches. + * Rank comes from the complete history, never the currently filtered table. + */ export function usageHistoryModelColor(rank: number, colorCount: number): string { - return rank >= 0 && rank < colorCount && rank < MODEL_COLORS.length - ? MODEL_COLORS[rank] - : 'var(--text-tertiary)'; + if (!Number.isInteger(rank) || rank < 0 || rank >= colorCount) return 'var(--text-tertiary)'; + const base = MODEL_COLORS[rank % MODEL_COLORS.length]; + const group = Math.floor(rank / MODEL_COLORS.length); + if (group === 0) return base; + const hueRotation = (group * 137.50776405) % 360; + return `oklch(from ${base} l c calc(h + ${hueRotation.toFixed(3)}))`; +} + +/** Claude/Codex use engine colors; pi uses the owner-chosen teal. Never follow rank. */ +export function usageHistoryAgentColor(agentKind: UsageAgentKind): string { + const colors: Record = { + 'claude-code': 'var(--engine-badge-cc)', + codex: 'var(--engine-badge-codex)', + pi: MODEL_COLORS[0], + }; + return colors[agentKind]; } diff --git a/docs/design-evidence/2026-09-09/usage-category-colors.md b/docs/design-evidence/2026-09-09/usage-category-colors.md new file mode 100644 index 00000000000..0b267ce81d9 --- /dev/null +++ b/docs/design-evidence/2026-09-09/usage-category-colors.md @@ -0,0 +1,38 @@ +# 用量历史分类配色 + +## 当前:pi 青绿 + +所有者认为蓝色与紫色不易区分,指定 pi 改为青绿。三处 pi 标记统一消费 `--usage-model-1`,Light 实测 `rgb(20, 184, 166)`,Dark 实测 `rgb(45, 212, 191)`;Claude 陶土橙与 Codex 蓝色保持。沿用下述真实 Electron 沙箱与模拟数据重新检查,35 个模型、三处 harness 颜色对应与单日筛选检查通过。当前文件为同一忽略目录下 `pi-light.png`、`pi-dark.png`、`pi-light.json`、`pi-dark.json` 和 `pi-manifest.json`;之前的 brand 文件保留作历史对照。 + +## 上一版:harness 身份色调整 + +同日按所有者反馈,harness 的三处标记改为已有引擎身份色;下文初版青绿 / 靛蓝 / 琥珀记录仅保留作历史对照。真实 Electron 再次检查 Default Light 与 Default Dark:三种颜色分别为 `rgb(217, 119, 87)`(Claude 陶土橙)、`rgb(122, 157, 255)`(Codex 蓝)、`rgb(167, 139, 250)`(pi 紫)。两种模式同值符合这些身份 Token 的既有合同,模型颜色与日期筛选检查继续通过。 + +同一忽略目录中 `brand-light.png`、`brand-dark.png`、`brand-light.json`、`brand-dark.json` 与 `brand-manifest.json` 对应该版(harness 身份色版)实现;数据与平台条件同下。静态单测与 Desktop typecheck 重新执行。新增 Token 消费后先重新生成 design inventory,再通过相关测试门禁。未新增 Token 或修改种子值;视觉批准仍待设计师完成。 + +## 初版证据 + +- 日期:2026-09-09;平台:macOS Desktop / Electron;主题:Default Light、Default Dark。 +- 分支:`fix/usage-category-colors`;基于 `ebcb181a5b61f8c27984190f76de687e576b1686`,证据对应本次未提交源码,相关生产文件的 Git blob 记录在本地 manifest。提交后以文件 blob 核对,不把启动脚本打印的基线 SHA 当作改后源码 SHA。 +- 实例:独立 `usage-colors-20260909` 沙箱,CDP 9246;1280×800 CSS px,DPR 2。启动结果 `DESKTOP_DEV_VERDICT=ready`。 +- 数据:35 个明确标为 `preview-model-*` 的模拟模型、三个 harness、40 天用量,通过 CDP 注入真实页面的内存数据状态;实际 `UsageHistorySection` 与其子组件正常渲染,无替代 HTML 样张。未写入真实账号的数据库,也未验证生产用量采集链路。 + +## 验证结果 + +- 35 个模型均得到非透明分类色;模型表色块、占比短条与每日柱图对应片段的 computed 颜色一致,柱体保留 35 个片段。 +- Agent 顶部占比条、行前色块与占比短条一致(按 Claude / Codex / pi 顺序)。Light:`rgb(217, 119, 87)` / `rgb(122, 157, 255)` / `rgb(20, 184, 166)`;Dark:`rgb(217, 119, 87)` / `rgb(122, 157, 255)` / `rgb(45, 212, 191)`。初版青绿 / 靛蓝 / 琥珀方案的实测值(Light `rgb(20, 184, 166)` / `rgb(99, 102, 241)` / `rgb(245, 158, 11)`,Dark `rgb(45, 212, 191)` / `rgb(129, 140, 248)` / `rgb(251, 191, 36)`)已随方案废弃,仅保留作历史对照。 +- Electron 支持从主题变量派生 OKLCH;两种主题的所有模型 computed 值见 JSON。 +- 单日点击保持筛选,柱高不变,柱体四角仍为 2px。现有命中尺寸欠账不由本次颜色测试解决。 +- `pnpm test:unit:related`、`pnpm --filter desktop run --if-present typecheck` 通过。测试覆盖 1000 个颜色分配、35 个模型的分段/表格对应、Agent 排名反转、早于 30 天的历史模型与单日筛选。 +- 仅修改 Desktop;未测试 Mobile、其他操作系统和自定义主题组合。AI 采集和检查不构成设计师视觉批准。 + +## 截图与记录 + +当前版本的栅格证据保存在工作区忽略目录 `.cindy/usage-category-evidence-2026-09-09/`: + +- `pi-light.png`、`pi-dark.png`:当前实现(pi 青绿版)的 Agent 表与模型表,Default Light / Default Dark。 +- `pi-light.json`、`pi-dark.json`:逐项 computed 颜色与几何记录;三种 harness 色、35 个模型色、35 个柱体分段与 2px 圆角均已核对。 +- `pi-manifest.json`:证据采集时来源 commit `3a8f6cce2`、源文件 git blob 与各证据文件校验值。修复轮(2026-09-09)复核这四个源文件 blob 与 PR head `377baf24de` 仍逐项一致;本轮修复仅改本文档,不改运行时代码,证据继续对应最终提交版本。 +- 初版的 `light-tables.png`、`dark-tables.png`、`dark-models-tail.png`、`light.json`、`dark.json`、`manifest.json` 与 harness 身份色版的 `brand-*` 文件保留作历史对照。 + +尚未上传 PR 附件:按治理合同 §6,栅格证据须由人经 PR 评论附件上传(`gh` CLI 与 API 不能传图),上传后把该评论链接回写本索引作为稳定入口。设计师视觉验收待完成,AI 采集与核对不构成批准;截图中的模型名与数值均为测试数据。 diff --git a/docs/design-rules/DESIGN.md b/docs/design-rules/DESIGN.md index 218adb7bbd7..e1ff5c13d0f 100644 --- a/docs/design-rules/DESIGN.md +++ b/docs/design-rules/DESIGN.md @@ -90,7 +90,7 @@ The grayscale rule is near-absolute. The following are the **only** sanctioned n > **Additional narrowly-scoped exceptions** (documented in their respective component specs, do NOT generalize as system semantic colors): > > - **Toast Info / Success / Warning / Error** — `#417CDD` / `#2AAE5B` / `#F3A115` / `#D91F37`(finalized 2026-07-17; Toast exemption lifted) — used ONLY on the 16×16 lucide icon inside Toast pill notifications. The pill body (background, text, border, close icon) remains strictly grayscale. Info blue #417CDD equals the focus-ring / Auto Approval value (originally #3B82F6, added 2026-07-14, now finalized); success/warning/error equal the global status colors (done green / status error / warning foreground). -> - **Usage History chart color (owner-approved 2026-09-08).** The registered usage heatmap uses a blue intensity scale. Daily token bars and their corresponding model-table swatches/share marks use five owner-refined reference hues through `--usage-model-1` … `--usage-model-5`; unlisted models stay neutral. `--usage-heatmap-high` supplies the high end of the heatmap. The heatmap retains its process-blue alias; model roles use the owner-provided reference palette, registered in colors.ts, without inheriting process/status semantics. Agent/task marks, text and control frames stay neutral. See [the component specification](./usage-history-charts.md); colors.ts remains the value source. +> - **Usage History category color (owner-approved 2026-09-08, extended 2026-09-09).** The registered usage heatmap uses a blue intensity scale. Daily token bars and their corresponding model-table swatches/share marks use `--usage-model-1` … `--usage-model-5` as the seed palette; additional models receive theme-derived hues without a top-five cutoff. All model identities in the full history are included, even when absent from the last 30 days. Agent/harness swatches, share marks and the stacked share strip reuse the existing engine identity tokens: `--engine-badge-cc` (terracotta orange), `--engine-badge-codex` (blue), and `--usage-model-1` (owner-chosen teal for pi, to distinguish it from Codex blue). Claude/Codex retain their cross-mode fixed-value contract; pi follows the existing Light/Dark teal values. `--usage-heatmap-high` supplies the high end of the heatmap. The heatmap retains its process-blue alias; category colors do not inherit process/status semantics. Task marks, text and control frames stay neutral. See [the component specification](./usage-history-charts.md); colors.ts remains the seed-value source, with runtime derivation under governance §3.4. > - **Resource Usage Process Categories** — the Resource Usage table may color only its 14px process-type glyphs so dense rows can be scanned by category (user-requested 2026-08-06). The six category pairs are task Agent blue `#2563EB / #60A5FA`, Agent service purple `#7C3AED / #A78BFA`, main-process pink `#DB2777 / #F472B6`, renderer cyan `#0891B2 / #22D3EE`, GPU amber `#D97706 / #F59E0B`, and utility green `#059669 / #34D399` (Light / Dark). These colors encode process category, **not health or status**; row backgrounds, labels, metrics, selection and actions remain on the neutral system. Direct use is confined to `resource-usage` process glyphs; the explicitly registered Usage History heatmap alias above may also reference the task-blue value. No other table or process UI inherits this permission. Tokens: `--process-agent-task-icon`, `--process-agent-service-icon`, `--process-main-icon`, `--process-renderer-icon`, `--process-gpu-icon`, `--process-utility-icon`. > - **Bot Avatar Hues** — the Bots feature may fill a Bot's round avatar with one of nine registered tints so a list of persistent assistants can be told apart at a glance (registered 2026-08-17). The hue encodes **which Bot this is** — an identity cue like the file-type badges above, never health, status, or channel. Light mode uses soft tints, Dark mode the matching deep tints; the emoji and the initial-letter fallback (always `--text-primary`) stay legible in both. Tokens: `--bot-avatar-red-bg` / `-orange-` / `-amber-` / `-green-` / `-teal-` / `-blue-` / `-violet-` / `-pink-` / `-graphite-bg` (the last is the neutral step, and is where legacy `graphite` avatar data lands). Scope is strictly the Bot avatar fill in `features/bots` — do not reuse these tints for status dots, badges, row backgrounds, or any other surface. External theme import never touches them (the import template is allow-list only), so Bot identity colors do not drift between themes. > - **Bot Unread Badge** — the Bots sidebar may paint its unread pill and its pending-todo dot in information blue `#417CDD` with white text (registered 2026-08-19). The color encodes **IM unread semantics** ("how much have I not seen"), the one signal every chat list has taught users to read by color alone; it is not a CTA and not a health/status tone. The pill was previously the inverse-CTA fill, which on the selected row's light-gray pill turned into two high-contrast marks competing for the same glance. Same value both modes — an unread count means the same thing in Light and Dark, and the value is already the registered focus-ring / Auto Approval / Toast-info blue. The foreground is a standalone white: `--accent-pure-cta-fg` flips to black in Dark and cannot be borrowed. Tokens: `--bot-unread-bg` / `--bot-unread-fg`. Scope is strictly the teammate-list unread badge and the pending-todo dot in `features/bots/BotsSidebar.tsx` — do not reuse it for other badges, status dots, row backgrounds, or any other surface. @@ -328,7 +328,7 @@ Reference implementation: `apps/desktop/src/renderer/components/ui/confirm-dialo ### Usage History Charts -The usage heatmap, daily-token bars and their separate interaction indicators follow [the Usage History component specification](./usage-history-charts.md). Their 2px data-mark geometry is set by the §5 registered data-mark members (`usage-heatmap-day` / `usage-token-bar`); the bounded hover/focus/selection emphasis is registered with those members in §5 (interaction constraints), and the five model category hues plus the heatmap blue are the §2 Usage History chart color registration. Date filtering enters through the charts themselves (cell / bar hit targets) plus the range selector on the page — the formerly drafted single-day date form is not part of this entry, and the component spec records the unresolved target-size ruling separately. +The usage heatmap, daily-token bars and their separate interaction indicators follow [the Usage History component specification](./usage-history-charts.md). Their 2px data-mark geometry is set by the §5 registered data-mark members (`usage-heatmap-day` / `usage-token-bar`); the bounded hover/focus/selection emphasis is registered with those members in §5 (interaction constraints), and the extensible model palette, fixed Agent/harness colors and heatmap blue are the §2 Usage History color registration. Date filtering enters through the charts themselves (cell / bar hit targets) plus the range selector on the page — the formerly drafted single-day date form is not part of this entry, and the component spec records the unresolved target-size ruling separately. ## 5. Layout Principles diff --git a/docs/design-rules/design-decision-log.md b/docs/design-rules/design-decision-log.md index a5767d0b420..922c659610f 100644 --- a/docs/design-rules/design-decision-log.md +++ b/docs/design-rules/design-decision-log.md @@ -12,6 +12,12 @@ ## 2026-09 +- **09-09** **用量历史 pi 标记改为青绿**——所有者试用后认为 Codex 蓝色与 pi 紫色不易区分,指定 pi 改用青绿。仅用量历史 harness 表的 pi 三处标记改为 `--usage-model-1`,随 Light/Dark 使用已有青绿值;Claude 陶土橙和 Codex 蓝色保留。替代本日上一条 pi 紫色选择,不修改全局 `--engine-badge-pi` 或模型配色。 + +- **09-09** **用量历史 harness 改用已有引擎身份色**——所有者要求 Claude 品牌偏橙、Codex 蓝色,pi 可自选但不用红色。替代本日早先的青绿 / 靛蓝 / 琥珀方案:复用 `--engine-badge-cc` / `--engine-badge-codex` / `--engine-badge-pi`,分别为陶土橙 / 蓝 / 紫;三种标记位置同步,沿用这些身份 Token 的 Light/Dark 固定值合同。仅扩展这些既有 Token 在用量历史 Agent/harness 表中的消费范围,不改 Token 值、模型配色或其他功能。当前规则见 `usage-history-charts.md`。 + +- **09-09** **用量历史:Agent 分类上色,模型配色取消前五名上限**——所有者根据真实用量截图要求 Agent/harness 区块有颜色,且无论模型数量多少都能分配颜色。Agent 顶部占比条、行前色块和行内占比条固定对应青绿 / 靛蓝 / 琥珀;模型沿用现有五个主题色作为种子,为后续模型派生色相。完整历史模型统一登记,切换筛选不跳色,历史用量排名更新可重新分配;模型表与柱图片段保持一致,不再把第六名起的真实模型全部染灰。派生公式与当前范围见 `usage-history-charts.md`,数值来源仍为 colors.ts 的既有 Token,动态计算留在 `usageHistoryColors.ts`(治理合同 §3.4)。大量模型可能颜色近似,不承诺无限可辨识颜色;名称和 tooltip 保留。本次只优化分类颜色,不改日期筛选、几何、热力图蓝色、动效或统计口径,不扩张首页金额与任务表配色。本条为需求授权记录,不表示视觉验收已通过。 + - **09-08 DS-6 表单五项裁决(用户已采纳)**:输入焦点环按 soft/50% 实施,实际亮暗/错误/旧主题可辨识度仍须验收;保留 elevated 默认及有明确白面板用途的 ivory。普通确认复用标准按钮但保留中性反相/轮廓主次、排列、默认取消焦点和显式/typed 分支,不整体改为 CTA、不迁授权。必填/格式错误移到字段并定位首错,服务失败仍 Toast;真正保存时防重复并禁止关闭,成功关闭、失败恢复,独立连接测试/模型获取不被锁住。取代治理 §10 对这三组视觉项的 pending,以及 §4 对 ivory 暂留/焦点偏差和普通确认焦点的旧描述。业务规则、payload、凭证和配置语义不变。落点:`DESIGN.md §4`、治理 §10、真实 provider/MCP 表单;工程/证据/G2 状态分别见 [证据索引](../design-evidence/2026-09-08/ds6-forms.md),批准不等于验证通过。 - **09-08** **手机版操作菜单图标统一为 18 pt**——用户对照现有手机版菜单后确认「好的,统一一下,并且作为以后的规范」。消息、任务、文件操作菜单以 `iconSize.lg = 18`、`iconStroke.regular = 2` 为基线;原生菜单资源按 18 / 36 / 54 px 输出,保留桌面同名 Lucide 的 24-unit viewBox 与笔画几何。替代本次恢复图标时误选的 24 pt,以及尚未实施的 20 pt 建议。既有紧凑筛选/导航菜单的 16 档、工具栏/lightbox 的 20 档保留各自用途,不全局改尺寸。落点:`DESIGN.md §15.13 Iconography` 与消息菜单资源尺寸校验;本条记录规范裁决,不代表用户已验收最终画面。 diff --git a/docs/design-rules/design-inventory.md b/docs/design-rules/design-inventory.md index 0b1d53eb270..40cff083c0b 100644 --- a/docs/design-rules/design-inventory.md +++ b/docs/design-rules/design-inventory.md @@ -38,7 +38,7 @@ | `desktop.overlay.toast` | desktop | Toast | App 常驻 ToastContainer(用户可见出口,不展开业务逻辑) | Toast, ToastContainer | apps/desktop/src/renderer/components/ui/toast/Toast.tsx, apps/desktop/src/renderer/components/ui/toast/ToastContainer.tsx, apps/desktop/src/renderer/components/ui/toast/index.ts | 7 | 4 | 3 | | `desktop.plugins.app-main` | desktop | 插件主视图 | hash `/apps/:ghostId`(GhostMainViewFeatureLayout) | GhostMainViewFeatureLayout, GhostMainViewHost | apps/desktop/src/renderer/features/plugin/GhostMainViewFeatureLayout.tsx, apps/desktop/src/renderer/features/plugin/GhostMainViewHost.tsx | 0 | 0 | 0 | | `desktop.plugins.installed` | desktop | 已装插件 | hash `/plugins`(GhostPluginPage) | AddMarketplaceDialog, GhostPluginDetailView, GhostPluginPage, MarketPluginDetailView, MyPublishesSection, PluginManagementLayout, PluginScopePicker, UpdateAllDialog | apps/desktop/src/renderer/features/plugin/AddMarketplaceDialog.tsx, apps/desktop/src/renderer/features/plugin/GhostPluginDetailView.tsx, apps/desktop/src/renderer/features/plugin/GhostPluginPage.tsx, apps/desktop/src/renderer/features/plugin/MarketPluginDetailView.tsx, apps/desktop/src/renderer/features/plugin/MyPublishesSection.tsx, apps/desktop/src/renderer/features/plugin/PluginManagementLayout.tsx, apps/desktop/src/renderer/features/plugin/PluginScopePicker.tsx, apps/desktop/src/renderer/features/plugin/UpdateAllDialog.tsx, apps/desktop/src/renderer/features/plugin/plugin-motion.css | 49 | 0 | 82 | -| `desktop.settings` | desktop | 设置 | hash `/settings`(SettingsView;tab 含 general / personalization / providers / billing / usage / voice-input / im-bot / shortcuts / agent-island / import / remote-control / ghosts / builtin-tools / computer-use / help / about) | SettingsView | apps/desktop/src/renderer/components/settings/AboutSection.tsx, apps/desktop/src/renderer/components/settings/AccountDeletionSection.tsx, apps/desktop/src/renderer/components/settings/AddProviderWizard.tsx, apps/desktop/src/renderer/components/settings/AgentIslandSection.tsx, apps/desktop/src/renderer/components/settings/AgentResourceSection.tsx, apps/desktop/src/renderer/components/settings/AppearanceSection.tsx, apps/desktop/src/renderer/components/settings/AuxiliaryModelSection.tsx, apps/desktop/src/renderer/components/settings/BetaChannelCell.tsx, apps/desktop/src/renderer/components/settings/BrowserBackendSubsection.tsx, apps/desktop/src/renderer/components/settings/BrowserRealProfileSubsection.tsx, apps/desktop/src/renderer/components/settings/BuiltinToolsSection.tsx, apps/desktop/src/renderer/components/settings/ChatEmbeddingCell.tsx, apps/desktop/src/renderer/components/settings/ChipMetricsSection.tsx, apps/desktop/src/renderer/components/settings/CindyMakeSection.tsx, apps/desktop/src/renderer/components/settings/CollaborationSection.tsx, apps/desktop/src/renderer/components/settings/CompactionSection.tsx, apps/desktop/src/renderer/components/settings/ComposerSendShortcutSection.tsx, apps/desktop/src/renderer/components/settings/ComputerPermissionGuideWindow.tsx, apps/desktop/src/renderer/components/settings/ComputerPermissionRow.tsx, apps/desktop/src/renderer/components/settings/ComputerUseSection.tsx, apps/desktop/src/renderer/components/settings/CustomProviderDialog.tsx, apps/desktop/src/renderer/components/settings/CustomProviderRuntimeFillOverlay.tsx, apps/desktop/src/renderer/components/settings/DefaultOverrideControls.tsx, apps/desktop/src/renderer/components/settings/DingTalkBotSection.tsx, apps/desktop/src/renderer/components/settings/DiscordBotSection.tsx, apps/desktop/src/renderer/components/settings/DownloadMeter.tsx, apps/desktop/src/renderer/components/settings/ExperimentalSection.tsx, apps/desktop/src/renderer/components/settings/FeishuBotNotificationSection.tsx, apps/desktop/src/renderer/components/settings/FeishuBotSection.tsx, apps/desktop/src/renderer/components/settings/FontFamilyPicker.tsx, apps/desktop/src/renderer/components/settings/GenericGamepadLayout.tsx, apps/desktop/src/renderer/components/settings/GitSafetySection.tsx, apps/desktop/src/renderer/components/settings/HelpAssistantPanel.tsx, apps/desktop/src/renderer/components/settings/HelpSection.tsx, apps/desktop/src/renderer/components/settings/HelpThreadView.tsx, apps/desktop/src/renderer/components/settings/HookConnectionsSection.tsx, apps/desktop/src/renderer/components/settings/HookWorkspacePrefsEditor.tsx, apps/desktop/src/renderer/components/settings/ImBotSection.tsx, apps/desktop/src/renderer/components/settings/ImChannelSettingsCard.tsx, apps/desktop/src/renderer/components/settings/ImDefaultSettingsSection.tsx, apps/desktop/src/renderer/components/settings/ImLifecycleAnnouncementSection.tsx, apps/desktop/src/renderer/components/settings/InputDeviceConnectionStatus.tsx, apps/desktop/src/renderer/components/settings/JoyConGamepadLayout.tsx, apps/desktop/src/renderer/components/settings/KeyboardShortcutsSection.tsx, apps/desktop/src/renderer/components/settings/LanguageSection.tsx, apps/desktop/src/renderer/components/settings/LayoutResetControl.tsx, apps/desktop/src/renderer/components/settings/LinkOpenSection.tsx, apps/desktop/src/renderer/components/settings/LocalOllamaInstall.tsx, apps/desktop/src/renderer/components/settings/LocalPackagingTag.tsx, apps/desktop/src/renderer/components/settings/LogoutSection.tsx, apps/desktop/src/renderer/components/settings/LspBetaCell.tsx, apps/desktop/src/renderer/components/settings/McpServerDialog.tsx, apps/desktop/src/renderer/components/settings/McpServersSection.tsx, apps/desktop/src/renderer/components/settings/MemorySection.tsx, apps/desktop/src/renderer/components/settings/MessageNavRailCell.tsx, apps/desktop/src/renderer/components/settings/ModelAdvancedDrawer.tsx, apps/desktop/src/renderer/components/settings/ModelPriceOverrideDialog.tsx, apps/desktop/src/renderer/components/settings/MyDevicesPanel.tsx, apps/desktop/src/renderer/components/settings/NotificationSection.tsx, apps/desktop/src/renderer/components/settings/OAuthDeviceCodeCard.tsx, apps/desktop/src/renderer/components/settings/OllamaProviderDetail.tsx, apps/desktop/src/renderer/components/settings/PiPackagesSection.tsx, apps/desktop/src/renderer/components/settings/PlayStationGamepadLayout.tsx, apps/desktop/src/renderer/components/settings/ProfileEditDialog.tsx, apps/desktop/src/renderer/components/settings/ProvidersSection.tsx, apps/desktop/src/renderer/components/settings/RemoteControlSection.tsx, apps/desktop/src/renderer/components/settings/RemoteDesktopPermissions.tsx, apps/desktop/src/renderer/components/settings/RemoteDesktopSetting.tsx, apps/desktop/src/renderer/components/settings/RemoteHostDetail.tsx, apps/desktop/src/renderer/components/settings/RemoteSection.tsx, apps/desktop/src/renderer/components/settings/SessionImportSection.tsx, apps/desktop/src/renderer/components/settings/SessionRuntimeFallbackCell.tsx, apps/desktop/src/renderer/components/settings/SessionShareImportWizard.tsx, apps/desktop/src/renderer/components/settings/SettingsCatalogPanel.tsx, apps/desktop/src/renderer/components/settings/SettingsSegmentedControl.tsx, apps/desktop/src/renderer/components/settings/SettingsSidebarNav.tsx, apps/desktop/src/renderer/components/settings/SettingsTextInput.tsx, apps/desktop/src/renderer/components/settings/SettingsView.tsx, apps/desktop/src/renderer/components/settings/SilentEncryptedRetryCell.tsx, apps/desktop/src/renderer/components/settings/SshKeySetupDialog.tsx, apps/desktop/src/renderer/components/settings/StorageManagementCard.tsx, apps/desktop/src/renderer/components/settings/StreamFadeSection.tsx, apps/desktop/src/renderer/components/settings/SubagentModelSection.tsx, apps/desktop/src/renderer/components/settings/SwitchProGamepadLayout.tsx, apps/desktop/src/renderer/components/settings/TelegramBehaviorSettings.tsx, apps/desktop/src/renderer/components/settings/TelegramBotSection.tsx, apps/desktop/src/renderer/components/settings/TelegramRemoteDevices.tsx, apps/desktop/src/renderer/components/settings/TerminalShellSection.tsx, apps/desktop/src/renderer/components/settings/TipsSection.tsx, apps/desktop/src/renderer/components/settings/UnifiedModelList.tsx, apps/desktop/src/renderer/components/settings/UserProfileCard.tsx, apps/desktop/src/renderer/components/settings/UserPromptSection.tsx, apps/desktop/src/renderer/components/settings/VisionBridgeSection.tsx, apps/desktop/src/renderer/components/settings/VoiceInputSection.tsx, apps/desktop/src/renderer/components/settings/WechatBotSection.tsx, apps/desktop/src/renderer/components/settings/WecomBotSection.tsx, apps/desktop/src/renderer/components/settings/WindowBehaviorSection.tsx, apps/desktop/src/renderer/components/settings/WorkLouderCodexKeyboardLayout.tsx, apps/desktop/src/renderer/components/settings/WorkLouderCodexKeycapGlyphs.tsx, apps/desktop/src/renderer/components/settings/WorkLouderCodexSettings.tsx, apps/desktop/src/renderer/components/settings/XUsageGuide.tsx, apps/desktop/src/renderer/components/settings/XboxGamepadLayout.tsx, apps/desktop/src/renderer/components/settings/XboxGamepadSettings.tsx, apps/desktop/src/renderer/components/settings/androidStatusPresentation.ts, apps/desktop/src/renderer/components/settings/billingVisibility.ts, apps/desktop/src/renderer/components/settings/browserOpenForLoginError.ts, apps/desktop/src/renderer/components/settings/computerPermissionFlow.ts, apps/desktop/src/renderer/components/settings/contacts/ContactDetailPane.tsx, apps/desktop/src/renderer/components/settings/contacts/ContactsImportDialog.tsx, apps/desktop/src/renderer/components/settings/contacts/ContactsListPane.tsx, apps/desktop/src/renderer/components/settings/contacts/ContactsManagerDialog.tsx, apps/desktop/src/renderer/components/settings/contacts/ContactsSection.tsx, apps/desktop/src/renderer/components/settings/contacts/startContactsAiSession.ts, apps/desktop/src/renderer/components/settings/dualSenseSilhouette.ts, apps/desktop/src/renderer/components/settings/feishuBotPresentation.ts, apps/desktop/src/renderer/components/settings/fontFamilyValue.ts, apps/desktop/src/renderer/components/settings/gamepadLayoutPrimitives.tsx, apps/desktop/src/renderer/components/settings/gamepadSilhouetteGeom.ts, apps/desktop/src/renderer/components/settings/hookWorkspacePrefsLogic.ts, apps/desktop/src/renderer/components/settings/imBotVisibility.ts, apps/desktop/src/renderer/components/settings/imDefaultSettingsLogic.ts, apps/desktop/src/renderer/components/settings/joyConSilhouette.ts, apps/desktop/src/renderer/components/settings/modelManagementPresentation.ts, apps/desktop/src/renderer/components/settings/modelManagementState.ts, apps/desktop/src/renderer/components/settings/myDevicesModel.ts, apps/desktop/src/renderer/components/settings/providerAssetModule.ts, apps/desktop/src/renderer/components/settings/realProfilePermissionGuide.ts, apps/desktop/src/renderer/components/settings/switchProSilhouette.ts, apps/desktop/src/renderer/components/settings/ultimateC1Silhouette.ts, apps/desktop/src/renderer/components/settings/usage/UsageBreakdownTables.tsx, apps/desktop/src/renderer/components/settings/usage/UsageHistorySection.tsx, apps/desktop/src/renderer/components/settings/usage/UsageStatRow.tsx, apps/desktop/src/renderer/components/settings/usage/UsageTaskTable.tsx, apps/desktop/src/renderer/components/settings/usage/UsageTokenBars.tsx, apps/desktop/src/renderer/components/settings/usage/formatUsagePercent.ts, apps/desktop/src/renderer/components/settings/usage/usageCharts.css, apps/desktop/src/renderer/components/settings/usage/usageHistoryColors.ts, apps/desktop/src/renderer/components/settings/usage/usageHistoryStats.ts, apps/desktop/src/renderer/components/settings/usageVisibility.ts, apps/desktop/src/renderer/components/settings/wizardRecommend.ts, apps/desktop/src/renderer/components/settings/workLouderCodexCommandCopy.ts, apps/desktop/src/renderer/components/settings/xboxSeriesSilhouette.ts, apps/desktop/src/renderer/styles/sortable.css | 151 | 121 | 724 | +| `desktop.settings` | desktop | 设置 | hash `/settings`(SettingsView;tab 含 general / personalization / providers / billing / usage / voice-input / im-bot / shortcuts / agent-island / import / remote-control / ghosts / builtin-tools / computer-use / help / about) | SettingsView | apps/desktop/src/renderer/components/settings/AboutSection.tsx, apps/desktop/src/renderer/components/settings/AccountDeletionSection.tsx, apps/desktop/src/renderer/components/settings/AddProviderWizard.tsx, apps/desktop/src/renderer/components/settings/AgentIslandSection.tsx, apps/desktop/src/renderer/components/settings/AgentResourceSection.tsx, apps/desktop/src/renderer/components/settings/AppearanceSection.tsx, apps/desktop/src/renderer/components/settings/AuxiliaryModelSection.tsx, apps/desktop/src/renderer/components/settings/BetaChannelCell.tsx, apps/desktop/src/renderer/components/settings/BrowserBackendSubsection.tsx, apps/desktop/src/renderer/components/settings/BrowserRealProfileSubsection.tsx, apps/desktop/src/renderer/components/settings/BuiltinToolsSection.tsx, apps/desktop/src/renderer/components/settings/ChatEmbeddingCell.tsx, apps/desktop/src/renderer/components/settings/ChipMetricsSection.tsx, apps/desktop/src/renderer/components/settings/CindyMakeSection.tsx, apps/desktop/src/renderer/components/settings/CollaborationSection.tsx, apps/desktop/src/renderer/components/settings/CompactionSection.tsx, apps/desktop/src/renderer/components/settings/ComposerSendShortcutSection.tsx, apps/desktop/src/renderer/components/settings/ComputerPermissionGuideWindow.tsx, apps/desktop/src/renderer/components/settings/ComputerPermissionRow.tsx, apps/desktop/src/renderer/components/settings/ComputerUseSection.tsx, apps/desktop/src/renderer/components/settings/CustomProviderDialog.tsx, apps/desktop/src/renderer/components/settings/CustomProviderRuntimeFillOverlay.tsx, apps/desktop/src/renderer/components/settings/DefaultOverrideControls.tsx, apps/desktop/src/renderer/components/settings/DingTalkBotSection.tsx, apps/desktop/src/renderer/components/settings/DiscordBotSection.tsx, apps/desktop/src/renderer/components/settings/DownloadMeter.tsx, apps/desktop/src/renderer/components/settings/ExperimentalSection.tsx, apps/desktop/src/renderer/components/settings/FeishuBotNotificationSection.tsx, apps/desktop/src/renderer/components/settings/FeishuBotSection.tsx, apps/desktop/src/renderer/components/settings/FontFamilyPicker.tsx, apps/desktop/src/renderer/components/settings/GenericGamepadLayout.tsx, apps/desktop/src/renderer/components/settings/GitSafetySection.tsx, apps/desktop/src/renderer/components/settings/HelpAssistantPanel.tsx, apps/desktop/src/renderer/components/settings/HelpSection.tsx, apps/desktop/src/renderer/components/settings/HelpThreadView.tsx, apps/desktop/src/renderer/components/settings/HookConnectionsSection.tsx, apps/desktop/src/renderer/components/settings/HookWorkspacePrefsEditor.tsx, apps/desktop/src/renderer/components/settings/ImBotSection.tsx, apps/desktop/src/renderer/components/settings/ImChannelSettingsCard.tsx, apps/desktop/src/renderer/components/settings/ImDefaultSettingsSection.tsx, apps/desktop/src/renderer/components/settings/ImLifecycleAnnouncementSection.tsx, apps/desktop/src/renderer/components/settings/InputDeviceConnectionStatus.tsx, apps/desktop/src/renderer/components/settings/JoyConGamepadLayout.tsx, apps/desktop/src/renderer/components/settings/KeyboardShortcutsSection.tsx, apps/desktop/src/renderer/components/settings/LanguageSection.tsx, apps/desktop/src/renderer/components/settings/LayoutResetControl.tsx, apps/desktop/src/renderer/components/settings/LinkOpenSection.tsx, apps/desktop/src/renderer/components/settings/LocalOllamaInstall.tsx, apps/desktop/src/renderer/components/settings/LocalPackagingTag.tsx, apps/desktop/src/renderer/components/settings/LogoutSection.tsx, apps/desktop/src/renderer/components/settings/LspBetaCell.tsx, apps/desktop/src/renderer/components/settings/McpServerDialog.tsx, apps/desktop/src/renderer/components/settings/McpServersSection.tsx, apps/desktop/src/renderer/components/settings/MemorySection.tsx, apps/desktop/src/renderer/components/settings/MessageNavRailCell.tsx, apps/desktop/src/renderer/components/settings/ModelAdvancedDrawer.tsx, apps/desktop/src/renderer/components/settings/ModelPriceOverrideDialog.tsx, apps/desktop/src/renderer/components/settings/MyDevicesPanel.tsx, apps/desktop/src/renderer/components/settings/NotificationSection.tsx, apps/desktop/src/renderer/components/settings/OAuthDeviceCodeCard.tsx, apps/desktop/src/renderer/components/settings/OllamaProviderDetail.tsx, apps/desktop/src/renderer/components/settings/PiPackagesSection.tsx, apps/desktop/src/renderer/components/settings/PlayStationGamepadLayout.tsx, apps/desktop/src/renderer/components/settings/ProfileEditDialog.tsx, apps/desktop/src/renderer/components/settings/ProvidersSection.tsx, apps/desktop/src/renderer/components/settings/RemoteControlSection.tsx, apps/desktop/src/renderer/components/settings/RemoteDesktopPermissions.tsx, apps/desktop/src/renderer/components/settings/RemoteDesktopSetting.tsx, apps/desktop/src/renderer/components/settings/RemoteHostDetail.tsx, apps/desktop/src/renderer/components/settings/RemoteSection.tsx, apps/desktop/src/renderer/components/settings/SessionImportSection.tsx, apps/desktop/src/renderer/components/settings/SessionRuntimeFallbackCell.tsx, apps/desktop/src/renderer/components/settings/SessionShareImportWizard.tsx, apps/desktop/src/renderer/components/settings/SettingsCatalogPanel.tsx, apps/desktop/src/renderer/components/settings/SettingsSegmentedControl.tsx, apps/desktop/src/renderer/components/settings/SettingsSidebarNav.tsx, apps/desktop/src/renderer/components/settings/SettingsTextInput.tsx, apps/desktop/src/renderer/components/settings/SettingsView.tsx, apps/desktop/src/renderer/components/settings/SilentEncryptedRetryCell.tsx, apps/desktop/src/renderer/components/settings/SshKeySetupDialog.tsx, apps/desktop/src/renderer/components/settings/StorageManagementCard.tsx, apps/desktop/src/renderer/components/settings/StreamFadeSection.tsx, apps/desktop/src/renderer/components/settings/SubagentModelSection.tsx, apps/desktop/src/renderer/components/settings/SwitchProGamepadLayout.tsx, apps/desktop/src/renderer/components/settings/TelegramBehaviorSettings.tsx, apps/desktop/src/renderer/components/settings/TelegramBotSection.tsx, apps/desktop/src/renderer/components/settings/TelegramRemoteDevices.tsx, apps/desktop/src/renderer/components/settings/TerminalShellSection.tsx, apps/desktop/src/renderer/components/settings/TipsSection.tsx, apps/desktop/src/renderer/components/settings/UnifiedModelList.tsx, apps/desktop/src/renderer/components/settings/UserProfileCard.tsx, apps/desktop/src/renderer/components/settings/UserPromptSection.tsx, apps/desktop/src/renderer/components/settings/VisionBridgeSection.tsx, apps/desktop/src/renderer/components/settings/VoiceInputSection.tsx, apps/desktop/src/renderer/components/settings/WechatBotSection.tsx, apps/desktop/src/renderer/components/settings/WecomBotSection.tsx, apps/desktop/src/renderer/components/settings/WindowBehaviorSection.tsx, apps/desktop/src/renderer/components/settings/WorkLouderCodexKeyboardLayout.tsx, apps/desktop/src/renderer/components/settings/WorkLouderCodexKeycapGlyphs.tsx, apps/desktop/src/renderer/components/settings/WorkLouderCodexSettings.tsx, apps/desktop/src/renderer/components/settings/XUsageGuide.tsx, apps/desktop/src/renderer/components/settings/XboxGamepadLayout.tsx, apps/desktop/src/renderer/components/settings/XboxGamepadSettings.tsx, apps/desktop/src/renderer/components/settings/androidStatusPresentation.ts, apps/desktop/src/renderer/components/settings/billingVisibility.ts, apps/desktop/src/renderer/components/settings/browserOpenForLoginError.ts, apps/desktop/src/renderer/components/settings/computerPermissionFlow.ts, apps/desktop/src/renderer/components/settings/contacts/ContactDetailPane.tsx, apps/desktop/src/renderer/components/settings/contacts/ContactsImportDialog.tsx, apps/desktop/src/renderer/components/settings/contacts/ContactsListPane.tsx, apps/desktop/src/renderer/components/settings/contacts/ContactsManagerDialog.tsx, apps/desktop/src/renderer/components/settings/contacts/ContactsSection.tsx, apps/desktop/src/renderer/components/settings/contacts/startContactsAiSession.ts, apps/desktop/src/renderer/components/settings/dualSenseSilhouette.ts, apps/desktop/src/renderer/components/settings/feishuBotPresentation.ts, apps/desktop/src/renderer/components/settings/fontFamilyValue.ts, apps/desktop/src/renderer/components/settings/gamepadLayoutPrimitives.tsx, apps/desktop/src/renderer/components/settings/gamepadSilhouetteGeom.ts, apps/desktop/src/renderer/components/settings/hookWorkspacePrefsLogic.ts, apps/desktop/src/renderer/components/settings/imBotVisibility.ts, apps/desktop/src/renderer/components/settings/imDefaultSettingsLogic.ts, apps/desktop/src/renderer/components/settings/joyConSilhouette.ts, apps/desktop/src/renderer/components/settings/modelManagementPresentation.ts, apps/desktop/src/renderer/components/settings/modelManagementState.ts, apps/desktop/src/renderer/components/settings/myDevicesModel.ts, apps/desktop/src/renderer/components/settings/providerAssetModule.ts, apps/desktop/src/renderer/components/settings/realProfilePermissionGuide.ts, apps/desktop/src/renderer/components/settings/switchProSilhouette.ts, apps/desktop/src/renderer/components/settings/ultimateC1Silhouette.ts, apps/desktop/src/renderer/components/settings/usage/UsageBreakdownTables.tsx, apps/desktop/src/renderer/components/settings/usage/UsageHistorySection.tsx, apps/desktop/src/renderer/components/settings/usage/UsageStatRow.tsx, apps/desktop/src/renderer/components/settings/usage/UsageTaskTable.tsx, apps/desktop/src/renderer/components/settings/usage/UsageTokenBars.tsx, apps/desktop/src/renderer/components/settings/usage/formatUsagePercent.ts, apps/desktop/src/renderer/components/settings/usage/usageCharts.css, apps/desktop/src/renderer/components/settings/usage/usageHistoryColors.ts, apps/desktop/src/renderer/components/settings/usage/usageHistoryStats.ts, apps/desktop/src/renderer/components/settings/usageVisibility.ts, apps/desktop/src/renderer/components/settings/wizardRecommend.ts, apps/desktop/src/renderer/components/settings/workLouderCodexCommandCopy.ts, apps/desktop/src/renderer/components/settings/xboxSeriesSilhouette.ts, apps/desktop/src/renderer/styles/sortable.css | 153 | 121 | 724 | | `desktop.shell.main-layout` | desktop | 主窗口壳(标题栏 / 左右栏 / 内容区) | main BrowserWindow → renderer/index.tsx → main-entry.tsx → App → MainLayout (`#/` 受保护壳) | ChromeActions, FeishuConflictDialogHost, GhostMediaLightboxHost, MainLayout, RightSidebar, RightSidebarShell, SessionShareImportWizard, Sidebar, UpdateNoticeDialog, WindowControls | apps/desktop/src/renderer/cindy-brain/GhostMediaLightboxHost.tsx, apps/desktop/src/renderer/components/UpdateNoticeDialog.tsx, apps/desktop/src/renderer/components/feishuBot/FeishuConflictDialog.tsx, apps/desktop/src/renderer/components/feishuBot/FeishuConflictDialogHost.tsx, apps/desktop/src/renderer/components/layout/BrowserWebviewPool.tsx, apps/desktop/src/renderer/components/layout/ChromeActions.tsx, apps/desktop/src/renderer/components/layout/ContentHeader.tsx, apps/desktop/src/renderer/components/layout/CredentialStoreBanner.tsx, apps/desktop/src/renderer/components/layout/FadeSwitcher.tsx, apps/desktop/src/renderer/components/layout/GhostPanelWindowLayout.tsx, apps/desktop/src/renderer/components/layout/GlobalDropImportListener.tsx, apps/desktop/src/renderer/components/layout/MainLayout.tsx, apps/desktop/src/renderer/components/layout/RightSidebar.tsx, apps/desktop/src/renderer/components/layout/RightSidebarDetach.tsx, apps/desktop/src/renderer/components/layout/RightSidebarMaximize.tsx, apps/desktop/src/renderer/components/layout/RightSidebarToggle.tsx, apps/desktop/src/renderer/components/layout/SidebarWindowLayout.tsx, apps/desktop/src/renderer/components/layout/chromeActionsGeometry.ts, apps/desktop/src/renderer/components/layout/chromeActionsLayout.ts, apps/desktop/src/renderer/components/layout/railChromeActions.ts, apps/desktop/src/renderer/components/layout/windowDrag.tsx, apps/desktop/src/renderer/components/settings/SessionShareImportWizard.tsx, apps/desktop/src/renderer/components/sidebar/AccountSwitcherDialog.tsx, apps/desktop/src/renderer/components/sidebar/AttentionDot.tsx, apps/desktop/src/renderer/components/sidebar/GhostMainViewNavEntries.tsx, apps/desktop/src/renderer/components/sidebar/MobileDownloadDialog.tsx, apps/desktop/src/renderer/components/sidebar/Sidebar.tsx, apps/desktop/src/renderer/components/sidebar/SidebarIconButton.tsx, apps/desktop/src/renderer/components/sidebar/SidebarTopNav.tsx, apps/desktop/src/renderer/components/sidebar/SortableList.tsx, apps/desktop/src/renderer/components/sidebar/UpdateBanner.tsx, apps/desktop/src/renderer/components/sidebar/UserInfoSection.tsx, apps/desktop/src/renderer/components/sidebar/VendorIcon.tsx, apps/desktop/src/renderer/components/sidebar/VendorReadinessBadge.tsx, apps/desktop/src/renderer/components/sidebar/WorktreeBadge.tsx, apps/desktop/src/renderer/components/title-bar/ChromeIconButton.tsx, apps/desktop/src/renderer/components/title-bar/MenuButton.tsx, apps/desktop/src/renderer/components/title-bar/WindowControls.tsx, apps/desktop/src/renderer/features/right-sidebar/AddTabDropdown.tsx, apps/desktop/src/renderer/features/right-sidebar/EmptyState.tsx, apps/desktop/src/renderer/features/right-sidebar/RightSidebarShell.tsx, apps/desktop/src/renderer/features/right-sidebar/TabBar.tsx, apps/desktop/src/renderer/features/right-sidebar/TabBodyErrorBoundary.tsx, apps/desktop/src/renderer/features/right-sidebar/hooks/useBrowserWebview.ts, apps/desktop/src/renderer/features/right-sidebar/hooks/useNativePopupSurface.ts, apps/desktop/src/renderer/features/right-sidebar/iosSimulatorPluginAvailability.ts, apps/desktop/src/renderer/features/right-sidebar/lib/browserPartition.ts, apps/desktop/src/renderer/features/right-sidebar/lib/browserWebviewPool.ts, apps/desktop/src/renderer/features/right-sidebar/lib/detachedSidebarRouting.ts, apps/desktop/src/renderer/features/right-sidebar/lib/executeSidebarCommand.ts, apps/desktop/src/renderer/features/right-sidebar/lib/iosSimulatorFocusBridge.ts, apps/desktop/src/renderer/features/right-sidebar/lib/lastTurnChangedFiles.ts, apps/desktop/src/renderer/features/right-sidebar/lib/nativePopupTabs.ts, apps/desktop/src/renderer/features/right-sidebar/lib/openBackgroundTasksTab.ts, apps/desktop/src/renderer/features/right-sidebar/lib/openInSidebarBrowser.ts, apps/desktop/src/renderer/features/right-sidebar/lib/openInSidebarFileBrowser.ts, apps/desktop/src/renderer/features/right-sidebar/lib/openRoutinesTab.ts, apps/desktop/src/renderer/features/right-sidebar/lib/openSubagentsTab.ts, apps/desktop/src/renderer/features/right-sidebar/lib/openTerminalShortcut.ts, apps/desktop/src/renderer/features/right-sidebar/lib/openTurnReview.ts, apps/desktop/src/renderer/features/right-sidebar/lib/popupRouter.ts, apps/desktop/src/renderer/features/right-sidebar/lib/popupTabs.ts, apps/desktop/src/renderer/features/right-sidebar/lib/rsbBrowserBridge.ts, apps/desktop/src/renderer/features/right-sidebar/lib/sidebarCommands.ts, apps/desktop/src/renderer/features/right-sidebar/lib/sidebarHostSession.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/background-tasks/BackgroundTasksBody.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/background-tasks/WorkflowAgentStrip.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/background-tasks/WorkflowProgressTree.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/background-tasks/chatTaskFocusIntent.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/background-tasks/index.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/background-tasks/listSessionTasks.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/background-tasks/workflowProgressModel.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/file-browser/FileBrowserBody.css, apps/desktop/src/renderer/features/right-sidebar/plugins/file-browser/FileBrowserBody.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/file-browser/dropExternalFile.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/file-browser/fileTreeImagePreview.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/file-browser/index.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/file-browser/useSessionScopedTreeWidth.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/index.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/ios-simulator/IOSSimulatorInstanceGrid.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/ios-simulator/IOSSimulatorTabBody.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/ios-simulator/index.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/ios-simulator/iosSimulatorH264Decoder.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/orca-workers/OrcaWorkersAttentionIcon.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/orca-workers/OrcaWorkersTabBody.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/orca-workers/actions.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/orca-workers/closeDecision.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/orca-workers/index.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/resource-usage/ResourceUsageBody.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/resource-usage/index.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/resource-usage/resource-usage.css, apps/desktop/src/renderer/features/right-sidebar/plugins/resource-usage/subscription.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/review/DiffViewer/ImageDiffPreview.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/review/DiffViewer/MarkdownDiffPreview.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/review/DiffViewer/PlainUnifiedDiff.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/review/DiffViewer/diffRows.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/review/DiffViewer/highlight.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/review/DiffViewer/inlineDiff.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/review/DiffViewer/useDiffHighlights.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/review/ReviewTabBody.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/review/diffExpansionPreference.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/review/fileTree.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/review/gitApplyCommand.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/review/index.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/review/markdownPreview.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/review/useLastTurnFilter.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/review/useReviewGitState.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/review/useReviewSource.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/routines/index.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/subagents/ConversationStream.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/subagents/DetailView.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/subagents/RunList.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/subagents/SubagentChrome.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/subagents/SubagentToolCard.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/subagents/SubagentsBody.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/subagents/index.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/subagents/subagentChangeFence.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/subagents/subagentConversation.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/subagents/subagentFormat.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/terminal/TerminalTabBody.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/terminal/index.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/terminal/lib/xtermPool.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/web-browser/BrowserChrome.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/web-browser/BrowserCommentPopover.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/web-browser/BrowserTabBody.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/web-browser/browserCommentEditorDraft.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/web-browser/index.tsx, apps/desktop/src/renderer/features/right-sidebar/plugins/web-browser/lib/parseOmnibox.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/web-browser/useBrowserComment.ts, apps/desktop/src/renderer/features/right-sidebar/plugins/web-browser/useLocalHtmlAutoReload.ts, apps/desktop/src/renderer/features/right-sidebar/registry.ts, apps/desktop/src/renderer/features/right-sidebar/store.ts, apps/desktop/src/renderer/features/right-sidebar/types.ts, apps/desktop/src/renderer/layout/LayoutRoot.tsx, apps/desktop/src/renderer/layout/PanelDragController.tsx, apps/desktop/src/renderer/layout/PanelDragPrototype.tsx, apps/desktop/src/renderer/layout/collapsePrefs.ts, apps/desktop/src/renderer/layout/layoutDevTools.ts, apps/desktop/src/renderer/layout/panePlacement.tsx, apps/desktop/src/renderer/layout/paneWidths.tsx, apps/desktop/src/renderer/layout/panelMaximize.tsx, apps/desktop/src/renderer/styles/globals.css | 145 | 72 | 288 | | `desktop.skillhub.local` | desktop | SkillHub 本地技能 | hash `/skillhub/local` 及详情 `/skillhub/local/:kind/global/:name`、`/skillhub/local/:kind/project/:projectHash/:name` | InstallTargetPicker, PluginManagementLayout, SkillhubDetailView, SkillhubFeatureLayout, SkillhubHomeView, SkillhubMarketPreviewPanel | apps/desktop/src/renderer/features/plugin/PluginManagementLayout.tsx, apps/desktop/src/renderer/features/skillhub/SkillhubDetailView.tsx, apps/desktop/src/renderer/features/skillhub/SkillhubFeatureLayout.tsx, apps/desktop/src/renderer/features/skillhub/SkillhubHomeView.tsx, apps/desktop/src/renderer/features/skillhub/SkillhubMarketPreviewPanel.tsx, apps/desktop/src/renderer/features/skillhub/components/InstallTargetPicker.tsx | 41 | 0 | 66 | | `desktop.skillhub.market` | desktop | SkillHub 市场 | hash `/skillhub/market`(SkillhubMarketListView) | InstallTargetPicker, MarketCard, MarketInfoEditDialog, SkillhubMarketListView, SkillhubMarketPreviewPanel, VisibilityEditorDialog | apps/desktop/src/renderer/features/skillhub/SkillhubMarketListView.tsx, apps/desktop/src/renderer/features/skillhub/SkillhubMarketPreviewPanel.tsx, apps/desktop/src/renderer/features/skillhub/components/HomeMarketCard.tsx, apps/desktop/src/renderer/features/skillhub/components/InstallTargetPicker.tsx, apps/desktop/src/renderer/features/skillhub/components/LocalSkillControls.tsx, apps/desktop/src/renderer/features/skillhub/components/MarketCard.tsx, apps/desktop/src/renderer/features/skillhub/components/MarketInfoEditDialog.tsx, apps/desktop/src/renderer/features/skillhub/components/MarketLocalSkills.tsx, apps/desktop/src/renderer/features/skillhub/components/MarketPreviewTree.tsx, apps/desktop/src/renderer/features/skillhub/components/PlatformTagSelector.tsx, apps/desktop/src/renderer/features/skillhub/components/SkillCategoryFilterBar.tsx, apps/desktop/src/renderer/features/skillhub/components/SkillIcon.tsx, apps/desktop/src/renderer/features/skillhub/components/SkillTagList.tsx, apps/desktop/src/renderer/features/skillhub/components/TeamScopePicker.tsx, apps/desktop/src/renderer/features/skillhub/components/VisibilityEditorDialog.tsx | 47 | 0 | 63 | @@ -151,3 +151,10 @@ Mobile 尚未展开顶层 screen,**待 DS-7 增量发现**;数值接管在 D | `desktop.window.sidebar` | unassigned | legacy | DESIGN.md §15 CINDY 皮肤族 | DS-8 提供适用数值;DS-9 按入口核对标准组件与呈现继承 | DS-9 核对实际消费者、局部 alias 与跨入口影响;保留现有保护合同,残余项登记理由、实际负责人及复查日期 | | `desktop.window.voice-dictionary-toast` | unassigned | legacy | — | DS-8 提供适用数值;DS-9 按入口核对标准组件与呈现继承 | DS-9 核对实际消费者、局部 alias 与跨入口影响;保留现有保护合同,残余项登记理由、实际负责人及复查日期 | | `desktop.window.voice-overlay` | unassigned | legacy | — | DS-8 提供适用数值;DS-9 按入口核对标准组件与呈现继承 | DS-9 核对实际消费者、局部 alias 与跨入口影响;保留现有保护合同,残余项登记理由、实际负责人及复查日期 | + +### Usage History category colors (2026-09-09) + +- Surface: `desktop.settings`; owner: kirozeng. +- All historical model identities share the category palette across table and daily bars; Agent/harness marks reuse `--engine-badge-cc`, `--engine-badge-codex` and `--usage-model-1` for pi teal (owner refinement, 2026-09-09). +- `usageHistoryColors.ts` derives OKLCH hues from existing `--usage-model-*` seeds at runtime (governance §3.4), outside static token migration. +- Geometry, hit testing and filtering retain their existing contracts. Light/Dark evidence and designer acceptance are recorded separately. diff --git a/docs/design-rules/usage-history-charts.md b/docs/design-rules/usage-history-charts.md index 6ed27eddea8..679f35a812e 100644 --- a/docs/design-rules/usage-history-charts.md +++ b/docs/design-rules/usage-history-charts.md @@ -7,7 +7,7 @@ | 可见角色 | 半径依据 | 尺寸与数据映射 | | --- | --- | --- | | 热力图日格 | §5 成员 `usage-heatmap-day` | 可见格 12×12px,行列间隔 3px。金额/Token 口径、分位色阶、日期窗口算法沿用组件原逻辑;静止形状和网格密度不随点击启用与否改变;09-08 登记的交互强调见下。滚动视口内预留 3px 外围空间,容纳边缘指示器。 | -| 每日 Token 柱 | §5 成员 `usage-token-bar` | 固定近 30 天、柱间隔 3px,均分可用绘图区宽度;不设每柱 24px 最小宽度或 807px 容器下限。非零高度按原比例计算、最低 3px;零值日保留 2px 高。堆叠顺序、tooltip 与纵轴刻度不变;分类颜色按下述 09-08 裁决更新。 | +| 每日 Token 柱 | §5 成员 `usage-token-bar` | 固定近 30 天、柱间隔 3px,均分可用绘图区宽度;不设每柱 24px 最小宽度或 807px 容器下限。非零高度按原比例计算、最低 3px;零值日保留 2px 高。总量、日期 tooltip 与纵轴刻度保留;模型分段按下述 09-09 裁决覆盖完整模型,tooltip 按实际模型列出。 | 柱体自身的裁剪采用同一登记处理。按钮的透明命中层不裁剪数据图元,不继承 pill 外形;键盘按钮语义、可访问日期/用量名称和 `aria-pressed` 保留。非交互渲染分支仍不创建按钮,其格子遵守同一成员登记。 @@ -26,11 +26,15 @@ 表头和统计标签使用 secondary 文字色,提高阅读清晰度。月份行高与纵轴宽度随字号缩放;表格沿用数字右对齐与等宽数字,长名称保留既有截断和完整名称提示。日期筛选、图表窗口、颜色和选中动效不由本次排版调整改变。 -## 配色(2026-09-08 所有者裁决) +## 配色(2026-09-09 所有者扩展裁决) -热力图用蓝色深浅表示用量,沿用四档强度映射,零值日保留 `--surface-chip`。高值色使用 `--usage-heatmap-high`。柱图前五个模型依次使用 `--usage-model-1` … `--usage-model-5`(青绿、珊瑚红、紫、靛蓝、琥珀),未列入 colorOrder 的模型统一中性灰;即便不足五个模型,“其它”也不占用某一模型的颜色。按所有者提供的参考图 3 微调为图表专用 Light/Dark 配色,取值只在 colors.ts 维护;替代本日早先的进程色别名方案,不修改原进程 Token/状态语义及用户主题。热力图蓝色别名保持不变。 +热力图继续使用蓝色四档强度映射,零值日为 `--surface-chip`,高值色为 `--usage-heatmap-high`。 -颜色对应完整图表窗口的模型顺序,与明细表同一模型的色块、占比短条保持一致,不因日期筛选后的排序变化而跳色。Agent 与任务排行仍用既有中性色;本裁决不登记新形状、不扩张其几何用途。颜色表示类别,不表示“成功/警告”。Tooltip 和带名称的明细表仍提供非颜色识别渠道;分类颜色不能保证仅靠颜色满足所有色觉条件。 +模型表的每个模型及其柱图片段均有分类颜色,不再只给前五名上色,也不把其余真实模型合并成灰色“其它”。身份仍为 agentKind × 完整模型名,不合并展示名相似的模型。完整历史按 token 排序建立统一 colorOrder;切换日期范围、选择单日、表格排序变化均不重新分配颜色。只在历史日期出现、最近 30 天未使用的模型也包括在内。刷新后完整历史的用量排名变化可以改变配色,这不是永久的模型品牌色。 + +前五个颜色继续使用 `--usage-model-1` … `--usage-model-5`(青绿、珊瑚红、紫、靛蓝、琥珀)。此后每组五个从对应 Token 派生 OKLCH 色相:第 g 组旋转 `(g × 137.50776405) mod 360` 度,保留源色的亮度和色度,由浏览器处理显示色域。值随活动主题解析,不写死 Light/Dark 色号,也不改写用户主题。派生算法唯一实现在 `usageHistoryColors.ts`,属于治理合同 §3.4 的运行时派生值;不为每个模型建立 Token。没有数量上限导致的灰色回退;只有调用方缺失登记的异常兜底仍为中性“其它”。大量模型可能有近似色,颜色不承诺无限的感知可区分性,名称、harness 标签与 tooltip 继续辅助识别。 + +Agent/harness 表复用现有引擎身份色(09-09 所有者再次反馈):claude-code → `--engine-badge-cc`(陶土橙),codex → `--engine-badge-codex`(蓝),pi → `--usage-model-1`(青绿,试用后替代不易与蓝色区分的紫色)。顶部占比条、行前色块和行内占比短条三处一致,不随 Agent 用量排名变化。Claude / Codex 与应用内既有品牌标识一致;pi 按所有者选择使用青绿色,不声称是官方品牌色。Claude / Codex Token 按既有合同跨 Light/Dark 固定值,pi 使用既有青绿的 Light/Dark 值;均表示身份而非成功/警告状态。任务排行、首页金额看板、文字和控件仍沿用既有中性色;本裁决不登记新形状、不改变几何、动效、日期功能或数值口径。 ## 点击、焦点与选中(2026-09-08 更新)