AskaEth
|
592f0c26f6
|
docs(packaging): M131 压回 400 行内,删 Slint 遗留祈使句/软渲染块,修 smoke 脚本 bin
- packaging/README.md 401 -> 398 行(回归 file-size 红线内)
- §5 删 SLINT_BACKEND=winit-software 命令块;现役入口改注 iced 的 --software-renderer
- §8 删「关于页保留 AboutSlint、不得移除」两句祈使句,只留 v0.1.0 资产事实段
- §7 抬头标明连字符产物名属哪次实测(现名形态写下划线 0.2.0),§7.3/§7.5 同步标注
- §11 删「该脚本现在会失败」的过期提醒,记录 M131 真跑证据
- linux-smoke-docker.sh: --bin/产物路径/target 全改 unlucky-launcher-iced(cargo clean -p 同步改
unlucky-app-iced,原来的 unlucky-app 已被 workspace exclude、清不掉陈旧产物)
|
2026-11-05 18:03:20 +08:00 |
|
AskaEth
|
5b217ae32d
|
Merge branch 'feat/m129-relnotes-rewrite-release-notes-to-c'
|
2026-11-05 18:01:29 +08:00 |
|
AskaEth
|
5a9d67530a
|
docs(packaging): M129 r1 P1-1 —— MSA 那段改成 GUI 现有行为,不再写「界面会告诉你怎么配」
复审 r1 把链路走了一遍:core 缺 id 早退(零网络)成立,但 presenter 的 fail() 只把原文
eprintln 进日志,事件只带原因码 Other;账户页据此画通用横幅「Sign-in failed」+「Code: other」,
完整原因在日志里。LaunchMsaNotConfigured 那条 i18n 目前只有定义/单测/golden,GUI 零调用点。
所以上一版「the app says "not configured" (with the command to fix it)」是把设计意图写成了现有行为。
改成分段如实描述:无 id 时在联网之前就停;GUI 显示通用横幅 + Code: other(原因在日志);
`ull accounts add msa` 会打出可照抄的修复命令(map_auth_error 的 hint,含变量名与 cargo build)。
另按 r1 P2-1 给 macOS 补 shasum 校验行(sha256sum 不是 macOS 自带)。
|
2026-11-05 18:00:55 +08:00 |
|
AskaEth
|
59eefdb052
|
docs(packaging): M129 发布说明重写到现行事实(下划线资产名 / MSA client id / 删 Termux 下载引导)
资产名全部改成 make_artifacts.py::asset_stem() 的 `名字_版本号_平台_架构`(下划线):
六行矩阵、ull 资产、tar 包名与解压出的顶层目录、PowerShell 校验命令。旧的连字符名
按文档下载会 404(D49 2026-11-05 改名,文档没跟)。
删掉「微软登录等 Mojang 白名单审批」整条(白名单 2026-11-03 已批准并真机验签),
换成真缺口:发布版不带 Azure 公共客户端 id,要 ULL_MSA_CLIENT_ID(环境变量或构建期
注入),未配置时界面说「未配置」而不是静默失败。不写任何 id 值。
Termux:删掉「下 ull 资产」的引导(ull 的 Linux 资产是 glibc 版,Android/bionic 上跑
不起来),矩阵行只留「构建自源码、GUI 未验证、不在本发布矩阵」。
顺带清掉三处过期陈述:Windows 数据根(原文说有 `portable.marker` 就便携、否则落
%APPDATA%——实际 portable 是默认模式,首次运行在 exe 旁建 .ULL/ 与 .minecraft/);
CLI 子命令表漏了 `pack import`;arm64 行里的内部决策编号 D21。124 → 107 行。
|
2026-11-05 17:54:18 +08:00 |
|
AskaEth
|
494a45fe12
|
Merge branch 'feat/m127-shotci-make-pixel-guards-actually-r'
|
2026-11-05 17:39:59 +08:00 |
|
AskaEth
|
02c7910ad5
|
ci(app-iced): M127 把下载页四条像素守门接进 CI(仅 Windows),缺图在 CI 里从 SKIP 改成失败
- 新增 tests/options_shot/feed.rs:四条断言共用一条「喂图」策略
· CI(CI 已设置)且 Windows 上缺 ULL_DL_*_SHOT ⇒ 失败并提示「守门未喂图」
· 非 Windows 上喂了图 ⇒ CI 里也失败(断言按 Segoe UI 标定的行高/墨迹宽度)
· 本机缺图仍可跳过,但每次都留一句提示
- ci.yml 的 test 作业(Windows 槽):pages_demo 现出 ready700/chips700/compact736/wide1180
四张图 → 写进 $GITHUB_ENV → cargo test -p unlucky-app-iced --lib options_shot
出图与断言两步都带 if: runner.os == 'Windows';失败时上传四张图便于复盘
- 顺带订正五处文档数字:main 7 整行 / r1 4 整行 / r2 7 整行,并写清第 8 条墨迹是 hint
(rows.rs ×3、options_shot/ready.rs、tests/view.rs),不动任何断言期望值
|
2026-11-05 17:17:43 +08:00 |
|
AskaEth
|
631d4647dc
|
Merge branch 'feat/m125-railfix-chips-tier-crushed-list-and'
|
2026-11-05 17:05:05 +08:00 |
|
AskaEth
|
0f26ed3753
|
fix(app-iced): M125 r2 —— chips 档「还没选版本」不再对半切;宽度表换成实测值
r1 复审 p2-1:`TailShare::of` 只判档位,于是 chips 档「刚进页面」(面板只有一行
提示)也被五五分——700×760 下列表 8 个版本只显示 4 个、下半块 ≈160px 空画布,
而修前(main)这一态是 7 整行。修法:判据加**面板形态**,`Hint` 在任何档位都钉死,
`Chips` + `Options`/`Install` 才共享。实测回到 7 整行(与 main 同一水位)。
守门:新增 `options_shot/ready.rs`(`ULL_DL_READY_SHOT`,不设 `ULL_DL_DEMO` 出的
700×760 图):列表区至少读出 6 个版本号。它在 r1 的图上**红**(4 个,原生输出见
mission 报告),在修后图上绿(7 个)。三条旧守门照旧(chips / compact / wide),
chips 与 compact 换修前图仍红。
r1 复审 p2-2:宽度表的实测值改正(同一张 736 图逐项量):
`1.21` 26(余 6)、`1.20` 28(余 4)、`24w` **29**(余 3)、`All versions` 墨迹画到
x=247(裁切边 248,只差 1px)⇒ 桶标签余量是 **3~6px** 而不是 6;「短标签『全部』= 32
零余量」这条论证的对比强度随之写准(比桶标签的 3~6px 更薄)。
`rails/major.rs` 的表格、`options_shot/compact.rs` 的测试文档一并改成实测值;
「墨迹右边界」那条反例也从 216..244 改成实测 216..247。
r1 复审 p3-1:第二级「全部版本」的图标从网格(与第一级同字形同药丸、像素级同形)
换成文档/清单字形 `IconKind::Logs`,理由与「图标集里没有版本/历史字形」写进
`major_shape` 文档。
r1 复审 p3-2:chips 档最多三层滚动(列表 / 面板 / loader 菜单)作为**已知风险**
登记在 `install.rs` 文档里,并写明为什么「把实例名移出滚动区」本轮不做(要拆
`options::section`,动三个文件,而且改完仍无法验证滚轮冒泡——没有通道)。
|
2026-11-05 16:43:35 +08:00 |
|
AskaEth
|
9c623a6fd3
|
style(app-iced): M125 拆文件时留下的孤儿文档片段(clippy 的 empty_line_after_doc_comments)
|
2026-11-05 16:03:10 +08:00 |
|
AskaEth
|
70dea995b7
|
refactor(app-iced): M125 主版本栏与选项区像素守门各拆一层(守 400 行红线)
两处拆分,只为过 `gate::every_source_file_stays_under_the_line_limit`:
- `download/rails.rs` 435 行 → `rails.rs` 311 + `rails/major.rs` 157:主版本桶那一栏
(`major_rail` / `major_entries` / `major_shape` / `MajorEntry` / `MajorShape`)搬进子模块,
`rail_title` 留在父模块(两条栏共用)。`Key::VersionMajorTitle` 的落点跟着搬,
`tests/i18n.rs` 的死 key 扫描表补上 `rails/major.rs`(漏扫会把活 key 判成死 key)。
- `tests/options_shot.rs` 475 行 → 345 + `options_shot/chips.rs` 75 + `options_shot/compact.rs` 67。
顺带修 `options_shot::bands()` 的一处既有缺口:它按三栏算列表列左缘(112+0+24+0+24 = 160),
chips 档没有侧栏时这个左缘是错的,右缘跟着冲出画面 22px——M125 的 chips 守门正好踩到
「扫描越界读到下一行像素」,`first_rule_y` 因此恒返回 None。现在 chips 档直接取页左缘
(112),三栏三档的数值一个不变(M123 那些 140/140、缝 24 的断言照旧)。
守门实跑(本机,tiny-skia):
- 解 ignore 的 `chips::a_chips_tier_shot_…` 在修后图绿、**修前图红**(实测 18px ≠ 44);
- `compact::a_compact_tier_shot_…` 修后图绿、修前图红(实测 44px 文字行 ≠ 40px 图标按钮);
- chips 那条换深色图同样绿(断言不依赖主题色)。
|
2026-11-05 16:01:03 +08:00 |
|
AskaEth
|
822f98022d
|
fix(app-iced): M125 chips 档列表不被选项区压成缝;Compact 档主版本首项不再裁字
两条缺陷都是 M123 取证时在真图上拍出来的,塔台另行核过图:
缺陷 1(medium):chips 档(窗口 < 736,可用宽 < 600)里选项区按内容高
≈316px 钉在列表下面,列表只剩 18px(0.4 行,行高 44)——版本号读不出来、
换不了版本。修法:`rows::TailShare` 两档,chips 档改成「列表与面板各吃一半、
面板自己可滚」(列表拿到 ≈182px = 4 整行,面板顶端那个实心「安装」与五个
loader chip 仍在屏内);三栏三档照旧钉死(760 高下列表还有 3 整行、面板
整块在屏内,两样都对)。判据按档位而不是按高度:页面模型里只有 window_width,
窗口高度没接进页面(边界写进 TailShare 文档与报告)。
缺陷 2(medium):Compact 档主版本栏宽 64,`ListItem` 两侧内边距各 16 ⇒ 文本格
32,首项「All versions」要 ≈84 ⇒ 图上只剩「All v」;中文「全部版本」= 4 全角字
= 64,32 里只剩「全部」且正好顶到裁切边。修法:图标条档的「全部版本」不画文字,
改画与本页第一级那个「全部」同一个网格图标(40×40 Tonal 药丸 + 全称 tooltip);
桶标签(1.21/24w,实测墨迹 26)放得下,照旧画原文。`rails.rs` 那条「64 宽里
1.21 仍完整」的注释改成实测宽度表,并写清它对首项不成立。
顺带:列表与尾部面板之间补上分隔线(三态一致),`rows::content` 与两个装配点
按档位传 `TailShare`。
守门:
- `tests/options_shot.rs` 解掉 M123 那条 `#[ignore]`(期望值一个字没改,只加
「列表区 ≥ 3 整行」与「面板仍在屏内、没被底边切」两条);
- 新增 `a_compact_tier_shot_draws_an_icon_for_the_first_major_entry`:缺陷 2 的
像素守门(首项是 40×40 居中药丸,不是 64×44 通栏文字行)。复审建议的
「墨迹右边界 ≤ 栏右内边界」在坏图上也是绿的(修前墨迹 216..244,边界 248),
所以改钉形状,理由写在测试文档里;
- `tests/rails.rs`:中文与英文都走 Icon 分支、以及 32px 文本格的宽度账
(「全部版本」= 64 > 32 是精确值,「全部」= 32 零余量所以不选短标签);
- `tests/view.rs`:档位 → TailShare 的决策面 + 两个装配点都按档位传。
未改产品文案、未改窗口最小宽、未动 M123 的任何断言期望值。
|
2026-11-05 15:48:57 +08:00 |
|
AskaEth
|
df0e98d811
|
Merge branch 'feat/m123-evidshot-close-three-acceptance-evi'
|
2026-11-05 15:46:28 +08:00 |
|
AskaEth
|
4a41263357
|
test(app-iced): M123 安装选项区 × 三栏同屏的宽度与行裁剪守门
安装选项区(`download/options`)与三栏(`download/rails`)各自都有断言,
但它们**同屏**这件事从来没验过:选项区一高列表就矮,窗口一窄两条栏一收列表
就窄,这两种挤压都不会让任何既有断言变红。
- `tests/options_screen.rs`:七档宽度(1180/1024/900/880/812/736/700)× 三种
选项态(原版 / Fabric / NeoForge 不兼容)。三栏数字按 `layout/rails.rs` 的
数值表逐格钉死;列表列在每一档都 ≥ 424(行内四样仍在一行);两条栏与选项
面板在同一份模型里、树能一次构建。
- `tests/options_shot.rs`:真 PNG 上的像素断言(需 `ULL_DL_OPTIONS_SHOT`)。
两栏同屏且像素宽度 = 裁决值 140/140、栏间 24 的缝真空着、第一行版本行整行
44px、选项区最后一行墨迹离底边还有余量。另有一条 `#[ignore]` 的 Chips 档
回归:700×760 下列表被压成 18px(现状红,缺陷已单列报告与 finding)。
出图(38 张)在 `DevDocs/artifacts/m123/`(git 忽略,不入库);旧的
`DevDocs/artifacts/m109/`(内容库页早于 M108 改名,导航轨写的是 `Download`)
已按任务 ② 删除,内容库 12 态在 m123 里按当前 tip 重出。
不动产品行为:本提交只有测试与模块声明。
|
2026-11-05 15:46:26 +08:00 |
|
AskaEth
|
c25e85b461
|
Merge branch 'feat/m124-cliloader-expose-parsed-loader-in-i'
|
2026-11-05 15:37:02 +08:00 |
|
AskaEth
|
9ead4ae892
|
feat(cli): M124 instances list --json 补 loader 字段,只加不改
`ull instances list` 的人读输出历来只逐字打 `version_id`,脚本/agent 要判断
「这是不是 Fabric」只能自己猜 id 形状——正是 M122 收敛掉的第二份真相。本提交只补
输出面:
- 每行实例加 `loader: {"kind": <稳定 id>, "version": <版本段>}`,解析一律调
`unlucky_core::game::InstanceLoader::parse`(M122 的全仓唯一入口),CLI 里没有
任何新的 id 匹配;CLI 只做「种类 → 稳定 id」的穷尽翻译。
- 原版恒为 `{"kind":"vanilla","version":""}`:键恒存在、不为 null,脚本不必判空。
- `kind` 是识别口径(7 值,含只能继承不能安装的 optifine/liteloader),不是
`install --loader` 的安装口径(5 值)——这条连同「vanilla 只表示 id 里没有加载器
痕迹」一起写进模块文档与 `ull help instances`(`--json` 是事实对外契约,字段说明
跟着契约走)。
- 向后兼容:`data.instances[]` 原有七个键一个没动,人类可读输出逐字不变。
- 测试(`commands::instances::tests`,走真入口 `crate::run`):四家自家 id + 原版 +
两家第三方前缀的真值表、原版非 null 的反向锚、人读输出与旧字段不变的兼容锚、
help 里有契约说明的文档锚。
|
2026-11-05 15:23:37 +08:00 |
|
AskaEth
|
e4b819a4cc
|
Merge branch 'feat/m122-loaderid-fix-loader-from-id-to-reco'
|
2026-11-05 15:06:50 +08:00 |
|
AskaEth
|
081ce65596
|
fix(core): M122 版本 id → 加载器只留一份解析,并认全自家 id 口径
`presenter::host::game::loader_from_id` 只认 `fabric-loader-…` / `quilt-loader-…`,
而本启动器自己生成的 id 是 `<mc>-<kind>-<loader>`(`1.20.4-fabric-0.16.5` /
`1.20.4-quilt-0.27.0`),于是刚装好的 Fabric / Quilt 实例在实例页显示成「原版」。
- 新增 `core::game::loader`:`InstanceLoader::parse` 是全仓唯一的 id 解析入口
(按 `-` 切段、段前缀匹配标记、跳过独立的 `loader` 段、砍老 HMCL 的 MC 尾巴)。
覆盖自家四种口径(生成端 `addon::loader::LoaderKind::version_id`)、老 HMCL / CF
口径、OptiFine / LiteLoader 第三方前缀;标记按整段前缀匹配,不再用「包含子串」。
- presenter 只留 DTO 翻译:`dto_kind` 穷尽 match(`dto/` 里仍不许出现 core 类型)。
- 测试:真值表**从生成端取 id**,反向锚(普通版本 id 不被认成加载器),映射单射,
以及端到端快照(`instances()` 的磁贴数据对四个加载器各自的版本号与徽标)。
|
2026-11-05 14:46:22 +08:00 |
|
AskaEth
|
829bd9043c
|
Merge branch 'feat/m121-verbump-0-2-0-workspace-version-and'
|
2026-11-05 14:08:39 +08:00 |
|
AskaEth
|
bf6b0c4126
|
build(release): workspace 版本 0.1.0 → 0.2.0,README 的 tag 事实改成 v0.2.0 口径
- Cargo.toml [workspace.package] version = 0.2.0;Cargo.lock 由 cargo 重新解出一致
(只有 8 个 unlucky-* 成员条目变动,第三方版本一字未动)
- 各 crate 的 `unlucky-*` path 依赖 `version = "0.1"` 同步提到 "0.2",否则工作区
根本解析不出来(cargo metadata 会报 `failed to select a version for
unlucky-core = "^0.1"`);crates/app 虽被 exclude、cargo 不读,也一并跟上避免将来
重新纳入时立刻解析失败
- core::launch::LauncherConfig 兜底的 launcher_version 改成 env!("CARGO_PKG_VERSION"),
不再是把版本写死一份(CLI / presenter 本来就这么写)
- README.md / README.zh-CN.md:删掉「v0.1.0 是目前唯一的 tag / the only tagged release」
这类随 v0.2.0 发布即变假的陈述,改写成「v0.1.0 是最后一个 Slint 版 tag,v0.2.0 是
重写后的第一个 tag」;所有讲 v0.1.0 资产 / Slint Royalty-Free 义务的历史记录原样保留
- packaging/README.md:命名与校验示例跟当前 tag 走(v0.2.0);§7 实测记录里的 0.1.0
输出加一句「属实测记录、不回填」,并把过期的「四个 workspace member」改成「任何一个」
|
2026-11-05 14:08:37 +08:00 |
|
AskaEth
|
7235cf08e4
|
Merge branch 'feat/m120-insti18n-instance-page-and-stage-la'
|
2026-11-05 13:36:55 +08:00 |
|
AskaEth
|
d0a8f68607
|
docs(m120): 页面模块的守门说明改成白名单口径
|
2026-11-05 13:36:45 +08:00 |
|
AskaEth
|
f86be2bd38
|
fix(app-iced): M120 r1 —— 徽标判据改回「类型是原版」;旧串黑名单翻成白名单
r1 复审 p2-1(真回归,我引入的):`loader_badge` 把「非原版 + 空版本号」也送进了
`Key::LoaderVanilla`。而 `host::game::loader_from_id("1.20.1-forge")` 这类 id 取不到版本段
(它按 `forge-` 找,id 却是 `-forge` 结尾),`LoaderInfo::badge()` 对它们给的正是品牌名
`Forge` / `NeoForge` / `OptiFine` —— 于是这三个显示成「原版 / Vanilla」。
判据改成「**加载器类型本身是原版** ⇒ 过词表,其余交给 `badge()`」:
- 修的是判据,不是加分支:函数注释本来就写着「只有原版这一支要过词表」,代码与注释相矛盾。
- 新回归测试 `the_loader_badge_keeps_the_brand_when_the_version_is_missing`:
`1.20.1-forge` / `1.20.1-neoforge` / `1.20.1-OptiFine` 三条 id 各断言列表副文案与 LOADER
卡的值都是品牌名(中英两套语言各跑一遍,品牌名两套语言相同)。
变异实测:把判据改回旧写法 ⇒ 立刻红(`left: "1.20.4 · Vanilla" / right: "1.20.4 · Forge"`,
与复审给的实证逐字相同)。
r1 复审 p2-2(不阻塞,按塔台要求顺手做):源码反扫从**黑名单 29 条旧串**翻成**白名单**。
- 先数过:五个页面源码里一共 13 个字面量,其中带 ASCII 单词的只有 6 条,全是专有名词 /
技术标识 / 占位符名 / 族名前缀(`{date} {time} UTC` / `isolated` / `shared` / `pid` /
`reason` / `Instances`),逐条写了理由。
- 判据反过来之后,往页面里**新写**一句英文也会红(复审抓到的哑点)——变异实测:
写进 `"Ready"` ⇒ 红;写回 `"ALL INSTANCES"` ⇒ 红。
- 反向锚保留:扫描器必须真扫到 ≥6 条、`Texts::of(` / `Header::new(texts.title())` /
`Key::Instances` / `Key::InstancesStage` 必须在用(防止断言空转)。
|
2026-11-05 13:36:45 +08:00 |
|
AskaEth
|
1a4e1fee12
|
feat(app-iced): M120 实例页与启动阶段标签跟着界面语言走
来源:M106 r1 复审点名——中文机器上首屏(实例页)仍是英中混排(页头 / New instance /
Import / ALL INSTANCES / VERSION / Present on disk / Running · pid / Launch failed)。
用户的原话是「我系统语言是中文 为什么打开软件是英文」。
改了什么
- i18n:catalog/instances.rs 从 6 条死 key 补到 34 条(页头 / 列表列头 / 详情动作 /
窄窗说明 / 元信息标签与值 / 隔离档位 / 运行状态 / 四个启动阶段)。族在装配链第 2 位,
新 key 追加在本族末尾,golden 对应位置插入(不是文件末尾追加)。
- 页面:新增 pages/instances/text.rs(Texts)——页面唯一的取词入口,实例页里不再有
任何可见英文字面量;detail / demo 也改从它取。
- 阶段标签:view/instances.rs 的 Running / Launch failed / 四阶段全部走目录;
**数字与分隔符不随语言变形**(运行中 · pid 1234 与 Running · pid 1234 只换前半句)。
- 顺手修掉两处「语言反向泄漏」:原版徽标此前直接画 presenter 的中文「原版」(英文界面
会漏中文),现在走 LoaderVanilla;隔离档位 isolated/shared 也从 core 的稳定 id 换成人话。
- 删掉 InstancesLastPlayed:M117 加的整句 key,从未接线,形状与「标签 + 值」的元信息卡
不符,由 InstancesMetaLastPlayed + InstancesNeverPlayed 取代。
守门(pages/instances/tests/i18n.rs + app/view/tests.rs)
- 中文态无 ASCII 英文单词(品牌 Java / 技术标识 pid 白名单);
- 每一条 Instances* 的两套译文必须不同(切语言时真的变);
- Running 的数字与 · pid 分隔符在两套语言里逐字保留;
- 页面源码里 29 条旧英文基线不许回来;每条 key 都必须被页面引用(死 key)。
出图:DevDocs/artifacts/m120/01~12(有实例 / 空态 / 运行中 × 浅深 × 中英)。
|
2026-11-05 13:36:44 +08:00 |
|
AskaEth
|
2f491c31f9
|
refactor(app-iced): M120 把实例页的展示转换从 view.rs 拆出去(纯搬移)
view.rs 是「快照 → 视图模型」的唯一入口,实例页那一段(列表副文案 / 运行状态 /
元信息卡 / 时间戳)占了它近一半。M120 要在这批文案上接 i18n,先按功能拆成
view/instances.rs(account_page / logs / rail 是同一个先例),本次一字未改行为。
唯一的非搬移改动:view/tests.rs 显式 import InstanceRuntime / LaunchStage
(它们原来是从 view.rs 的 use 里顺带借到的,拆分后不再可见)。
|
2026-11-05 13:36:43 +08:00 |
|
AskaEth
|
6a5c54102e
|
build(packaging): 产物改名成 名字_版本号_平台_架构
用户令:产物名不带实现细节字样(不出现 iced),格式统一成
`<名字>_<版本号>_<平台>_<架构>`,下划线分段。
- make_artifacts.py 新增 asset_stem() 作为唯一真相源;Windows 裸 exe 那条
路径原来还硬写着旧格式,一并改掉
- release.yml 的 sha256sum 与 assets glob 从 unlucky-launcher-* 改
unlucky-launcher_*(不改会因匹配不到而漏传产物)
- packaging/README.md 矩阵六行同步,并记下:Linux/macOS 仍为 tar.gz,
理由是许可文本随包 + tar 保留 755 可执行位
本地已跑通:unlucky-launcher_0.1.0_windows_x64.exe 14.44 MiB +
ull_0.1.0_windows_x64.exe 6.18 MiB + SHA256SUMS.txt。
|
2026-11-05 11:54:18 +08:00 |
|
AskaEth
|
43ab6c0a83
|
ci(guards): README 不适用 400 行红线
README 是对外介绍,不是可拆分的模块——拆成多份反而更难读。用户令
(2026-11-05):不用 400 行压它,但要保证介绍不臃肿、逻辑完备。
豁免范围只限仓库根的 README.md / README.<lang>.md;docs 下的长文
仍按模块拆分,其余规则不变。
|
2026-11-05 11:44:48 +08:00 |
|
AskaEth
|
1315b37f62
|
docs(readme): 状态段改成当前事实,模块地图补齐三个 crate
状态段原来停在「iced 版每个页面都是占位」——早就不成立了。改成:
十四页功能已齐、文案跟随系统语言、外观三件套、整合包导入导出与安装选项,
以及明确列出「还没做」的四项(安装器与自动更新、macOS 定型复核、
逐文件冲突策略、原生文件选择框)。
模块地图补上 crates/presenter、crates/i18n、crates/palette-extract,
并写明分层:app-iced → presenter → core/agent,design 与 i18n 不得反向依赖渲染框架。
|
2026-11-05 11:36:39 +08:00 |
|
AskaEth
|
4a212760b6
|
Merge branch 'feat/m119-apiscan-wire-presenter-accounts-and'
|
2026-11-05 11:09:56 +08:00 |
|
AskaEth
|
0e48ea15b8
|
fix(app-iced): M119 r2 —— 四处「模拟」文案全由 simulated 驱动;确认条不再替用户 arm 键盘
复审 r2 的两条 p2 都是「上一轮各差一步」:字段/焦点都真的做了,但**另有三处没接上**、
**键盘闸是代码自己打开的**。
## P2-1:四处文案只认一个开关
r1 只有标题胶囊由 `simulated` 驱动;副标题 / 输入框占位符 / 空态正文是**无条件取词**,
而它们的译文里写着「模拟 / 没接真实模型」——接上真 transport 之后这三句会**反向**留着
(`simulated == false` 时界面还在声明「没接真实模型」)。而且没有一条测试钉得住那个声明。
现在:
- 新增 `pages/agent::Disclaimer { simulated, subtitle, placeholder, empty_body }`,
`Disclaimer::of(catalog, simulated)` 一次算好;`Conversation.simulated: bool` 换成
`Conversation.note: Disclaimer`,header / composer / transcript 三处各自从它取词——
**视图里没有第二种取词路径**;
- i18n 配成对:模拟态用既有的 `AgentSubtitle` / `AgentPlaceholder` / `AgentEmptyBody`,
真实态新增 `AgentSubtitleReal` / `AgentPlaceholderReal` / `AgentEmptyBodyReal`(中性话)。
(没有把三句改成中性话:那句告知本身有价值,接真模型后它才该消失。)
- 证据:`crates/app-iced/src/pages/agent/tests/disclaimer.rs`
—— `all_four_disclaimers_appear_when_the_backend_is_simulated`(四处都提模拟)、
`no_disclaimer_survives_when_a_real_backend_is_wired`(四处都不提,且不许用空串糊)、
`the_two_states_use_different_keys`(两态不能共用同一个 key)。
**golden.rs 的 400 行边界**:+3 条会破线。按「按功能拆、不压注释」处理——把这张表的
**说明文档**(35 行)原样搬到 `catalog.rs` 里 `mod golden;` 的文档上(一个字的说明都没删),
`golden.rs` 只留表数据 + 3 行指针。现在 `golden.rs` 374 行、`catalog.rs` 328 行。
## P2-2:键盘闸不许由代码替用户打开
r1 在 `sync_confirm_focus`(`on_agent` 的无条件尾巴,`Events` 又由心跳驱动)里顺手
`keyboard_mode = true`——于是**只要冒出待确认,全局键盘闸就开了**;用户发完那句话接着打下一句,
第一个空格就成了「批准写操作」。复审用临时用例复现过(`rail.confirm` 变 `None`)。
现在:
- `sync_confirm_focus` **只挪焦点**到「允许」,不再碰 `keyboard_mode`(字段文档里写清了为什么);
- 键盘用户的表态是 `Tab`:`AgentUi::step_focus` 加了一条 **arming 规则**——待确认、焦点已在
确认条上、但键盘闸还关着时,这一次 `Tab` **只点亮焦点环、不移动焦点**(否则第一次 Tab 会把
「允许」挪到「拒绝」,用户按 Enter 就是反着裁决);之后再 Tab 才逐格走;
- 反向回归:`a_stray_space_or_enter_never_approves_a_pending_write`(打字 → 空格 / 回车都不得裁决,
审计也不许留);`a_blocking_confirm_can_be_decided_with_the_keyboard_alone` 改成
「确认到达时未 armed → Tab 点亮 → Enter 允许;第二轮 Tab·Tab 到拒绝 → Space」。
## 出图
`DevDocs/artifacts/m119/` 18 张全部重出(新增取证夹具 `ULL_AGENT_FIXTURE=armed`:
跑一轮真提交后再按一次 Tab,专供「焦点环可见」那张图):
- `08-agent-simulated-{light,dark}`:`simulated = true` 下四处「模拟」同时可见;
- `09-agent-confirm-focus-{light,dark}`:**按过 Tab 之后**的确认条(焦点环在「允许」上);
- `05-agent-confirm-{light,dark}`:确认条刚出现、**还没 arm**(无焦点环)——与 09 成对,
正好把 r2 的行为差异拍下来。
## 门禁(真实退出码,最终树)
| 命令 | 退出码 |
|---|---|
| `cargo fmt --all --check` | 0 |
| `cargo clippy -p unlucky-app-iced -p unlucky-presenter -p unlucky-agent -p unlucky-i18n --all-targets -- -D warnings` | 0(0 warning)|
| `cargo test -p unlucky-app-iced --lib` | 0(605 passed / 0 failed / 2 ignored)|
| `cargo test --workspace --exclude unlucky-app` | 0(58 套 2281 passed / 0 failed / 11 ignored)|
全树受检文件无 >400(`golden.rs` 374、`catalog.rs` 328、`pages/agent/mod.rs` 371)。
|
2026-11-05 10:43:22 +08:00 |
|
AskaEth
|
a22baebe70
|
fix(app-iced): M119 r1 —— 常驻「模拟」标识 + 确认条的键盘通路
复审 r1 的两条 p2,都是「说成真的、其实是假的」与「能走进死路」这个级别的问题。
## P2-1:界面必须自己说清「这是模拟」
原判:生产默认后端是**脚本化 rig**(`ULL_AGENT_SCRIPT` 的 6 种剧情),而「后端未接 / 模型还没
接上」两句诚实文案被我改成了中性话,理由写着「由端点标签说」——**那条端点标签在壳里根本不存在**。
于是默认配置下用户敲一句话得到一段像是模型编的回答。判据:没读过代码的用户,打开右栏、敲一句话,
必须能自己看出来这不是真模型(不接受 tooltip、不接受藏在设置里)。
修法(①恢复诚实文案 + ②常驻可见标记,并用):
| 位置 | 文案 / 形态 | 谁给的 |
|---|---|---|
| 标题右侧**常驻胶囊**(`SurfaceVariant` tonal)| `Key::AgentSimulated`(Simulated / 模拟)| `AgentHost::is_simulated()` → `Session.simulated` → 视图 |
| 标题下副标题 | 「模拟助手 · 还没接真实模型」 | `Key::AgentSubtitle`(原本是「未接」被改成中性的「会话」,现在写回来且更明确)|
| 空会话正文 | 「…当前是模拟助手,没有接真实模型。」 | `Key::AgentEmptyBody` |
| 输入框占位符 | 「给模拟助手发消息…」 | `Key::AgentPlaceholder` |
**标记不是硬编码在视图里**:`AgentHost.simulated` 是事实字段(今天 transport 只有 rig ⇒ 恒 true)。
接真 `AgentTransport` 那天把它传 `false`,四个位置的「模拟」措辞会一起消失——这是为什么用字段而不是
写死一句话。
## P2-2:确认条必须有键盘通路
原判:Allow/Deny 只有鼠标路径 ⇒ 键盘用户在写操作确认处**永久卡死**(`busy` 锁发送、
`Stop` 走主后端、清不掉 presenter 的 pending confirm)。
- 焦点环多两格:`Focus::ConfirmAllow` / `Focus::ConfirmDeny`,**只在有待确认时**存在
(鼠标用户不会 Tab 到不存在的按钮);
- 待确认出现 ⇒ `sync_confirm_focus()` 把焦点直接落到「允许」并**打开键盘巡航**(阻塞态主动亮环),
消失 ⇒ 焦点收回输入框、巡航关闭;
- `Enter` 与 `Space` 都映射到 `Activate`(`Space` 不抢输入框:`keyboard_mode` 那道闸在
鼠标点进输入框时是关的);
- 焦点环不是 10% 状态层——在**实心主色**按钮上那层几乎看不见(出图实测过一版就是没有)。
改成 2dp `Primary` 描边 + 3px 留白,两个按钮恒定加这层 padding,所以焦点变化不移动布局。
测试(`crates/app-iced/src/app/update/agent/tests.rs`):
`a_blocking_confirm_can_be_decided_with_the_keyboard_alone` —— 全程只有键盘消息:
确认到达后断言 `focus == ConfirmAllow && keyboard_mode`,发一条 `KeyPressed(Enter)` 完成允许;
第二轮 `Tab`(走到 Deny)+ `KeyPressed(Space)` 完成拒绝;两轮各记一条审计、焦点都收回 Composer。
另有 `the_default_backend_reports_itself_as_simulated` 钉住 P2-1 的事实来源。
## 视图入参顺带收成结构体
`docked`/`page` 本来是 6 个形参(`ui, agent, rail, failure, catalog, width`),这一轮还要加
`simulated` ⇒ 7 个。按塔台登记的可维护性口径改成 `Session { agent, rail, failure, simulated }`:
两个宿主各 3 个形参,以后再加展示事实只改 `Session`。
## 出图(18 张,浅深各一)
`DevDocs/artifacts/m119/`:新增 `08-agent-simulated-*`(空会话 + 四处「模拟」措辞)与
`09-agent-confirm-focus-*`(确认条 + 焦点环落在「允许」);其余 7 组全部重出(文案与焦点环都变了)。
## 门禁(真实退出码)
| 命令 | 退出码 |
|---|---|
| `cargo fmt --all --check` | 0 |
| `cargo clippy -p unlucky-app-iced -p unlucky-presenter -p unlucky-agent -p unlucky-i18n --all-targets -- -D warnings` | 0(0 warning)|
| `cargo test -p unlucky-app-iced --lib` | 0(601 passed / 0 failed / 2 ignored)|
| `cargo test --workspace --exclude unlucky-app` | 0(58 套 2277 passed / 0 failed / 11 ignored)|
`golden.rs` 400 行(+1 条 `AgentSimulated` 正好顶线);全树受检文件无 >400。
|
2026-11-05 09:17:50 +08:00 |
|
AskaEth
|
b5e96c61ac
|
fix(app-iced): M119 出图抓到的两处:右栏文案没跟 lang 走、确认条文字被压成竖缝
两张验收图(`DevDocs/artifacts/m119/`)暴露的问题,都是测试抓不住、只有图能看出来的那类:
1. **同屏两语**:`pages/agent/mod.rs` 里还留着 M104 的 `const LANG: Lang = Lang::En`,所以右栏的
标题/按钮恒英文,而 M119 新加的失败横幅走 `App::lang()`(这台机器是 zh-CN)⇒ 一屏里
「Assistant / Collapse」和「连不上助手后端」并排。现在删掉那个常量,`docked`/`page` 收一个
`Catalog` 形参,由 `App::lang().catalog()` 给——与设置页/账户页同一条口径(M106 的真值源)。
2. **确认条没有字**:`chip + 文字 + 两个按钮` 挤在同一条 `Row` 里,而右栗只有 280~360:320 宽下
文字列被压成一条竖缝,标题整段看不见(图 05 第一版就是这个样子)。改成文字行与动作**分行**,
动作右对齐——窄栏里才是可读的 M3 告警形态。
顺带:`Script::WriteOp` 补一句增量,免得那条助手气泡是空壳(出图更接近真后端)。
`cargo test -p unlucky-app-iced --lib`:599 passed / 0 failed;14 张图重出后逐张看过。
|
2026-11-05 08:11:14 +08:00 |
|
AskaEth
|
d4fd675935
|
feat(app-iced): M119 把 presenter 的 agent 四契约与账户窄入口接进 iced 壳
M103 把 AI 会话的投影(`AgentRail` + `submit/mark_read/confirm`)做完了,M114 把账户三轨做完,
但 iced 壳一直没接:M103 复审确认 `Presenter::` 零非测试消费者,M104 的右栏会话数据来自内置样本。
这一个提交就是那一步——**右栏与账户页吃 presenter 的真实状态**。
## 接线层(新增 `crates/app-iced/src/presenter/`)
| 文件 | 是什么 |
|---|---|
| `agent.rs` | `AgentHost`:presenter 句柄(四契约)+ 事件队列 + **具名**失败(`AgentFailure`:`unreachable` / `not_implemented`)|
| `transport.rs` | `AgentTransport` 的 iced 侧实现(脚本化 rig + 五种剧情 + 两种失败注入)|
| `rig.rs`(仅测试) | 账户轨的 `Backend`:`AppCommand` → host 形状的 `CoreEvent` |
| `mod.rs` | 逻辑路径说明 + **线程/通道边界** |
物理目录 `src/presenter/`,逻辑名字 `crate::app::state::presenter`——由 `app/state.rs` 用
`#[path]` 挂载(与 `shell::agent` 挂 `pages/agent/` 同一个先例:scope 不含 `lib.rs`)。
## 事件回流
`transport` 只**入队**、UI 只**出队**:`advance`→`pump` 里同拍抽干,抽出来的这批经
**既有 `Message::Agent(..)` 通道**(新变体 `Events(Vec<AppEvent>)`)回到 `on_agent`,再由
`Presenter::apply` 落地。同一条漏斗,所以用户点击与后端事件不会各改一次状态。
## 会话内容只留一份
`AgentUi` 里的 `transcript/busy/unread` **删掉**,右栏与独立页直接读 `agent_rail()`
(`pages/agent::spec` 零拷贝借)。M104 的样本会话(`demo::transcript()`)一并删除:出厂就是
空会话,取证夹具要内容就**跑一轮真提交**(`ULL_AGENT_FIXTURE=turn`),消息只能由 presenter 产生。
## 视图
- 气泡按 `ChatMessage.role` 画(User/Assistant/Tool/System),没有任何壳自己的会话类型;
- 新增两条横幅(`conversation/banners.rs`):**失败态**(catalog 标题 + 已脱敏正文)与
**写操作确认**(摘要 + Allow/Deny → `agent_confirm`);
- 拖动缝与折叠条按 400 行硬线拆到 `mod.rs` 之外(`strip.rs`、`conversation/{transcript,banners}.rs`)。
## 未读口径
presenter 判「栏可见吗」用的是它自己的 router + `prefs.ai_collapsed`,而这两件的真源在 `App`。
`refresh_view` 用 presenter 的两个**窄入口**(`navigate` / `prefs_mut().ai_collapsed`)单点写回,
带幂等闸(相同就不重复写,避免 `navigate(Route::Agent)` 顺手 mark_read 吃掉用户攒的未读)。
## 账户页
账户列表与登录事件仍走 `AppCommand`/`CoreEvent`(M114 的形状没动),本轮把**登录指令与
列表消费**纳入同一套树测试:添加离线档案 → 列表出现 → 切换 → 删除;设备码「进行中可见 + 可取消」;
失败态(名字不合法 / 凭据失效)都给具名错误。
## 树测试(9 条,`cargo test -p unlucky-app-iced --lib` 全绿:599 passed / 0 failed)
- `app/update/agent/tests.rs`:提交 → 事件回流 → 右栏出现该轮 + `busy` 翻转 + 标记已读归零;
确认条可裁决且记审计;三种失败各给具名横幅;草稿逐字同步。
- `app/update/account/tests.rs`:增/切/删端到端;设备码进行中与取消;两种失败态;
M114 的三条回归闸(原内联测试搬出来)。
- `pages/agent/tests.rs`:视图契约(`spec` 原样借 rail)、a11y 标签、焦点环、两种宿主全态构图。
顺带:`app/tests.rs` 里三条 `agent.unread` 断言跟着 rail 走(出厂 0 未读),`view/account_page.rs`
的 `docked` 调用点补上 rail 与失败横幅。
|
2026-11-05 08:02:58 +08:00 |
|
AskaEth
|
6505493d4e
|
feat(i18n): M119 追加 6 条 AI 助手错误/确认文案,并订正两条已成假话的译文
接线(M119)之后右栏才第一次有「失败态」与「写操作确认」这两块真 UI,而 catalog 里
一条对应的 key 都没有——任务书要求「失败必须显示**具名**错误」,没有键就只能硬编码,
和 M106 立的「界面文案一律走 catalog + lang」直接冲突。所以这批条目是修不成就做不成。
新增(按金表规矩追加在 `agent_entries` 族尾、`golden.rs` 同序追加):
| key | 用途 |
|---|---|
| `AgentErrorTitle` | 失败横幅标题 |
| `AgentErrorUnavailable` | AI 端点不可达(原因码 `unreachable`) |
| `AgentErrorNotImplemented` | 该路由还没接线(原因码 `not_implemented`) |
| `AgentConfirmRequest` | 确认条的句子(`{summary}` = presenter 已脱敏的请求摘要) |
| `AgentConfirmAllow` / `AgentConfirmDeny` | 确认条两个按钮 |
订正两条**已成假话**的译文(接线前它们陈述「后端未接」):
- `AgentSubtitle`:`Conversation shell · no backend yet` → `Conversation`
- `AgentEmptyBody`:去掉 `No model is wired up yet.` 一句
真接的是什么由端点标签与失败横幅说,不在标题下重复。`golden.rs` 现在 399 行(+6 行后仍 ≤400)。
|
2026-11-05 08:02:49 +08:00 |
|
AskaEth
|
3c61a2397e
|
refactor(app-iced): 拆 state.rs(400 行) 与 update.rs(399 行) 为功能子文件
两个文件都贴着 400 行硬线、零余量,M119 要往里加字段与一行 drain。**这一个提交只搬不改**
(下一次提交才是改动),搬的位置按功能切:
| 原位置 | 现在 | 内容 |
|---|---|---|
| `app/state.rs` | `state.rs`(172) | 只剩 crate 文档 + 常量 + `struct App` 的字段与注释 |
| 同上 | `state/construct.rs` | 两个构造器 + 字段初始化表 |
| 同上 | `state/refresh.rs` | `refresh_view`(快照 → 视图模型的唯一转换点) |
| 同上 | `state/theme.rs` | `Ui` 重算 / 主题档 / `has_screenshot` / `ui()` |
| `app/update.rs` | `update/clock.rs` | 推进一拍 / 派发 / 抽干事件 / 自动选中 |
一处**非字面**的改动:搬进子模块之后 `pub(super)` 的语义从「crate::app」变成「crate::app::state」,
所以这些方法改成 `pub(in crate::app)`——可见范围与搬家前逐字相同,只是写法得跟着模块路径走。
除此之外照抄原文(含原有注释与文档)。
搬完 `cargo check -p unlucky-app-iced --all-targets` 零 error 零 warning。
|
2026-11-05 08:02:45 +08:00 |
|
AskaEth
|
1b77a36d3d
|
Merge branch 'feat/m117-msacfg-accurate-wording-and-local-c'
|
2026-11-05 07:31:57 +08:00 |
|
AskaEth
|
8780f26308
|
fix(auth): M117 微软登录缺 client id 说「未配置」而不是「未支持」
用户实跑 debug 二进制看到「本期未支持」,误以为功能没做;实际是构建/启动时
没注入 Azure 公共客户端 id(登录三轨已实现、白名单已批、真机验签过)。
- core/auth/microsoft/client_id.rs(新):id 的来源判定(env > build,空白值不算配)、
`ClientIdStatus`/`log_line()` 那行 `[auth] msa client_id=… source=…`、以及
「未配置 + 怎么配(含可照抄命令)」的共用文案 `MISSING_CLIENT_ID_MESSAGE`。零网络。
- core/auth/error.rs:`AuthError::Unsupported` 不再统一拼「本期未支持:」前缀——该变体
全仓唯一构造点就是缺 client id,硬拼前缀让「没配置」被读成「没实现」。`code()` 仍是
`unsupported`,枚举形状与其它分支不动(tower 批的最小改动)。
- core/auth/microsoft/mod.rs:`from_env()` 走 `resolve_client_id()`,失败措辞取自上面那条
文案,且在网络之前早退(连 HTTP 客户端都不建);导出 `client_id_status()`。
- cli:`map_auth_error` 的 Unsupported hint 给两条能照抄的命令;`accounts` 帮助说清
「不是功能缺失」与两种注入方式;新增 `msa_client_id_log_line()` 给 GUI 侧出面。
- app-iced:`on_boot` 打一行 `[auth] msa client_id=… source=…`(判据来自 core,不重算)。
- i18n:`LaunchMsaNotConfigured` 中英各一条,golden 按 `Key::ALL` 顺序在
`LaunchFailedWithCode` 之后追加一行。
本地便利路径(gitignored,随报告交付):`DevDocs/ops/ull-msa-run.sh` 读本地 id 文件
或 `azure-msa-app.md` 注入环境变量再起 GUI,脚本本身不含 id。
|
2026-11-05 07:31:55 +08:00 |
|
AskaEth
|
7ddcec4941
|
Merge branch 'feat/m116-winround-window-corner-policy-with'
|
2026-11-05 07:28:03 +08:00 |
|
AskaEth
|
dc1fa8d082
|
fix(app-iced): M116 r5 —— 圆角状态从「换色正文」升级成 tonal notice 容器
r5 复审的第二轮意见(改前那条是一行紫灰正文,与说明段只差色相、中间没有间距,读起来还是
「一段话的前两句」)。按 M3 惯例改成**容器**:
- 新增 `pages/appearance/mod.rs::notice`:`Role::SurfaceContainerHighest` 底 +
`ShapeScale::Small` 圆角 + 左右 `Space::X2` / 上下 `Space::X1` 内边距,文字
`Role::OnSurfaceVariant` + 加粗。**只用既有角色与间距档位**,不新增令牌、不引入图标。
(放在模块根而不是 `section.rs`:后者已 395 行,400 行是硬顶。)
- 状态行与下面的说明段之间再补 `Space::X1`(列间距 X1 + 这里 X1 = X2 总量),
让它与说明段在**形态**上分开,而不是靠色相。
- 出现条件不变:仅「跟随系统 + α<1」;档位照常显示用户选的值。
- 像素实测(浅色 α=85,1180×760 / 100% DPI):容器底色 `(216,228,239)`、卡片底
`(235,245,255)`;容器内文字 `(58,72,83)`;容器 y=372..411,说明段首行 y=428
⇒ ink 间距 16px(布局 8px + 行高留白);α=100 两张图该底色出现次数为 0。
门禁:fmt --check 0 / `cargo test -p unlucky-app-iced --lib -j 2` 0(475 passed)。
|
2026-11-05 06:35:21 +08:00 |
|
AskaEth
|
67deb103b0
|
fix(app-iced): M116 r5 —— 圆角状态行独立成「状态」(换色加粗)+ 说明段去掉重复并压短
r5 复审 p2-1(图上)、p2-2(证据文档两处)。
## P2-1:状态行要被当成「状态」看见
- 说明段 `SettingsAppearanceCornerHint` 里那句 α 说明**删掉**,只留平台分工(Win11/Server 2025
请求系统圆角、Win10 直角、macOS/Linux 看桌面合成器、我们不自绘)——英文从约 470 字符 /
7 行压到 3 行(1180 宽、100% DPI 实测)。
- `SettingsAppearanceCornerSuspended` 改成**只由状态行说**,且状态行不再是「说明段的续行」:
紧贴在档位行下面,颜色换成设计令牌 `Role::Tertiary`、字重加粗(`note_line` 保持原形,
新增 4 行的 `state_line` 助手)。档位本身照常显示用户选的值,不改成「直角」。
- 出图复核(四张,浅/深 × α=100/85):α=85 两张能看到一条**加粗的第三色**状态行,
α=100 两张没有;状态行墨色由 `(63,76,87)`(与说明段同色)变为 `(116,108,140)`(Tertiary)。
## P2-2:证据文档两处
- `DevDocs/artifacts/m116/probe-table.md` 的 `readback` 归因:`0x04000000`=`WS_CLIPSIBLINGS`
是 **`CreateWindowExW` 自己补的**(建完就在),`ShowWindow` **只补** `0x10000000`=`WS_VISIBLE`;
按 r5 复审的最小实验写清(req `0x00CF0000` → before `0x04CF0000` → after `0x14CF0000`),
读数一行未动。
- `corner/tests.rs` 里「本 mission 没构建 bin」改成指向 2026-11-04 的两次真跑记录
(`DevDocs/artifacts/m116/README.md` 的 `[corner] boot …` 两行)。
门禁(`CARGO_TARGET_DIR=C:/Temp/ull-target/wt-116-r2`,`-j 2`,跑完已删):
fmt --check 0 / `cargo test -p unlucky-app-iced --lib` 0(475 passed)/ `-p unlucky-i18n --lib` 0(24 passed)。
|
2026-11-05 06:35:21 +08:00 |
|
AskaEth
|
91f723a019
|
docs(app-iced): M116 r4 —— 日志/模板示例改用真进程实测值(frame=overlapped)
真进程两跑(`--software-renderer`,同一个 bin,只改整窗 α)拿到:
α=1.0 → … frame=overlapped layered=off
α<1 → … frame=overlapped layered=on suppressed_by=window_alpha
所以把两处仍拿 `frame=popup` 当示例的文档改成实测值:
- `pages/appearance/mod.rs` 的取证模板:`frame=overlapped layered=on … suppressed_by=window_alpha`,
并注明真进程两条在 `DevDocs/artifacts/m116/README.md`、`frame=` 恒为 `overlapped`/`other`。
- `transparency/dwm.rs` 的 `style_summary` 示例:同上,并点明 `popup` 只属于带 owner 的弹出窗。
注释级改动,行为与断言零改动。
|
2026-11-05 06:35:20 +08:00 |
|
AskaEth
|
1cb36da1e2
|
docs(presenter): M116 r4 P3 —— settings_bridge 的「三个键」漂移对齐到四个字段
AppearanceBase 自 M116 起有四个字段(整窗 α / 标题栏三态 / 配色来源 / 窗口圆角),
load_base 的文档与测试名还写着「三个键」。只改注释与测试函数名,行为零改动。
|
2026-11-05 06:35:19 +08:00 |
|
AskaEth
|
abc51a957b
|
fix(app-iced): M116 r4/r5 —— 产品窗口不是 WS_POPUP(真凶是整窗 α 的 layered)+ 裁定 A 落地
r4 复审 p1(证据链前提错误)+ 裁定 A(D47:整窗透明度优先于 DWM 圆角)。
## 事实订正(四处错前提 + 两处残留断言)
- `probe.rs`:删掉「winit 在 decorations=false 下给 WS_POPUP」与「popup+layered = 本产品
窗口的完整形状」。产品样式按 winit `window_state.rs::to_window_styles` 的来源写成
`PRODUCT_STYLE`(`WS_CAPTION|WS_BORDER|WS_CLIPSIBLINGS|WS_SYSMENU|WS_THICKFRAME|
WS_MINIMIZEBOX|WS_MAXIMIZEBOX` = `0x04CF0000`,**不含 WS_POPUP**),并新增
`product` / `product+alpha` 两行(五行实验)。裸 popup 行改名为 `probe:popup*`,
明确标注「对照,不是产品形状」。
- `corner.rs`:平台矩阵第 ③ 条改成「调整带只给带 WS_CAPTION|WS_THICKFRAME 的窗口;
裸 popup 假形状上的 `+0,+0` 不能解释产品」;补第 ④ 条「整窗 α 会关掉圆角(裁定 A)」
并说明日志里 `pref=` 有值即证明请求确实发出。
- `app/update/appearance.rs` 与 `transparency/{dwm.rs,transparency.rs}` 的注释:把「第一嫌疑是
无边框 WS_POPUP」改成「整窗 α 引入的 WS_EX_LAYERED」;`frame=` 的真进程取值写明是
`overlapped`(关 min/max 则 `other`),**永远不会是 popup**。
- 实测复核(`m116_corner_controlled_experiment`,本机 Server 2025 / RDP 断开):
`product` 行 `Δframe = +0,-7`(与 `decorated` 同格),`product+alpha` 也是 `+0,-7`
⇒ 分层位不改变那条带子,`Δframe` 不能解释圆角失效。
## 裁定 A 落地
- 日志新增中立观测栏 `suppressed_by=window_alpha`(`format_line` 多一个参数;进 `CornerApplied`
指纹,α 1.0↔<1 各触发一次重下/一行日志)。只陈述「分层位来自我们的整窗 α」。
- 设置页:外观说明句写明「开启整窗透明度时 Windows 不会给窗口圆角,回到 100% 自动恢复」;
新增 `SettingsAppearanceCornerSuspended` 一档,仅在「跟随系统 + α<1」时作为**当前状态提示**
出现;档位本身照常显示用户选的值(不偷偷改成「直角」)。中英双份 + golden。
- 未动:per-pixel α、D36 的 LWA_ALPHA 实现、α 与圆角的优先级。
## 顺带
- `presenter/prefs.rs` 的 `AppearanceBase` 文档「三个键/只读」→「四个键」(r4 P3)。
- 证据目录(DevDocs/artifacts/m116/{README.md,probe-table.md})按上面重写:五行真值表原样贴出、
产品行标明「跑出来的不是手写的」、旧的「无边框 ⇒ 没有边框带」结论改为「只在假形状上成立,
产品不用这个形状」。
门禁:check 0 / fmt --check 0 / `cargo test -p unlucky-app-iced --lib` 0(448 passed, 0 failed,
2 ignored)。ignore 探针实跑:`PROBE_RC=0`,两行产品读数见报告。
|
2026-11-05 06:35:19 +08:00 |
|
AskaEth
|
bc3a2b56a4
|
docs(app-iced): M116 r3 复审 —— 只对齐注释里的「只读下层键」数量与取证日志模板
复审 r3:p2 ×2,纯注释/文档漂移,**行为零改动、断言零改动**。
1. `app/appearance.rs:8,15-20,68` 与 `app/state.rs:170-171`:M116 之后
`AppearanceBase` 是**四个**字段(整窗 α / 标题栏三态 / 配色来源 / 窗口圆角),
走 `AppCommand::SetSetting` 的是**三个**键(标题栏三态还没有 UI 写者,D37 接线未落地)。
两处数字与列举按 grep 到的实际代码改(`AppearanceBase` 的 `pub` 字段数 = 4;
`on_appearance` 的 match 守卫 = WindowOpacity / PaletteSource / Corner)。
2. `pages/appearance/mod.rs:41` 的取证件模板补 `corner=`:`[appearance]` 行确实有这一栏,
圆角还有一条独立管线 `[corner] <ctx> corner=policy=… source=… os=… excluded_reason=…
radius=system pref=… readback=… read_hr=… frame=… layered=…`(取值形状取自探针实跑,
见 corner/tests.rs 的格式单测),模板已按此补齐,并注明「看上去圆不圆」同样不可信。
门禁(`CARGO_TARGET_DIR=C:/Temp/ull-target/wt-116`, `-j 4`,退出码逐个落盘):
check 0 / fmt --check 0 / `cargo test -p unlucky-app-iced --lib` 0(447 passed, 0 failed, 2 ignored)。
行数:appearance.rs 130、state.rs 343、pages/appearance/mod.rs 180(均 ≤400)。
|
2026-11-05 06:35:18 +08:00 |
|
AskaEth
|
41746d1612
|
fix(app-iced): M116 r1 复审 —— 口径降级为「不承诺」、修掉伪造读数与两处错注释、圆角下发去重
四条 P1(全是「口径与证据不符」,行为未变):
1. 把「Win11 一定有系统圆角、边缘平滑」从事实降级:`corner.rs` 的平台矩阵改成
「实测 / 解释 / 推断 / 未验」四列,并明写本机只测到「写入被接受 / 回读 E_INVALIDARG /
无边框窗口没有 DWM 调整带」;设置页与 golden 的说明句改成「我们**请求**系统圆角,
是否圆、边缘什么样由合成器决定」,不再承诺结果。
2. 删掉证据目录里那条伪造读数 `readback=2`:本机三种样式回读全是 `E_INVALIDARG`,
该值物理上产不出来,也不可能由 `format_line` 产出。改成显式的「合成示例(非实测)」,
并把每一栏标注成真实/示例来源。probe-table 的「没有一条是推断」标题一并拆开,
结论 4/5 明确标为**解释**(并写明 Δframe 之差是样式位的同义反复,不能反推 DWM 决策)。
3. `keys.rs` 的「旧读新」语义按实际链路改写:1.4.0 读 1.5.0 文件走
`SchemaCompat::check_url` → `ReadOnlyPreserveSchema` → `SchemaCompat::access` →
`FileAccess::ReadOnly`,**整份配置只读**(值照读、`store.set` 报 `SettingError::ReadOnly`、
`save` 跳过),不是「只是少一档」。
4. 两处错注释订正:
- `hresult_ok` 的理由改成 `S_FALSE` 的通用语义,并明写 `0x7FF8FFA9` **不是** DWM 的返回值
(本机实测是 `E_INVALIDARG 0x80070057`),那是我早期用 `unsigned_abs()` 打印 HRESULT 的产物;
对应单测也把该常量标为「合成值,只用来钉规则」。
- `DWMWA_VISIBLE_FRAME_BORDER_THICKNESS` 的文档改成「本机三样式三态实测全 1,不能当判据,
仅作诊断读数」。
两条裁定:
- CPU 档在 Win11 仍走 DWM(tower 裁定保留):理由写进 `plan_corner` 文档——D36 约束的是我们自己的
渲染路径,DWM 圆角是合成器侧的,不增加我方负担;设置页说明同步加了「软件渲染也一样」。
- 「圆角窗口上拖边缩放仍命中」实测未做(RDP 断开、无指针注入手段):mission 里以 note 记为 dropped
(store 的 task_drop 对已完成任务不生效,工具层面无法取消勾选,已在 note 里写清)。
六条 P2:
1. probe-table 拆「实测 / 解释 / 未验」并删掉绝对化标题。
2. CPU 档裁定见上(任务书由 tower 侧调整)。
3. 拖边缩放任务记账更正见上。
4. **圆角下发去重**:新增 `corner::{CornerApplied, needs_redispatch}`(指纹 = 策略 / 来源 /
原因码 / 有没有句柄,不含只有真下发才拿得到的读数),`App::apply_window_corner` 改成
`&mut self` 并在判定未变时**直接返回**(不动窗口、不打日志);`App` 新增 `corner_applied` 字段。
新增测试 `the_same_policy_is_dispatched_only_once`:连续 20 次同策略只下发/记 1 次,
换档与「首次拿到句柄」必须重下。
5. `dwm_get_rect` 的 `hr == 0` 改成 `hresult_ok(hr)`,「判据只有一处」名副其实。
6. 报告口径修正(诊断已产出、UI 接线待做——`SettingsBridge` 未暴露 doc,接线要改 presenter 的
`settings_bridge.rs`,不在本 mission scope),本轮不动代码。
门禁:fmt --check exit 0;clippy(5 个 crate)--all-targets -D warnings 零 warning;
lib 测试 app-iced 419(+1 去重测试)/ core 266 / design 163 / i18n 24 / presenter 129 全绿;
改动文件仍全部 ≤400 行(最大 keys.rs 398)。
|
2026-11-05 06:35:17 +08:00 |
|
AskaEth
|
25d860b2b3
|
refactor(app-iced): M116 把圆角单测搬出 corner.rs、日志行收敛到 format_line
- `platform/win32/corner.rs` 从 413 行降到 200 行(AGENTS.md §3-1 的 400 行硬线):
单测搬到 `corner/tests.rs`。
- 日志行的**形状**收敛成一个纯函数 `corner::format_line(context, plan, policy, outcome, style)`,
于是「`excluded_reason` 这一栏还在不在」不依赖真窗口也能被断言。
新增 `the_log_line_shape_is_pinned_with_the_measured_readings`:用本机探针窗口的真实读数
(Server 2025 / 26100、读回 E_INVALIDARG、frame=popup)逐字段钉死,
并把 policy × 系统 × 渲染档四种 `excluded_reason`(none / user / os-build / cpu-tier / unknown-os)全覆盖。
- `set_layered` 补一段「这里是 GetLastError 的 DWORD 语义,`unsigned_abs()` 有意保留;
DWM 那侧是 HRESULT 语义、必须 `as u32`」的注释 + 对应用例(tower 2026-11-03 裁定:两者不同源,不许统一)。
门禁:fmt --check ✓ / clippy -D warnings(5 个改动 crate)✓ /
lib 测试 core 266 + design 163 + i18n 24 + presenter 129 + app-iced 418 全绿。
|
2026-11-05 06:35:03 +08:00 |
|
AskaEth
|
88dbf6d955
|
feat(app-iced): M116 窗口外框圆角策略(跟随系统/直角)+ DWM 读写的受控对照实验
用户问「为什么窗口没有圆角」。核实:组件层圆角齐,但窗口外框**零处理**——代码里没有任何
Dwm* / SetWindowRgn。本 mission 先做实测,再按平台分开落策略。
判据(纯函数、可真值表):
- `platform::plan_corner(策略 × 系统版本 × 渲染档)` → `source=dwm|square` + `excluded_reason`。
Win11 / Server 2025(build ≥ 22000)→ DWM;Win10 及更早 → 直角(不做 SetWindowRgn 毛边默认);
探测不到版本(macOS / X11 / Wayland)→ 直角 + `unknown-os`(「不承诺」而不是「做不到」)。
CPU 档在 Win11 上照样用 DWM(D36 限制的是我们自己的合成技巧,不是系统合成器)。
落点:`platform/win32/transparency.rs` 仍是全仓唯一 unsafe 文件,新增 DWM 的 FFI 与安全包装;
`alpha.rs` / `dwm.rs` / `probe.rs` 三个新子模块零 unsafe、零 extern(沿 exstyle.rs 的既有形状)。
实测(本机 Server 2025 build 26100、RDP 会话断开、同进程三窗口对照):
- `DwmSetWindowAttribute(DWMWA_WINDOW_CORNER_PREFERENCE)` 在 WS_OVERLAPPEDWINDOW / WS_POPUP /
WS_POPUP|WS_EX_LAYERED(α=0.8) 上**全部成功** ⇒ 无边框与整窗 α 不会让属性设不上;
- `DwmGetWindowAttribute` 同一属性三种样式**全部 E_INVALIDARG** ⇒ 本机不支持回读,与样式无关;
- `DWMWA_EXTENDED_FRAME_BOUNDS − GetWindowRect`:有边框 = +0,−7,popup = +0,+0
⇒ DWM 只给有边框窗口加不可见调整带,我们的窗口没有可画的圆角区域(结构性解释)。
- 像素(角上透明/方块)**未验**:RDP 断连导致 CopyFromScreen / gdigrab 全废。
同时修掉一个自己踩到的判据错误:`i32::unsigned_abs()` 不能用来打印 HRESULT
(E_INVALIDARG 0x80070057 会被印成 0x7FF8FFA9)。现在 HRESULT 用 `hr as u32` + 严重性位,
GetLastError 的 DWORD 语义保持 `unsigned_abs` 并写死注释(两者不同源,别统一)。
配置:新键 `appearance.window_corner ∈ {system, square}`(出厂 system),schema 1.5.0 只推 minor、
不写迁移:老文件缺键 = 取默认 = 跟随系统;旧版本读新文件时未知键被登记并原样写回(`RenderMode::Full`),
只在显式 `store.set` 该键时才 `UnknownKey`。
设置 UI:外观组只留两档(跟随系统 / 直角)+ 一句按平台说清的说明,文案走 i18n 中英。
日志:`[corner] <ctx> corner=policy=… source=… os=… excluded_reason=… radius=system pref=… readback=… frame=… layered=…`
(形状由 `corner::format_line` 的格式化单测钉住,用探针窗口真实读数构造)。
证据:DevDocs/artifacts/m116/{README.md,probe-table.md}
|
2026-11-05 06:35:03 +08:00 |
|
AskaEth
|
aace0c5836
|
Merge branch 'feat/m114-accountui-accounts-page-three-track'
|
2026-11-05 06:34:09 +08:00 |
|
AskaEth
|
0fdc889af6
|
refactor(presenter): M114 rebase 后守 400 行 —— 实时日志滚动窗口搬进 state/live.rs(纯搬移)
|
2026-11-05 05:37:43 +08:00 |
|
AskaEth
|
a183adc072
|
refactor(app-iced, presenter): M114 rebase 后守 400 行 —— 四处纯搬移(不动语义、不压注释、不挪断言)
主线六条合入后,四个被我碰过的文件越过 400 行;按模块拆开,只搬不改:
- `presenter/host/mod.rs` 421→366:账户刷新/皮肤三段(refresh_accounts / attach_skins /
request_skins)搬进 `host/accounts/refresh.rs`(`impl CoreHost`,pub(in crate::host))
- `presenter/state.rs` 410→392:`TileCell` + `grid_positions` 搬进 `state/tiles.rs`,
`state.rs` 用 `pub use` 再导出,老路径不变
- `app-iced/app/state.rs` 401→394:账户页重建那一段搬进 `app/view/account_page.rs`
的 `App::rebuild_account_page`
- `app-iced/app/update.rs` 407→388:`ULL_START_ROUTE` 取值表搬进 `app/update/boot_env.rs`
(纯函数、无状态)
|
2026-11-05 05:13:54 +08:00 |
|
AskaEth
|
0f69eec64d
|
refactor(presenter): M114 mock 的「取消登录」搬进 mock/accounts.rs(守 400 行)
|
2026-11-05 05:10:48 +08:00 |
|