跨端: 语义色/三级文字在深色下没提亮(设备扫出 12 处)+ 判据取样法修到第三版
## 一、判据基建先修对,否则是在听噪声
深色可读性扫描的**取样法迭代了三版**,每版都因为**假红**才改的
(记在这里,因为"判据自己错"比"界面错"更费时间):
1. **取中心一个像素** ⇒ 落在笔画之间、读到的是底色 ⇒ 一片 ratio=1.00 假红。
2. **取全局最暗/最亮** ⇒ 会采到**两个不同的东西**:细字的抗锯齿中间色。
实例:segmented「日」报 ink `rgb(32,34,35)` / bg `rgb(98,100,101)` 2.69:1 ——
而截图里它要么蓝底白字、要么深底浅字,两种都远高于 3:1。
**反复核对才确认是判据的错,不是界面的错。**
3. **步长采样 + 众数** ⇒ 仍然假红:周表头「一」报 2.03:1,
而逐像素的真实墨是 `rgb(166,167,167)`(**6.6:1**)——
稀疏网格**整个跳过了笔画**。这不是调参能修好的(字越小越糟)。
4. **逐像素 + 众数=底 + 离底最远者=墨**(最终版)。
理由:一个文字框里**面积最大的一定是底**,而"离底色最远"就是笔画最实那部分。
⚠️ 逐像素是**负担得起的**(整屏已解码在内存,一个框最多 9 万像素)——
**不要再为了省开销退回抽样**,前两版都因此假红。
## 二、真 bug 三批(都是"深色下没提亮")
### ① 语义色前景(12 处 → 修完设备复扫 0)
WebUI 的语义色也是**双通道**,`.dark` 段把 `red/green/amber-700` 换成浅色
(`248/118/249 → 164/219/195` 那组)。鸿蒙只有一个深色值:
- 「归档」按钮 `Theme.danger` #B91C1C 压深面 ⇒ **2.47:1**(联系人页 7 处)
- 「今天」/「日」等 ⇒ **2.38–2.77:1**
加 `dangerDark/approveDark/warnFgDark`(逐字对齐 WebUI `.dark`)
+ `dangerFor()/approveFor()/warnFgFor()` 入口,22 处接入。
### ② 日历工具条整块配色抄错(不是"深色档选错")
对照 WebUI `CalendarView.tsx:313-331`:
| 元素 | WebUI | 鸿蒙(改前) |
|---|---|---|
| 「今天」 | `border-gray-300` + `text-gray-700`(**中性**) | `accentSoft` 底 + `accentStrong` 字 |
| 月/周/日 未选中 | `bg-white` + `text-gray-700` | `accentSoft` 底 |
鸿蒙把**次要的中性按钮**画成了**品牌色按钮** —— 视觉重量跟主操作一样,
三个档看起来"都像选中"。已按 WebUI 改成中性。
### ③ 三级文字在深色下 2.82:1(**71 处**,全局最大的一个)
WebUI 的 `.dark` 段注释逐字写着这个坑:
> 「`gray-400/500`(次要文字)→ **提亮**。深底上的浅色 gray-400
> 只有约 2:1 对比度,远低于 WCAG AA 的 4.5:1 —— **看得见但读不动**。」
鸿蒙用的系统三级色 `ohos_id_color_text_tertiary` 深色下是 `rgb(102,103,105)`
压深面 **2.82–3.05:1**,而它被用在 10–11px 的**小字**上
(时间戳、农历、空态、辅助说明)。
**不换掉系统令牌**("系统拥有的维度"纪律照旧),加 `textSubtleFor()`:
深色下改用二级色(实测 `rgb(166,167,167)` ≈ 7.15:1,
且与 WebUI 的 `gray-500` 同档位)。71 处接入。
**设备复扫(修后)**:通信/日历/联系三页 **176 段文字全部 ≥3:1**。
## 三、欠账与底本
- `baseline.sha` 第 4 次重算。仍**先逐个取证**:
`git diff --quiet HEAD -- <f> && echo STALE || echo INTENTIONAL`
⇒ 三个全是 `INTENTIONAL`(我改的),不是变异残留。
把这条取证命令也写进文件,下次照抄。
- `dangerDark/approveDark/warnFgDark` 已登记进 `SELF_OWNED_COLORS`
+ `Theme.ets` 登记表(各带理由与 WebUI 出处)。
`run-all.mjs` → `checks=514 pass=514 fail=0 skip=0 red=0 broken=0 unreported=0`。
**未验**:①「我的」页(底部头像按钮,扫描脚本没找到入口)没扫;
② 浅色下的对比度没扫(本轮全部针对深色)。
This commit is contained in:
@ -259,7 +259,7 @@ export struct BackgroundPicker {
|
||||
Column() {
|
||||
Row() {
|
||||
Text('背景').fontSize(Theme.fontBody).fontColor(Theme.textPrimary).layoutWeight(1)
|
||||
Text(this.statusText).fontSize(Theme.fontTiny).fontColor(Theme.textSubtle)
|
||||
Text(this.statusText).fontSize(Theme.fontTiny).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%')
|
||||
|
||||
@ -302,17 +302,17 @@ export struct BackgroundPicker {
|
||||
.onClick(() => { this.pickAndUpload(); })
|
||||
|
||||
if (this.progress.length > 0) {
|
||||
Text(this.progress).fontSize(Theme.fontTiny).fontColor(Theme.textSubtle)
|
||||
Text(this.progress).fontSize(Theme.fontTiny).fontColor(Theme.textSubtleFor())
|
||||
.width('100%').margin({ top: 6 })
|
||||
}
|
||||
if (this.uploadError.length > 0) {
|
||||
Text(this.uploadError)
|
||||
.fontSize(Theme.fontTiny).fontColor(Theme.danger)
|
||||
.fontSize(Theme.fontTiny).fontColor(Theme.dangerFor())
|
||||
.width('100%').padding(8).margin({ top: 6 })
|
||||
.backgroundColor(Theme.dangerBg).borderRadius(Theme.radiusControl)
|
||||
}
|
||||
Text('上传前会先压到最长边 ' + MAX_EDGE + ' 像素(手机直出照片 4–8MB,服务端上限 4MB)。')
|
||||
.fontSize(Theme.fontTiny).fontColor(Theme.textSubtle)
|
||||
.fontSize(Theme.fontTiny).fontColor(Theme.textSubtleFor())
|
||||
.width('100%').margin({ top: 6 })
|
||||
}
|
||||
.width('100%').alignItems(HorizontalAlign.Start).margin({ top: 8 })
|
||||
@ -352,7 +352,7 @@ export struct BackgroundPicker {
|
||||
*/
|
||||
Text('压暗').fontSize(Theme.fontTiny).fontColor(Theme.textMuted)
|
||||
Blank()
|
||||
Text(Math.round(this.bgDim) + '%').fontSize(Theme.fontTiny).fontColor(Theme.textSubtle)
|
||||
Text(Math.round(this.bgDim) + '%').fontSize(Theme.fontTiny).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%')
|
||||
Slider({ value: this.bgDim, min: 0, max: 80, step: 1 })
|
||||
@ -369,7 +369,7 @@ export struct BackgroundPicker {
|
||||
Text('模糊').fontSize(Theme.fontTiny).fontColor(Theme.textMuted)
|
||||
Blank()
|
||||
/* 同 ①:原先印 `3.000000`;WebUI 是 `suffix="px"` ⇒ `3px` */
|
||||
Text(Math.round(this.bgBlur) + 'px').fontSize(Theme.fontTiny).fontColor(Theme.textSubtle)
|
||||
Text(Math.round(this.bgBlur) + 'px').fontSize(Theme.fontTiny).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%')
|
||||
Slider({ value: this.bgBlur, min: 0, max: 40, step: 1 })
|
||||
|
||||
@ -70,6 +70,47 @@ export class Theme {
|
||||
static readonly textMuted: Resource = $r('sys.color.ohos_id_color_text_secondary');
|
||||
static readonly textSubtle: Resource = $r('sys.color.ohos_id_color_text_tertiary');
|
||||
|
||||
/*
|
||||
* ──────── 三级文字在**深色**下的可读性下限(2026-09-19 加)────────
|
||||
*
|
||||
* ★★ 问题(设备实测,不是推测):
|
||||
* 系统三级色 `ohos_id_color_text_tertiary` 在**深色**下是 `rgb(102,103,105)`,
|
||||
* 压在卡片面 `rgb(32,34,36)` 上只有 **2.82–3.05:1**。
|
||||
* 它被用在**小字**上(10–11px 的时间戳、农历、空态、辅助说明,全局 66 处)
|
||||
* ⇒ 远低于 WCAG AA 对正文的 4.5:1。
|
||||
*
|
||||
* ★ 这不是鸿蒙独有的问题 —— WebUI 早就遇到过并写下了结论
|
||||
* (`client/electron/src/index.css` 的 `.dark` 段注释,逐字引用):
|
||||
*
|
||||
* 「`gray-400/500`(次要文字)→ **提亮**。深底上的浅色 gray-400
|
||||
* 只有约 2:1 对比度,远低于 WCAG AA 的 4.5:1 —— **看得见但读不动**。」
|
||||
*
|
||||
* 它的做法是在 `.dark` 段把 gray-400 从 `107 114 128` 提亮到 `138 146 161`,
|
||||
* gray-500 从 `90 98 112` 提到 `165 173 186`。实测那两组压在深卡片上
|
||||
* 分别是 **5.51:1 / 7.63:1**,都在 AA 之上。
|
||||
*
|
||||
* ★ 系统色为什么没有这层保护:`ohos_id_color_text_tertiary` 的语义是
|
||||
* 「比二级更淡」——它只保证**层次关系**,不保证可读性下限。
|
||||
* 当"更淡"用在浅底上时刚好合适;深底上同样的亮度就掉下去了。
|
||||
* (这与 `Theme.surface` 当前景色是同一类:**系统语义 ≠ 我们的可读性要求**。)
|
||||
*
|
||||
* ★ 修法:**不换掉系统令牌**(上面那条"系统拥有的维度"纪律照旧),
|
||||
* 而是提供一个「按主题取三级文字」的入口:深色下改用二级色。
|
||||
* 之所以用二级而不是另写一个白:
|
||||
* · 二级色深色下实测 `rgb(166,167,167)` ≈ 7.15:1,在 AA 之上;
|
||||
* · 它与 WebUI 的 `gray-500` 是同一个档位(两端视觉层次一致);
|
||||
* · 不引入新的手写色(不用再过登记表)。
|
||||
*
|
||||
* ★ 浅色下**不改**:浅色下系统三级色压在浅底上够用(与 WebUI 的
|
||||
* gray-400 `107 114 128` 同量级),换了反而会让层次变平。
|
||||
*
|
||||
* ★ 使用入口是 `Theme.textSubtleFor()` —— 与 `accentFor` / `accentSoftFor`
|
||||
* 同一条纪律:**别在页面里自己写 `isDark ? a : b`**。
|
||||
*/
|
||||
static textSubtleFor(dark?: boolean): Resource {
|
||||
return Theme.isDarkNow(dark) ? Theme.textMuted : Theme.textSubtle;
|
||||
}
|
||||
|
||||
// ─────────────── 遮罩与材质:交给系统 ───────────────
|
||||
|
||||
/**
|
||||
@ -161,6 +202,15 @@ export class Theme {
|
||||
* ★ 不登记会怎样:深色下选中卡片是接近纯白的浅蓝,
|
||||
* 在深色页面上刺眼得像 bug(2026-09-19 设备实测到)
|
||||
* · accentStrong #1D4ED8 品牌深色变体(选中文字/边框)
|
||||
* · dangerDark #F8A4A4 危险/拒绝在**深色**下当前景的取值 ——
|
||||
* 对齐 WebUI `.dark --c-red-700`(`248 164 164`)。
|
||||
* ★ 入口 `Theme.dangerFor()`。
|
||||
* 不登记会怎样:深色下「归档」按钮只有 2.47:1
|
||||
* (2026-09-19 设备实测,联系人页 7 处)
|
||||
* · approveDark #76DB9B 同意在**深色**下当前景的取值 ——
|
||||
* 对齐 WebUI `.dark --c-green-700`。入口 `approveFor()`。
|
||||
* · warnFgDark #F9C368 警示文字在**深色**下的取值 ——
|
||||
* 对齐 WebUI `.dark --c-amber-700`。入口 `warnFgFor()`。
|
||||
* · accentDark #80AFF9 品牌色在**深色**下当前景用的取值 ——
|
||||
* 对齐 WebUI `index.css:481` 的
|
||||
* `.dark --c-blue-600`(`128 175 249`)。
|
||||
@ -420,6 +470,45 @@ export class Theme {
|
||||
static readonly warnBg: string = '#FFFBEB';
|
||||
static readonly warnFg: string = '#B45309';
|
||||
|
||||
/*
|
||||
* ──── 语义色的**深色档**(2026-09-19 加)────
|
||||
*
|
||||
* ★★ 和 `accentDark` 完全同一个问题、同一套证据:WebUI 的语义色也是
|
||||
* **双通道**。浅色下 `red-700` 是 `185 28 28`(深红,给白底用);
|
||||
* 深色下它被换成 `248 164 164`(**浅红**),否则深底上读不动。
|
||||
*
|
||||
* 实测(设备,深色主题):联系人页的「归档」按钮是
|
||||
* `Theme.danger` = `#B91C1C` 压在卡片面上 ⇒ **2.47:1**,
|
||||
* 低于 WCAG 图形下限 3:1。同样的还有日历页的
|
||||
* 「今天」标(`accentStrong` 底 + 深字)等 4 处。
|
||||
*
|
||||
* ★ 取值直接对 WebUI `index.css` 的 `.dark` 段(`grep '^ --c-'` 可得):
|
||||
* red-700 `248 164 164`
|
||||
* green-700 `118 219 155`
|
||||
* amber-700 `249 195 104`
|
||||
*
|
||||
* ★ 入口是下面那组 `*For(dark?)` —— 与 `accentFor` 同一条纪律:
|
||||
* **别在页面里自己 `isDark ? a : b`**(约定已经漏过 8 处)。
|
||||
*/
|
||||
static readonly dangerDark: string = '#F8A4A4';
|
||||
static readonly approveDark: string = '#76DB9B';
|
||||
static readonly warnFgDark: string = '#F9C368';
|
||||
|
||||
/** 按主题给拒绝/危险**前景**色(压在面上的红字/红图标)。 */
|
||||
static dangerFor(dark?: boolean): string {
|
||||
return Theme.isDarkNow(dark) ? Theme.dangerDark : Theme.danger;
|
||||
}
|
||||
|
||||
/** 按主题给同意**前景**色。 */
|
||||
static approveFor(dark?: boolean): string {
|
||||
return Theme.isDarkNow(dark) ? Theme.approveDark : Theme.approve;
|
||||
}
|
||||
|
||||
/** 按主题给警示**前景**色。 */
|
||||
static warnFgFor(dark?: boolean): string {
|
||||
return Theme.isDarkNow(dark) ? Theme.warnFgDark : Theme.warnFg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限档位 → 徽标底色 / 文字色。
|
||||
*
|
||||
|
||||
@ -288,13 +288,13 @@ struct AdminUsersPage {
|
||||
Text('管理入口仅管理员可见').fontSize(Theme.fontBody).fontColor(Theme.textMuted).margin({ left: 6 })
|
||||
}
|
||||
Text('当前账号的角色不是管理员。')
|
||||
.fontSize(Theme.fontSmall).fontColor(Theme.textSubtle).margin({ top: 6 })
|
||||
.fontSize(Theme.fontSmall).fontColor(Theme.textSubtleFor()).margin({ top: 6 })
|
||||
}
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else {
|
||||
if (this.errorText.length > 0) {
|
||||
Text(this.errorText)
|
||||
.fontSize(Theme.fontSmall).fontColor(Theme.danger)
|
||||
.fontSize(Theme.fontSmall).fontColor(Theme.dangerFor())
|
||||
.width('100%').padding(10).margin({ left: 12, right: 12, top: 8 })
|
||||
.backgroundColor(Theme.dangerBg)
|
||||
.borderRadius(Theme.radiusControl)
|
||||
@ -302,7 +302,7 @@ struct AdminUsersPage {
|
||||
|
||||
Row() {
|
||||
Text('用户管理').fontSize(Theme.fontSmall).fontColor(Theme.textMuted)
|
||||
Text(' ' + this.users.length).fontSize(Theme.fontSmall).fontColor(Theme.textSubtle)
|
||||
Text(' ' + this.users.length).fontSize(Theme.fontSmall).fontColor(Theme.textSubtleFor())
|
||||
Blank()
|
||||
Button(this.showCreate ? '收起' : '新建用户')
|
||||
.height(34).fontSize(Theme.fontSmall)
|
||||
@ -316,7 +316,7 @@ struct AdminUsersPage {
|
||||
}
|
||||
|
||||
if (this.loading && this.users.length === 0) {
|
||||
Text('加载中…').fontSize(Theme.fontSmall).fontColor(Theme.textSubtle)
|
||||
Text('加载中…').fontSize(Theme.fontSmall).fontColor(Theme.textSubtleFor())
|
||||
.width('100%').textAlign(TextAlign.Center).margin({ top: 24 })
|
||||
} else {
|
||||
List({ space: 8 }) {
|
||||
@ -377,7 +377,7 @@ struct AdminUsersPage {
|
||||
Column() {
|
||||
Row() {
|
||||
Text(this.editingId === user.user_id ? '⌄' : '›')
|
||||
.fontSize(Theme.fontSmall).fontColor(Theme.textSubtle).width(18)
|
||||
.fontSize(Theme.fontSmall).fontColor(Theme.textSubtleFor()).width(18)
|
||||
Text(user.username)
|
||||
.fontSize(Theme.fontBody).fontColor(Theme.textPrimary).layoutWeight(1)
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
@ -404,10 +404,10 @@ struct AdminUsersPage {
|
||||
|
||||
Row() {
|
||||
Text(user.display_name.length > 0 ? user.display_name : user.username)
|
||||
.fontSize(Theme.fontSmall).fontColor(Theme.textSubtle).layoutWeight(1)
|
||||
.fontSize(Theme.fontSmall).fontColor(Theme.textSubtleFor()).layoutWeight(1)
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
Text(lastLoginLabel(user))
|
||||
.fontSize(Theme.fontTiny).fontColor(Theme.textSubtle)
|
||||
.fontSize(Theme.fontTiny).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%').margin({ top: 4 })
|
||||
|
||||
@ -467,14 +467,14 @@ struct AdminUsersPage {
|
||||
this.ScopePick('可访问目录', this.scopes.paths, this.editPaths, false)
|
||||
} else {
|
||||
Text('管理员不受白名单限制(服务端对管理员忽略这两项)。')
|
||||
.fontSize(Theme.fontTiny).fontColor(Theme.textSubtle)
|
||||
.fontSize(Theme.fontTiny).fontColor(Theme.textSubtleFor())
|
||||
.width('100%').margin({ top: 8 })
|
||||
}
|
||||
|
||||
Row() {
|
||||
Button(user.status === 'active' ? '禁用' : '启用')
|
||||
.height(34).fontSize(Theme.fontSmall)
|
||||
.backgroundColor(Theme.surfaceMuted).fontColor(Theme.danger)
|
||||
.backgroundColor(Theme.surfaceMuted).fontColor(Theme.dangerFor())
|
||||
.onClick(() => {
|
||||
this.setStatus(user, user.status === 'active' ? 'disabled' : 'active');
|
||||
})
|
||||
@ -530,14 +530,14 @@ struct AdminUsersPage {
|
||||
Row() {
|
||||
Text(label).fontSize(Theme.fontTiny).fontColor(Theme.textMuted)
|
||||
Text(selected.length > 0 ? '(已选 ' + selected.length + ' 项)' : '')
|
||||
.fontSize(Theme.fontTiny).fontColor(Theme.textSubtle)
|
||||
.fontSize(Theme.fontTiny).fontColor(Theme.textSubtleFor())
|
||||
Blank()
|
||||
Text('未勾选 = 不限').fontSize(Theme.fontTiny).fontColor(Theme.textSubtle)
|
||||
Text('未勾选 = 不限').fontSize(Theme.fontTiny).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%').margin({ top: 10 })
|
||||
|
||||
if (items.length === 0) {
|
||||
Text('(服务端没给出候选)').fontSize(Theme.fontTiny).fontColor(Theme.textSubtle)
|
||||
Text('(服务端没给出候选)').fontSize(Theme.fontTiny).fontColor(Theme.textSubtleFor())
|
||||
.width('100%').margin({ top: 4 })
|
||||
} else {
|
||||
Flex({ wrap: FlexWrap.Wrap }) {
|
||||
@ -600,7 +600,7 @@ struct AdminUsersPage {
|
||||
.width('100%').margin({ top: 10 })
|
||||
|
||||
Text('白名单建完再配:创建后在新用户那张卡里勾「可调用 Agent / 可访问目录」。未勾 = 不限。')
|
||||
.fontSize(Theme.fontTiny).fontColor(Theme.textSubtle)
|
||||
.fontSize(Theme.fontTiny).fontColor(Theme.textSubtleFor())
|
||||
.width('100%').margin({ top: 8 })
|
||||
|
||||
Row() {
|
||||
|
||||
@ -862,7 +862,20 @@ export struct CalendarPage {
|
||||
*/
|
||||
Text(this.lunarTextOf(cell.iso))
|
||||
.fontSize(9)
|
||||
.fontColor(cell.iso === this.selectedIso ? Theme.accentFg : Theme.textSubtle)
|
||||
/*
|
||||
* ★ ★ 2026-09-19 改:`textSubtle`(系统三级文字)在**深色**下太暗 ——
|
||||
* 设备实测农历"二十"只有 **2.49:1**(`rgb(93,95,96)` 底上)。
|
||||
*
|
||||
* WebUI 用的是 `text-gray-500`(`CalendarView.tsx` 农历那行),
|
||||
* 而它在 `.dark` 段被**提亮**到 `165 173 186`(见 `index.css`)——
|
||||
* 正是 `--c-gray-*` 那组注释说的"深底上的浅色 gray-400 只有约 2:1,
|
||||
* 远低于 WCAG AA,看得见但读不动"。
|
||||
*
|
||||
* 系统三级色没有这层适配(它只管"比二级更淡",不管可读性下限),
|
||||
* ⇒ 深色下用它当**小字**会掉到 2.5:1。改用二级(`textMuted`),
|
||||
* 与 WebUI 的 gray-500 档位一致。
|
||||
*/
|
||||
.fontColor(cell.iso === this.selectedIso ? Theme.accentFg : Theme.textMuted)
|
||||
.maxLines(1)
|
||||
if (this.eventCountOf(cell.iso) > 0) {
|
||||
Column()
|
||||
@ -1077,7 +1090,7 @@ export struct CalendarPage {
|
||||
if (this.formError.length > 0) {
|
||||
Text(this.formError)
|
||||
.fontSize(Theme.fontSmall)
|
||||
.fontColor(Theme.danger)
|
||||
.fontColor(Theme.dangerFor())
|
||||
.width('100%')
|
||||
.margin({ top: 8 })
|
||||
}
|
||||
@ -1094,7 +1107,7 @@ export struct CalendarPage {
|
||||
Button(this.confirmDelete ? '确认删除' : '删除')
|
||||
.fontSize(Theme.fontSmall)
|
||||
.backgroundColor(Theme.dangerBg)
|
||||
.fontColor(Theme.danger)
|
||||
.fontColor(Theme.dangerFor())
|
||||
.margin({ left: 6 })
|
||||
.onClick(() => {
|
||||
if (this.confirmDelete) {
|
||||
@ -1177,10 +1190,26 @@ export struct CalendarPage {
|
||||
.padding({ left: 6, right: 6, top: 4, bottom: 0 })
|
||||
|
||||
Row() {
|
||||
/*
|
||||
* ★★ 2026-09-19 改:这个按钮**不该是品牌蓝**。
|
||||
*
|
||||
* WebUI 原样(`CalendarView.tsx:313-316`):
|
||||
* className="px-2.5 py-1 text-xs border border-gray-300
|
||||
* rounded hover:bg-gray-50 text-gray-700"
|
||||
* ⇒ 是**中性灰**(白底/灰边/深灰字),不是蓝底。
|
||||
*
|
||||
* 鸿蒙这边原来写成 `accentSoft` 底 + `accentStrong` 字 ——
|
||||
* 把"次要的中性按钮"画成了"品牌色按钮",视觉重量跟主操作一样。
|
||||
*
|
||||
* ★ 这也是设备实测发现的:深色下它读出来 2.38:1
|
||||
* (`accentStrong` #1D4ED8 压在 `accentSoftDark` #1C2536 上),
|
||||
* 而根因不是"选错了深色档",是**这一整块的配色语义就抄错了**。
|
||||
*/
|
||||
Button('今天')
|
||||
.fontSize(Theme.fontSmall)
|
||||
.backgroundColor(Theme.accentSoftFor(this.isDarkNow))
|
||||
.fontColor(Theme.accentStrong)
|
||||
.backgroundColor(Color.Transparent)
|
||||
.fontColor(Theme.textPrimary)
|
||||
.border({ width: 1, color: Theme.border })
|
||||
.onClick(() => this.goToday())
|
||||
Blank()
|
||||
/*
|
||||
@ -1191,7 +1220,15 @@ export struct CalendarPage {
|
||||
Text(scaleLabel(s))
|
||||
.fontSize(Theme.fontSmall)
|
||||
.fontColor(this.calScale === s ? Theme.accentFg : Theme.textPrimary)
|
||||
.backgroundColor(this.calScale === s ? Theme.accent : Theme.accentSoftFor(this.isDarkNow))
|
||||
/*
|
||||
* ★ 未选中档的底色是 **`surface`(面)**,不是 `accentSoft`。
|
||||
* WebUI 原样(`CalendarView.tsx:331`):
|
||||
* 未选中 `bg-white text-gray-700` / 选中 `bg-blue-600 text-white`
|
||||
* `bg-white` 在这套 token 里就是"卡片面"。
|
||||
* 原来用 `accentSoft` 等于把未选中项也染成品牌浅蓝 ——
|
||||
* 三个档看起来"都像选中",只剩深浅差别。
|
||||
*/
|
||||
.backgroundColor(this.calScale === s ? Theme.accent : Theme.surface)
|
||||
.textAlign(TextAlign.Center)
|
||||
.width(34)
|
||||
.height(26)
|
||||
@ -1294,7 +1331,7 @@ export struct CalendarPage {
|
||||
if (this.loading) {
|
||||
Text('加载中…')
|
||||
.fontSize(Theme.fontTiny)
|
||||
.fontColor(Theme.textSubtle)
|
||||
.fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
}
|
||||
.width('100%')
|
||||
@ -1310,7 +1347,7 @@ export struct CalendarPage {
|
||||
Column() {
|
||||
Text('这一天没有日程')
|
||||
.fontSize(Theme.fontSmall)
|
||||
.fontColor(Theme.textSubtle)
|
||||
.fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%')
|
||||
.layoutWeight(1)
|
||||
|
||||
@ -209,7 +209,7 @@ struct ComposePage {
|
||||
// 顶栏
|
||||
Row() {
|
||||
Text('取消')
|
||||
.fontSize(15).fontColor(Theme.danger)
|
||||
.fontSize(15).fontColor(Theme.dangerFor())
|
||||
.onClick(() => { this.getUIContext().getRouter().back(); })
|
||||
Blank()
|
||||
Text('写邮件').fontSize(16).fontWeight(FontWeight.Bold).fontColor(Theme.textPrimary)
|
||||
@ -236,13 +236,13 @@ struct ComposePage {
|
||||
// 发信账号选择(多账号时显示)
|
||||
if (this.accountList.length > 1) {
|
||||
Row() {
|
||||
Text('发信账号').fontSize(13).fontColor(Theme.textSubtle).width(72)
|
||||
Text('发信账号').fontSize(13).fontColor(Theme.textSubtleFor()).width(72)
|
||||
// 显示当前选中的账号名
|
||||
Text(this.getSelectedAccountName())
|
||||
.fontSize(13).fontColor(Theme.accentFor())
|
||||
.layoutWeight(1)
|
||||
.onClick(() => { this.showAccountPicker = !this.showAccountPicker; })
|
||||
Text(' ▾').fontSize(12).fontColor(Theme.textSubtle)
|
||||
Text(' ▾').fontSize(12).fontColor(Theme.textSubtleFor())
|
||||
.onClick(() => { this.showAccountPicker = !this.showAccountPicker; })
|
||||
}
|
||||
.width('100%').height(40).padding({ left: 12, right: 12 })
|
||||
@ -288,7 +288,7 @@ struct ComposePage {
|
||||
|
||||
// 收件人
|
||||
Row() {
|
||||
Text('收件人').fontSize(14).fontColor(Theme.textSubtle).width(60)
|
||||
Text('收件人').fontSize(14).fontColor(Theme.textSubtleFor()).width(60)
|
||||
TextInput({ placeholder: 'name@path.session', text: this.to })
|
||||
.layoutWeight(1).fontSize(14).backgroundColor(Color.Transparent)
|
||||
.onChange((v: string) => { this.to = v; })
|
||||
@ -300,7 +300,7 @@ struct ComposePage {
|
||||
|
||||
// 主题
|
||||
Row() {
|
||||
Text('主题').fontSize(14).fontColor(Theme.textSubtle).width(60)
|
||||
Text('主题').fontSize(14).fontColor(Theme.textSubtleFor()).width(60)
|
||||
TextInput({ placeholder: '邮件主题', text: this.subject })
|
||||
.layoutWeight(1).fontSize(14).backgroundColor(Color.Transparent)
|
||||
.onChange((v: string) => { this.subject = v; })
|
||||
@ -328,7 +328,7 @@ struct ComposePage {
|
||||
.margin({ left: 6 })
|
||||
.layoutWeight(1)
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
AmIcon({ iconName: 'close', iconSize: 14, iconColor: Theme.danger })
|
||||
AmIcon({ iconName: 'close', iconSize: 14, iconColor: Theme.dangerFor() })
|
||||
.onClick(() => { this.removeAttachment(idx); })
|
||||
}
|
||||
.width('100%').height(32)
|
||||
@ -354,7 +354,7 @@ struct ComposePage {
|
||||
.margin({ left: 12 })
|
||||
} else if (this.status.length > 0) {
|
||||
Text(this.status)
|
||||
.fontSize(11).fontColor(Theme.approve)
|
||||
.fontSize(11).fontColor(Theme.approveFor())
|
||||
.margin({ left: 12 })
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,20 +110,20 @@ struct InboxPage {
|
||||
if (this.loading) {
|
||||
Column() {
|
||||
LoadingProgress().width(40).height(40)
|
||||
Text('加载中…').fontSize(14).fontColor(Theme.textSubtle).margin({ top: 8 })
|
||||
Text('加载中…').fontSize(14).fontColor(Theme.textSubtleFor()).margin({ top: 8 })
|
||||
}
|
||||
.width('100%').height('80%')
|
||||
.justifyContent(FlexAlign.Center)
|
||||
} else if (this.error.length > 0) {
|
||||
Column() {
|
||||
Text(this.error).fontSize(14).fontColor(Theme.danger)
|
||||
Text(this.error).fontSize(14).fontColor(Theme.dangerFor())
|
||||
Button('重试').margin({ top: 12 }).onClick(() => { this.loadInbox(); })
|
||||
}
|
||||
.width('100%').height('80%')
|
||||
.justifyContent(FlexAlign.Center)
|
||||
} else if (this.mails.length === 0) {
|
||||
Column() {
|
||||
Text('收件箱为空').fontSize(16).fontColor(Theme.textSubtle)
|
||||
Text('收件箱为空').fontSize(16).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%').height('80%')
|
||||
.justifyContent(FlexAlign.Center)
|
||||
@ -204,14 +204,14 @@ struct InboxPage {
|
||||
Row() {
|
||||
Text(mail.from_name).fontSize(14).fontWeight(mail.is_read ? FontWeight.Normal : FontWeight.Bold).fontColor(Theme.textPrimary)
|
||||
Blank()
|
||||
Text(mail.created_at).fontSize(11).fontColor(Theme.textSubtle)
|
||||
Text(mail.created_at).fontSize(11).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%')
|
||||
|
||||
Text(mail.subject).fontSize(13).fontColor(Theme.textPrimary)
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.margin({ top: 2 })
|
||||
Text(mail.body_preview).fontSize(12).fontColor(Theme.textSubtle)
|
||||
Text(mail.body_preview).fontSize(12).fontColor(Theme.textSubtleFor())
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.margin({ top: 2 })
|
||||
}
|
||||
|
||||
@ -321,7 +321,7 @@ struct LoginPage {
|
||||
if (this.addrWarning.length > 0) {
|
||||
Text(this.addrWarning)
|
||||
.fontSize(11)
|
||||
.fontColor(Theme.warnFg)
|
||||
.fontColor(Theme.warnFgFor())
|
||||
.width('100%')
|
||||
.margin({ top: 6 })
|
||||
}
|
||||
@ -383,7 +383,7 @@ struct LoginPage {
|
||||
.type(InputType.Password)
|
||||
.onChange((v: string) => { this.userKey = v; })
|
||||
Text('密钥仅保存在本机,用于客户端访问你的邮箱。')
|
||||
.width('100%').fontSize(10).fontColor(Theme.textSubtle)
|
||||
.width('100%').fontSize(10).fontColor(Theme.textSubtleFor())
|
||||
.lineHeight(15).margin({ top: 5 })
|
||||
}
|
||||
|
||||
@ -405,7 +405,7 @@ struct LoginPage {
|
||||
Text('地址须以 /api/v1 结尾;模拟器 NAT 不通时可用 ' + EMULATOR_HOST_BASE)
|
||||
.width('100%')
|
||||
.fontSize(10)
|
||||
.fontColor(Theme.textSubtle)
|
||||
.fontColor(Theme.textSubtleFor())
|
||||
.textAlign(TextAlign.Center)
|
||||
.lineHeight(15)
|
||||
.margin({ top: 12 })
|
||||
|
||||
@ -357,7 +357,7 @@ export struct MailDetailView {
|
||||
MetaRow(label: string, value: string) {
|
||||
Row() {
|
||||
Text(label)
|
||||
.fontSize(11).fontColor(Theme.textSubtle)
|
||||
.fontSize(11).fontColor(Theme.textSubtleFor())
|
||||
.width(36).flexShrink(0)
|
||||
Text(value)
|
||||
.fontSize(12).fontColor(Theme.textMuted)
|
||||
@ -380,7 +380,7 @@ export struct MailDetailView {
|
||||
PermissionRow() {
|
||||
Row() {
|
||||
Text('权限')
|
||||
.fontSize(11).fontColor(Theme.textSubtle)
|
||||
.fontSize(11).fontColor(Theme.textSubtleFor())
|
||||
.width(36).flexShrink(0)
|
||||
if (this.switchingPerm) {
|
||||
LoadingProgress().width(16).height(16)
|
||||
@ -414,12 +414,12 @@ export struct MailDetailView {
|
||||
BudgetRow() {
|
||||
Row() {
|
||||
Text('预算')
|
||||
.fontSize(11).fontColor(Theme.textSubtle)
|
||||
.fontSize(11).fontColor(Theme.textSubtleFor())
|
||||
.width(36).flexShrink(0)
|
||||
|
||||
if (this.budget === null) {
|
||||
/* 还没读到就不显示 —— 不为一块可选信息留空壳 */
|
||||
Text('—').fontSize(11).fontColor(Theme.textSubtle)
|
||||
Text('—').fontSize(11).fontColor(Theme.textSubtleFor())
|
||||
} else if (this.budgetEditing) {
|
||||
/* 编辑态:空串 = 不限(WebUI 同口径,`placeholder` 也是「不限」) */
|
||||
TextInput({ text: this.budgetDraft, placeholder: '不限' })
|
||||
@ -453,7 +453,7 @@ export struct MailDetailView {
|
||||
*/
|
||||
Text(this.budgetLabel())
|
||||
.fontSize(11)
|
||||
.fontColor(this.budgetExhausted() ? Theme.danger : Theme.textMuted)
|
||||
.fontColor(this.budgetExhausted() ? Theme.dangerFor() : Theme.textMuted)
|
||||
.backgroundColor(this.budgetExhausted() ? Theme.dangerBg : Theme.chipNeutralBg)
|
||||
.borderRadius(4)
|
||||
.padding({ left: 8, right: 8, top: 3, bottom: 3 })
|
||||
@ -577,13 +577,13 @@ export struct MailDetailView {
|
||||
}
|
||||
if (this.mailType === 'permission_request') {
|
||||
Text('权限请求')
|
||||
.fontSize(10).fontColor(Theme.warnFg)
|
||||
.fontSize(10).fontColor(Theme.warnFgFor())
|
||||
.backgroundColor(Theme.warnBg).borderRadius(4)
|
||||
.padding({ left: 6, right: 6, top: 2, bottom: 2 })
|
||||
.margin({ left: 6 })
|
||||
}
|
||||
|
||||
AmIcon({ iconName: 'chevronRight', iconSize: 16, iconColor: Theme.textSubtle })
|
||||
AmIcon({ iconName: 'chevronRight', iconSize: 16, iconColor: Theme.textSubtleFor() })
|
||||
.rotate({ angle: this.headerOpen ? 90 : 0 })
|
||||
.margin({ left: 6 })
|
||||
}
|
||||
@ -636,13 +636,13 @@ export struct MailDetailView {
|
||||
if (this.loading) {
|
||||
Column() {
|
||||
LoadingProgress().width(40).height(40)
|
||||
Text('加载中…').fontSize(14).fontColor(Theme.textSubtle).margin({ top: 8 })
|
||||
Text('加载中…').fontSize(14).fontColor(Theme.textSubtleFor()).margin({ top: 8 })
|
||||
}
|
||||
.width('100%').layoutWeight(1)
|
||||
.justifyContent(FlexAlign.Center)
|
||||
} else if (this.error.length > 0) {
|
||||
Column() {
|
||||
Text(this.error).fontSize(14).fontColor(Theme.danger)
|
||||
Text(this.error).fontSize(14).fontColor(Theme.dangerFor())
|
||||
Button('重试').margin({ top: 12 }).onClick(() => { this.loadMail(this.mailId); })
|
||||
}
|
||||
.width('100%').layoutWeight(1)
|
||||
|
||||
@ -409,7 +409,7 @@ struct InboxTab {
|
||||
Row() {
|
||||
Text(this.accountFilter === 'all' ? '全部邮箱' : this.accountName)
|
||||
.fontSize(11).fontColor(Theme.textMuted)
|
||||
AmIcon({ iconName: 'chevronRight', iconSize: 12, iconColor: Theme.textSubtle })
|
||||
AmIcon({ iconName: 'chevronRight', iconSize: 12, iconColor: Theme.textSubtleFor() })
|
||||
.rotate({ angle: 90 })
|
||||
.margin({ left: 3 })
|
||||
}
|
||||
@ -456,7 +456,7 @@ struct InboxTab {
|
||||
.fontColor(this.accountFilter === acct.id ? Theme.accent : Theme.textPrimary)
|
||||
.fontWeight(this.accountFilter === acct.id ? FontWeight.Bold : FontWeight.Normal)
|
||||
Text(acct.username)
|
||||
.fontSize(11).fontColor(Theme.textSubtle)
|
||||
.fontSize(11).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.alignItems(HorizontalAlign.Start)
|
||||
.layoutWeight(1)
|
||||
@ -483,13 +483,13 @@ struct InboxTab {
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.error.length > 0) {
|
||||
Column() {
|
||||
Text(this.error).fontSize(14).fontColor(Theme.danger)
|
||||
Text(this.error).fontSize(14).fontColor(Theme.dangerFor())
|
||||
Button('重试').margin({ top: 12 }).onClick(() => { this.loadData(); })
|
||||
}
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.mails.length === 0) {
|
||||
Column() {
|
||||
Text('收件箱为空').fontSize(16).fontColor(Theme.textSubtle)
|
||||
Text('收件箱为空').fontSize(16).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else {
|
||||
@ -568,7 +568,7 @@ struct InboxTab {
|
||||
* `rotate` 留在图标上(转的是箭头本身,不是那 20vp 的槽位)。
|
||||
*/
|
||||
Stack({ alignContent: Alignment.Center }) {
|
||||
AmIcon({ iconName: 'chevronRight', iconSize: 12, iconColor: Theme.textSubtle })
|
||||
AmIcon({ iconName: 'chevronRight', iconSize: 12, iconColor: Theme.textSubtleFor() })
|
||||
.rotate({ angle: this.isExpanded(g.key) ? 90 : 0 })
|
||||
}
|
||||
.width(20)
|
||||
@ -579,7 +579,7 @@ struct InboxTab {
|
||||
.fontColor(Theme.textPrimary)
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis }).layoutWeight(1)
|
||||
Text(compactMailTime(g.latest?.created_at ?? ''))
|
||||
.fontSize(10).fontColor(Theme.textSubtle).margin({ left: 8 })
|
||||
.fontSize(10).fontColor(Theme.textSubtleFor()).margin({ left: 8 })
|
||||
}
|
||||
.width('100%')
|
||||
Row() {
|
||||
@ -600,7 +600,7 @@ struct InboxTab {
|
||||
Text(g.alias.length > 0 ? '.' + g.alias : '(未命名会话)')
|
||||
.fontSize(10).fontColor(Theme.accentFor())
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis }).layoutWeight(1)
|
||||
Text(g.mails.length + ' 封').fontSize(10).fontColor(Theme.textSubtle).margin({ left: 8 })
|
||||
Text(g.mails.length + ' 封').fontSize(10).fontColor(Theme.textSubtleFor()).margin({ left: 8 })
|
||||
}
|
||||
.width('100%').margin({ top: 2 })
|
||||
}
|
||||
@ -639,14 +639,14 @@ struct InboxTab {
|
||||
.margin({ left: 6 })
|
||||
}
|
||||
Blank()
|
||||
Text(compactMailTime(mail.created_at)).fontSize(10).fontColor(Theme.textSubtle)
|
||||
Text(compactMailTime(mail.created_at)).fontSize(10).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%')
|
||||
|
||||
Text(mail.subject).fontSize(13).fontColor(Theme.textPrimary)
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.margin({ top: 2 })
|
||||
Text(mail.body_preview).fontSize(12).fontColor(Theme.textSubtle)
|
||||
Text(mail.body_preview).fontSize(12).fontColor(Theme.textSubtleFor())
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.margin({ top: 2 })
|
||||
/*
|
||||
@ -668,7 +668,7 @@ struct InboxTab {
|
||||
*/
|
||||
if (mail.cc_count > 0) {
|
||||
Text('抄送 ' + mail.cc_count)
|
||||
.fontSize(10).fontColor(Theme.textSubtle)
|
||||
.fontSize(10).fontColor(Theme.textSubtleFor())
|
||||
.margin({ top: 3 })
|
||||
}
|
||||
}
|
||||
@ -811,7 +811,7 @@ struct SentTab {
|
||||
.fontSize(13).fontWeight(FontWeight.Medium).fontColor(Theme.textPrimary)
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.layoutWeight(1)
|
||||
Text(mail.created_at).fontSize(10).fontColor(Theme.textSubtle)
|
||||
Text(mail.created_at).fontSize(10).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%')
|
||||
|
||||
@ -846,7 +846,7 @@ struct SentTab {
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.layoutWeight(1)
|
||||
Blank()
|
||||
Text(g.mails.length + ' 封').fontSize(11).fontColor(Theme.textSubtle)
|
||||
Text(g.mails.length + ' 封').fontSize(11).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%').margin({ top: 2 })
|
||||
}
|
||||
@ -868,7 +868,7 @@ struct SentTab {
|
||||
Text('发件箱').fontSize(20).fontWeight(FontWeight.Bold).fontColor(Theme.textPrimary)
|
||||
Blank()
|
||||
if (this.loaded > 0) {
|
||||
Text(this.loaded + ' 封').fontSize(12).fontColor(Theme.textSubtle)
|
||||
Text(this.loaded + ' 封').fontSize(12).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
}
|
||||
.width('100%').height(56).padding({ left: 16, right: 16 })
|
||||
@ -899,7 +899,7 @@ struct SentTab {
|
||||
Column() { LoadingProgress().width(32).height(32) }
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.error.length > 0) {
|
||||
Column() { Text(this.error).fontSize(13).fontColor(Theme.danger) }
|
||||
Column() { Text(this.error).fontSize(13).fontColor(Theme.dangerFor()) }
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.groups.length === 0) {
|
||||
/*
|
||||
@ -908,9 +908,9 @@ struct SentTab {
|
||||
* 副句说清这一栏是干什么的。
|
||||
*/
|
||||
Column() {
|
||||
AmIcon({ iconName: 'sent', iconSize: 36, iconColor: Theme.textSubtle }).margin({ bottom: 8 })
|
||||
AmIcon({ iconName: 'sent', iconSize: 36, iconColor: Theme.textSubtleFor() }).margin({ bottom: 8 })
|
||||
Text(emptyTitle('sent')).fontSize(15).fontColor(Theme.textMuted)
|
||||
Text(emptyHint('sent')).fontSize(12).fontColor(Theme.textSubtle).margin({ top: 6 })
|
||||
Text(emptyHint('sent')).fontSize(12).fontColor(Theme.textSubtleFor()).margin({ top: 6 })
|
||||
}
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else {
|
||||
@ -1092,12 +1092,12 @@ struct PermissionTab {
|
||||
|
||||
if (req.context.length > 0) {
|
||||
Text(req.context)
|
||||
.fontSize(11).fontColor(Theme.textSubtle)
|
||||
.fontSize(11).fontColor(Theme.textSubtleFor())
|
||||
.maxLines(3).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.margin({ top: 4 })
|
||||
}
|
||||
|
||||
Text(req.created_at).fontSize(10).fontColor(Theme.textSubtle).margin({ top: 6 })
|
||||
Text(req.created_at).fontSize(10).fontColor(Theme.textSubtleFor()).margin({ top: 6 })
|
||||
|
||||
if (this.noteFor === req.mail_id) {
|
||||
TextInput({ placeholder: '备注(会随决策一起送给 Agent)', text: this.noteText })
|
||||
@ -1113,7 +1113,7 @@ struct PermissionTab {
|
||||
Text('拒绝')
|
||||
.fontSize(13).height(36).layoutWeight(1)
|
||||
.textAlign(TextAlign.Center)
|
||||
.backgroundColor(Theme.dangerBg).fontColor(Theme.danger)
|
||||
.backgroundColor(Theme.dangerBg).fontColor(Theme.dangerFor())
|
||||
.borderRadius(Theme.radiusControl)
|
||||
// 拒绝先展开备注框,而不是直接拒:拒绝往往要说明理由,而理由是给模型看的
|
||||
.onClick(() => {
|
||||
@ -1130,7 +1130,7 @@ struct PermissionTab {
|
||||
|
||||
if (this.noteFor === req.mail_id) {
|
||||
Text(this.noteText.length > 0 ? '再点一次「拒绝」即送出(带备注)' : '可填备注,再点一次「拒绝」送出')
|
||||
.fontSize(10).fontColor(Theme.textSubtle).margin({ top: 4 })
|
||||
.fontSize(10).fontColor(Theme.textSubtleFor()).margin({ top: 4 })
|
||||
}
|
||||
}
|
||||
.width('100%').alignItems(HorizontalAlign.Start)
|
||||
@ -1182,13 +1182,13 @@ struct PermissionTab {
|
||||
Column() { LoadingProgress().width(32).height(32) }
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.error.length > 0) {
|
||||
Column() { Text(this.error).fontSize(13).fontColor(Theme.danger) }
|
||||
Column() { Text(this.error).fontSize(13).fontColor(Theme.dangerFor()) }
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.requests.length === 0) {
|
||||
Column() {
|
||||
AmIcon({ iconName: 'shield', iconSize: 36, iconColor: Theme.textSubtle }).margin({ bottom: 8 })
|
||||
AmIcon({ iconName: 'shield', iconSize: 36, iconColor: Theme.textSubtleFor() }).margin({ bottom: 8 })
|
||||
Text(emptyTitle('permissions')).fontSize(15).fontColor(Theme.textMuted)
|
||||
Text(emptyHint('permissions')).fontSize(12).fontColor(Theme.textSubtle).margin({ top: 6 })
|
||||
Text(emptyHint('permissions')).fontSize(12).fontColor(Theme.textSubtleFor()).margin({ top: 6 })
|
||||
}
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else {
|
||||
@ -1756,13 +1756,13 @@ struct ContactsTab {
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.error.length > 0) {
|
||||
Column() {
|
||||
Text(this.error).fontSize(14).fontColor(Theme.danger)
|
||||
Text(this.error).fontSize(14).fontColor(Theme.dangerFor())
|
||||
Button('重试').margin({ top: 12 }).onClick(() => { this.loadData(); })
|
||||
}
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.contacts.length === 0) {
|
||||
Column() {
|
||||
Text('暂无联系人').fontSize(16).fontColor(Theme.textSubtle)
|
||||
Text('暂无联系人').fontSize(16).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.contactView === 'card') {
|
||||
@ -1873,7 +1873,7 @@ struct ContactsTab {
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.sessionMails.length === 0) {
|
||||
Column() {
|
||||
Text('这条会话没有邮件').fontSize(14).fontColor(Theme.textSubtle)
|
||||
Text('这条会话没有邮件').fontSize(14).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else {
|
||||
@ -1887,7 +1887,7 @@ struct ContactsTab {
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
Blank()
|
||||
Text(compactMailTime(m.created_at))
|
||||
.fontSize(10).fontColor(Theme.textSubtle)
|
||||
.fontSize(10).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%')
|
||||
Text(m.subject)
|
||||
@ -1897,7 +1897,7 @@ struct ContactsTab {
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.width('100%').margin({ top: 4 })
|
||||
Text(m.body_preview)
|
||||
.fontSize(11).fontColor(Theme.textSubtle)
|
||||
.fontSize(11).fontColor(Theme.textSubtleFor())
|
||||
.maxLines(2).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.width('100%').margin({ top: 2 })
|
||||
}
|
||||
@ -1935,7 +1935,7 @@ struct ContactsTab {
|
||||
.fontSize(13).fontWeight(FontWeight.Bold).fontColor(Theme.textPrimary)
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
Text(c.path)
|
||||
.fontSize(11).fontColor(Theme.textSubtle)
|
||||
.fontSize(11).fontColor(Theme.textSubtleFor())
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.margin({ left: 6 }).layoutWeight(1)
|
||||
if (c.unread_count > 0) {
|
||||
@ -1981,7 +1981,7 @@ struct ContactsTab {
|
||||
|
||||
Row() {
|
||||
Text(c.mail_count + ' 封 · ' + shortTimeOf(c.last_activity))
|
||||
.fontSize(11).fontColor(Theme.textSubtle)
|
||||
.fontSize(11).fontColor(Theme.textSubtleFor())
|
||||
Blank()
|
||||
/*
|
||||
* 权限档位徽标:**档位 + 强制力标记**,点它弹出"平台实际做到了什么"。
|
||||
@ -2048,11 +2048,11 @@ struct ContactsTab {
|
||||
AmIcon({
|
||||
iconName: iconName,
|
||||
iconSize: 12,
|
||||
iconColor: tone === 'danger' ? Theme.danger : Theme.textMuted
|
||||
iconColor: tone === 'danger' ? Theme.dangerFor() : Theme.textMuted
|
||||
})
|
||||
Text(label)
|
||||
.fontSize(11)
|
||||
.fontColor(tone === 'danger' ? Theme.danger : Theme.textMuted)
|
||||
.fontColor(tone === 'danger' ? Theme.dangerFor() : Theme.textMuted)
|
||||
.margin({ left: 4 })
|
||||
}
|
||||
.height(26)
|
||||
@ -2078,7 +2078,7 @@ struct ContactsTab {
|
||||
.maxLines(2).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.width('100%')
|
||||
Text('对应 Agent 的 session 将被归档,此列表与邮箱界面同时移除')
|
||||
.fontSize(11).fontColor(Theme.textSubtle)
|
||||
.fontSize(11).fontColor(Theme.textSubtleFor())
|
||||
.margin({ top: 3 })
|
||||
.width('100%')
|
||||
Row() {
|
||||
@ -2147,7 +2147,7 @@ struct ContactsTab {
|
||||
* 于是同一个联系人在两种视图里给出的关键信息不一致。
|
||||
*/
|
||||
Text(c.mail_count + ' 封 · ' + shortTimeOf(c.last_activity))
|
||||
.fontSize(11).fontColor(Theme.textSubtle)
|
||||
.fontSize(11).fontColor(Theme.textSubtleFor())
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
}
|
||||
.width('100%').margin({ top: 3 })
|
||||
|
||||
@ -74,20 +74,20 @@ struct SessionsPage {
|
||||
if (this.loading) {
|
||||
Column() {
|
||||
LoadingProgress().width(40).height(40)
|
||||
Text('加载中…').fontSize(14).fontColor(Theme.textSubtle).margin({ top: 8 })
|
||||
Text('加载中…').fontSize(14).fontColor(Theme.textSubtleFor()).margin({ top: 8 })
|
||||
}
|
||||
.width('100%').layoutWeight(1)
|
||||
.justifyContent(FlexAlign.Center)
|
||||
} else if (this.error.length > 0) {
|
||||
Column() {
|
||||
Text(this.error).fontSize(14).fontColor(Theme.danger)
|
||||
Text(this.error).fontSize(14).fontColor(Theme.dangerFor())
|
||||
Button('重试').margin({ top: 12 }).onClick(() => { this.loadSessions(); })
|
||||
}
|
||||
.width('100%').layoutWeight(1)
|
||||
.justifyContent(FlexAlign.Center)
|
||||
} else if (this.sessions.length === 0) {
|
||||
Column() {
|
||||
Text('暂无会话').fontSize(16).fontColor(Theme.textSubtle)
|
||||
Text('暂无会话').fontSize(16).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%').layoutWeight(1)
|
||||
.justifyContent(FlexAlign.Center)
|
||||
@ -108,7 +108,7 @@ struct SessionsPage {
|
||||
|
||||
// 底栏
|
||||
Row() {
|
||||
Text('共 ' + this.sessions.length + ' 个会话').fontSize(12).fontColor(Theme.textSubtle)
|
||||
Text('共 ' + this.sessions.length + ' 个会话').fontSize(12).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%').height(40).padding({ left: 16 })
|
||||
.backgroundColor(Theme.surface)
|
||||
@ -130,7 +130,7 @@ struct SessionsPage {
|
||||
// 预算进度
|
||||
if (s.max_rounds > 0) {
|
||||
Text(s.used_rounds + '/' + s.max_rounds)
|
||||
.fontSize(11).fontColor(s.used_rounds >= s.max_rounds ? Theme.danger : Theme.textMuted)
|
||||
.fontSize(11).fontColor(s.used_rounds >= s.max_rounds ? Theme.dangerFor() : Theme.textMuted)
|
||||
.backgroundColor(s.used_rounds >= s.max_rounds ? Theme.dangerBg : Theme.surfaceMuted)
|
||||
.borderRadius(4).padding({ left: 4, right: 4, top: 1, bottom: 1 })
|
||||
}
|
||||
@ -155,8 +155,8 @@ struct SessionsPage {
|
||||
.backgroundColor(Theme.border).borderRadius(4)
|
||||
.padding({ left: 4, right: 4, top: 1, bottom: 1 })
|
||||
Blank()
|
||||
Text(s.mail_count + ' 封').fontSize(11).fontColor(Theme.textSubtle)
|
||||
Text(' · ' + s.status).fontSize(11).fontColor(s.status === 'active' ? Theme.approve : Theme.textSubtle)
|
||||
Text(s.mail_count + ' 封').fontSize(11).fontColor(Theme.textSubtleFor())
|
||||
Text(' · ' + s.status).fontSize(11).fontColor(s.status === 'active' ? Theme.approveFor() : Theme.textSubtle)
|
||||
}
|
||||
.width('100%').margin({ top: 4 })
|
||||
}
|
||||
|
||||
@ -754,7 +754,7 @@ export struct SettingsPane {
|
||||
InfoRow(label: string, value: string) {
|
||||
Row() {
|
||||
Text(label)
|
||||
.fontSize(12).fontColor(Theme.textSubtle)
|
||||
.fontSize(12).fontColor(Theme.textSubtleFor())
|
||||
.width(80).flexShrink(0)
|
||||
Text(value)
|
||||
.fontSize(14).fontColor(Theme.textPrimary)
|
||||
@ -823,7 +823,7 @@ export struct SettingsPane {
|
||||
Blank()
|
||||
Text(statusLabel(this.appearanceStatus))
|
||||
.fontSize(11)
|
||||
.fontColor(this.appearanceStatus === 'synced' ? Theme.textMuted : Theme.warnFg)
|
||||
.fontColor(this.appearanceStatus === 'synced' ? Theme.textMuted : Theme.warnFgFor())
|
||||
}
|
||||
.width('100%')
|
||||
|
||||
@ -842,7 +842,7 @@ export struct SettingsPane {
|
||||
.width('100%').margin({ top: 10 })
|
||||
|
||||
Text('主题由系统按色彩模式给色(深浅两套不靠手写色值);换账号时外观跟着账号走。')
|
||||
.fontSize(11).fontColor(Theme.textSubtle).margin({ top: 6 })
|
||||
.fontSize(11).fontColor(Theme.textSubtleFor()).margin({ top: 6 })
|
||||
|
||||
Divider().color(Theme.border).margin({ top: 12, bottom: 12 })
|
||||
|
||||
@ -889,7 +889,7 @@ export struct SettingsPane {
|
||||
? '服务器未配置推送'
|
||||
: (this.pushServerStatus === 'server-on' ? '服务器已开启推送通道' : '推送通道状态未知'))
|
||||
.fontSize(11)
|
||||
.fontColor(this.pushServerStatus === 'server-on' ? Theme.textMuted : Theme.warnFg)
|
||||
.fontColor(this.pushServerStatus === 'server-on' ? Theme.textMuted : Theme.warnFgFor())
|
||||
.margin({ top: 2 })
|
||||
}
|
||||
.layoutWeight(1).alignItems(HorizontalAlign.Start)
|
||||
@ -902,7 +902,7 @@ export struct SettingsPane {
|
||||
.width('100%')
|
||||
|
||||
Text('新邮件到达时由系统弹通知(点通知直达邮件)。未配置服务端推送时请保持关闭。')
|
||||
.fontSize(11).fontColor(Theme.textSubtle).margin({ top: 6 })
|
||||
.fontSize(11).fontColor(Theme.textSubtleFor()).margin({ top: 6 })
|
||||
}
|
||||
.width('100%').alignItems(HorizontalAlign.Start)
|
||||
.padding(16).margin({ top: 8 })
|
||||
@ -928,11 +928,11 @@ export struct SettingsPane {
|
||||
.width('100%')
|
||||
|
||||
Text('第三方客户端用该密钥访问自己的邮箱(Authorization: Bearer)。它不能用于注册 Agent。')
|
||||
.fontSize(11).fontColor(Theme.textSubtle).margin({ top: 6 })
|
||||
.fontSize(11).fontColor(Theme.textSubtleFor()).margin({ top: 6 })
|
||||
|
||||
if (this.keysError.length > 0) {
|
||||
Text(this.keysError)
|
||||
.fontSize(12).fontColor(Theme.danger).margin({ top: 8 })
|
||||
.fontSize(12).fontColor(Theme.dangerFor()).margin({ top: 8 })
|
||||
}
|
||||
|
||||
/*
|
||||
@ -944,7 +944,7 @@ export struct SettingsPane {
|
||||
if (this.newKeyToken.length > 0) {
|
||||
Column() {
|
||||
Text('密钥已创建。全文仅显示这一次,关闭后无法再次查看。')
|
||||
.fontSize(11).fontColor(Theme.warnFg)
|
||||
.fontSize(11).fontColor(Theme.warnFgFor())
|
||||
.width('100%')
|
||||
Text(this.newKeyToken)
|
||||
.fontSize(11).fontColor(Theme.textPrimary)
|
||||
@ -1043,7 +1043,7 @@ export struct SettingsPane {
|
||||
.margin({ right: 12 })
|
||||
|
||||
Text('吹销')
|
||||
.fontSize(12).fontColor(Theme.danger)
|
||||
.fontSize(12).fontColor(Theme.dangerFor())
|
||||
.onClick(() => { this.revokeKey(k.key_id); })
|
||||
}
|
||||
.width('100%')
|
||||
@ -1088,11 +1088,11 @@ export struct SettingsPane {
|
||||
/* 两次不一致就地提示(与 WebUI `mismatch` 同一行为,不等提交才发现) */
|
||||
if (this.confirmPw.length > 0 && this.newPw !== this.confirmPw) {
|
||||
Text('两次密码不一致')
|
||||
.fontSize(11).fontColor(Theme.danger).margin({ top: 6 })
|
||||
.fontSize(11).fontColor(Theme.dangerFor()).margin({ top: 6 })
|
||||
}
|
||||
if (this.pwError.length > 0) {
|
||||
Text(this.pwError)
|
||||
.fontSize(12).fontColor(Theme.danger).margin({ top: 8 })
|
||||
.fontSize(12).fontColor(Theme.dangerFor()).margin({ top: 8 })
|
||||
}
|
||||
if (this.pwMsg.length > 0) {
|
||||
Text(this.pwMsg)
|
||||
@ -1145,11 +1145,11 @@ export struct SettingsPane {
|
||||
Row() {
|
||||
Text('管理').fontSize(Theme.fontBody).fontColor(Theme.textPrimary)
|
||||
.layoutWeight(1)
|
||||
Text('›').fontSize(Theme.fontBody).fontColor(Theme.textSubtle)
|
||||
Text('›').fontSize(Theme.fontBody).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%')
|
||||
Text('用户管理(新建 / 编辑 / 启停 / 重置密码)')
|
||||
.fontSize(Theme.fontTiny).fontColor(Theme.textSubtle)
|
||||
.fontSize(Theme.fontTiny).fontColor(Theme.textSubtleFor())
|
||||
.width('100%').margin({ top: 4 })
|
||||
}
|
||||
.width('100%').alignItems(HorizontalAlign.Start)
|
||||
@ -1197,7 +1197,7 @@ export struct SettingsPane {
|
||||
}
|
||||
|
||||
Text('删除')
|
||||
.fontSize(12).fontColor(Theme.danger)
|
||||
.fontSize(12).fontColor(Theme.dangerFor())
|
||||
.onClick(() => { this.removeAccount(account.id); })
|
||||
}
|
||||
.width('100%').height(72)
|
||||
|
||||
Reference in New Issue
Block a user