mirror of
https://github.com/zyycn/codex-proxy-rs.git
synced 2026-11-06 16:06:08 +00:00
refactor(theme): standardize container color roles
Rename primary, semantic and preset background roles to container and their paired foreground roles to on-container across derivation, CSS variables, utilities, consumers and documentation. Preserve existing palette values and contrast relationships while making text on neutral surfaces distinct from content inside semantic containers. Validated with token equivalence across presets and custom seeds, frontend typecheck, lint and production build. Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
+22
-17
@@ -29,8 +29,9 @@ Token 分层与十阶色板,再按项目的明暗角色规则派生;中性 S
|
||||
- **无边设计**:默认依靠表面色差、间距和轻阴影表达层级;边框只用于焦点、错误和必要分隔。
|
||||
- **运行时可定制**:用户输入在浏览器中实时派生,因此使用 CSS Variables,不使用构建时 SCSS 变量。
|
||||
- **单一事实源**:Store 只保存最小配置,所有 Map、Alias 与未覆盖的 Component Token 均由纯函数生成。
|
||||
- **行为透明**:任意自定义 Seed 都进入统一算法,页面只选择角色。功能色文字同时依据 Container 和自身背景三态做
|
||||
对比度保护,不使用固定色相的文字锚点覆盖自定义 Seed;交互仍尊重 `prefers-reduced-motion`。
|
||||
- **行为透明**:任意自定义 Seed 都进入统一算法,页面只选择角色。功能色 `text` 与 `on-container` 分别依据中性
|
||||
Surface 和自身 Container 三态做对比度保护,不使用固定色相的文字锚点覆盖自定义 Seed;交互仍尊重
|
||||
`prefers-reduced-motion`。
|
||||
|
||||
## 架构概览
|
||||
|
||||
@@ -120,20 +121,23 @@ interface ThemeCustomization {
|
||||
| 功能色 Base | P6 | P6 |
|
||||
| Primary / 功能色 Hover、Active | P5、P7 | P8、P6 |
|
||||
| Primary Text Hover、Text、Text Active | P5、P6、P7 | P8、P7、P6 |
|
||||
| 功能色 Text Hover、Text、Text Active | P5、P6、P7 | P8、P9、P10 |
|
||||
| Primary On Container | P6 | P7 |
|
||||
| 功能色 Text Hover、Text、Text Active | P5、P7、P7 | P8、P7、P10 |
|
||||
| 功能色 On Container | P6 | P9 |
|
||||
|
||||
弱背景由 Container 与对应 Base/Hover/Active 按 recipe 权重混合;描边同样混色,再相对 Container 保证 3:1。
|
||||
功能色 Hover/Active 相对 Container 保证 3:1,Base 不做该校正。每种文字状态都同时检查 Container、Background、
|
||||
Background Hover、Background Active,取最弱配对并校正到至少 4.5:1,兼容消费者在背景切换时保留同一文字角色。
|
||||
弱语义 Container 由中性 Container 与对应 Base/Hover/Active 按 recipe 权重混合;描边同样混色,再相对中性
|
||||
Container 保证 3:1。功能色 Hover/Active 相对中性 Container 保证 3:1,Base 不做该校正。`text` 依据页面、
|
||||
容器、浮层和交互填充等中性 Surface 校正;`on-container` 单独依据语义 Container 的默认、Hover、Active 三态
|
||||
校正到至少 4.5:1,避免为了彩色底对比度而削弱中性表面上的颜色辨识度。
|
||||
`ensureContrast` 以每步 1% 的黑/白混合寻找满足所有配对的颜色;不能满足全部约束时返回最弱配对表现最好的候选,
|
||||
因此不承诺任意互相矛盾的自定义前景/背景组合都能达标。
|
||||
|
||||
分类、图表与数据强调继续使用 Ant Design Preset Color 的角色结构。Blue、Green、Orange、Red 分别复用
|
||||
`colorInfo`、`colorSuccess`、`colorWarning`、`colorError`,保证通用彩色与可编辑语义 Seed 同源;没有语义对应的
|
||||
Cyan、Purple 从 `@ant-design/colors` 的 `presetPrimaryColors` 取得 Seed。
|
||||
Preset 的实心色使用 P6,弱背景、较强背景与边界按 recipe 权重混合;浅色文字取 P7,深色取 P8 并保留 HSL
|
||||
最低明度 0.7。`text` 相对 Container 校正,`text-on-bg` 同时相对弱背景和较强背景校正到 4.5:1;彩色底上的文字
|
||||
应使用 `text-on-bg`,普通数值与标签不直接使用 `solid`。
|
||||
Preset 的实心色使用 P6,普通 Container、Strong Container 与边界按 recipe 权重混合;浅色文字取 P7,深色取 P8
|
||||
并保留 HSL 最低明度 0.7。`text` 相对中性 Container 校正,`on-container` 同时相对普通和 Strong Container
|
||||
校正到 4.5:1;彩色容器里的文字与图标应使用 `on-container`,普通数值与标签不直接使用 `solid`。
|
||||
|
||||
背景与文本 Seed 进入独立的 Surface Map,生成:
|
||||
|
||||
@@ -151,11 +155,12 @@ Input、阴影与其他 Component Token 继续从 Surface、Primary 和 Semantic
|
||||
正常文字同时检查 Layout、Container、Elevated、文字交互背景与三级 Fill。正文、标题和 Secondary 至少 7:1,
|
||||
Tertiary 至少 5.5:1,Quaternary 至少 4.5:1;这些是相对全部上述表面的最低目标,对 Container 的实测比值通常更高。
|
||||
Disabled 保留独立的弱化颜色,不承担正常信息。输入背景主要从这些 Surface 混色派生,placeholder 继续消费
|
||||
Quaternary;主按钮白字与功能色文字遵循各自的背景配对规则。
|
||||
Quaternary;主按钮白字与功能色文字遵循各自的容器配对规则。
|
||||
|
||||
### Alias Token
|
||||
|
||||
Alias 按视觉角色命名,使用 `--cp-` 命名空间,并优先对齐 Ant Design 的公开 Token 词汇:
|
||||
Alias 按视觉角色命名,使用 `--cp-` 命名空间;整体分层对齐 Ant Design,并以 Container / On Container 表达成对的
|
||||
语义表面与前景:
|
||||
|
||||
| 角色 | CSS Token | 主要消费者 |
|
||||
| --- | --- | --- |
|
||||
@@ -167,7 +172,7 @@ Alias 按视觉角色命名,使用 `--cp-` 命名空间,并优先对齐 Ant
|
||||
| 选择 | `--cp-control-item-bg-active*` | Segmented、Select 和选择控件 |
|
||||
| 焦点 | `--cp-control-outline` | 键盘焦点和输入反馈 |
|
||||
| 功能色 | `--cp-color-info / success / warning / error-*` | 系统反馈与状态 |
|
||||
| 预设彩色 | `--cp-color-{blue,cyan,green,orange,purple,red}-{bg,bg-strong,border,solid,text,text-on-bg}` | 分类标签与数据强调 |
|
||||
| 预设彩色 | `--cp-color-{blue,cyan,green,orange,purple,red}-{container,container-strong,border,solid,text,on-container}` | 分类标签与数据强调 |
|
||||
|
||||
Alias 不在页面中追加修色;中性与彩色文字在派生层执行各自的背景配对约束。Component
|
||||
Token 直接覆盖时不会自动重算同组件的其他状态;需要保持梯度关系时应修改 Seed,而不是逐个覆盖 Map Token。
|
||||
@@ -201,9 +206,9 @@ Theme Editor 只开放真正由对应组件消费的 Component Token。全局 Al
|
||||
|
||||
### 通用颜色消费
|
||||
|
||||
主题层禁止声明账号套餐、推理类型或具体页面名称。业务组件只能选择通用 Preset Color 角色,例如 Cyan 弱背景、
|
||||
Purple 强背景或 Purple 实心色;同一组颜色仍由运行时色板统一派生。`styles/tokens.css` 只保留白色、透明色与
|
||||
作用域 `color-scheme`,不保存可换肤值或业务标识色。账户活动热力图以 Success Background 为起点、Success Solid
|
||||
主题层禁止声明账号套餐、推理类型或具体页面名称。业务组件只能选择通用 Preset Color 角色,例如 Cyan Container、
|
||||
Purple Strong Container 或 Purple Solid;同一组颜色仍由运行时色板统一派生。`styles/tokens.css` 只保留白色、透明色与
|
||||
作用域 `color-scheme`,不保存可换肤值或业务标识色。账户活动热力图以 Success Container 为起点、Success Solid
|
||||
为终点,按 22% / 46% / 70% 生成中间密度;浅色与暗色使用同一规则,因此不会在浅色背景退化成近白方块。
|
||||
图表数据系列也只引用通用 Preset Color Token。
|
||||
|
||||
@@ -311,7 +316,7 @@ Alias 算法,避免主题配置逐渐退化成一份无法维护的完整 CSS
|
||||
|
||||
- 全局 Token:`--cp-color-bg-container`、`--cp-font-size`。
|
||||
- Component Token:`--cp-table-row-hover-bg`、`--cp-input-active-shadow`。
|
||||
- Preset Color Token:`--cp-color-purple-bg-strong`、`--cp-color-cyan-text-on-bg`。
|
||||
- Preset Color Token:`--cp-color-purple-container-strong`、`--cp-color-cyan-on-container`。
|
||||
- 主题层禁止业务域命名;套餐、模型或页面只能消费通用 Alias、Preset 或 Component Token。
|
||||
- Map 与 Component 字段由 `theme/core/tokens.ts` 统一生成 CSS Token;禁止在解析器中再写平行的逐项映射表。
|
||||
- 禁止继续引入 `accent`、`soft`、`current`、`subtle` 等与现有角色重叠的平行词汇。
|
||||
@@ -331,7 +336,7 @@ Alias 算法,避免主题配置逐渐退化成一份无法维护的完整 CSS
|
||||
|
||||
`@theme inline` 只注册 Tailwind 名称,不保存主题值。注册表按基础、排版、颜色、圆角、阴影、间距与尺寸排序;
|
||||
颜色再按基元、表面、主色与链接、语义、预设、数据、组件分组。
|
||||
Preset 家族按字母排序,每个家族固定使用 `bg → bg-strong → border → solid → text → text-on-bg`。主题值由
|
||||
Preset 家族按字母排序,每个家族固定使用 `container → container-strong → border → solid → text → on-container`。主题值由
|
||||
`initializeTheme()` 在 Vue 挂载前动态生成并提交,不增加 `theme:generate`、`theme:check` 或构建期快照。
|
||||
|
||||
全局元素基线统一放进 `styles/base.css` 的 `@layer base`,确保组件 utility 可以按 Tailwind 层级正常覆盖;可复用的
|
||||
|
||||
@@ -35,7 +35,7 @@ const variantClasses: Record<ButtonVariant, string> = {
|
||||
'bg-cp-fill-tertiary text-cp-text shadow-cp-tertiary hover:bg-cp-bg-text-active active:bg-cp-bg-text-active',
|
||||
ghost:
|
||||
'bg-transparent text-cp-text-secondary shadow-none hover:bg-cp-fill-quaternary hover:text-cp-text active:bg-cp-fill-tertiary',
|
||||
destructive: 'bg-cp-error-bg text-cp-error-text-on-bg shadow-none hover:bg-cp-error-bg-hover active:bg-cp-error-bg-active',
|
||||
destructive: 'bg-cp-error-container text-cp-error-on-container shadow-none hover:bg-cp-error-container-hover active:bg-cp-error-container-active',
|
||||
}
|
||||
|
||||
const sizeClasses: Record<ButtonSize, string> = {
|
||||
|
||||
@@ -36,10 +36,10 @@ const variantClasses: Record<IconButtonVariant, string> = {
|
||||
secondary:
|
||||
'bg-cp-bg-container text-cp-text-secondary shadow-cp-tertiary hover:bg-cp-bg-text-hover hover:text-cp-text active:bg-cp-bg-text-active',
|
||||
success:
|
||||
'bg-cp-success-bg text-cp-success-text-on-bg shadow-none hover:bg-cp-success-bg-hover active:bg-cp-success-bg-active',
|
||||
'bg-cp-success-container text-cp-success-on-container shadow-none hover:bg-cp-success-container-hover active:bg-cp-success-container-active',
|
||||
ghost:
|
||||
'bg-transparent text-cp-text-secondary shadow-none hover:bg-cp-fill-quaternary hover:text-cp-text active:bg-cp-fill-tertiary',
|
||||
destructive: 'bg-transparent text-cp-error-text shadow-none hover:bg-cp-error-bg-hover hover:text-cp-error-text-on-bg active:bg-cp-error-bg-active active:text-cp-error-text-on-bg',
|
||||
destructive: 'bg-transparent text-cp-error-text shadow-none hover:bg-cp-error-container-hover hover:text-cp-error-on-container active:bg-cp-error-container-active active:text-cp-error-on-container',
|
||||
}
|
||||
|
||||
const sizeClasses: Record<IconButtonSize, string> = {
|
||||
|
||||
@@ -26,7 +26,7 @@ const classes = computed(() => [
|
||||
'focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-cp-control-outline',
|
||||
'disabled:cursor-not-allowed disabled:text-cp-text-disabled',
|
||||
props.tone === 'destructive'
|
||||
? 'text-cp-error-text hover:bg-cp-error-bg-hover hover:text-cp-error-text-on-bg'
|
||||
? 'text-cp-error-text hover:bg-cp-error-container-hover hover:text-cp-error-on-container'
|
||||
: 'text-cp-text hover:bg-cp-bg-text-hover',
|
||||
])
|
||||
</script>
|
||||
|
||||
@@ -78,19 +78,19 @@ const toneClasses: Record<ModalTone, { iconBg: string, icon: string }> = {
|
||||
icon: 'text-cp-text-secondary',
|
||||
},
|
||||
info: {
|
||||
iconBg: 'bg-cp-info-bg',
|
||||
iconBg: 'bg-cp-info-container',
|
||||
icon: 'text-cp-info',
|
||||
},
|
||||
warning: {
|
||||
iconBg: 'bg-cp-warning-bg',
|
||||
iconBg: 'bg-cp-warning-container',
|
||||
icon: 'text-cp-warning',
|
||||
},
|
||||
danger: {
|
||||
iconBg: 'bg-cp-error-bg',
|
||||
iconBg: 'bg-cp-error-container',
|
||||
icon: 'text-cp-error',
|
||||
},
|
||||
success: {
|
||||
iconBg: 'bg-cp-success-bg',
|
||||
iconBg: 'bg-cp-success-container',
|
||||
icon: 'text-cp-success',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -118,11 +118,11 @@ function updateModel(event: Event) {
|
||||
}
|
||||
|
||||
.base-range:focus-visible::-webkit-slider-thumb {
|
||||
box-shadow: 0 0 0 3px var(--cp-color-primary-bg-hover);
|
||||
box-shadow: 0 0 0 3px var(--cp-color-primary-container-hover);
|
||||
}
|
||||
|
||||
.base-range:focus-visible::-moz-range-thumb {
|
||||
box-shadow: 0 0 0 3px var(--cp-color-primary-bg-hover);
|
||||
box-shadow: 0 0 0 3px var(--cp-color-primary-container-hover);
|
||||
}
|
||||
|
||||
.base-range:disabled {
|
||||
|
||||
@@ -98,7 +98,7 @@ const triggerClasses = computed(() => [
|
||||
props.disabled
|
||||
? 'cursor-not-allowed bg-cp-bg-container-disabled text-cp-text-disabled shadow-none'
|
||||
: invalid.value
|
||||
? 'cursor-pointer bg-(--cp-input-error-active-bg) text-cp-error-text-on-bg shadow-cp-input-error-active'
|
||||
? 'cursor-pointer bg-(--cp-input-error-active-bg) text-cp-error-on-container shadow-cp-input-error-active'
|
||||
: open.value
|
||||
? 'cursor-pointer bg-(--cp-input-active-bg) text-cp-text shadow-cp-input-active'
|
||||
: [
|
||||
|
||||
@@ -20,19 +20,19 @@ const titleMap = {
|
||||
|
||||
const colorClasses = {
|
||||
success: {
|
||||
iconBg: 'bg-cp-success-bg',
|
||||
iconBg: 'bg-cp-success-container',
|
||||
icon: 'text-cp-success',
|
||||
},
|
||||
error: {
|
||||
iconBg: 'bg-cp-error-bg',
|
||||
iconBg: 'bg-cp-error-container',
|
||||
icon: 'text-cp-error',
|
||||
},
|
||||
warning: {
|
||||
iconBg: 'bg-cp-warning-bg',
|
||||
iconBg: 'bg-cp-warning-container',
|
||||
icon: 'text-cp-warning',
|
||||
},
|
||||
info: {
|
||||
iconBg: 'bg-cp-info-bg',
|
||||
iconBg: 'bg-cp-info-container',
|
||||
icon: 'text-cp-info',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -342,7 +342,7 @@ onBeforeUnmount(() => {
|
||||
class="inline-flex h-4.5 min-w-0 cursor-pointer items-center gap-1 rounded-cp-sm border-0 px-1.5 font-mono text-[10px] leading-none font-bold transition-colors outline-none focus-visible:ring-2 focus-visible:ring-cp-control-outline focus-visible:ring-offset-2 focus-visible:ring-offset-cp-bg-container"
|
||||
:class="[
|
||||
hasUpdate
|
||||
? 'bg-cp-success-bg text-cp-success-text-on-bg hover:bg-cp-success-bg-hover'
|
||||
? 'bg-cp-success-container text-cp-success-on-container hover:bg-cp-success-container-hover'
|
||||
: 'bg-cp-fill-quaternary text-cp-text-quaternary hover:bg-cp-fill-tertiary hover:text-cp-text-secondary',
|
||||
]"
|
||||
:title="updateButtonLabel"
|
||||
@@ -399,7 +399,7 @@ onBeforeUnmount(() => {
|
||||
>
|
||||
<span
|
||||
v-if="!isCollapsed"
|
||||
class="inline-flex whitespace-nowrap h-7 items-center gap-1.5 rounded-lg bg-cp-success-bg px-2.5 text-xs leading-none font-emphasis text-cp-success-text-on-bg"
|
||||
class="inline-flex whitespace-nowrap h-7 items-center gap-1.5 rounded-lg bg-cp-success-container px-2.5 text-xs leading-none font-emphasis text-cp-success-on-container"
|
||||
>
|
||||
<i class="size-1.5 rounded-full bg-cp-success" />
|
||||
在线
|
||||
|
||||
@@ -257,7 +257,7 @@ watch(
|
||||
|
||||
<p
|
||||
v-if="updateError || updateInfo?.warning"
|
||||
class="m-0 rounded-cp bg-cp-error-bg px-3 py-2 text-cp-sm leading-normal font-bold text-cp-error-text-on-bg"
|
||||
class="m-0 rounded-cp bg-cp-error-container px-3 py-2 text-cp-sm leading-normal font-bold text-cp-error-on-container"
|
||||
>
|
||||
{{ updateError || updateInfo?.warning }}
|
||||
</p>
|
||||
|
||||
@@ -47,7 +47,7 @@ function resolveStatus(input: SystemUpdatePresentationInput) {
|
||||
return {
|
||||
label: input.restarting ? '重启中' : '更新中',
|
||||
icon: RefreshCw,
|
||||
badge: 'bg-cp-info-bg text-cp-info-text-on-bg',
|
||||
badge: 'bg-cp-info-container text-cp-info-on-container',
|
||||
iconClass: 'text-cp-info',
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ function resolveStatus(input: SystemUpdatePresentationInput) {
|
||||
return {
|
||||
label: '异常',
|
||||
icon: XCircle,
|
||||
badge: 'bg-cp-error-bg text-cp-error-text-on-bg',
|
||||
badge: 'bg-cp-error-container text-cp-error-on-container',
|
||||
iconClass: 'text-cp-error',
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,7 @@ function resolveStatus(input: SystemUpdatePresentationInput) {
|
||||
return {
|
||||
label: input.updateSuccess ? '已更新' : input.hasUpdate ? '有新版本' : '已是最新',
|
||||
icon: input.hasUpdate ? ArrowUpCircle : CheckCircle2,
|
||||
badge: 'bg-cp-success-bg text-cp-success-text-on-bg',
|
||||
badge: 'bg-cp-success-container text-cp-success-on-container',
|
||||
iconClass: 'text-cp-success',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,8 +47,8 @@
|
||||
--color-cp-text-light-solid: var(--cp-color-text-light-solid);
|
||||
|
||||
/* 颜色 · 主色与链接 */
|
||||
--color-cp-primary-bg: var(--cp-color-primary-bg);
|
||||
--color-cp-primary-bg-hover: var(--cp-color-primary-bg-hover);
|
||||
--color-cp-primary-container: var(--cp-color-primary-container);
|
||||
--color-cp-primary-container-hover: var(--cp-color-primary-container-hover);
|
||||
--color-cp-primary-border: var(--cp-color-primary-border);
|
||||
--color-cp-primary-border-hover: var(--cp-color-primary-border-hover);
|
||||
--color-cp-primary-hover: var(--cp-color-primary-hover);
|
||||
@@ -56,15 +56,16 @@
|
||||
--color-cp-primary-active: var(--cp-color-primary-active);
|
||||
--color-cp-primary-text-hover: var(--cp-color-primary-text-hover);
|
||||
--color-cp-primary-text: var(--cp-color-primary-text);
|
||||
--color-cp-primary-on-container: var(--cp-color-primary-on-container);
|
||||
--color-cp-primary-text-active: var(--cp-color-primary-text-active);
|
||||
--color-cp-link: var(--cp-color-link);
|
||||
--color-cp-link-hover: var(--cp-color-link-hover);
|
||||
--color-cp-link-active: var(--cp-color-link-active);
|
||||
|
||||
/* 颜色 · 语义 */
|
||||
--color-cp-info-bg: var(--cp-color-info-bg);
|
||||
--color-cp-info-bg-hover: var(--cp-color-info-bg-hover);
|
||||
--color-cp-info-bg-active: var(--cp-color-info-bg-active);
|
||||
--color-cp-info-container: var(--cp-color-info-container);
|
||||
--color-cp-info-container-hover: var(--cp-color-info-container-hover);
|
||||
--color-cp-info-container-active: var(--cp-color-info-container-active);
|
||||
--color-cp-info-border: var(--cp-color-info-border);
|
||||
--color-cp-info-border-hover: var(--cp-color-info-border-hover);
|
||||
--color-cp-info-hover: var(--cp-color-info-hover);
|
||||
@@ -72,11 +73,11 @@
|
||||
--color-cp-info-active: var(--cp-color-info-active);
|
||||
--color-cp-info-text-hover: var(--cp-color-info-text-hover);
|
||||
--color-cp-info-text: var(--cp-color-info-text);
|
||||
--color-cp-info-text-on-bg: var(--cp-color-info-text-on-bg);
|
||||
--color-cp-info-on-container: var(--cp-color-info-on-container);
|
||||
--color-cp-info-text-active: var(--cp-color-info-text-active);
|
||||
--color-cp-success-bg: var(--cp-color-success-bg);
|
||||
--color-cp-success-bg-hover: var(--cp-color-success-bg-hover);
|
||||
--color-cp-success-bg-active: var(--cp-color-success-bg-active);
|
||||
--color-cp-success-container: var(--cp-color-success-container);
|
||||
--color-cp-success-container-hover: var(--cp-color-success-container-hover);
|
||||
--color-cp-success-container-active: var(--cp-color-success-container-active);
|
||||
--color-cp-success-border: var(--cp-color-success-border);
|
||||
--color-cp-success-border-hover: var(--cp-color-success-border-hover);
|
||||
--color-cp-success-hover: var(--cp-color-success-hover);
|
||||
@@ -84,11 +85,11 @@
|
||||
--color-cp-success-active: var(--cp-color-success-active);
|
||||
--color-cp-success-text-hover: var(--cp-color-success-text-hover);
|
||||
--color-cp-success-text: var(--cp-color-success-text);
|
||||
--color-cp-success-text-on-bg: var(--cp-color-success-text-on-bg);
|
||||
--color-cp-success-on-container: var(--cp-color-success-on-container);
|
||||
--color-cp-success-text-active: var(--cp-color-success-text-active);
|
||||
--color-cp-warning-bg: var(--cp-color-warning-bg);
|
||||
--color-cp-warning-bg-hover: var(--cp-color-warning-bg-hover);
|
||||
--color-cp-warning-bg-active: var(--cp-color-warning-bg-active);
|
||||
--color-cp-warning-container: var(--cp-color-warning-container);
|
||||
--color-cp-warning-container-hover: var(--cp-color-warning-container-hover);
|
||||
--color-cp-warning-container-active: var(--cp-color-warning-container-active);
|
||||
--color-cp-warning-border: var(--cp-color-warning-border);
|
||||
--color-cp-warning-border-hover: var(--cp-color-warning-border-hover);
|
||||
--color-cp-warning-hover: var(--cp-color-warning-hover);
|
||||
@@ -96,11 +97,11 @@
|
||||
--color-cp-warning-active: var(--cp-color-warning-active);
|
||||
--color-cp-warning-text-hover: var(--cp-color-warning-text-hover);
|
||||
--color-cp-warning-text: var(--cp-color-warning-text);
|
||||
--color-cp-warning-text-on-bg: var(--cp-color-warning-text-on-bg);
|
||||
--color-cp-warning-on-container: var(--cp-color-warning-on-container);
|
||||
--color-cp-warning-text-active: var(--cp-color-warning-text-active);
|
||||
--color-cp-error-bg: var(--cp-color-error-bg);
|
||||
--color-cp-error-bg-hover: var(--cp-color-error-bg-hover);
|
||||
--color-cp-error-bg-active: var(--cp-color-error-bg-active);
|
||||
--color-cp-error-container: var(--cp-color-error-container);
|
||||
--color-cp-error-container-hover: var(--cp-color-error-container-hover);
|
||||
--color-cp-error-container-active: var(--cp-color-error-container-active);
|
||||
--color-cp-error-border: var(--cp-color-error-border);
|
||||
--color-cp-error-border-hover: var(--cp-color-error-border-hover);
|
||||
--color-cp-error-hover: var(--cp-color-error-hover);
|
||||
@@ -108,46 +109,46 @@
|
||||
--color-cp-error-active: var(--cp-color-error-active);
|
||||
--color-cp-error-text-hover: var(--cp-color-error-text-hover);
|
||||
--color-cp-error-text: var(--cp-color-error-text);
|
||||
--color-cp-error-text-on-bg: var(--cp-color-error-text-on-bg);
|
||||
--color-cp-error-on-container: var(--cp-color-error-on-container);
|
||||
--color-cp-error-text-active: var(--cp-color-error-text-active);
|
||||
|
||||
/* 颜色 · 预设(色系按字母排序,角色顺序固定) */
|
||||
--color-cp-blue-bg: var(--cp-color-blue-bg);
|
||||
--color-cp-blue-bg-strong: var(--cp-color-blue-bg-strong);
|
||||
--color-cp-blue-container: var(--cp-color-blue-container);
|
||||
--color-cp-blue-container-strong: var(--cp-color-blue-container-strong);
|
||||
--color-cp-blue-border: var(--cp-color-blue-border);
|
||||
--color-cp-blue-solid: var(--cp-color-blue-solid);
|
||||
--color-cp-blue-text: var(--cp-color-blue-text);
|
||||
--color-cp-blue-text-on-bg: var(--cp-color-blue-text-on-bg);
|
||||
--color-cp-cyan-bg: var(--cp-color-cyan-bg);
|
||||
--color-cp-cyan-bg-strong: var(--cp-color-cyan-bg-strong);
|
||||
--color-cp-blue-on-container: var(--cp-color-blue-on-container);
|
||||
--color-cp-cyan-container: var(--cp-color-cyan-container);
|
||||
--color-cp-cyan-container-strong: var(--cp-color-cyan-container-strong);
|
||||
--color-cp-cyan-border: var(--cp-color-cyan-border);
|
||||
--color-cp-cyan-solid: var(--cp-color-cyan-solid);
|
||||
--color-cp-cyan-text: var(--cp-color-cyan-text);
|
||||
--color-cp-cyan-text-on-bg: var(--cp-color-cyan-text-on-bg);
|
||||
--color-cp-green-bg: var(--cp-color-green-bg);
|
||||
--color-cp-green-bg-strong: var(--cp-color-green-bg-strong);
|
||||
--color-cp-cyan-on-container: var(--cp-color-cyan-on-container);
|
||||
--color-cp-green-container: var(--cp-color-green-container);
|
||||
--color-cp-green-container-strong: var(--cp-color-green-container-strong);
|
||||
--color-cp-green-border: var(--cp-color-green-border);
|
||||
--color-cp-green-solid: var(--cp-color-green-solid);
|
||||
--color-cp-green-text: var(--cp-color-green-text);
|
||||
--color-cp-green-text-on-bg: var(--cp-color-green-text-on-bg);
|
||||
--color-cp-orange-bg: var(--cp-color-orange-bg);
|
||||
--color-cp-orange-bg-strong: var(--cp-color-orange-bg-strong);
|
||||
--color-cp-green-on-container: var(--cp-color-green-on-container);
|
||||
--color-cp-orange-container: var(--cp-color-orange-container);
|
||||
--color-cp-orange-container-strong: var(--cp-color-orange-container-strong);
|
||||
--color-cp-orange-border: var(--cp-color-orange-border);
|
||||
--color-cp-orange-solid: var(--cp-color-orange-solid);
|
||||
--color-cp-orange-text: var(--cp-color-orange-text);
|
||||
--color-cp-orange-text-on-bg: var(--cp-color-orange-text-on-bg);
|
||||
--color-cp-purple-bg: var(--cp-color-purple-bg);
|
||||
--color-cp-purple-bg-strong: var(--cp-color-purple-bg-strong);
|
||||
--color-cp-orange-on-container: var(--cp-color-orange-on-container);
|
||||
--color-cp-purple-container: var(--cp-color-purple-container);
|
||||
--color-cp-purple-container-strong: var(--cp-color-purple-container-strong);
|
||||
--color-cp-purple-border: var(--cp-color-purple-border);
|
||||
--color-cp-purple-solid: var(--cp-color-purple-solid);
|
||||
--color-cp-purple-text: var(--cp-color-purple-text);
|
||||
--color-cp-purple-text-on-bg: var(--cp-color-purple-text-on-bg);
|
||||
--color-cp-red-bg: var(--cp-color-red-bg);
|
||||
--color-cp-red-bg-strong: var(--cp-color-red-bg-strong);
|
||||
--color-cp-purple-on-container: var(--cp-color-purple-on-container);
|
||||
--color-cp-red-container: var(--cp-color-red-container);
|
||||
--color-cp-red-container-strong: var(--cp-color-red-container-strong);
|
||||
--color-cp-red-border: var(--cp-color-red-border);
|
||||
--color-cp-red-solid: var(--cp-color-red-solid);
|
||||
--color-cp-red-text: var(--cp-color-red-text);
|
||||
--color-cp-red-text-on-bg: var(--cp-color-red-text-on-bg);
|
||||
--color-cp-red-on-container: var(--cp-color-red-on-container);
|
||||
|
||||
/* 颜色 · 数据 */
|
||||
--color-cp-activity-level-0: var(--cp-activity-level-0);
|
||||
|
||||
@@ -28,9 +28,9 @@ const DIRECT_THEME_MAP_KEYS = [
|
||||
] as const satisfies readonly DirectThemeMapKey[]
|
||||
|
||||
const FUNCTIONAL_TOKEN_ROLES = [
|
||||
['background', '-bg'],
|
||||
['backgroundHover', '-bg-hover'],
|
||||
['backgroundActive', '-bg-active'],
|
||||
['container', '-container'],
|
||||
['containerHover', '-container-hover'],
|
||||
['containerActive', '-container-active'],
|
||||
['border', '-border'],
|
||||
['borderHover', '-border-hover'],
|
||||
['hover', '-hover'],
|
||||
@@ -38,22 +38,22 @@ const FUNCTIONAL_TOKEN_ROLES = [
|
||||
['active', '-active'],
|
||||
['textHover', '-text-hover'],
|
||||
['text', '-text'],
|
||||
['textOnBackground', '-text-on-bg'],
|
||||
['onContainer', '-on-container'],
|
||||
['textActive', '-text-active'],
|
||||
] as const satisfies readonly (readonly [keyof FunctionalColorMap, FunctionalTokenSuffix])[]
|
||||
|
||||
const PRESET_TOKEN_ROLES = [
|
||||
['background', 'bg'],
|
||||
['backgroundStrong', 'bg-strong'],
|
||||
['container', 'container'],
|
||||
['containerStrong', 'container-strong'],
|
||||
['border', 'border'],
|
||||
['solid', 'solid'],
|
||||
['text', 'text'],
|
||||
['textOnBackground', 'text-on-bg'],
|
||||
['onContainer', 'on-container'],
|
||||
] as const satisfies readonly (readonly [keyof PresetColorRoleMap, PresetTokenSuffix])[]
|
||||
|
||||
const CONTROL_TOKEN_BINDINGS = [
|
||||
['--cp-control-item-bg-active', 'colorPrimaryBg'],
|
||||
['--cp-control-item-bg-active-hover', 'colorPrimaryBgHover'],
|
||||
['--cp-control-item-bg-active', 'colorPrimaryContainer'],
|
||||
['--cp-control-item-bg-active-hover', 'colorPrimaryContainerHover'],
|
||||
['--cp-control-outline', 'colorPrimaryBorder'],
|
||||
] as const satisfies readonly (readonly [ControlTokenName, keyof ThemePrimaryMap])[]
|
||||
|
||||
|
||||
@@ -98,8 +98,8 @@ export function deriveThemePrimaryMap(
|
||||
)
|
||||
|
||||
return {
|
||||
colorPrimaryBg: roles.background,
|
||||
colorPrimaryBgHover: roles.backgroundHover,
|
||||
colorPrimaryContainer: roles.container,
|
||||
colorPrimaryContainerHover: roles.containerHover,
|
||||
colorPrimaryBorder: roles.border,
|
||||
colorPrimaryBorderHover: roles.borderHover,
|
||||
colorPrimaryHover: roles.hover,
|
||||
@@ -107,6 +107,7 @@ export function deriveThemePrimaryMap(
|
||||
colorPrimaryActive: roles.active,
|
||||
colorPrimaryTextHover: roles.textHover,
|
||||
colorPrimaryText: roles.text,
|
||||
colorPrimaryOnContainer: roles.onContainer,
|
||||
colorPrimaryTextActive: roles.textActive,
|
||||
colorTextLightSolid: LIGHT_FOREGROUND,
|
||||
}
|
||||
@@ -210,7 +211,7 @@ export function deriveThemeDataMap(
|
||||
aliases: ThemeAliasMap,
|
||||
semantics: ThemeSemanticMap,
|
||||
): ThemeDataMap {
|
||||
const activityBase = semantics.success.background
|
||||
const activityBase = semantics.success.container
|
||||
const activitySolid = semantics.success.color
|
||||
|
||||
return {
|
||||
@@ -454,10 +455,10 @@ function deriveColorRoleMap(
|
||||
const color = resolve(recipe.color)
|
||||
const hover = resolve(recipe.hover)
|
||||
const active = resolve(recipe.active)
|
||||
const background = mix(containerBg, color, recipe.backgroundMix)
|
||||
const backgroundHover = mix(containerBg, hover, recipe.backgroundHoverMix)
|
||||
const backgroundActive = mix(containerBg, active, recipe.backgroundActiveMix)
|
||||
const semanticBackgrounds = [background, backgroundHover, backgroundActive]
|
||||
const container = mix(containerBg, color, recipe.containerMix)
|
||||
const containerHover = mix(containerBg, hover, recipe.containerHoverMix)
|
||||
const containerActive = mix(containerBg, active, recipe.containerActiveMix)
|
||||
const semanticContainers = [container, containerHover, containerActive]
|
||||
const resolveText = (
|
||||
source: ThemePaletteSource,
|
||||
backgrounds: string | readonly string[] = textBackgrounds,
|
||||
@@ -470,9 +471,9 @@ function deriveColorRoleMap(
|
||||
}
|
||||
|
||||
return {
|
||||
background,
|
||||
backgroundHover,
|
||||
backgroundActive,
|
||||
container,
|
||||
containerHover,
|
||||
containerActive,
|
||||
border: ensureContrast(mix(containerBg, color, recipe.borderMix), containerBg, 3),
|
||||
borderHover: ensureContrast(mix(containerBg, hover, recipe.borderHoverMix), containerBg, 3),
|
||||
hover: recipe.hoverContrast === undefined
|
||||
@@ -484,15 +485,15 @@ function deriveColorRoleMap(
|
||||
: ensureContrast(active, containerBg, recipe.activeContrast),
|
||||
textHover: resolveText(recipe.textHover),
|
||||
text: resolveText(recipe.text),
|
||||
textOnBackground: resolveText(
|
||||
recipe.textOnBackground ?? recipe.text,
|
||||
semanticBackgrounds,
|
||||
onContainer: resolveText(
|
||||
recipe.onContainer ?? recipe.text,
|
||||
semanticContainers,
|
||||
),
|
||||
textActive: resolveText(recipe.textActive),
|
||||
}
|
||||
}
|
||||
|
||||
/** 背景、边框和实心色对齐 Colorful Tag;文字在当前 Surface 上保持可读。 */
|
||||
/** 容器、边框和实心色对齐 Colorful Tag;文字在当前 Surface 上保持可读。 */
|
||||
function derivePresetColorRoleMap(
|
||||
palette: readonly string[],
|
||||
containerBg: string,
|
||||
@@ -501,20 +502,20 @@ function derivePresetColorRoleMap(
|
||||
const color = (step: number): string =>
|
||||
normalizeHexColor(palette[step - 1]) ?? DEFAULT_CUSTOM_THEME_COLOR
|
||||
const solid = color(recipe.solid)
|
||||
const background = mix(containerBg, solid, recipe.backgroundMix)
|
||||
const backgroundStrong = mix(containerBg, solid, recipe.backgroundStrongMix)
|
||||
const container = mix(containerBg, solid, recipe.containerMix)
|
||||
const containerStrong = mix(containerBg, solid, recipe.containerStrongMix)
|
||||
const text = color(recipe.text)
|
||||
const textOnBackground = color(recipe.textOnBackground)
|
||||
const onContainer = color(recipe.onContainer)
|
||||
const toneText = (value: string): string => recipe.minimumTextLightness === undefined
|
||||
? value
|
||||
: ensureLightness(value, recipe.minimumTextLightness)
|
||||
|
||||
return {
|
||||
background,
|
||||
backgroundStrong,
|
||||
container,
|
||||
containerStrong,
|
||||
border: ensureContrast(mix(containerBg, solid, recipe.borderMix), containerBg, 3),
|
||||
solid,
|
||||
text: ensureContrast(toneText(text), containerBg, 4.5),
|
||||
textOnBackground: ensureContrast(toneText(textOnBackground), [background, backgroundStrong], 4.5),
|
||||
onContainer: ensureContrast(toneText(onContainer), [container, containerStrong], 4.5),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ function deriveDarkComponentMap(
|
||||
error: FunctionalColorMap,
|
||||
shadowStrength: number,
|
||||
): SurfaceComponentMap {
|
||||
const inputOutlineColor = 'var(--cp-color-primary-bg-hover)'
|
||||
const inputOutlineColor = 'var(--cp-color-primary-container-hover)'
|
||||
const appearanceInfluence = deriveComponentAppearanceInfluence(
|
||||
surfaces.colorBgContainer,
|
||||
DARK_CONTAINER_BASE,
|
||||
@@ -167,7 +167,7 @@ function deriveDarkComponentMap(
|
||||
mix(surfaces.colorBgContainer, surfaces.colorBgElevated, 0.4),
|
||||
appearanceInfluence,
|
||||
),
|
||||
inputErrorActiveBg: error.background,
|
||||
inputErrorActiveBg: error.container,
|
||||
brandMarkBg: surfaces.colorBgElevated,
|
||||
cardBg: surfaces.colorBgContainer,
|
||||
modalBg: surfaces.colorBgContainer,
|
||||
@@ -175,7 +175,7 @@ function deriveDarkComponentMap(
|
||||
tableRowBg: surfaces.colorBgContainer,
|
||||
tableRowStripeBg: aliases.colorFillAlter,
|
||||
tableRowHoverBg: surfaces.colorBgTextHover,
|
||||
tableRowSelectedBg: primary.colorPrimaryBg,
|
||||
tableRowSelectedBg: primary.colorPrimaryContainer,
|
||||
progressRemainingColor: mix(surfaces.colorBgContainer, surfaces.colorBorderSecondary, 0.92),
|
||||
layoutSiderBg: surfaces.colorBgContainer,
|
||||
cardShadow: scaleShadowAlpha(`0 18px 34px -26px ${withAlpha(BLACK, 0.64)}`, shadowStrength),
|
||||
@@ -199,7 +199,7 @@ function deriveLightComponentMap(
|
||||
error: FunctionalColorMap,
|
||||
shadowStrength: number,
|
||||
): SurfaceComponentMap {
|
||||
const inputOutlineColor = 'var(--cp-color-primary-bg-hover)'
|
||||
const inputOutlineColor = 'var(--cp-color-primary-container-hover)'
|
||||
const appearanceInfluence = deriveComponentAppearanceInfluence(
|
||||
surfaces.colorBgContainer,
|
||||
LIGHT_CONTAINER_BASE,
|
||||
@@ -220,7 +220,7 @@ function deriveLightComponentMap(
|
||||
aliases.colorFillAlter,
|
||||
appearanceInfluence,
|
||||
),
|
||||
inputErrorActiveBg: error.background,
|
||||
inputErrorActiveBg: error.container,
|
||||
brandMarkBg: surfaces.colorBgSpotlight,
|
||||
cardBg: surfaces.colorBgContainer,
|
||||
modalBg: surfaces.colorBgContainer,
|
||||
@@ -228,7 +228,7 @@ function deriveLightComponentMap(
|
||||
tableRowBg: surfaces.colorBgContainer,
|
||||
tableRowStripeBg: aliases.colorFillAlter,
|
||||
tableRowHoverBg: surfaces.colorBgTextHover,
|
||||
tableRowSelectedBg: primary.colorPrimaryBg,
|
||||
tableRowSelectedBg: primary.colorPrimaryContainer,
|
||||
progressRemainingColor: surfaces.colorBorderSecondary,
|
||||
layoutSiderBg: surfaces.colorBgContainer,
|
||||
cardShadow: scaleShadowAlpha(`0 10px 22px -18px ${withAlpha(LIGHT_SHADOW_BASE, 0.08)}`, shadowStrength),
|
||||
|
||||
@@ -13,9 +13,9 @@ export const THEME_COLOR_ROLE_RECIPES = {
|
||||
textHover: 5,
|
||||
text: 6,
|
||||
textActive: 7,
|
||||
backgroundMix: 0.09,
|
||||
backgroundHoverMix: 0.14,
|
||||
backgroundActiveMix: 0.18,
|
||||
containerMix: 0.09,
|
||||
containerHoverMix: 0.14,
|
||||
containerActiveMix: 0.18,
|
||||
borderMix: 0.46,
|
||||
borderHoverMix: 0.56,
|
||||
},
|
||||
@@ -25,11 +25,11 @@ export const THEME_COLOR_ROLE_RECIPES = {
|
||||
active: 7,
|
||||
textHover: 5,
|
||||
text: 7,
|
||||
textOnBackground: 6,
|
||||
onContainer: 6,
|
||||
textActive: 7,
|
||||
backgroundMix: 0.08,
|
||||
backgroundHoverMix: 0.12,
|
||||
backgroundActiveMix: 0.18,
|
||||
containerMix: 0.08,
|
||||
containerHoverMix: 0.12,
|
||||
containerActiveMix: 0.18,
|
||||
borderMix: 0.4,
|
||||
borderHoverMix: 0.5,
|
||||
hoverContrast: 3,
|
||||
@@ -38,9 +38,9 @@ export const THEME_COLOR_ROLE_RECIPES = {
|
||||
preset: {
|
||||
solid: 6,
|
||||
text: 7,
|
||||
textOnBackground: 7,
|
||||
backgroundMix: 0.08,
|
||||
backgroundStrongMix: 0.22,
|
||||
onContainer: 7,
|
||||
containerMix: 0.08,
|
||||
containerStrongMix: 0.22,
|
||||
borderMix: 0.4,
|
||||
},
|
||||
},
|
||||
@@ -52,9 +52,9 @@ export const THEME_COLOR_ROLE_RECIPES = {
|
||||
textHover: 8,
|
||||
text: 7,
|
||||
textActive: 6,
|
||||
backgroundMix: 0.17,
|
||||
backgroundHoverMix: 0.24,
|
||||
backgroundActiveMix: 0.3,
|
||||
containerMix: 0.17,
|
||||
containerHoverMix: 0.24,
|
||||
containerActiveMix: 0.3,
|
||||
borderMix: 0.52,
|
||||
borderHoverMix: 0.62,
|
||||
},
|
||||
@@ -64,11 +64,11 @@ export const THEME_COLOR_ROLE_RECIPES = {
|
||||
active: 6,
|
||||
textHover: 8,
|
||||
text: 7,
|
||||
textOnBackground: 9,
|
||||
onContainer: 9,
|
||||
textActive: 10,
|
||||
backgroundMix: 0.18,
|
||||
backgroundHoverMix: 0.24,
|
||||
backgroundActiveMix: 0.3,
|
||||
containerMix: 0.18,
|
||||
containerHoverMix: 0.24,
|
||||
containerActiveMix: 0.3,
|
||||
borderMix: 0.46,
|
||||
borderHoverMix: 0.56,
|
||||
hoverContrast: 3,
|
||||
@@ -77,9 +77,9 @@ export const THEME_COLOR_ROLE_RECIPES = {
|
||||
preset: {
|
||||
solid: 6,
|
||||
text: 8,
|
||||
textOnBackground: 8,
|
||||
backgroundMix: 0.18,
|
||||
backgroundStrongMix: 0.24,
|
||||
onContainer: 8,
|
||||
containerMix: 0.18,
|
||||
containerStrongMix: 0.24,
|
||||
borderMix: 0.46,
|
||||
minimumTextLightness: 0.7,
|
||||
},
|
||||
|
||||
+25
-24
@@ -90,11 +90,11 @@ export interface ThemeColorRoleRecipe {
|
||||
active: ThemePaletteSource
|
||||
textHover: ThemePaletteSource
|
||||
text: ThemePaletteSource
|
||||
textOnBackground?: ThemePaletteSource
|
||||
onContainer?: ThemePaletteSource
|
||||
textActive: ThemePaletteSource
|
||||
backgroundMix: number
|
||||
backgroundHoverMix: number
|
||||
backgroundActiveMix: number
|
||||
containerMix: number
|
||||
containerHoverMix: number
|
||||
containerActiveMix: number
|
||||
borderMix: number
|
||||
borderHoverMix: number
|
||||
hoverContrast?: number
|
||||
@@ -111,16 +111,16 @@ export interface ThemeColorRoleRecipes {
|
||||
export interface ThemePresetColorRoleRecipe {
|
||||
solid: ThemePaletteStep
|
||||
text: ThemePaletteStep
|
||||
textOnBackground: ThemePaletteStep
|
||||
backgroundMix: number
|
||||
backgroundStrongMix: number
|
||||
onContainer: ThemePaletteStep
|
||||
containerMix: number
|
||||
containerStrongMix: number
|
||||
borderMix: number
|
||||
minimumTextLightness?: number
|
||||
}
|
||||
|
||||
export interface ThemePrimaryMap {
|
||||
colorPrimaryBg: string
|
||||
colorPrimaryBgHover: string
|
||||
colorPrimaryContainer: string
|
||||
colorPrimaryContainerHover: string
|
||||
colorPrimaryBorder: string
|
||||
colorPrimaryBorderHover: string
|
||||
colorPrimaryHover: string
|
||||
@@ -128,6 +128,7 @@ export interface ThemePrimaryMap {
|
||||
colorPrimaryActive: string
|
||||
colorPrimaryTextHover: string
|
||||
colorPrimaryText: string
|
||||
colorPrimaryOnContainer: string
|
||||
colorPrimaryTextActive: string
|
||||
colorTextLightSolid: string
|
||||
}
|
||||
@@ -206,16 +207,16 @@ export interface FunctionalColorMap {
|
||||
color: string
|
||||
hover: string
|
||||
active: string
|
||||
background: string
|
||||
backgroundHover: string
|
||||
backgroundActive: string
|
||||
container: string
|
||||
containerHover: string
|
||||
containerActive: string
|
||||
border: string
|
||||
borderHover: string
|
||||
textHover: string
|
||||
/** 语义色在中性表面上的强调文字。 */
|
||||
text: string
|
||||
/** 语义色在自身背景上的高对比文字。 */
|
||||
textOnBackground: string
|
||||
/** 语义色容器内文字与图标使用的高对比前景色。 */
|
||||
onContainer: string
|
||||
textActive: string
|
||||
}
|
||||
|
||||
@@ -227,12 +228,12 @@ export interface ThemeSemanticMap {
|
||||
}
|
||||
|
||||
export interface PresetColorRoleMap {
|
||||
background: string
|
||||
backgroundStrong: string
|
||||
container: string
|
||||
containerStrong: string
|
||||
border: string
|
||||
solid: string
|
||||
text: string
|
||||
textOnBackground: string
|
||||
onContainer: string
|
||||
}
|
||||
|
||||
export type ThemePresetColorName
|
||||
@@ -305,26 +306,26 @@ export type FunctionalTokenSuffix
|
||||
= ''
|
||||
| '-hover'
|
||||
| '-active'
|
||||
| '-bg'
|
||||
| '-bg-hover'
|
||||
| '-bg-active'
|
||||
| '-container'
|
||||
| '-container-hover'
|
||||
| '-container-active'
|
||||
| '-border'
|
||||
| '-border-hover'
|
||||
| '-text-hover'
|
||||
| '-text'
|
||||
| '-text-on-bg'
|
||||
| '-on-container'
|
||||
| '-text-active'
|
||||
|
||||
type SemanticColorName = Extract<keyof ThemeSemanticMap, string>
|
||||
export type SemanticTokenName = `--cp-color-${SemanticColorName}${FunctionalTokenSuffix}`
|
||||
|
||||
export type PresetTokenSuffix
|
||||
= 'bg'
|
||||
| 'bg-strong'
|
||||
= 'container'
|
||||
| 'container-strong'
|
||||
| 'border'
|
||||
| 'solid'
|
||||
| 'text'
|
||||
| 'text-on-bg'
|
||||
| 'on-container'
|
||||
|
||||
type PresetColorName = Extract<keyof ThemePresetColorMap, string>
|
||||
export type PresetTokenName = `--cp-color-${PresetColorName}-${PresetTokenSuffix}`
|
||||
|
||||
@@ -121,7 +121,7 @@ const { allSelected, indeterminate, selectedRowKeys, toggleSelection, toggleAll
|
||||
<span
|
||||
class="inline-flex h-6 items-center rounded-lg px-2 text-cp-xs font-bold"
|
||||
:class="row.enabled
|
||||
? 'bg-cp-success-bg text-cp-success-text-on-bg'
|
||||
? 'bg-cp-success-container text-cp-success-on-container'
|
||||
: 'bg-cp-fill-tertiary text-cp-text-quaternary'"
|
||||
>
|
||||
{{ row.enabled ? '已启用' : '已禁用' }}
|
||||
|
||||
@@ -44,15 +44,15 @@ const overviewItems = computed(() => [
|
||||
|
||||
function overviewIconClass(tone: string) {
|
||||
if (tone === 'success') {
|
||||
return 'bg-cp-green-bg text-cp-green-text-on-bg'
|
||||
return 'bg-cp-green-container text-cp-green-on-container'
|
||||
}
|
||||
if (tone === 'warning') {
|
||||
return 'bg-cp-orange-bg text-cp-orange-text-on-bg'
|
||||
return 'bg-cp-orange-container text-cp-orange-on-container'
|
||||
}
|
||||
if (tone === 'danger') {
|
||||
return 'bg-cp-error-bg text-cp-error-text-on-bg'
|
||||
return 'bg-cp-error-container text-cp-error-on-container'
|
||||
}
|
||||
return 'bg-cp-blue-bg text-cp-blue-text-on-bg'
|
||||
return 'bg-cp-blue-container text-cp-blue-on-container'
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -15,16 +15,16 @@ const props = withDefaults(
|
||||
)
|
||||
|
||||
const planPalettes: Record<string, string> = {
|
||||
free: 'bg-cp-cyan-bg text-cp-cyan-text-on-bg',
|
||||
pro: 'bg-cp-purple-bg-strong text-cp-purple-text-on-bg',
|
||||
prolite: 'bg-cp-purple-bg text-cp-purple-text-on-bg',
|
||||
free: 'bg-cp-cyan-container text-cp-cyan-on-container',
|
||||
pro: 'bg-cp-purple-container-strong text-cp-purple-on-container',
|
||||
prolite: 'bg-cp-purple-container text-cp-purple-on-container',
|
||||
}
|
||||
|
||||
const fallbackPalettes = [
|
||||
'bg-cp-blue-bg text-cp-blue-text-on-bg',
|
||||
'bg-cp-green-bg text-cp-green-text-on-bg',
|
||||
'bg-cp-cyan-bg text-cp-cyan-text-on-bg',
|
||||
'bg-cp-orange-bg text-cp-orange-text-on-bg',
|
||||
'bg-cp-blue-container text-cp-blue-on-container',
|
||||
'bg-cp-green-container text-cp-green-on-container',
|
||||
'bg-cp-cyan-container text-cp-cyan-on-container',
|
||||
'bg-cp-orange-container text-cp-orange-on-container',
|
||||
] as const
|
||||
|
||||
const label = computed(() => props.planType?.trim() || 'Free')
|
||||
|
||||
@@ -139,10 +139,10 @@ async function handleConfirmConsume() {
|
||||
</div>
|
||||
|
||||
<div v-else class="grid gap-4">
|
||||
<section v-if="ambiguous" class="flex items-start gap-3 rounded-cp bg-cp-warning-bg px-4 py-3.5" role="status">
|
||||
<AlertTriangle class="mt-0.5 size-4 shrink-0 text-cp-warning-text-on-bg" />
|
||||
<section v-if="ambiguous" class="flex items-start gap-3 rounded-cp bg-cp-warning-container px-4 py-3.5" role="status">
|
||||
<AlertTriangle class="mt-0.5 size-4 shrink-0 text-cp-warning-on-container" />
|
||||
<div class="min-w-0">
|
||||
<p class="m-0 text-cp-sm font-heavy text-cp-warning-text-on-bg">
|
||||
<p class="m-0 text-cp-sm font-heavy text-cp-warning-on-container">
|
||||
上次操作结果待确认
|
||||
</p>
|
||||
<p class="mt-1 mb-0 text-cp-xs leading-normal font-emphasis text-cp-text-secondary">
|
||||
@@ -193,7 +193,7 @@ async function handleConfirmConsume() {
|
||||
|
||||
<p
|
||||
v-if="loadError"
|
||||
class="m-0 rounded-cp bg-cp-error-bg px-4 py-3 text-cp-xs leading-normal font-emphasis text-cp-error-text-on-bg"
|
||||
class="m-0 rounded-cp bg-cp-error-container px-4 py-3 text-cp-xs leading-normal font-emphasis text-cp-error-on-container"
|
||||
role="status"
|
||||
>
|
||||
{{ loadError }},请刷新重试。
|
||||
|
||||
@@ -114,7 +114,7 @@ function quotaWindowCode(windowSeconds: number | null, role: AccountQuotaWindow[
|
||||
<article v-for="item in detailItems" :key="item.key">
|
||||
<div class="grid grid-cols-[32px_minmax(0,1fr)_auto] items-center gap-2.5">
|
||||
<span
|
||||
class="grid h-5 w-8 place-items-center rounded-sm bg-cp-blue-bg-strong font-mono text-[9px] leading-none font-heavy tracking-[0.04em] text-cp-blue-text-on-bg"
|
||||
class="grid h-5 w-8 place-items-center rounded-sm bg-cp-blue-container-strong font-mono text-[9px] leading-none font-heavy tracking-[0.04em] text-cp-blue-on-container"
|
||||
>
|
||||
{{ item.code }}
|
||||
</span>
|
||||
|
||||
@@ -53,26 +53,26 @@ const statusStyles: Record<AccountStatusTone, AccountStatusStyle> = {
|
||||
success: {
|
||||
text: 'text-cp-success-text',
|
||||
dot: 'bg-cp-success',
|
||||
badge: 'bg-cp-success-bg text-cp-success-text-on-bg',
|
||||
icon: 'bg-cp-success-bg text-cp-success-text-on-bg',
|
||||
badge: 'bg-cp-success-container text-cp-success-on-container',
|
||||
icon: 'bg-cp-success-container text-cp-success-on-container',
|
||||
},
|
||||
danger: {
|
||||
text: 'text-cp-error-text',
|
||||
dot: 'bg-cp-error',
|
||||
badge: 'bg-cp-error-bg text-cp-error-text-on-bg',
|
||||
icon: 'bg-cp-error-bg text-cp-error-text-on-bg',
|
||||
badge: 'bg-cp-error-container text-cp-error-on-container',
|
||||
icon: 'bg-cp-error-container text-cp-error-on-container',
|
||||
},
|
||||
warning: {
|
||||
text: 'text-cp-warning-text',
|
||||
dot: 'bg-cp-warning',
|
||||
badge: 'bg-cp-warning-bg text-cp-warning-text-on-bg',
|
||||
icon: 'bg-cp-warning-bg text-cp-warning-text-on-bg',
|
||||
badge: 'bg-cp-warning-container text-cp-warning-on-container',
|
||||
icon: 'bg-cp-warning-container text-cp-warning-on-container',
|
||||
},
|
||||
info: {
|
||||
text: 'text-cp-info-text',
|
||||
dot: 'bg-cp-info',
|
||||
badge: 'bg-cp-info-bg text-cp-info-text-on-bg',
|
||||
icon: 'bg-cp-info-bg text-cp-info-text-on-bg',
|
||||
badge: 'bg-cp-info-container text-cp-info-on-container',
|
||||
icon: 'bg-cp-info-container text-cp-info-on-container',
|
||||
},
|
||||
normal: {
|
||||
text: 'text-cp-text-secondary',
|
||||
|
||||
@@ -40,32 +40,32 @@ const modelUsageColumns = defineTableColumns<AccountModelUsage>([
|
||||
<span class="text-cp-xs font-emphasis text-cp-text-quaternary">当前额度窗口</span>
|
||||
</div>
|
||||
<div class="grid gap-2 xl:min-h-0 xl:flex-1 xl:grid-rows-5">
|
||||
<div class="flex items-center justify-between rounded-lg bg-cp-green-bg px-3 py-2">
|
||||
<span class="text-cp-sm font-bold text-cp-green-text-on-bg">输入</span>
|
||||
<div class="flex items-center justify-between rounded-lg bg-cp-green-container px-3 py-2">
|
||||
<span class="text-cp-sm font-bold text-cp-green-on-container">输入</span>
|
||||
<strong class="font-mono text-cp text-cp-text">
|
||||
{{ account.usage.inputTokensDisplay }}
|
||||
</strong>
|
||||
</div>
|
||||
<div class="flex items-center justify-between rounded-lg bg-cp-orange-bg px-3 py-2">
|
||||
<span class="text-cp-sm font-bold text-cp-orange-text-on-bg">输出</span>
|
||||
<div class="flex items-center justify-between rounded-lg bg-cp-orange-container px-3 py-2">
|
||||
<span class="text-cp-sm font-bold text-cp-orange-on-container">输出</span>
|
||||
<strong class="font-mono text-cp text-cp-text">
|
||||
{{ account.usage.outputTokensDisplay }}
|
||||
</strong>
|
||||
</div>
|
||||
<div class="flex items-center justify-between rounded-lg bg-cp-cyan-bg px-3 py-2">
|
||||
<span class="text-cp-sm font-bold text-cp-cyan-text-on-bg">缓存</span>
|
||||
<div class="flex items-center justify-between rounded-lg bg-cp-cyan-container px-3 py-2">
|
||||
<span class="text-cp-sm font-bold text-cp-cyan-on-container">缓存</span>
|
||||
<strong class="font-mono text-cp text-cp-text">
|
||||
{{ account.usage.cachedTokensDisplay }}
|
||||
</strong>
|
||||
</div>
|
||||
<div class="flex items-center justify-between rounded-lg bg-cp-blue-bg px-3 py-2">
|
||||
<span class="text-cp-sm font-bold text-cp-blue-text-on-bg">推理</span>
|
||||
<div class="flex items-center justify-between rounded-lg bg-cp-blue-container px-3 py-2">
|
||||
<span class="text-cp-sm font-bold text-cp-blue-on-container">推理</span>
|
||||
<strong class="font-mono text-cp text-cp-text">
|
||||
{{ account.usage.reasoningTokensDisplay }}
|
||||
</strong>
|
||||
</div>
|
||||
<div class="flex items-center justify-between rounded-lg bg-cp-blue-bg px-3 py-2">
|
||||
<span class="text-cp-sm font-bold text-cp-blue-text-on-bg">读取</span>
|
||||
<div class="flex items-center justify-between rounded-lg bg-cp-blue-container px-3 py-2">
|
||||
<span class="text-cp-sm font-bold text-cp-blue-on-container">读取</span>
|
||||
<strong class="font-mono text-cp text-cp-text">
|
||||
{{ account.usage.readTokensDisplay }}
|
||||
</strong>
|
||||
|
||||
@@ -191,7 +191,7 @@ export function useAccountConnectionTest(options: { reload: () => Promise<unknow
|
||||
label: '正在测试',
|
||||
description: '正在向所选模型发送请求并接收流式响应',
|
||||
icon: Clock3,
|
||||
badge: 'bg-cp-info-bg text-cp-info-text-on-bg',
|
||||
badge: 'bg-cp-info-container text-cp-info-on-container',
|
||||
iconClass: 'text-cp-info',
|
||||
}
|
||||
}
|
||||
@@ -200,7 +200,7 @@ export function useAccountConnectionTest(options: { reload: () => Promise<unknow
|
||||
label: '连接正常',
|
||||
description: '请求已完成,可在下方查看模型、耗时和事件轨迹',
|
||||
icon: CheckCircle2,
|
||||
badge: 'bg-cp-success-bg text-cp-success-text-on-bg',
|
||||
badge: 'bg-cp-success-container text-cp-success-on-container',
|
||||
iconClass: 'text-cp-success',
|
||||
}
|
||||
}
|
||||
@@ -209,7 +209,7 @@ export function useAccountConnectionTest(options: { reload: () => Promise<unknow
|
||||
label: '测试失败',
|
||||
description: '请求未完成,请在下方查看失败来源与原始诊断',
|
||||
icon: XCircle,
|
||||
badge: 'bg-cp-error-bg text-cp-error-text-on-bg',
|
||||
badge: 'bg-cp-error-container text-cp-error-on-container',
|
||||
iconClass: 'text-cp-error',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@ export type PresetVisualTone = 'blue' | 'cyan' | 'green' | 'orange'
|
||||
|
||||
const presetVisualTones: readonly PresetVisualTone[] = ['blue', 'green', 'orange', 'cyan']
|
||||
const presetVisualToneClasses = {
|
||||
blue: 'bg-cp-blue-bg-strong text-cp-blue-text-on-bg',
|
||||
cyan: 'bg-cp-cyan-bg-strong text-cp-cyan-text-on-bg',
|
||||
green: 'bg-cp-green-bg-strong text-cp-green-text-on-bg',
|
||||
orange: 'bg-cp-orange-bg-strong text-cp-orange-text-on-bg',
|
||||
blue: 'bg-cp-blue-container-strong text-cp-blue-on-container',
|
||||
cyan: 'bg-cp-cyan-container-strong text-cp-cyan-on-container',
|
||||
green: 'bg-cp-green-container-strong text-cp-green-on-container',
|
||||
orange: 'bg-cp-orange-container-strong text-cp-orange-on-container',
|
||||
} as const satisfies Record<PresetVisualTone, string>
|
||||
|
||||
export function stableVisualIndex(value: unknown, length: number): number {
|
||||
|
||||
@@ -110,8 +110,8 @@ const title = computed(() => props.editing ? '编辑 API Key' : '创建 API Key'
|
||||
size="md"
|
||||
>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="rounded-cp border border-cp-warning-border bg-cp-warning-bg px-4 py-3">
|
||||
<p class="m-0 text-cp font-semibold text-cp-warning-text-on-bg">
|
||||
<div class="rounded-cp border border-cp-warning-border bg-cp-warning-container px-4 py-3">
|
||||
<p class="m-0 text-cp font-semibold text-cp-warning-on-container">
|
||||
该密钥具有网关访问权限,请仅发送给可信调用方
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@ defineProps<{
|
||||
<template>
|
||||
<div class="grid w-full justify-items-center gap-1.5">
|
||||
<div v-if="apiKey.routingScope === 'all'" class="flex items-center justify-center gap-2">
|
||||
<span class="inline-flex h-6 items-center rounded-lg bg-cp-warning-bg px-2 text-cp-xs font-bold text-cp-warning-text-on-bg">
|
||||
<span class="inline-flex h-6 items-center rounded-lg bg-cp-warning-container px-2 text-cp-xs font-bold text-cp-warning-on-container">
|
||||
全部账号权限
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,7 @@ defineProps<{
|
||||
<span
|
||||
class="inline-flex h-6 min-w-14 items-center justify-center rounded-full px-2 text-cp-sm leading-none font-bold"
|
||||
:class="{
|
||||
'bg-cp-success-bg text-cp-success-text-on-bg': apiKey.enabled,
|
||||
'bg-cp-success-container text-cp-success-on-container': apiKey.enabled,
|
||||
'bg-cp-fill-quaternary text-cp-text-secondary': !apiKey.enabled,
|
||||
}"
|
||||
>
|
||||
|
||||
@@ -75,7 +75,7 @@ const releaseStatus = computed(() => {
|
||||
return {
|
||||
label: '当前生效',
|
||||
title: '当前 Provider 请求正在使用此运行时画像',
|
||||
tone: 'bg-cp-info-bg text-cp-info-text-on-bg',
|
||||
tone: 'bg-cp-info-container text-cp-info-on-container',
|
||||
icon: ShieldCheck,
|
||||
}
|
||||
}
|
||||
@@ -85,7 +85,7 @@ const releaseStatus = computed(() => {
|
||||
return {
|
||||
label: '制品一致',
|
||||
title: '当前生效版本与官方最新发布一致',
|
||||
tone: 'bg-cp-success-bg text-cp-success-text-on-bg',
|
||||
tone: 'bg-cp-success-container text-cp-success-on-container',
|
||||
icon: CheckCircle2,
|
||||
}
|
||||
}
|
||||
@@ -93,7 +93,7 @@ const releaseStatus = computed(() => {
|
||||
return {
|
||||
label: '发现新版',
|
||||
title: `官方最新版本 ${releaseLabel.value}`,
|
||||
tone: 'bg-cp-warning-bg text-cp-warning-text-on-bg',
|
||||
tone: 'bg-cp-warning-container text-cp-warning-on-container',
|
||||
icon: TriangleAlert,
|
||||
}
|
||||
}
|
||||
@@ -101,14 +101,14 @@ const releaseStatus = computed(() => {
|
||||
return {
|
||||
label: '检查失败',
|
||||
title: current.release.error || '官方版本检查失败',
|
||||
tone: 'bg-cp-error-bg text-cp-error-text-on-bg',
|
||||
tone: 'bg-cp-error-container text-cp-error-on-container',
|
||||
icon: TriangleAlert,
|
||||
}
|
||||
}
|
||||
return {
|
||||
label: '待检查',
|
||||
title: '尚未检查官方发布渠道',
|
||||
tone: 'bg-cp-cyan-bg text-cp-cyan-text-on-bg',
|
||||
tone: 'bg-cp-cyan-container text-cp-cyan-on-container',
|
||||
icon: RefreshCw,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -3,11 +3,11 @@ import { formatInteger } from '@/utils/number'
|
||||
|
||||
// tone 到 Tailwind 类的唯一映射;仪表盘各卡片共用。
|
||||
export const metricToneIconClasses: Record<MetricTone, string> = {
|
||||
normal: 'bg-cp-cyan-bg text-cp-cyan-text-on-bg',
|
||||
info: 'bg-cp-blue-bg text-cp-blue-text-on-bg',
|
||||
success: 'bg-cp-green-bg text-cp-green-text-on-bg',
|
||||
warning: 'bg-cp-orange-bg text-cp-orange-text-on-bg',
|
||||
danger: 'bg-cp-error-bg text-cp-error-text-on-bg',
|
||||
normal: 'bg-cp-cyan-container text-cp-cyan-on-container',
|
||||
info: 'bg-cp-blue-container text-cp-blue-on-container',
|
||||
success: 'bg-cp-green-container text-cp-green-on-container',
|
||||
warning: 'bg-cp-orange-container text-cp-orange-on-container',
|
||||
danger: 'bg-cp-error-container text-cp-error-on-container',
|
||||
}
|
||||
|
||||
export const metricToneValueClasses: Record<MetricTone, string> = {
|
||||
@@ -71,22 +71,22 @@ export const healthStatusMeta: Record<HealthStatus, HealthStatusMeta> = {
|
||||
unavailable: {
|
||||
label: '不可达',
|
||||
cellClass: 'bg-cp-error',
|
||||
badgeClass: 'bg-cp-error-bg text-cp-error-text-on-bg',
|
||||
badgeClass: 'bg-cp-error-container text-cp-error-on-container',
|
||||
},
|
||||
unstable: {
|
||||
label: '不稳定',
|
||||
cellClass: 'bg-cp-warning',
|
||||
badgeClass: 'bg-cp-warning-bg text-cp-warning-text-on-bg',
|
||||
badgeClass: 'bg-cp-warning-container text-cp-warning-on-container',
|
||||
},
|
||||
low_sample: {
|
||||
label: '低样本',
|
||||
cellClass: 'bg-cp-cyan-solid',
|
||||
badgeClass: 'bg-cp-cyan-bg text-cp-cyan-text-on-bg',
|
||||
badgeClass: 'bg-cp-cyan-container text-cp-cyan-on-container',
|
||||
},
|
||||
stable: {
|
||||
label: '稳定',
|
||||
cellClass: 'bg-cp-success',
|
||||
badgeClass: 'bg-cp-success-bg text-cp-success-text-on-bg',
|
||||
badgeClass: 'bg-cp-success-container text-cp-success-on-container',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ function togglePasswordVisible(): void {
|
||||
--cp-color-text: var(--cp-login-title-color);
|
||||
--cp-color-text-secondary: var(--cp-login-description-color);
|
||||
--cp-color-text-quaternary: var(--cp-login-placeholder-color);
|
||||
--cp-color-error-bg: var(--cp-login-error-bg);
|
||||
--cp-color-error-container: var(--cp-login-error-bg);
|
||||
--cp-color-error-border: transparent;
|
||||
--cp-color-error-text: var(--cp-login-error-text-color);
|
||||
--cp-color-error: var(--cp-login-error-icon-color);
|
||||
|
||||
@@ -118,7 +118,7 @@ async function handleSubmit(): Promise<void> {
|
||||
--cp-login-input-active-bg: var(--cp-input-active-bg);
|
||||
--cp-login-input-icon-color: var(--cp-color-primary-text);
|
||||
--cp-login-placeholder-color: var(--cp-color-text-secondary);
|
||||
--cp-login-error-bg: var(--cp-color-error-bg);
|
||||
--cp-login-error-bg: var(--cp-color-error-container);
|
||||
--cp-login-error-icon-color: var(--cp-color-error);
|
||||
--cp-login-error-text-color: var(--cp-color-error-text);
|
||||
--cp-login-button-shadow-color: color-mix(in srgb, var(--cp-color-primary) 30%, transparent);
|
||||
@@ -144,7 +144,7 @@ async function handleSubmit(): Promise<void> {
|
||||
:global(html[data-theme='dark'] .login-page) {
|
||||
--cp-login-page-bg-start: color-mix(in srgb, var(--cp-color-bg-layout) 86%, var(--cp-color-bg-spotlight));
|
||||
--cp-login-page-bg-middle: color-mix(in srgb, var(--cp-color-bg-container) 68%, var(--cp-color-bg-layout));
|
||||
--cp-login-page-bg-end: color-mix(in srgb, var(--cp-color-bg-spotlight) 76%, var(--cp-color-primary-bg));
|
||||
--cp-login-page-bg-end: color-mix(in srgb, var(--cp-color-bg-spotlight) 76%, var(--cp-color-primary-container));
|
||||
--cp-login-canvas-bg-start: color-mix(in srgb, var(--cp-color-bg-container) 82%, var(--cp-color-fill-tertiary));
|
||||
--cp-login-canvas-bg-middle: color-mix(in srgb, var(--cp-color-bg-layout) 88%, var(--cp-color-bg-container));
|
||||
--cp-login-canvas-bg-end: color-mix(in srgb, var(--cp-color-bg-spotlight) 88%, var(--cp-color-bg-layout));
|
||||
|
||||
+3
-3
@@ -59,7 +59,7 @@ function formatFileSize(value: number | null): string {
|
||||
<section class="grid gap-3.5 rounded-cp-card bg-cp-fill-quaternary p-4" aria-labelledby="windows-offline-download-title">
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div class="flex min-w-0 items-start gap-3">
|
||||
<span class="inline-grid size-9 shrink-0 place-items-center rounded-cp bg-cp-info-bg text-cp-info">
|
||||
<span class="inline-grid size-9 shrink-0 place-items-center rounded-cp bg-cp-info-container text-cp-info">
|
||||
<ArrowDownToLine class="size-4.5" />
|
||||
</span>
|
||||
<div class="min-w-0">
|
||||
@@ -92,14 +92,14 @@ function formatFileSize(value: number | null): string {
|
||||
|
||||
<p
|
||||
v-if="downloads?.warning"
|
||||
class="m-0 rounded-cp bg-cp-warning-bg px-3 py-2.5 text-cp-sm leading-[1.45] font-bold text-cp-warning-text-on-bg"
|
||||
class="m-0 rounded-cp bg-cp-warning-container px-3 py-2.5 text-cp-sm leading-[1.45] font-bold text-cp-warning-on-container"
|
||||
>
|
||||
{{ downloads.warning }}
|
||||
</p>
|
||||
|
||||
<p
|
||||
v-if="error && downloads"
|
||||
class="m-0 rounded-cp bg-cp-error-bg px-3 py-2.5 text-cp-sm leading-[1.45] font-bold text-cp-error-text-on-bg"
|
||||
class="m-0 rounded-cp bg-cp-error-container px-3 py-2.5 text-cp-sm leading-[1.45] font-bold text-cp-error-on-container"
|
||||
role="alert"
|
||||
>
|
||||
{{ error }},当前仍显示上次成功结果。
|
||||
|
||||
@@ -28,7 +28,7 @@ const emit = defineEmits<{
|
||||
<div class="min-w-0">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<strong class="text-cp-sm font-bold text-cp-text">{{ label }}</strong>
|
||||
<span v-if="overridden" class="rounded-full bg-cp-primary-bg px-1.5 py-0.5 text-[9px] font-heavy text-cp-primary-text">
|
||||
<span v-if="overridden" class="rounded-full bg-cp-primary-container px-1.5 py-0.5 text-[9px] font-heavy text-cp-primary-on-container">
|
||||
已修改
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -112,11 +112,11 @@ const pagination: BaseTablePaginationState = {
|
||||
|
||||
function statusClass(status: PreviewTableRow['status']) {
|
||||
if (status === '正常')
|
||||
return 'bg-cp-success-bg text-cp-success-text-on-bg'
|
||||
return 'bg-cp-success-container text-cp-success-on-container'
|
||||
if (status === '限流')
|
||||
return 'bg-cp-warning-bg text-cp-warning-text-on-bg'
|
||||
return 'bg-cp-warning-container text-cp-warning-on-container'
|
||||
if (status === '异常')
|
||||
return 'bg-cp-error-bg text-cp-error-text-on-bg'
|
||||
return 'bg-cp-error-container text-cp-error-on-container'
|
||||
return 'bg-cp-fill-tertiary text-cp-text-secondary'
|
||||
}
|
||||
</script>
|
||||
@@ -137,7 +137,7 @@ function statusClass(status: PreviewTableRow['status']) {
|
||||
</div>
|
||||
|
||||
<div class="flex shrink-0 items-center gap-6">
|
||||
<span class="rounded-full bg-cp-primary-bg px-3 py-1.5 font-mono text-[10px] font-heavy tracking-wide text-cp-primary-text uppercase">
|
||||
<span class="rounded-full bg-cp-primary-container px-3 py-1.5 font-mono text-[10px] font-heavy tracking-wide text-cp-primary-on-container uppercase">
|
||||
20 Base Components
|
||||
</span>
|
||||
<div class="flex items-center gap-5" aria-label="当前主题语义色">
|
||||
@@ -300,16 +300,16 @@ function statusClass(status: PreviewTableRow['status']) {
|
||||
<BaseCard padding="compact" class="col-span-4 h-full min-w-0 bg-cp-fill-quaternary! shadow-none!" title="反馈语义" description="Success · Warning · Error · Info">
|
||||
<template #body>
|
||||
<div class="grid gap-2.5">
|
||||
<div class="flex items-center gap-3 rounded-cp bg-cp-success-bg px-3.5 py-3 text-cp-success-text-on-bg">
|
||||
<div class="flex items-center gap-3 rounded-cp bg-cp-success-container px-3.5 py-3 text-cp-success-on-container">
|
||||
<Check class="size-4 shrink-0" /><strong class="text-cp-sm">主题设置已保存</strong>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 rounded-cp bg-cp-warning-bg px-3.5 py-3 text-cp-warning-text-on-bg">
|
||||
<div class="flex items-center gap-3 rounded-cp bg-cp-warning-container px-3.5 py-3 text-cp-warning-on-container">
|
||||
<TriangleAlert class="size-4 shrink-0" /><strong class="text-cp-sm">3 个账号额度接近上限</strong>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 rounded-cp bg-cp-error-bg px-3.5 py-3 text-cp-error-text-on-bg">
|
||||
<div class="flex items-center gap-3 rounded-cp bg-cp-error-container px-3.5 py-3 text-cp-error-on-container">
|
||||
<CircleAlert class="size-4 shrink-0" /><strong class="text-cp-sm">连接测试未通过</strong>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 rounded-cp bg-cp-info-bg px-3.5 py-3 text-cp-info-text-on-bg">
|
||||
<div class="flex items-center gap-3 rounded-cp bg-cp-info-container px-3.5 py-3 text-cp-info-on-container">
|
||||
<Info class="size-4 shrink-0" /><strong class="text-cp-sm">调度规则将在下次请求生效</strong>
|
||||
</div>
|
||||
</div>
|
||||
@@ -320,10 +320,10 @@ function statusClass(status: PreviewTableRow['status']) {
|
||||
<template #body>
|
||||
<div class="grid gap-4">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<span class="rounded-full bg-cp-success-bg px-3 py-1.5 text-cp-xs font-heavy text-cp-success-text-on-bg">运行正常</span>
|
||||
<span class="rounded-full bg-cp-warning-bg px-3 py-1.5 text-cp-xs font-heavy text-cp-warning-text-on-bg">额度受限</span>
|
||||
<span class="rounded-full bg-cp-error-bg px-3 py-1.5 text-cp-xs font-heavy text-cp-error-text-on-bg">需要处理</span>
|
||||
<span class="rounded-full bg-cp-info-bg px-3 py-1.5 text-cp-xs font-heavy text-cp-info-text-on-bg">同步中</span>
|
||||
<span class="rounded-full bg-cp-success-container px-3 py-1.5 text-cp-xs font-heavy text-cp-success-on-container">运行正常</span>
|
||||
<span class="rounded-full bg-cp-warning-container px-3 py-1.5 text-cp-xs font-heavy text-cp-warning-on-container">额度受限</span>
|
||||
<span class="rounded-full bg-cp-error-container px-3 py-1.5 text-cp-xs font-heavy text-cp-error-on-container">需要处理</span>
|
||||
<span class="rounded-full bg-cp-info-container px-3 py-1.5 text-cp-xs font-heavy text-cp-info-on-container">同步中</span>
|
||||
</div>
|
||||
<div v-for="item in progressItems" :key="item.label" class="grid gap-2">
|
||||
<div class="flex justify-between text-cp-sm font-heavy text-cp-text-secondary">
|
||||
|
||||
@@ -288,7 +288,7 @@ function presetSwatches(preset: ThemeColorPreset) {
|
||||
|
||||
<section v-else class="grid gap-2" aria-label="风格 Token">
|
||||
<div
|
||||
class="rounded-cp-lg bg-cp-primary-bg px-3 py-3 text-[10px] leading-normal font-emphasis text-cp-primary-text"
|
||||
class="rounded-cp-lg bg-cp-primary-container px-3 py-3 text-[10px] leading-normal font-emphasis text-cp-primary-on-container"
|
||||
>
|
||||
固定无边设计;圆角塑形,阴影分层。
|
||||
</div>
|
||||
|
||||
@@ -28,7 +28,7 @@ const emit = defineEmits<{
|
||||
<div class="min-w-0">
|
||||
<div class="flex items-center gap-2">
|
||||
<strong class="text-cp-sm font-bold text-cp-text">{{ label }}</strong>
|
||||
<span v-if="overridden" class="rounded-full bg-cp-primary-bg px-1.5 py-0.5 text-[9px] font-heavy text-cp-primary-text">已修改</span>
|
||||
<span v-if="overridden" class="rounded-full bg-cp-primary-container px-1.5 py-0.5 text-[9px] font-heavy text-cp-primary-on-container">已修改</span>
|
||||
</div>
|
||||
<code class="mt-1 block truncate font-mono text-[10px] text-cp-text-quaternary">{{ token }}</code>
|
||||
</div>
|
||||
|
||||
@@ -130,7 +130,7 @@ function upstreamSendStateText(value: string | null | undefined) {
|
||||
</code>
|
||||
<span
|
||||
v-if="row.metadata.recoveredAt"
|
||||
class="inline-flex h-5 shrink-0 items-center rounded-full bg-cp-success-bg px-2 text-cp-xs leading-none font-heavy text-cp-success-text-on-bg"
|
||||
class="inline-flex h-5 shrink-0 items-center rounded-full bg-cp-success-container px-2 text-cp-xs leading-none font-heavy text-cp-success-on-container"
|
||||
>
|
||||
已自动恢复
|
||||
</span>
|
||||
|
||||
@@ -11,7 +11,7 @@ const clientIp = computed(() => usageClientIp(props.record))
|
||||
|
||||
<template>
|
||||
<span
|
||||
class="inline-flex h-6 max-w-full items-center rounded-full bg-cp-blue-bg px-2.5 font-mono text-cp-sm leading-none font-bold text-cp-blue-text-on-bg"
|
||||
class="inline-flex h-6 max-w-full items-center rounded-full bg-cp-blue-container px-2.5 font-mono text-cp-sm leading-none font-bold text-cp-blue-on-container"
|
||||
:title="clientIp"
|
||||
>
|
||||
<span class="min-w-0 truncate">
|
||||
|
||||
@@ -18,7 +18,7 @@ defineProps<{
|
||||
<template #trigger>
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex size-4 items-center justify-center rounded-full bg-cp-primary-bg text-cp-primary-text outline-none hover:bg-cp-primary-bg-hover focus-visible:ring-2 focus-visible:ring-cp-control-outline"
|
||||
class="inline-flex size-4 items-center justify-center rounded-full bg-cp-primary-container text-cp-primary-on-container outline-none hover:bg-cp-primary-container-hover focus-visible:ring-2 focus-visible:ring-cp-control-outline"
|
||||
:aria-label="triggerLabel"
|
||||
>
|
||||
<Info class="size-3" />
|
||||
|
||||
@@ -9,14 +9,14 @@ function statusClass(statusCode?: number | null) {
|
||||
}
|
||||
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
return 'bg-cp-success-bg text-cp-success-text-on-bg'
|
||||
return 'bg-cp-success-container text-cp-success-on-container'
|
||||
}
|
||||
|
||||
if (statusCode >= 300 && statusCode < 400) {
|
||||
return 'bg-cp-warning-bg text-cp-warning-text-on-bg'
|
||||
return 'bg-cp-warning-container text-cp-warning-on-container'
|
||||
}
|
||||
|
||||
return 'bg-cp-error-bg text-cp-error-text-on-bg'
|
||||
return 'bg-cp-error-container text-cp-error-on-container'
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ const items = computed(() => [
|
||||
icon: Activity,
|
||||
value: props.summary.totalRequests,
|
||||
detail: '筛选范围内',
|
||||
tone: 'bg-cp-blue-bg text-cp-blue-text-on-bg',
|
||||
tone: 'bg-cp-blue-container text-cp-blue-on-container',
|
||||
},
|
||||
{
|
||||
key: 'tokens',
|
||||
@@ -29,7 +29,7 @@ const items = computed(() => [
|
||||
icon: FileText,
|
||||
value: props.summary.totalTokens,
|
||||
detail: `输入 ${props.summary.inputTokens} / 输出 ${props.summary.outputTokens}`,
|
||||
tone: 'bg-cp-green-bg text-cp-green-text-on-bg',
|
||||
tone: 'bg-cp-green-container text-cp-green-on-container',
|
||||
},
|
||||
{
|
||||
key: 'cached',
|
||||
@@ -37,7 +37,7 @@ const items = computed(() => [
|
||||
icon: Database,
|
||||
value: props.summary.cachedTokens,
|
||||
detail: '缓存读取命中',
|
||||
tone: 'bg-cp-orange-bg text-cp-orange-text-on-bg',
|
||||
tone: 'bg-cp-orange-container text-cp-orange-on-container',
|
||||
},
|
||||
{
|
||||
key: 'latency',
|
||||
@@ -45,7 +45,7 @@ const items = computed(() => [
|
||||
icon: Timer,
|
||||
value: averageLatencyDisplay(props.summary.averageLatencyMs),
|
||||
detail: '成功请求平均值',
|
||||
tone: 'bg-cp-cyan-bg text-cp-cyan-text-on-bg',
|
||||
tone: 'bg-cp-cyan-container text-cp-cyan-on-container',
|
||||
},
|
||||
])
|
||||
</script>
|
||||
|
||||
@@ -164,9 +164,9 @@ export function usageTransportType(transport?: string | null) {
|
||||
export function usageTransportTypeClass(transport?: string | null) {
|
||||
const type = usageTransportType(transport)
|
||||
if (type === 'WS')
|
||||
return 'bg-cp-blue-bg text-cp-blue-text-on-bg'
|
||||
return 'bg-cp-blue-container text-cp-blue-on-container'
|
||||
if (type === 'SSE')
|
||||
return 'bg-cp-green-bg text-cp-green-text-on-bg'
|
||||
return 'bg-cp-green-container text-cp-green-on-container'
|
||||
return 'bg-cp-fill-tertiary text-cp-text-secondary'
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user