fix(app-iced, presenter): M114 r2 —— 登录失败走结构化错误码 + i18n,修 4 条 p2 与 3 条看图意见

p1(语言泄漏):后端诊断串不再上屏
- presenter 新增 LoginFailure 原因码(denied / whitelist_pending / unreachable /
  not_authlib_server / bad_credentials / no_profile / store_failed / other)
- 页面按码查 i18n 出标题+正文+「Code: …」,原文只进日志/详细信息
- 进行中(Requesting)也走 i18n,不再复读后端中文状态串
- presenter/state 不再发中文 toast(成败由页面按 i18n 画)
- 新增守门测试:注入带 AADSTS 的失败事件,断言英文视图无 CJK 且含 code

p2-1 复制:on_account 返回 Task,page_messages 把 iced::clipboard::write 交回 runtime
p2-2 可见失败态:新增 AccountNotice(added/removed/switched/名字非法/重名/落盘失败/
      找不到),页面用 M3 内联 alert 画(不是 toast);离线名字先按 3–16 位规则校验,
      失败不再清空输入
p2-3 重新登录按账户类型分派:微软→授权码流;第三方→预填服务器/登录名并提示补密码;
      离线→明确说明没有可重登的服务器账户
p2-4 入口收敛:删 chip 行;空态=三卡片;有账号时三卡收进 Add an account(按钮不再绑死微软轨)

看图意见/tower 三条:
- 失败横幅改成 M3 alert(SurfaceContainerHigh + 描边,errorContainer 只作图标块,
  不再通栏实心红);色值本身经复审像素采样确认是 90/30 档,未改角色
- 当前账户卡补齐与列表一致的「服务器/登录名」行;无皮肤时明说 No skin available
- 页面级 scrollable 加 8px 右 gutter,滚动条不再压卡片
- 倒计时改 mm:ss(15:00 left),数字与冒号语言无关

p3(顺带):AuthlibRequest / Form 手写 Debug 脱敏密码;HTTP 响应体与 base64 输出加上限;
离线提示文案与行为对齐;删除无人使用的 key;皮肤测试断言帽子层真的 blend

测试:app-iced lib 418 绿、presenter 151 / i18n 24 绿(workspace 全量另跑)
This commit is contained in:
AskaEth committed 2026-11-05 05:09:51 +08:00
1 parent 1ff00bbe50
commit 8d8552113a
31 files changed
+1540 -497

No files matched your search

+24 -10
View File
@@ -48,6 +48,17 @@ use crate::cli::ForcedState;
use crate::icons;
use crate::theme::Ui;
/// 导航轨的一帧状态(M114):主序列高亮项 + 轨底账户头像位。
///
/// 绑成一个结构体是为了让 [`view_with_account`] 的参数停在 8 个以内(clippy `too_many_arguments`),
/// 也把「这两个值永远一起传」这件事写进类型:谁加导航项都绕不开轨底那一格。
pub struct NavRail {
/// 高亮哪个主序列项(账户页是 `NavItem::Account`)
pub selected: NavItem,
/// 轨底头像位的状态(**按值持有**:它是每帧现算的,引用会让整棵树的生存期挂到临时值上)
pub account: nav::RailAccount,
}
/// 整棵树(**内容槽位版**):外壳画标题栏 / 缩放热区 / 导航轨 / 右栏,中间那块交给调用方。
///
/// 产品页的唯一入口(M90 r2 / P1-1;M104 起多两样东西:右侧助手栏与拖拽取指针层;
@@ -68,14 +79,14 @@ use crate::theme::Ui;
///
/// `background` 由 `pages::appearance::layer` 产出(已与 `Message::Page` 映射过);垫在 `stack!`
/// 最底层,于是「面板 α < 1」时能真的看见它——这就是 D34 在像素上的落点。
// 10 个形参:M104 的 `right`/`overlay`、M106 的 `lang`(导航轨标签按语言取词)、
// M114 的 `account`(轨底账户头像位状态)。拆成结构体要动 `app/view.rs` / `account_page.rs`
// 的调用点,而那正是 M104/M105/M109 反复设锚点的地方——先就地 `allow`,等下次合流再结构体化。
// 9 个形参:M104 的 `right`/`overlay`、M106 的 `lang`(导航轨标签按语言取词),
// 加上 M114 把「高亮项 + 轨底账户态」捆成的 `NavRail`(捆它是为了少一个形参)。
// 再拆就得动 `app/view.rs` / `account_page.rs` 的调用点,而那正是 M104/M105/M109 反复设锚点的
// 地方——先就地 `allow`(主线原本也在这条上 `allow`),等下次合流再结构体化。
#[allow(clippy::too_many_arguments)]
pub fn view_with_background<'a>(
pub fn view_with_account<'a>(
ui: &'a Ui,
selected: NavItem,
account: &nav::RailAccount,
nav_state: NavRail,
app_title: &'a str,
background: Option<Element<'a, Message>>,
content: Element<'a, Message>,
@@ -87,10 +98,13 @@ pub fn view_with_background<'a>(
// 右栏(M104)是 `row` 的**第三个兄弟**而不是叠层:它是布局宽度的一部分,压在上面会让
// 中栏以为自己还有那么宽(那就是任务里禁止的「溢出」)。它的宽度由 `agent::docked` 自己声明。
// M106:导航轨标签按当前界面语言取词(`lang` 一路从 `App::lang()` 递进来)。
// M114:`account` 只影响轨底那一格,`right`/`overlay` 的语义一字未动。
let mut work = row![nav::rail(ui, selected, account, forced, lang), content]
.width(Length::Fill)
.height(Length::Fill);
// M114:`nav_state` 只影响轨底那一格,`right`/`overlay` 的语义一字未动。
let mut work = row![
nav::rail(ui, nav_state.selected, &nav_state.account, forced, lang),
content
]
.width(Length::Fill)
.height(Length::Fill);
if let Some(right) = right {
work = work.push(right);
}