fix(app-iced): M107 r1 复审五条 p2 —— 搜索与主版本桶一致、chips 可滚、单复数、端到端

复审 r1(p2-5items / fix-then-merge)的五条可达问题,逐条落点:

- **P2-1** 搜索命中数与主版本桶不再自相矛盾:`group::active_bucket` 让第二级在搜索期间
  **自动放宽到「全部」**(选择本身留在模型里,清空搜索后回来),侧栏点亮「全部版本」⇒ 没有幽灵选中;
  `rebuild_after_query` 回填时校验桶仍存在(新桶列表里没有的选择直接丢弃)。
  判据:`a_search_hit_never_lands_on_an_empty_list`(4 分类 × 5 桶值 × 4 查询词的矩阵)+ 出图 11
- **P2-2** chips 档的主版本行套**横向滚动**(显式滚动条),竖栏与 chips 吃同一份 `major_entries`;
  22 桶夹具断言最后一个桶 `1.0` 在列表里,出图 12 看得到滚动条
- **P2-3** 只做搜索这条路径(`rebuild_after_query` + 校验 + app 层测试);`app/state.rs::refresh_view`
  的全量修法**留到 M105 之后**(不在 scope,且 M105 正改同一片文件)——报告里明写
- **P2-4** `1 matches` 修掉:新增 `VersionSearchHitsOne` / `VersionSearchElsewhereOne` 两条单数 key,
  「在别处」用独立标题(不再拿命中数当标题,同屏一个数只报一次)
- **P2-5** 搜索链路补 4 条 **app 层端到端**(键入 → 快照 → 重建 → 列表);变异自证:
  删 `self.state.download_query = query` ⇒ 2 条红;`from_snapshot` 忽略 query ⇒ 8 条红;
  去掉 `active_bucket` 的放宽 ⇒ 4 条红
- 顺带:64 宽图标条每项挂 tooltip(复审建议);`install.rs` 那段自相矛盾的文档订正

门禁:fmt 0 / app-iced lib 431 passed / workspace --exclude unlucky-app 全绿(见报告)
This commit is contained in:
AskaEth committed 2026-11-05 02:01:11 +08:00
1 parent 89a51a63b1
commit 45544791e5
11 files changed
+622 -56

No files matched your search

+30 -7
View File
@@ -118,10 +118,7 @@ pub(super) fn search_row<'a>(
ui,
TypeRole::LabelMedium,
false,
catalog.render(
Key::VersionSearchHits,
&[("count", &model.hits.to_string())],
),
hits_text(catalog, model.hits),
ui.color(Role::OnSurfaceVariant),
));
line = line.push(buttons::icon(
@@ -136,6 +133,29 @@ pub(super) fn search_row<'a>(
line.into()
}
/// 命中数的文案:`1` 与 `>1` 是两条 key(英文的 `1 matches` 是错的,复审 P2-4)。
///
/// 中文两侧都是「个匹配」,但**不能**因此只留一条 key:英文的复数规则要在目录里可读,
/// 而且将来加第三种语言时这条分支已经在那儿了。
pub(super) fn hits_text(catalog: Catalog, count: usize) -> String {
let key = if count == 1 {
Key::VersionSearchHitsOne
} else {
Key::VersionSearchHits
};
catalog.render(key, &[("count", &count.to_string())])
}
/// 「命中在别的类型里」的正文(同样单复数两条 key)。
pub(super) fn elsewhere_text(catalog: Catalog, count: usize) -> String {
let key = if count == 1 {
Key::VersionSearchElsewhereOne
} else {
Key::VersionSearchElsewhere
};
catalog.render(key, &[("count", &count.to_string())])
}
/// 计数头(`LabelMedium`):一行 = 一个 key,数字走占位符,不拼片段。
pub(super) fn head<'a>(ui: &'a Ui, catalog: Catalog, count: usize) -> Element<'a, Message> {
frame::text_role_owned(
@@ -147,9 +167,12 @@ pub(super) fn head<'a>(ui: &'a Ui, catalog: Catalog, count: usize) -> Element<'a
)
}
/// 当前可见的行:先按分类(模型已过滤),再按第二级侧栏选中的主版本桶。
fn visible(model: &Download) -> Vec<&VersionRow> {
match model.selected_major.as_deref() {
/// 当前可见的行:先按分类(模型已过滤),再按第二级侧栏**此刻生效**的主版本桶。
///
/// 桶的生效值来自 [`super::group::active_bucket`]:搜索期间它恒为「全部」,于是
/// 「命中数说 N 个、列表却 0 行」这种自相矛盾在结构上不可能出现(复审 P2-1)。
pub(super) fn visible(model: &Download) -> Vec<&VersionRow> {
match super::group::active_bucket(model) {
None => model.rows.iter().collect(),
Some(key) => model
.rows