AskaEth
|
111955efae
|
fix(app-iced): M79 r3 —— 分隔线守卫的三处「假绿」根治(众数参考色 / 角色身份校验 / 锚定行无条件打印)
代码复审 r3 = p1-2items,两条都是判据自身的假绿,产品代码零改动。
1. **参考色取「跨度中点那一个像素」→ 缺口落在中点就假绿**
`line_segments` 改用该行**墨迹众数色**(`modal_color`):242px 的线 vs ≤4px 的缺口,
众数天然是线自己的颜色。反例 `gap_at_mid`(r2 图在 x=144..145 挖 2px 孔):
改前 `ok. 1 passed`(假绿),改后 **2 段 red**。
2. **行锚定可被「更靠上的另一条细低对比横线」抢走**
`find_thin_line` 增加 `expected` 身份色 + `blend_coverage`:线色必须落在
`Surface → SurfaceVariant` 的插值线上(覆盖率 0.30~1.05,三通道 t 最大差 ≤0.25)。
这比「单通道差 ≤20」稳得多:实测 `OutlineVariant (200,212,224)` → t=(1.84,2.24,3.1) 出界;
hover 8% `(228,233,238)` → t=(0.72,1.0,1.7) 散开 0.98;两者都被拒。
反例 `decoy_outline_gap` / `decoy_hover_gap`:改前 `ok`(假绿,锚到 y=400/401),
改后打印 `divider skip y=… (不是预期角色色的插值,拒绝)` 并退回真分隔线 → **3 段 red**。
3. **锚定行无条件打印**(不管红绿):
`[probe] divider anchored at y=471 span=x24..266 color=[221, 233, 245] segments=1 (light, 1180x760)`
——「锚错行」从此不再静默。
4. **深色档不再是零覆盖**:`divider::check` 浅色必跑,`tabs-dark.png` 存在就跑深色
(实测 `y=471 color=[46,60,70] segments=1 (dark)`)。判据/锚定逻辑抽到 `probe/divider.rs`,
`probe.rs` 393 行仍 <400。
9 张图全跑(退出码即判据):
r2 rc=0(1段) · before rc=101(3段@471) · mutB rc=101(2段) · mutC rc=101(2段) ·
mutE rc=101(3段@471) · mutF rc=101(3段@471) · **gap_at_mid rc=101(2段)** ·
**decoy_outline_gap rc=101(3段@471,y=400 被身份拒)** · **decoy_hover_gap rc=101(3段@471,y=401 被身份拒)**。
已知边界写进 `probe/scan.rs` 模块文档:≤25% 覆盖的淡线 → **loud red**(锚定失败 panic,
不会假绿);身份校验绑定 `SurfaceVariant`(换角色色会 loud red,改 `divider.rs` 一行);
只认横线;深色图缺失时跳过深色断言但不失败。
门禁:fmt ✓ · `cargo test -p unlucky-app-iced` 197 passed · gate 6 passed · probe 5 passed ·
`clippy --all-targets -D warnings` 干净。变异图在 `target/m79/{mutB,mutC,mutE,mutF,gap_at_mid,decoy_outline_gap,decoy_hover_gap}/`,不入库。
|
2026-11-02 11:56:08 +08:00 |
|
AskaEth
|
dffae2c429
|
fix(app-iced): M79 r2 —— 分隔线守卫换成「锚定细线行 + 整段跨度数段」
代码复审 r2 P1-1:上一条守卫只认 r1 那种「半透明 AA 缝」,四处假通过。
两个结构性原因与对应修法:
1. **窗口被截成 150px**:`longest_h_line` 凑满 150px 就 return,`line_segments` 只在那
150px 里数段 → 分隔线实长 242px,只看前 62%;缺口在窗口外看不见、在窗口内则被
「跳到缺口之后重新起跑」滑过。现在 `find_thin_line` 返回的是**该行整段墨迹跨度**,
`line_segments` 在这个跨度上数段。
2. **行的锚定不稳**:原来「第一条 ≥150px 的连续带」能被五态行的 40px 高底色带、
`Primary` 指示条顶替(mutE/mutF 会量到完全不同的元素)。现在四条判据一起锚定:
跨度 ≥200 · 近乎实心(墨迹 ≥ 跨度×4/5)· 薄(跨中点纵向墨迹 ≤3 行)· 低对比
(线色与底色单通道差 ≤120)。
`TOL=6` 的口径写进注释:**单通道**最大可辨差;40% 覆盖的抗锯齿行实测差 `(9,6,4)`,
6 能看见它又挡住 ±3 渲染噪声;相邻那行是满覆盖(线色差 25/17/10),所以行锚定不会丢。
五次验收(真跑 probe,退出码即判据;变异图在 `target/m79/{mutB,mutC,mutE,mutF}/`
由 r2/r1 图改像素生成,不入库):
| 图 | 结果 | 实测 |
|---|---|---|
| r2 原图 | **rc=0 绿** | 1 段 |
| `before/`(r1 逐 tab + AA 缝) | **rc=101 红** | 3 段,y=471 x24..265 |
| mutB(缺口 x=200..201,旧窗口外) | **rc=101 红** | 2 段,y=471 x24..266 |
| mutC(缺口 x=100..101,旧窗口内) | **rc=101 红** | 2 段,y=471 x24..266 |
| mutE(r1 两个缝位 102/192 挖真缝) | **rc=101 红** | 3 段,**y=471 x24..266**(仍是分隔线那行,不是 y=503 的五态带) |
| mutF(额外:r1 分段线 + 上方 200px Primary 带) | **rc=101 红** | 3 段,y=471(锚定没被 Primary 带顶替) |
门禁:fmt ✓ · `cargo test -p unlucky-app-iced` 197 passed · 全套 probe 对 r2 图 5 passed ·
`clippy --all-targets -D warnings` 干净。未改任何组件实现。
|
2026-11-02 11:56:06 +08:00 |
|
AskaEth
|
55a775fca7
|
test(app-iced): M79 r2 —— probe 复跑暴露的两处修正(分隔线判据 / 可见性警告)
真跑 `navigation::probe` 才发现的两件事,都只影响测试代码:
1. **Secondary 分隔线判据错了**:原来按 `SurfaceVariant` 的**精确值**找块,
但 1px 的线画在分数像素上,真实像素是「线色 ⊗ 页面底色」的混合值
(实测 `(221,233,245)`,不是令牌原值)→ 找到 **0 段**,断言假红。
改成:先找「与页面底色不同、横跨 >150px 的那一行」,再用**该行自己的颜色**数连通段
(`probe/scan.rs` 新增 `longest_h_line` / `line_segments` / `differs`)。
现在:r1 基线图 → 实测 **2 段**(红);r2 新图 → **1 段**(绿)。
2. `matches` 原来被写成 `differs` 的正向委托(语义反了),以及 `pub(super) fn shot` 暴露了
更私有的 `Shot`(`private_interfaces` 警告,`-D warnings` 会红):前者改回
`!differs(..)`,后者把 `shot`/`shot_dir` 收回私有(子模块 `probe::long` 仍可见)。
其他:`probe/long.rs` 的 `filter(..).next()` → `find(..)`(clippy::filter_next)。
复跑(真实输出):
- `ULL_NAV_DIR=target/m79/r2 cargo test -p unlucky-app-iced navigation::probe` → **5 passed**
- `cargo test -p unlucky-app-iced` → **197 passed**(+ gate 6 passed,1 ignored)
- `cargo clippy -p unlucky-app-iced --all-targets -- -D warnings` → 干净
|
2026-11-02 11:56:04 +08:00 |
|
AskaEth
|
a36f542f28
|
fix(app-iced): M79 r2 —— 四条 review 修复(对齐/单行/可比对象/整行分隔线)
代码 r1 P2-1 = 视觉 r1 P2-1(同一处,必修)
- `app_bar::action`:容器只给了 `align_y` → 24px 图标贴 40×40 命中区左缘(实测前导墨迹
`38..58`,应 `44..68`;hover 的圆形状态层也露在图标一侧)。补 `align_x(Center)`;
同时页头内容行补 `align_y(Center)`(`Container` 默认 Top,整行原本贴顶)。
- 把 Q21 升格成规则并全目录复查(结论写进 `navigation/mod.rs` 的表):
app_bar 命中区/页头行、tabs tab 内容、drawer 药丸 = 两轴显式居中;
list_item 文本块与 drawer 分组标题 = 左上起排(`Start` 是对的,不需要居中)。
回归钉子:`navigation::probe` 的 appbar 断言量「左内缩 == 右内缩 ±2」。
代码 r1 P2-2(必修):四件文本都没有单行约束,容器高度却是固定值
- `app_bar`(标题/副标题)、`list_item::line`、`drawer::line`、`tabs` 标签统一
`.wrapping(Wrapping::None)` + 外层 `clip(true)`;页头/ListiItem/drawer 的文本块改成
`Fill + clip`,长文本切在文本区里,不折行、不顶穿、不压 trailing 动作。
- iced 没有 ellipsis → 按 `inputs/menu.rs` 的 D19 先例登记 **D-M79-5**
(官方要单行 ellipsis,我们单行 + 超长直接切割)。
- 取证:gallery 新增 `ULL_NAV_PART=long`(长实例名整块)+ `navigation/probe/long.rs`
量「行高仍 44/60/76 / 页头仍 56 / 标题墨迹右缘 < trailing 左缘」。取证块按
`selection::matrix` 的先例收进 `navigation/matrix.rs`(gallery 单文件贴 400 红线)。
视觉 r1 P2-2(必修):ListItem 三列内容不同 → 「选中 trailing = Primary」没有可比对象
- 三列都挂 `trailing(settings)`,只有 selected 列走 `Primary`(其余 `OnSurface` / 38%)。
视觉 r1 P2-2(可选,已做):Secondary 分隔线逐 tab 分段 + 0.5 圆角留了 1px 亮缝
- 改成**整行一条**:`Stack` 把一条 `Fill` 的 1px `SurfaceVariant` 压在 tab 行底下
(底座是 tab 行本身 → 线长 = tab 行总宽,横向滚动跟着走)。
- `TabsGeometry::bar_radius(bool)` 收成 `indicator_radius()`(只有 3px 的 Primary 条要圆角),
单测同步;probe 新增「Secondary 分隔线必须 1 段」的断言。
门禁:`cargo fmt --all --check` ✓ · `cargo build -p unlucky-app-iced --lib` 零 warning ✓
(截图/探针/测试的实时输出见完成报告;本轮宿主机被多条并行线的 rustc 打满,
bin 链接排队严重,如未取到会如实标注未完成项。)
|
2026-11-02 11:56:02 +08:00 |
|
AskaEth
|
81e34d77da
|
feat(app-iced): M79 导航家族 —— ListItem / Tabs / NavigationDrawer / AppBar
四件组件 + gallery 状态矩阵 + 渲染层几何断言,全部只落在
`components/navigation/**` 与 `gallery/navigation.rs`(不改 mod.rs / interaction / layout)。
- ListItem(12b §4):一行/两行/三行 44/60/76(A1 56/72/88,登记偏离)、leading/trailing 槽位、
hover/focus/pressed 只叠状态层(内容色不变)、禁用是修饰器且内容角色取 A1 的 `OnSurface`@38%。
- Tabs(12b §6):Primary 3px 指示条(用 `Stack` 分层让 Fill 条拿到「该 tab 的宽」)、
Secondary 1px `SurfaceVariant` 分隔线、带图标时容器 64、横向滚动;选中色按变体分(Primary / OnSurface)。
- NavigationDrawer(15 §21 + D33):**同一个容器两个宽度** —— rail 88 ⇄ drawer 240;
药丸从容器宽派生(56×32 / 216×56,左右各缩 16 / 12,A1 `ItemPadding`);
分组标题 TitleSmall、组间 1px OutlineVariant;drawer 项高 56(G24)。
- AppBar small(12b §16):页头 56(A1 64,登记偏离)、TitleLarge emphasized + LabelMedium 副标题、
leading/trailing 槽位、滚动只换 `SurfaceContainer` + L2(A1 无 OnScroll…Height)。
- gallery:`ULL_NAV_PART=list|tabs|drawer|appbar|all` 分块出图;四块矩阵(ListItem 三行高 × 三态、
Tabs 三个选中位置 + 五态、rail/drawer 同尺度并排、AppBar 静息/滚动)。
- `navigation/probe.rs`:读真截图量像素的 4 条断言(行高 44/60/76、禁用 OnSurface@38%、
指示条宽 = 标签墨迹 + 2×16、rail 88 / drawer 240 与药丸内缩 16/12、AppBar 56),
已用两次变异(行高改 56/72/88;drawer 内缩改 16)验证能被打红。
门禁:fmt ✓ · clippy -p unlucky-app-iced --all-targets -D warnings ✓ · test -p unlucky-app-iced 195 passed ✓
|
2026-11-02 11:55:59 +08:00 |
|
AskaEth
|
c9e6236a87
|
refactor(cli): 判据收敛到 unlucky_cli::cli,banner 按被调用的二进制名自称(M82 复审)
去重:`looks_like_cli(args) -> bool` 落在 `unlucky_cli::cli`,两个 GUI 的 main.rs 只调用它,
判据从两份变一份。cli crate 里**不**出现 GUI 的 flag 表——语义仍是「首个 token 是裸词就接管;
首个 token 是 flag 时只有解析出真命令才接管,解析失败不接管」。原来两个 main.rs 里的 5+5 条
单测合并成 cli crate 的一份(用例集按复审要求原样搬,含 --screenshot/--renderer/--diff/
--mock/--force-state/--scale-factor、--help/-h/--version/-V、10 个已知子命令、--json 前置、
未知子命令),两个 main.rs 的旧测试已删除。
banner:新增 cli::program 模块,从 argv[0] 的 basename 取程序名(剥目录、剥 .exe、空值回落
ull),把帮助/版本/错误提示里独立出现的 `ull` 换成它——`unlucky-launcher --version` 不再自称
`ull`,也不会把人指去敲 `ull --help`。`run()` 签名未改;`ull` 自己跑时输出逐字不变(替换是
恒等),打包脚本的 `ull --version` 冒烟不受影响。
|
2026-11-02 08:08:12 +08:00 |
|
AskaEth
|
fecff989e7
|
docs: 修掉「把没量的东西标成实测」的四处引用(M82 复审 P2)
- 两个 GUI main.rs:把「不 dlopen 任何图形库」改成「不依赖任何图形库(缺失时不影响)」,
并把「实测见 §7」换成「§7.4 是依赖取证;耗时是结构性判断,没重量(§10-8)」
- packaging/README §1:§7.6(不存在)→ §7.4
- packaging/README §4:实测引用 §7.1 → §7.4(ELF 取证在 §7.4)
- packaging/README §7.4 标题补 ELF DT_NEEDED,并记下复审在 bookworm-slim 里的真跑结果
- packaging/release-notes:ull 示例前补 chmod +x(Release 资产不带可执行位)
|
2026-11-02 08:08:11 +08:00 |
|
AskaEth
|
1c7285cea9
|
feat(cli): GUI 二进制内建 CLI 子命令分发,ull 纳入发布资产(M82)
一个二进制两种用法:两个 GUI 的 main.rs 在进 UI 之前分发子命令,命中就把控制权交给
unlucky_cli::run —— 不建窗口、不探后端、不扫字体、不 dlopen 图形库。
分发判据只有一条,子命令表不重复:crates/cli 的解析器是唯一真相源。
- 首个 token 是裸词 → 子命令位置,交 CLI(未知子命令由 CLI 报错 + 提示 --help,退出 1,
不静默开窗)
- 首个 token 是 flag → 只有 CLI 解析出真命令才算 CLI 调用;--data-dir X 解析成 Welcome、
--screenshot / --renderer / --diff 等 GUI 参数解析失败,一律仍走 GUI(CI 脚本零改动)
顺带:unlucky-launcher --version 以前会直接开窗,现在走 CLI 分发报版本。
packaging:ull 作为独立裸资产随每个槽位发布(make_artifacts.py + release.yml 的 assets
数组与 SHA256SUMS glob),矩阵完整性检查改成 GUI/CLI 各查一次。
app-iced 的 x11 / wayland / linux-theme-detection 收进 target 专属的 cfg(target_os) 段。
门禁实跑:cargo fmt --all --check 通过;cargo clippy --workspace --all-targets -D warnings
通过(exit 0);cargo test --workspace 通过(51 个测试二进制 / 1369 个用例,含本次新增的
5 + 5 条分发判据用例)。
|
2026-11-02 08:08:09 +08:00 |
|
AskaEth
|
05fefc8a7b
|
fix(app-iced): M77 r1 —— 菜单项 trailing 槽位靠右(视觉 r5 P1)
`item_row` 的 Row 早先是 `leading → label → trailing`,中间没有撑开的空间,于是
trailing(快捷键 / 子菜单箭头 / 勾)**紧贴 label 右边**,一列菜单里读不出一条竖线,
也与 M3「trailing 落在行右内边距上」不符。
改法(只动 `menu.rs` 的行装配):
- label 与 trailing 之间插一个 `space::horizontal()`(宽度 Fill),**只在有 trailing 时插**——
没有 trailing 的项仍然左边起排,不被居中/右对齐;
- 把 Row 包进 `container(..).width(Fill).height(Fill).align_y(Center)`:Fill 空间要有宽度可撑
(Q8 的教训:撑开靠容器,不靠子元素),顺带把内容垂直居中(`button` 只把内容放左上角);
- trailing 的右缘现在落在 item 右内边距上,与 leading 左内边距对称(12 = 4 内缩 + 8 button pad);
- 分组分隔线 / 禁用项的行为未动(同一函数的其它分支没改)。
渲染层断言(`probe/menu.rs`):带 trailing 的项,其 trailing 墨迹右缘 = `menu_right − 17`
(= 4 指示器内缩 + 12 内边距 + `Check` 字形在 24 viewBox 里约 5 的边界)±1。
变异(删掉那行 `space::horizontal()`)后实测 `menu_right=559、墨迹右缘=438 ⇒ 偏 121`,断言红;
修复后偏 **17**。重出 `menu-{light,dark}.png`,勾已贴行右缘成一条竖线。
|
2026-11-02 07:45:39 +08:00 |
|
AskaEth
|
21f2734981
|
test(app-iced): M77 渲染层 probe —— 逐行几何断言(5 条,能被变异打红)
M68 的教训:纯函数返回对、图可能是错的。`inputs/probe.rs` 对着**真窗截图**量像素,
没给 `ULL_INPUTS_DIR` 就大声跳过(与 `gallery::probe` 同一口径)。
量什么(每条对应一次真实可能画错的形态):
- 文本框高**精确 56**(320 宽格)、Filled 指示线 静息 1dp / 聚焦 2dp 的墨量比;
- Outlined 描边 1dp / 2dp(描边墨量随亚像素相位在 1.0~1.67 浮动,所以用**比值**判);
- 错误态:框内标签与框下辅助文本都是 `Error` 墨迹;
- 搜索条高 56、前置图标墨迹偏移 20(= pad 12 + offset 4 + viewBox ≈4)、
docked 面板 宽 360 / 头 56 / 1px `Outline` 分隔 / 结果区 240;
- Slider 竖条 4×44、0% 贴左、50% 分界在正中、100% 无未激活段(thumb 不越界);
- 菜单容器 161(= 8 + 48×3 + 1 + 8)、项高 48、选中指示器内缩 4、分组线 1dp。
像素工具因 `gallery::scan` 是私有模块而自带一份最小读图器(约 60 行),已报 finding。
|
2026-11-02 07:45:38 +08:00 |
|
AskaEth
|
ddd234b3c8
|
feat(app-iced): M77 输入家族 —— TextField / Search / Slider / Menu(含 gallery 登记)
D39 五家族并行的「输入类」这一条线。四个组件全部对消息类型泛型(`M: Clone`,不碰
`app::Message`),状态一律走 `components::interaction`,精确尺寸走 `components::layout`,
零尺寸字面量(取不到的集中登记 G16~G20 在 `inputs/geometry.rs`)。
- TextField(12b §9):Filled / Outlined;56 高(无标签 40)、4 圆角、内边距 16/16、
前置图标可视边 12(文本从 48 起)、指示线/描边 静息 1dp → 聚焦与错误 2dp、
错误态线/标签/辅助文本全 Error、禁用 12%/38%(Filled 容器官方 4%,令牌只有 12% → D10)。
标签常浮起(无上浮动画,D12);A1 无 Pressed token,press 按 hover(D11)。
- Search(15 §20):SearchBar(收起)+ SearchView(展开 docked)分开实现。bar 消费
`ComponentMetrics::SEARCH_BAR`,图标可视边 = pad 12 + icon_offset 4 = 16(只做加法,
不写 16/20),聚焦 1px Secondary;view 头部 56 + 1px **Outline** 分隔 + 结果区 240。
- Slider(12b §11):自绘 `advanced::Widget`——轨道 16 full、竖条 4×44(focus/pressed 收窄到 2)、
激活 Primary / 未激活 SecondaryContainer、命中 48、离散停靠点 4(未激活轨在点两侧留 6 空隙)。
位移区间取 handle/2..W−handle/2,所以 0%/100% 竖条不越界。
- Menu / DropdownMenuItem(12b §8):SurfaceContainer + L2 阴影(Elevation 令牌)、项 48、
水平 12、上下 8、选中 SecondaryContainer(指示器内缩 4、圆角 8)、分组分隔线复用
`components::divider`。圆角取令牌表 4(12b 说本项目 12 → D18)。
- gallery/inputs.rs:四组件状态矩阵登记(五态 × 两式 + 空/有值/错误、0%/50%/100%、菜单展开态),
按 `part` 拆成四段(整段比 760 高的窗口还高)。
单文件 ≤400 行:四个组件的单测与 Slider 的纯几何分别拆到 `<name>/tests.rs` 与 `slider/track.rs`。
|
2026-11-02 07:45:36 +08:00 |
|
AskaEth
|
9519ce8e1b
|
test(selection): 状态层的两种 α 都要查(复审 P2-4 补,第二次变异曾漏网)
原来只查 hover(8%)那一色,于是「把 pressed 列的状态层画成方块」照样绿——实测变异
mut-one-layer-box 当时是 passed。现在 hover(8%) 与 pressed/focus(10%) 两种颜色各查一遍,
每一块的四个角都不许有墨。
变异证据(六个变异全部转红,见报告):
- mut-one-box-22 → 18.3:Primary 块不得超过 21,实测 22×22 @ (541,222)
- mut-one-layer-box → 18.5:Pressed 状态层必须是圆(四角不该有墨)@ (532,212)
- mut-chip-34 → 13.1:filter chip 应恰好四块,实测 3
|
2026-11-02 07:03:13 +08:00 |
|
AskaEth
|
4155429f45
|
test(selection): 像素判据从「存在性」改成「逐处都对」(复审 P2-4)
- 20(±1) 的 Primary 块:**每一块**都不许超过 21(22 = 描边画到框外),且总数恰好 12
- 状态层:**每一块** Ø40 都要四角无墨(原来只看两块角点),数量 ≥5
- switch 轨道 **恰好一块** 52×32、chip **恰好一块** 32 高(原来 any(...) 就算过)
- 新增 P2-1 / P2-3 的像素断言:禁用 chip 4 块完整宽度 + filter·on 仍填充;20×20 禁用块不许是 12%
- 断言与像素扫描搬到 selection/probe.rs + selection/pixel.rs:gallery/selection.rs 只剩「摆矩阵」
(原文件 458 行,超 400 红线)
|
2026-11-02 06:55:34 +08:00 |
|
AskaEth
|
0c9d4049b3
|
fix(selection): Checkbox / Radio 命中区恒 48×48(复审 P2-2)
- 无标签时行宽只有状态层的 40dp(A1 的 minimumInteractiveComponentSize 是 48)
- ControlGeometry::hit_padding() = (48−40)/2 = 4,view 侧左右各留这段透明内边距
- 单测钉住 40 + 2×4 == 48(checkbox 与 radio 各一遍)
|
2026-11-02 06:55:31 +08:00 |
|
AskaEth
|
c5765c57e7
|
fix(selection): Chip 禁用改成修饰器,只清消息、不删图标与选中态(复审 P2-1)
- 旧 API Chip::disabled(label, variant) 强制 leading/trailing/selected = None/false:实测
assist 78→54、filter·on 74→50(连填充和勾都没了),禁用列里 filter·on 与 filter 逐像素同形
- 新 API Chip::assist(label, msg).leading(k).disabled():只清 on_press,视觉信息全保留
(research/12b §13 的禁用态是「容器/描边 12%、文字与图标 38%」——只降不删)
- 矩阵里禁用格改用修饰器,图标与选中态一并保留
|
2026-11-02 06:55:28 +08:00 |
|
AskaEth
|
00dbb5a5d5
|
fix(selection): 选中禁用的 Checkbox 容器改用 OnSurface@38%,并补逐格断言(复审 P2-3)
A1:SelectedDisabledContainerColor=OnSurface + SelectedDisabledContainerOpacity=0.38(不是普通禁用容器的
12%)。断言同时钉住三件事:fill == 38% 叠色、fill != 12% 容器色、未选中禁用描边与它是同一个 38%。
(同一提交还带着 P2-2 的命中区单测:两处断言都在 tests.rs,无法按 hunk 切分。)
|
2026-11-02 06:55:25 +08:00 |
|
AskaEth
|
17394b73ff
|
refactor(app-iced): paint.rs 按组件拆成 paint/ 目录(复审 P2-5)
- paint.rs 原本正好 400 行、零余量:再加一个态就得动它(和 app.rs 同一个问题)
- 拆成 mod.rs(Frame / 状态层 / 禁用色 / 标签色)+ check.rs / radio.rs / switch.rs / chip.rs
- 对外 API 一字不改(selection::paint::* 路径全部保持)
- 顺带把「选中且禁用的 Checkbox 容器」从 12% 改成 A1 的 38%(复审 P2-3):这一行就在被移动的
check.rs 里,单独切 hunk 只会让历史不完整;断言在下一条提交
|
2026-11-02 06:52:10 +08:00 |
|
AskaEth
|
b69a2cdd7f
|
feat(app-iced): M76 选择类家族 —— Checkbox / Radio / Switch / Chip(15 §18/§19 + 12b §10/§13)
- selection/:geometry(令牌→像素,缺档登记 G16)/ paint(Interaction×选中×错误→角色)/
shell(48 透明外壳 + 帧内状态通道 + 勾形 SVG)/ checkbox·radio·switch·chip / matrix(12×5 状态矩阵)/ tests
- gallery/selection.rs:小节 + **渲染层像素断言**(20×20 选中与未选中同尺寸 · Radio 内点 10 ·
Ø40 状态层是圆 · 勾形 14.8 · 短横 · 轨道 52×32 · Chip 32 · 禁用 38%),按颜色+墨迹块定位、不假设行号
- gallery/mod.rs:新增 ULL_GALLERY_SECTION 段落过滤(tower 批准,M77 零成本复用)+ 四个 helper 放宽到 pub(crate)
A1 证据(本机 AAR javap):未选中 hover/focus/pressed 描边是 OnSurface(不是 OnSurfaceVariant)、
Radio 选中态 hover 仍 Primary、Checkbox SelectedDisabledIconColor=Surface、Switch hover 滑块 PrimaryContainer。
|
2026-11-02 06:11:21 +08:00 |
|
AskaEth
|
4d7b198822
|
fix(app-iced): M75 r6 —— probe 删 19 行重复块 + 补第 8 行(icon·tonal) + 三处注释按 visual-r4 P2-2 更正
① 19 行重复块(第 4 次报):probe.rs 218-236 与 198-216 逐字重复(on_primary/left_ink 双层循环/
icon_pad/assert 各两遍,靠 shadowing 编过),已删;粘在一起的注释头随之消失。
滑动窗口脚本:改前 'DUP at 199 and 219' → 改后 'no 19-line duplicate'。
② 逐行护栏补到 8 行:(7, OnSurface, true, "icon·tonal");汇总打印 '8 行通过'。
变异(该行墨迹涂回满档色)→ probe 红。
③ 三处过期注释按 review 的 §P2-2 逐条改:
- probe 模块文档 药丸顶行宽 ≈9 → 左内缩 ≈14;
- 圆角推导改成像素中心闭式 inset(k)=r−√(r²−(r−k−0.5)²),r=20,k=1 给 12.40;
- gallery/mod.rs 列宽注释 132 → 160(cell_width 的实际值)。
|
2026-11-02 04:51:07 +08:00 |
|
AskaEth
|
4954ba3463
|
chore(app-iced): M75 r5 —— 删掉被 count_in 取代的 count_pixels(clippy dead_code)
|
2026-11-02 04:51:06 +08:00 |
|
AskaEth
|
b563c6ac44
|
docs(app-iced): M75 r5 —— 三处过期注释按 A1 更正(会被四条家族线当规格抄)
- buttons/mod.rs 表行 1.8:禁用内容角色「按变体」(Variant::disabled_content),容器 α 注明 A1 是 10%
- buttons/mod.rs 表行 2.7:IconButton 禁用内容 = OnSurface@38%(A1),落点改到 disabled_content_over_role
- interaction α 表 / disabled_colors 文档:容器 0.12 与 A1 的 0.10f 差异点名 D8;内容默认为 OnSurfaceVariant
- overlay_color 的 content 说明从 OnSurface 改成 OnSurfaceVariant(与 A1 一致)
|
2026-11-02 04:51:04 +08:00 |
|
AskaEth
|
eed1c9d3f7
|
fix(app-iced): M75 r5 —— 禁用内容角色按变体(A1)+ 删 probe 重复块 + 逐行护栏
1) A1 逐变体(AAR javap):Filled/Elevated/Outlined/Text = OnSurfaceVariant@0.38;
FilledTonal = OnSurface@0.38(FilledTonalButtonTokens.DisabledLabelTextColor);
Standard/Filled IconButton = OnSurface@0.38(*IconButtonTokens.DisabledColor)。
新增 Variant::disabled_content() / IconVariant::disabled_content(),
interaction::disabled_content_over_role(ui, role, on_container) 成唯一入口。
2) probe.rs 删掉被两个 reviewer 点名的重复块(disabled_colors 调用两次、
disabled_container 绑定两次),并补 grep 证据。
3) 护栏改成**逐行**:矩阵 7 个有内容的变体行各自断言 disabled 列的 38% 墨迹 > 40 px
(浅/深同一套阈值,单行回退就会红),取代原来按浅色标定的全局 500 阈值。
6) 删掉 interaction/mod.rs 里挂在 mod tests 头上的悬挂文档。
|
2026-11-02 04:51:03 +08:00 |
|
AskaEth
|
b31a758cda
|
test(app-iced): M75 r4b —— 无容器禁用墨迹的护栏收窄到 disabled 列 + 阈值 500(变异实测会红)
整图计数会把别处抗锯齿像素算进来(正常 800/1966 px,涂回满档色后仍剩 348 px),
护栏实际上拦不住 code-r2 说的「text/standard 那两行」。改为只在矩阵最右 1/3 列内计数,
阈值 500(浅 800 / 深 1966 正常,变异后 348)——变异实测:红。
|
2026-11-02 04:51:02 +08:00 |
|
AskaEth
|
a878fc6a58
|
refactor(app-iced): M75 r4 —— interaction 拆成 mod.rs(287) + tracker.rs(159),恢复被压掉的文档
interaction.rs 连压三笔才回到 400 行(靠删文档腾空间),而它会被四条家族线各扩一次。
- interaction/mod.rs:五态枚举 + overlay_color / disabled_colors / disabled_content_over(态→颜色)
- interaction/tracker.rs:事件→态的状态机 + 它的两条单测;Tracker 用法示例(ignore 代码块)恢复
- 对外路径不变:components::interaction::* 照旧(mod.rs 里再导出 Tracker)
- 两份文件都写了「加东西前先看行数余额」
顺带修两处门禁:注释里去掉 hex 色值(守门连注释一起扫)、再导出显式 allow。
|
2026-11-02 04:51:01 +08:00 |
|
AskaEth
|
ab5f5802a8
|
test(app-iced): M75 r3f —— 去掉一条与上一行重复的断言,interaction.rs 400 行
|
2026-11-02 04:50:59 +08:00 |
|
AskaEth
|
95a6f56060
|
docs(app-iced): M75 r3d —— interaction.rs 再收 2 行,确保 ≤400
|
2026-11-02 04:50:58 +08:00 |
|
AskaEth
|
a3b3c9eba7
|
docs(app-iced): M75 r3c —— interaction.rs 文档收口(Tracker 用法/α 表压行,回到 400 行内)
|
2026-11-02 04:50:57 +08:00 |
|
AskaEth
|
86f7a31d9a
|
fix(app-iced): M75 r3b —— IconButton 高度断言按抗锯齿实测改为 39..=40 + interaction.rs 收回到 400 行内
exact-color 的连通块高度会排除底部抗锯齿行,实测 40×39;宽度仍精确 40。
区间收到 39..=40 仍能抓住 ±2px 回归(42→41、38→37 都落区间外)。
|
2026-11-02 04:50:55 +08:00 |
|
AskaEth
|
5fd1f3ca26
|
fix(app-iced): M75 r3 —— 禁用内容角色按 A1 改 OnSurfaceVariant + 两项语汇 + probe 双色护栏
A1 定案(本机 gradle 缓存 material3.aar + javap):
FilledButtonTokens.<clinit>: DisabledContainerColor=OnSurface, DisabledContainerOpacity=0.1f
DisabledIconColor/DisabledLabelTextColor=OnSurfaceVariant, 两者 Opacity=0.38f
OutlinedButtonTokens/TextButtonTokens: LabelTextColor=OnSurfaceVariant(12b §1.9 原本就对)
据此:
- interaction::disabled_content_over / disabled_colors 的内容角色从 OnSurface 改成
OnSurfaceVariant(α 仍取令牌 DISABLED_CONTENT_ALPHA=0.38);设计层 color::state::disabled_content()
用的是 OnSurface,与 A1 不符 —— 令牌线修好前这里按 A1 直接求值,已登记。
- content_color 的「底」按有无容器分开:有容器叠 12% 容器、无容器叠 surface(视觉 r2 P2-1)。
- probe 加第二条护栏:无容器三式的 38% 墨迹单独计数,避免只护住有容器那四式(code r2 p2-1)。
- gallery 样例图标改描边(Download + ChevronRight),并加单测钉「同控件不混用 Fill/Stroke」(P2-3)。
- 滚动条:透明轨 + OutlineVariant thumb(P2-4;宽度 10px 是 iced 内部常量,只能改颜色)。
- probe 文档修好被 //! 打断的命令、shot_path 注释归位、去掉空转的重复断言(P2-5)。
|
2026-11-02 04:50:54 +08:00 |
|
AskaEth
|
d075feb77d
|
test(app-iced): M75 复审 P1-B —— 渲层断言收紧到 2px 级(变异实测会红)
- 高度断言改 assert_eq!(true_height, 40):变异成 42 实测变红(证据 target/m75/evidence.sh)
- 补「带图标左内边距 16」断言:墨迹 ≈21(16→21 / 24→29 / 8→13 三档互不重叠)
- 分隔线:行数 2..=4 + 单条墨量覆盖率 0.8..=1.3(1.5dp 会到 ~1.5),
只统计「连续非底色 > 900」的长横线行,避免把文字墨量算进去
- 两条判据都踩过误报:白色与浅色页面底色在容差内(padding 量出 3)、
圆角透出的底色被当成满档 OnPrimary;已改成「内带 + nearer 判定」并注明原因
|
2026-11-02 04:50:52 +08:00 |
|
AskaEth
|
e905fa3922
|
fix(app-iced): M75 复审 P1-A/P1-B/P2 —— 内容色跟态走 + 断言收紧 + 矩阵排版
P1-A 禁用内容色没下发(视觉 reviewer:text/standard 的 enabled 与 disabled 逐像素相同):
assemble 里内容色恒取 ui.color(content) 再被 text().color() 钉死,Style.text_color 的 38%
永远下发不出去。新增 assemble::content_color(ui, forced, content),文字按钮与 IconButton
共用;probe 补两条像素断言(禁用 38% 墨迹必须存在、禁用容器内不得有满档 OnPrimary)。
P1-B 渲染层断言抓不住 2px 级回归(reviewer 变异验证:高度 42 仍绿):
高度改成精确 == 40;补「带图标左内边距 16(墨迹 ≈21)」断言;分隔线从「行数」改成
「墨量覆盖率 ≈1.0」(1.5dp 会到 ≈1.5)。
P2-1 probe 给了图却读不到时硬失败(只有完全没给 ULL_GALLERY_SHOT 才跳过)。
P2-2 Geometry::stroke 真正接到 Border.width;IconGeometry::icon 改由 side−2×padding 推导。
P2-3 矩阵 cell 宽 192→160(总宽 1040<1180,disabled 列不再被裁);禁用格保留前后图标。
P2-4 补注:snap 仅 wgpu 实现,CPU 验收图不受它影响。
|
2026-11-02 04:50:51 +08:00 |
|
AskaEth
|
8072f1cc7e
|
test(app-iced): M75 —— probe 分隔线判据改为「非底色长横线」+ 修 forced 改成 Option 后的两处测试
线在 fractional y 上被抗锯齿摊到两行,按精确 OutlineVariant 色匹配抓不到(实测 0 行);
改为「非 Surface 的长横线」计数,断言 2..=6 行(两条线 × 各 1~2 行)。
|
2026-11-02 04:50:49 +08:00 |
|
AskaEth
|
5f85891e7e
|
fix(app-iced): M75 —— 截图取证必须能强制「静息」态(光标污染修掉)
StyleSpec.forced 从 Interaction 改成 Option<Interaction>:Some(Enabled) 与 None 语义不同——
前者强制画静息,后者才跟随 button::Status。修前光标恰好停在某个格子上时,gallery 的
enabled 列会被画成 hover(表现为 text 行出现不该有的灰底容器,看着像 Text 按钮自带容器)。
|
2026-11-02 04:50:47 +08:00 |
|
AskaEth
|
5861881073
|
refactor(app-iced): M75 —— probe 拆出 scan 工具 + clippy 全绿 + 三个字面/生命周期修正
- gallery/{probe,scan}.rs 拆分:像素扫描(连通块/外接框/顶行宽度)与判据分离,
后续四条家族线可以自带 probe 复用扫描逻辑
- probe 的实心块判据跳过字形割出的小碎片(area < 800),阈值写明原因
- app.rs / gallery::mod 的 clippy 修正(needless_lifetime / field_reassign_with_default / collapsible_if)
- cargo test -p unlucky-app-iced 与 cargo clippy --all-targets -D warnings 已绿(见报告)
|
2026-11-02 04:50:46 +08:00 |
|
AskaEth
|
4774e7b788
|
WIP(app-iced): M75 组件基座 checkpoint —— 组件骨架/按钮五式/分隔线/gallery harness
已落地:
- components/{mod,interaction,layout,divider,gate}.rs + buttons/{mod,geometry,assemble,tests}.rs
- gallery/{mod,buttons,probe,scan}.rs + 四个家族桩(selection/inputs/feedback/navigation)
- cli.rs: --screen=shell|gallery + capture_flavor(截图默认 Light,删掉等系统主题的闸门)
- app.rs: gallery 路由 + DemoMessage 接线(400 行)
待办(下一个 commit):编译/测试全绿、浅深矩阵出图、1:1 并排与数值表。
|
2026-11-02 04:50:44 +08:00 |
|
AskaEth
|
831bd00620
|
docs(app-iced): SIM 常量口径精确化(字符最多行 402 / 全段最宽行 411)
视觉 r3 的不计入裁决的精度提醒:`SIM_SAFE_MAX_INK = 402` 其实是**字符最多**那一行的 ink,
全段**墨迹最宽**的行是 411px(渲染 410),所以「余量 22px」实际是 13px。
- 拆成两个常量并分别断言:`SIM_SAFE_MAX_CHARS_INK = 402`(67 字符那行)、
`SIM_SAFE_WIDEST_INK = 411`(墨迹最宽那行,新加断言 `cap >= 411`,并在失败信息里打印余量);
- 文档写明「两者不是同一行:402 那行字符多但字形窄」。
证据:`cargo test -p unlucky-app-iced --no-default-features`
→ `test result: ok. 86 passed; 0 failed` / `test result: ok. 6 passed; 0 failed`。
|
2026-11-02 04:10:05 +08:00 |
|
AskaEth
|
589f51f4fb
|
docs(app-iced): 订正测试注释(1180 上限是 424,不是 435)
r3 复审:`layout/tests.rs` 的注释说「现在必须是 435」,同一函数的断言却是 424。
证据:`grep -n 435 crates/app-iced/src/pages/layout/tests.rs`
- 第 229 行(旧):`// 1180 —— M68 就是在这条上翻的车(712px),现在必须是 435` → 改成 **424**;
- 剩下两处 435 都是**平均口径上界**的正确引用,不是残留:
第 176 行注释「不是『平均步进 × 72』的算术值 435」、
第 180 行 `assert_eq!(measure_average_bound_px(), 435.0)`。
|
2026-11-02 03:52:28 +08:00 |
|
AskaEth
|
87d5dd8f67
|
docs(app-iced): 订正 meta_grid 注释(1180 与 900 都是 2 列)
r3 复审:`meta_grid` 的文档还写着「1180 → 3 列,900 → 2 列」,但本轮起两者都是 2 列。
证据:`grep -n "3 列" crates/app-iced/src/pages/instances.rs`
- 旧:`/// 元信息网格:列数由 [...] 决定(1180 → 3 列,900 → 2 列)。`
- 新:写明「1180 与 900 都是 2 列;3 列需要 3×160 + 2×12 = 504px > 行宽上限 424,数学上不可行;
硬塞回 3 列会让 NeoForge 20.4.237(118px)折行、同排卡底不齐」。
- 现在文件里唯一出现「3 列」的地方就是这条「不可行」的说明(第 275/276 行)。
|
2026-11-02 03:52:24 +08:00 |
|
AskaEth
|
0de8e63ad6
|
fix(app-iced): 必修1 跟进 —— 旧断言 435→424 + clippy 常量断言
必修1 改完 cap 之后跑门禁抓到的两处自身的漏改:
1. `settings_pane_keeps_the_drawer_width_and_a_readable_form_column` 里还留着
`assert_eq!(measure_cap_px(), 435.0)`(P2-1 轮加的锚点),cap 收到 424 后它必红。
已改 424 —— 这正是「锚点断言」该有的行为:常量一改就红,而不是悄悄跟着漂。
2. clippy `assertions_on_constants`:`assert!(SIM_SAFE_MAX_CHARS <= MAX_MEASURE_CHARS)`
两边都是 const,改成 `const { assert!(..) }`。
门禁(本机私有 target,CPU 档):86 lib passed + 6 集成 passed;clippy -D warnings 干净。
|
2026-11-02 02:42:58 +08:00 |
|
AskaEth
|
c5c543ba43
|
docs(app-iced): 必修3 504→435 一轮留下的三处文档漂移
视觉 r2 逐条点出来的三处「偏离登记与对外口径没跟着改」:
1. `pages/mod.rs` 偏离 #1:详情列右缘写 976 / 分歧 180px —— 行宽收到 424 后实测是
页头右缘 **1156**、详情列右缘 **896**(472 + 424),分歧 **260px**;
2. `pages/mod.rs` 偏离 #3:单列页占比写 48%(435/1044)—— 现在是 **40.6%(424/1044)**;
3. `frame/pieces.rs` 的 `scroll_style` 函数头仍写「滑块 `Outline`、尺寸取 iced 默认」——
实现早已是 `OutlineVariant` + 6px 轨 + 2px 内缩,文档跟上。
三处都在 D30 要求「逐条写清」的偏离登记/口径里,属于对外承诺,不是内部注释。
|
2026-11-02 02:04:32 +08:00 |
|
AskaEth
|
ddf6937b2b
|
fix(app-iced): 必修1 「72 字符」改成逐行口径,上限 435 → 424
代码 r2 的复核:`435 / 6.0526 = 71.9` 是**整段平均步进**换来的数,不是每行都 ≤72;
它实测交付图里就有 **73 字符**行(`guess. Nothing on this page is wired to real data yet: the layout is, the`,
ink 428px)。上一轮是系数错,这一轮是**把平均当成了逐行上界**。
选 **(a)**:不变式改成逐行可验证,cap 跟着压到能满足它的地方。依据是 FreeType 量宽 + 贪心词折行的模拟表
(同一段 361 字符正文扫列宽):
| 列宽 | 最长一行 | 该行 ink | 判定 |
|---|---|---|---|
| 435(= 平均口径上界) | 73 字符 | 428px | ✗ |
| 428 | 73 字符 | 428px | ✗ |
| 426 | 71 字符 | 426px | ✓ 只剩 1 字符余量 |
| **424**(4px 栅格) | **67 字符** | **402px** | ✓ 取这个 |
- `measure_cap_px()` = `min(逐行安全宽 424, 平均口径上界 435)` = **424**;平均口径另立
`measure_average_bound_px()`(435)并在文档里标明「只是上界,不是取值」。
- 单测 `line_length_is_capped_at_72_characters` 改成**两侧夹逼**:`402 ≤ cap < 428` ——
放大到 428 会重新出现 73 字符行(红),压到 402 以下白丢宽度(也红);另断言
`cap < measure_average_bound_px()`,防止有人把平均口径当上限。
- §3 新增「逐行口径」小节:模拟表 + **作用域**(承诺只覆盖 14px 正文;12px 辅助文案是 1~2 行短句,
不做逐行承诺;CJK 全角 1em 另行定)。
- 四页文档与 `pages/mod.rs` 的 ≤435 同步改 424。
|
2026-11-02 02:04:23 +08:00 |
|
AskaEth
|
a650f8184b
|
fix(app-iced): 必修2 元信息卡最小宽改由内容推(136 → 160),1180 下网格 2 列
视觉 r2 抓到的回归:上一轮为塞进三列把 `META_CARD_MIN_WIDTH` 收到 136,只算外框、忘了
`card_padding = 20`,卡内只剩 96px —— demo 里 `NeoForge 20.4.237`(实测 118px)与
`NeoForge 21.1.72`(110px)都会按词折成两行,同排另两张卡仍一行 → **卡底不齐**。
- 最小宽改由**卡内最长内容**反推:118(Segoe UI 14px 实测)+ 2×20(令牌 `card_padding`)= 158
→ 取 4px 栅格 **160**(值上等于最初的 160,但这次是内容推出来的,不是凑上限凑出来的)。
- 于是元信息网格在 1180 宽落到 **2 列**(2×160 + 12 = 332;3 列要 504 > 行宽上限),
卡内可排 **166px** ≥ 118,所有值单行放下;6 格 = 3 行 2 列。
- 新增单测 `meta_card_is_wide_enough_for_the_longest_value`(此前 39 条 pages 测试没有一条覆盖它):
① 夹具 `NeoForge 20.4.237` 必须仍在 DEMO 里(数据变了就红,提醒重新量);
② `META_CARD_MIN_WIDTH − 2×card_padding ≥ 118`;③ 实际两列布局下每卡内宽也 ≥ 118。
- `meta_grid_drops_columns_as_the_detail_narrows` 跟着改成「上限处 2 列」(1180 与 900 同为 2 列,
差别落在详情列宽 435 vs 404)。
|
2026-11-02 02:04:04 +08:00 |
|
AskaEth
|
dc67be529d
|
docs(app-iced): P2-2 r2 712 是 ink 不是列宽 + 补偏离登记
代码 reviewer 把 P2-2 说得更准,我上一轮只改了数字没改算式,这里按它的复核改到底:
- **712 是那一行的墨迹宽度,不是列宽**。早期文档写「1180 − 88 − 2×24 = 712」,这个减法得 **1044**;
M68 的卡片是 x=112..1156(1044px),卡片内文本区 **1004px**,那一行 ink 才是 712px(x=133..845)。
- 交叉验算对上了:137 字符 × 5.19px(12px × 实测 0.4323em)= **711px** ≈ ink 712。
所以事故是「1004px 的文本区里,一行排了 137 字符」,不是「列宽 712」。
- 订正记录改成两条(① 712 是 ink ② 字符数按实测),并写明早期口径的错法。
顺带把视觉 reviewer 的「请登记」落地:`pages/mod.rs` 新增**偏离登记表** 6 条
(页头右缘与详情右缘 180px 分歧、空态标题比页标题大、单列页只用 48% 内容宽、空态字形 40、
返回入口无箭头、实例空详情态未出图),每条写了「为什么」与「什么时候收口」。
|
2026-11-02 01:05:27 +08:00 |
|
AskaEth
|
c495fccd6c
|
fix(app-iced): 视觉 P2-2/3 滚动条变细变淡 + 图标语汇统一成描边
视觉复审两条:
- **P2-2 滚动条偏重**:原来直接用 iced 默认 10px 轨 + `Role::Outline`(那份灰绿在浅色下压过列表内容)。
改 **6px 轨 + 2px 外边距 + `Role::OutlineVariant` 滑块**(比 `Outline` 弱一档的容器描边角色),
圆角由 full 按 6px 折算成 3。功能不变(仍是列表列独立滚动、仍只有这一条 scrollable)。
- **P2-3 图标线宽语汇不一致**:设置页「Java」分组用的是令牌里的 `Play`——它是**填充**字形,
同屏其它图标都是描边。改成描边的 `IconKind::Folder`(Java 分组讲的是运行时安装位置,语义也对),
并在 `pieces.rs` 写明规矩:页面骨架**只用描边图标**,将来若要「主操作允许实心图标」,
由 `components/` 统一定义并只留一处。守门见上一条 commit 的 `pages_use_only_stroked_icons`。
|
2026-11-02 00:39:58 +08:00 |
|
AskaEth
|
367100848a
|
docs(app-iced): P2-2 M68 事故口径订正(实测 137 字符,不再引用估算值)
代码 reviewer 指出:712px ≈ 「118 字符」的那个数本身不是实测,是拿 0.5em 估的步进换算的,
和 P2-1 是同一个根因(高估了步进)。按实测重写事故描述:
- M68 那张卡里的正文是 `BodySmall` **12px**,步进 = 14px 实测值 6.0526 × 12/14 = **5.19px/字符**,
所以 712px **实测 137 字符**(不是 118);
- 补一条**订正记录**说明早期数错在哪:既高估了步进、也对错了字号,两条叠加把事故说轻了 16%;
- `pages/mod.rs` 的历史判词同步改成 712px + 137 字符;
- 全 `pages/` 已无「118 字符」这个数(`grep` 为空)。
|
2026-11-02 00:13:42 +08:00 |
|
AskaEth
|
fae1b61d9e
|
fix(app-iced): P2-1 「72 字符」改成实测口径(上限 504 → 435)
两位 reviewer 都量到:`CHAR_ADVANCE_EM = 0.5` 是估的,14 × 0.5 × 72 = 504px 实际能放 83 字符,
「72 字符」只活在名字里。改成实测:
- 口径:`BodyMedium` 14px / Segoe UI Regular / 无 letter-spacing(iced 0.14 不支持字距),
样本 = 实例页详情那段正文(361 字符,大小写/标点/空格/`→` 混排),FreeType getlength
实测 **2185.00px / 361 = 6.0526 px/字符 = 0.43233em**(旧 0.5 高估 15.7%);
交叉验证:同段首行 78 字符渲染墨迹 484px,FreeType 对同一串 479px,差 1%(抗锯齿外扩)。
- `CHAR_ADVANCE_EM` 0.5 → **0.43233**;`measure_cap_px()` 加 **floor** → **435**
(435.79 四舍五入成 436 就会让最后一两个字符挤不进 72 字符的承诺);下限 315 → 272。
- 版面连带重算:`META_CARD_MIN_WIDTH` 160 → **136**(3×136 + 2×12 = 432 ≤ 435,否则 1180 宽下
元信息网格会从 3 列掉到 2 列)。**816 / 720 两个两列阈值不受影响**(它们由 LIST/DETAIL/FORM 宽度算,
与行宽上限无关),单测里的绝对值断言原样成立。
- 单测:`line_length_is_capped_at_72_characters` 里新增 tower 要求的断言 —— 用**实测步进**
(样本字符数 + 实测宽度,独立于常量)反算「上限列宽能放多少字符」≤ 72(实际 71.87),
并加一条回归护栏:同一实测步进下旧口径 504px = 83.3 字符,改回 0.5em 立刻红。
- 文档:`layout.rs` §3 补「怎么重新量」(字体、样本、工具、交叉验证、CJK 不适用)与全部数字;
`mod.rs` / 四页文档里的 ≤504 同步改 435。
|
2026-11-02 00:13:20 +08:00 |
|
AskaEth
|
2c442585b1
|
feat(app-iced): M84 页面骨架与布局系统(断点 / 行宽上限 / 三种版面)
视觉复审在 M68 的判词是不能把「全宽单卡」当页面模板(1180 宽下正文单行 712px ≈ 118 字符)。
本 mission 把它落成一套**布局系统**,四个页面只是它的用例:
- `pages/layout.rs`:官方 600 / 840 两个档位 → 三种版面(单列 / supporting-pane / list-detail),
阈值由列宽和算出(设置 240+24+320 → 720;实例 336+24+320 → 816 ≤ 840),行宽硬上限
14 × 0.5em × 72 = **504px**,全部是纯函数,`pages/layout/tests.rs` 用绝对值和边界表钉死。
- `pages/frame.rs`:页头(56 / 两行 84)+ 内容列(≤504)+ 两列骨架 + 空态位;
`pages/frame/pieces.rs` 是占位控件(卡片 / 分隔线 / 文本按钮 / 图标),令牌驱动、无完整状态机,
待 M75 的 components 家族合入后替换。
- 四页:实例(list-detail,列表列 336 独立滚动,窄窗退化成整页切换)、
设置(supporting-pane,240 分组导航 + ≤504 表单列)、下载 / 日志(单列,两行页头)。
- `pages/harness.rs` + `examples/pages_demo.rs`:自带 `--screen/--width/--screenshot-theme`,
不碰 `cli.rs` / `app.rs`(M75 在改)。
偏离与说明:
- 布局用「页边距 16/24」而非 16/24/32,官方 expanded 也是 24(少一条偏离登记);
- 实例空详情态一屏会有 5 档字号(22/16/24/12 + 14 的操作按钮),未截图的状态,已知并登记;
- `lib.rs` 只加一行 `pub mod pages;`。
|
2026-11-01 23:27:28 +08:00 |
|
AskaEth
|
a1adf024ba
|
fix(app-iced): M68 r1 —— M69 迁移 + 守门补洞 + 药丸 56×32 + caption 图标 24 + 东侧热区回 1180
代码复审 p2-2items / 视觉复审 p2-2items 的修复轮(配色重拍等 M70 定案)。
M69 迁移(2 行)
- nav 轨宽:nav_rail_width(metrics) 函数体改用 metrics.nav_rail_width
- theme::STROKE_PX 改为 pub use unlucky_design::stroke::HAIRLINE as STROKE_PX
P2-1 零硬编码守门补洞(reviewer 造样 5 个漏网样本全部补上,并进自检正向断言)
- COLOR 调用表补 from_rgb8 / rgb8 / rgba(`from_rgb(` 子串匹配不到 `from_rgb8(` 是根因)
- 新增 `Color { r: .., g: .. }` 结构体字面量检查
- 尺寸规则遇到 Length::Fixed( / Padding::from( / Size::new( 往里剥一层再判数字
- 新增渲染层回归守门 pill_never_overrides_its_size_with_fill(禁 nav.rs 出现 center_*( ):
视觉复审 P2-1 的病根就是 center_*(Length::Fill) 把容器尺寸覆盖成 Fill
视觉 P2-1 药丸 88×32 → 56×32(实测 x=16..71)
- 去掉 center_x/center_y(Fill);居中改由父 column 的 align_x + 显式 width(轨宽) 完成
(只去掉 center_* 还不够:column 会 shrink 到 56,药丸贴左缘 x=0..55,是同一个坑的第二层)
视觉 P2-2 / 用户点名 caption 图标 46 → 24(按钮容器保持 46×48)
- button 内包 container(...).width(24).height(24).center(Length::Fill):
只设 width/height 会让容器缩到 24 并贴 button 内容左上角;center(Fill) 才既撑满又居中
视觉 P3-1 东侧/南侧缩放热区改成叠层
- 原来是 row 的兄弟列,吃掉 4px 布局宽(右留白 28、关闭键离窗角 4px);
现在用 stack! + 对齐容器叠在工作区上,布局宽回到 1180(卡片右缘 x=1155 = 右留白 24)
P3 清单
1. --data-dir 接受并忽略(翻牌前 CI 兼容;现役 smoke 命令行可原样跑,exit 0)+ 单测
2. --diff 内建 ±N px 平移扫描(--diff-scan),CI 不再依赖 numpy
3. 截图模式等 SystemTheme 到达才出图(否则暗色系统上会拍到浅色)
4. (Auto, 未编入 wgpu) 返回 tiny-skia,不再把 wgpu 写进候选列表
5. --renderer=gpu 也跳过适配器枚举(决策与探测无关,省 ~0.6~1.2s)
6. icons::handle 加 debug_assert_eq!(IconKind::ALL[kind as usize], kind)
7. 轨顶留白改 container padding-top,实测 24(原来 spacer + column spacing = 28)
顺带:shot.rs 拆成 shot/mod.rs + shot/diff.rs(≤400 行门禁)
门禁:fmt OK / clippy -D warnings 干净 / 46+5 测试全过 / cargo check --workspace --all-targets OK
体积:双后端 6,769,664 B(6.46 MiB)、仅 CPU 3,955,712 B(3.77 MiB)
截图:r1 代码验证图(r1-verify / hover / pressed / compare-nav-rail)已出;4 张交付主图待 M70 定案后重拍
|
2026-11-01 17:46:55 +08:00 |
|
AskaEth
|
14da7b3dec
|
refactor(app-iced): M68 —— 把 nav 轨宽收敛成一个 M69 迁移点(三处调用 → 一行改动)
M69(141e9b1)把 LayoutMetrics::nav_expanded_width 改名 nav_rail_width、删掉
nav_collapsed_width、新增 nav_drawer_width=240。本分支基线 0aac307 还没有 M69 的
crates/design(那个目录也不在本 mission 的改动范围),现在直接改名会编不过。
所以把 shell/nav.rs 的三处 metrics.nav_expanded_width 收敛到 nav_rail_width(metrics),
并在函数文档里写清 M69 前后对照与「合入后只改函数体一行」;theme::STROKE_PX 同样
注明可换成 unlucky_design::stroke::HAIRLINE。行为/视觉零变化(值都是 88 / 1.0)。
|
2026-11-01 17:11:25 +08:00 |
|
AskaEth
|
df4ff373b7
|
feat(app-iced): M68 —— iced 0.14 骨架(后端探测/字体基座/自绘标题栏/导航轨/深浅主题/截图通道)
新增 crates/app-iced(package `unlucky-app-iced`,bin `unlucky-launcher-iced`),与 Slint 版并存,
为 D26「UI 从头写」打地基。本 mission 只做骨架:能开窗、颜色/字体对、层级在,不做业务页面。
- 后端(D27):启动探测 wgpu 适配器(只看 device_type,避开 WARP 误判)→ 写 ICED_BACKEND;
auto/gpu/cpu 三条路径都保留 tiny-skia 兜底,绝不硬失败;`--renderer=cpu` 跳过探测(省 ~0.85s)
- 字体(D28 前半):令牌三平台字体栈 → 用 iced 全局 FontSystem 查装机情况 → 族名与 400/500/700
字重落点(缺档按 Nearest/Synthetic 记录)打一行启动日志
- 令牌适配层 theme::Ui:唯一把 unlucky-design 的值翻成 iced Color/Font/Border/Radius/Theme 的地方;
49 个 M3 角色留在自己的 Scheme 里,iced 的 6 色 Palette 只给 devtools
- 骨架:1180×760 无边框窗口、48 高自绘标题栏(drag + 东/南 drag_resize + 最小化/最大化/关闭)、
88 宽导航轨(56×32 SecondaryContainer 药丸,由令牌推导)、页头 56 + 一张 OutlinedCard
- 主题:系统主题探测 + --theme,切主题只重建 Ui(Theme::name 变化触发重算),不动 UI 状态
- 截图通道:`--screenshot <png>` + `--screenshot-theme`(真开窗第 3 帧出图),以及
`--diff <a> <b>` 自带逐像素对比(CI 不依赖 Python/ImageMagick)
- 图标:令牌 SVG path → iced svg 控件(颜色走 alpha 蒙版,深浅共用一份位图缓存)
- 门禁:tests/no_hardcoded_tokens.rs 扫源码挡色值/尺寸字面量 + 挡 slint 依赖(自检也算过)
实测(Windows Server 2025 / 96 DPI / RX 6600 XT):浅/深 × GPU/CPU 四张真开窗截图;GPU vs CPU
差异 0.72% 像素、全部落在字形/图标抗锯齿与 1px 圆角描边、最佳对齐 (0,0)、平坦区颜色逐字节相同;
release 体积双后端 6.44 MiB / 仅 CPU 3.75 MiB;首帧 GPU 1.18~1.31s、CPU 33~37ms。
未做/未验证:macOS 与 Wayland 全链路、西/北边缩放热区、letter spacing(iced 0.14 无此属性)、
中文 CDN 字体、玻璃;翻牌步骤写在 DevDocs/SubAgentsTalk/app-iced-骨架与翻牌.md(不入库)。
|
2026-11-01 17:11:24 +08:00 |
|