Skip to content

feat(encyclopedia): 新增歌曲百科并适配上游 Clean 双主题 - #541

Merged
cwuom merged 35 commits into
cwuom:masterfrom
liaolon:feat/song-encyclopedia
Oct 21, 2026
Merged

cwuom merged 35 commits into
cwuom:masterfrom
liaolon:feat/song-encyclopedia

Conversation

@liaolon

@liaolon liaolon commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

描述 / Description

本 PR 在「正在播放」的「更多」面板中新增歌曲百科功能,同时包含此前 PR #525 的 B 站音乐首页模块,并已把上游 02611a5cd 的 Clean 预览主题合并进来、对本分支新增页面完成双主题适配。

百科入口位于「更多」面板「播放统计」之后、「一起听」之前;首页两个 B 站音乐模块位于网易云「私人雷达 / 雷达歌单」之后、「热力飙升 / 我的推荐」之前。国际版(YouTube Music)分支同样插在「每日发现」之后、「更多推荐」之前。

A. 歌曲百科(Song Encyclopedia)

  1. 数据层:新增 modules/model/.../data/model/encyclopedia/SongEncyclopedia.kt(百科数据模型);modules/platform/.../netease/encyclopedia/NeteaseEncyclopediaParser.kt(解析)、NeteaseSongEncyclopediaRepository.kt(仓库)、SongEncyclopediaMemoryCache.kt(内存缓存)。加载支持取消与重试,并通过 generation 计数避免旧请求覆盖当前歌曲的数据。
  2. 九个模块:歌曲详情、音乐属性、创作团队、获奖与收录、听歌记录、关于艺人、歌曲评论、相似歌曲、相关歌单。各模块独立三态渲染(加载中 / 空 / 错误重试),无内容的模块整块不渲染。
  3. 听歌记录:来自本机播放统计(首次收听 / 累计收听时长 / 累计播放次数),并在卡片脚注明确标注「听歌记录来自本机播放统计,并非账号完整历史」。
  4. 关于艺人:展示头像、专辑数 / 单曲数、简介,并提供登录态下的「关注 / 取消关注」胶囊按钮;未登录时按钮引导登录。
  5. 歌曲评论:复用通用半屏 / 全屏评论面板组件,支持从百科页直接跳转完整评论页,并保留预测性返回手势。
  6. 底部按钮统一:新增 SongEncyclopediaSectionAction.kt 统一各模块底部的「展开 / 播放全部 / 重试」按钮样式(图标 18dp、行间距 8dp),替换原先散落的裸 TextButton,共 26 处引用;同时把 MoreOptionsEncyclopediaPage 的双层嵌套 lambda 抽成命名私有函数,降低圈复杂度并通过 :app:verifyCrap。
  7. ViewModel:新增 SongEncyclopediaUiState.kt;NowPlayingViewModel 增加百科加载 / 取消 / 重试入口。
  8. 资源:modules/common/src/main/res/values/、values-en/、values-zh/ 三套 strings_encyclopedia.xml。
  9. 测试:新增 NeteaseEncyclopediaParserTest、NeteaseEncyclopediaExtendedParserTest、NeteaseSongEncyclopediaRepositoryTest、SongEncyclopediaMemoryCacheTest、NowPlayingViewModelEncyclopediaTest、SongEncyclopediaCommentPreviewPolicyTest、MoreOptionsSheetBackTargetTest。

B. Bilibili 音乐榜 / Bilibili 音乐热门(来自 PR #525)

  1. 音乐榜数据:走 B 站官方版权音乐榜接口 x/copyright-music-publicity/toplist/all_period(期数)与 .../music_list(曲目),支持热门榜 / 原创榜切换,并在「第 N 期」旁展示该期发布时间。
  2. 音乐热门数据源更换(Bug 修复):原用 Web 端 x/web-interface/ranking/v2?rid=3,实测该分区榜数据已冻结在 2025-04 且不再更新(B 站已废弃该接口)。改用移动端分区榜 app.bilibili.com/x/v2/rank/region?rid=3&type=all&pn=1&ps=100,实测数据新鲜。
  3. av→bv 本地换算:移动端接口只返回 param(aid 字符串),不含 bvid;新增 BiliVideoIdCodec 实现 aidToBvid / bvidToAid 双向换算(58 进制 + XOR,含两次字符交换),已用真实接口数据全量校验。
  4. 播放:榜单曲目按「榜单授权投稿视频」映射为可播放条目(creationAid / creationBvid / creationCid),无有效投稿时标记为不可播放。
  5. 卡片样式:两个模块的首页卡片与详情页行统一为 B 站收藏夹的图片样式(16:9 缩略图、168dp 卡片宽、圆角、buildBiliThumbnailUrl(cover, 336, 189)、离线缓存请求),并接入脱机模式(只读缓存)与首页整页刷新。

C. 合入上游 Clean 预览主题与适配

  1. merge cwuom/NeriPlayer master 02611a5cdd3cfb56a9ee9d4a36152f1677829eaf(292 files, +27040 −3541),获得 UiStyle、NeriDesign / NeriDesignTokens、neriRoundedShape、ContinuousRoundedCornerShape、CleanPressIndication、设置页 UiStyle 选择器与 SettingsRepository.uiStyleFlow 等双主题基建。
  2. 三处冲突手工解决:MoreOptionsMainContent.kt、HomeScreen.kt(保留滚动锚点 key 抽取,兼容上游回迁的 HomePhoneGridCells)、config/quality/crap-scope.json(取上游 patterns,追加本分支需要的作用域)。
  3. Clean 适配·第一批(13 处圆角):BiliMusicHomeSections.kt 8 处、BiliMusicDetailScreens.kt 2 处、SongEncyclopediaArtistFollowButton.kt 1 处、SongEncyclopediaSimilarSongsSection.kt 2 处,共 13 处 RoundedCornerShape → neriRoundedShape(Clean 下经 cleanCornerRadiusFor 放大为连续圆角)。
  4. Clean 适配·第二批(commit 7fb080c6 / e1e03c8e / a2af5f2e / ace338324,6 files +187/−82):沿用上游三招(MaterialTheme.colorScheme/shapes → neriRoundedShape → NeriDesign.isClean 分支),未另造体系:
    • SongEncyclopediaSectionCard.kt:Card 迁移到 Surface,Clean 走 neriRoundedShape(16.dp)(=20dp 连续圆角,与上游 MoreGroupShape 同构)+ onSurface×5% 墨色容器 + tokens.hairlineAlpha(6%)发丝线;标题 SemiBold/onSurface、图标 onSurfaceVariant(Clean 只用字重表达层级,主色留给动作,与上游 HomeSectionHeader 同思路)。Material 侧逐项对齐原 Card 默认值:shape=MaterialTheme.shapes.medium、tonalElevation=shadowElevation=0dp、contentColor=onSurface、标题沿用 titleSmall 自带 Medium(500) 字重、间距 12dp —— Material 零回归。
    • DensityScaledOverlays.kt:sheet 包装层 shape 显式接 NeriDesign.tokens.sheetShape(Clean 下二者等价,仅把圆角来源显式化)。
    • SongEncyclopediaContent.kt:页面横向 padding 接 tokens.pageHorizontalPadding(Material 16dp 不变)。
    • CommentItem.kt:评论区容器 Clean 走 onSurface×4%。
    • BiliMusicHomeSections.kt:SectionHeader 加 Clean 分支(去图标 + Bold 标题)、FilterChip → NeriChoiceChip(Clean 下胶囊)。
    • BiliMusicDetailScreens.kt:榜单类型切走 NeriChoiceChip;两处 Scaffold / TopAppBar 透明化以露出封面背景。
  5. 合并上游(commit 31b2b37c):merge upstream/master(含 PR fix(ui): stabilize playlist reordering and selection actions #542 intention/fix-library-reorder-selection、PR test(ui): stabilize preview theme prompt back dismissal #543),零冲突;合并后 :app:compileDebugKotlin 通过。本 PR 同时跟进上游 PlaylistSelectionActions.kt、NowPlayingQueueSheet.kt、LibraryScreenLocalContent.kt、libs.versions.toml、app/build.gradle.kts 等改动。
  6. Lint 修复::app:lintDebug 报 LocalContextGetResourceValueCall,将 5 处 LocalContext.current + context.getString(...) 改为 stringResource(...);本批 Clean 适配同样以 :app:lintDebug + :app:compileDebugKotlin 全绿为准。

D. 合并与评审修复

  1. 修复解冲突时把 listen_together 与 encyclopedia 入口写成两组重复项、且第一组缺失 MoreOptionsGroup 闭合括号的语法 bug(花括号平衡 116 / 116)。
  2. 百科页跳转艺人 / 歌单时,先收起更多面板并退出播放页再导航(与 MoreOptionsMainActions.onAlbum 一致),修复导航发生在播放页覆盖层下方、用户看不到变化的问题。
  3. 修正 NowPlayingScreenKt 的 CRAP 方法作用域 lambda 编号(lambda$154 → lambda$155),修复 :app:crapReport 因方法名漂移而退出码 2 的问题。
  4. 补齐 values-zh/strings_home.xml 中 15 条 home_bili_music_* 中文翻译,修复 :common:lintDebug 的 MissingTranslation。

界面截图 / Screenshots

更多面板入口 歌曲详情 / 听歌记录 / 关于艺人
更多面板中的歌曲百科入口 歌曲详情、听歌记录与关于艺人
歌曲评论 相似歌曲 / 相关歌单 / 基本信息
百科页内的歌曲评论预览 相似歌曲、相关歌单与基本信息
半屏面板形态(含封面主色背景)
歌曲百科半屏面板

类型 / Type

  • 新功能 / New Feature
  • Bug 修复 / Bug Fix
  • 文档更新 / Documentation Update
  • 其他(请描述)/ Other (please describe):

修复或解决的问题 / Issues Fixed or Closed by This PR

  • 播放页缺少歌曲的完整背景信息:新增歌曲百科,聚合歌曲详情、创作团队、听歌记录、艺人简介、评论、相似歌曲与相关歌单。
  • 百科页跳转艺人 / 歌单后用户看不到任何变化:导航原先发生在播放页覆盖层之下、更多面板仍展开;现在先收起面板再退出播放页再导航。
  • 百科页底部按钮样式分散(裸 TextButton / 手写间距):统一为 SongEncyclopediaSectionAction 一套组件。
  • B 站音乐热门长期不更新:Web 端 ranking/v2 分区榜已被 B 站废弃,数据冻结在 2025-04;改用移动端分区榜接口后恢复新鲜数据。
  • 首页缺少 B 站音乐内容入口:新增音乐榜与音乐热门两个模块,可直接播放并跳转原视频。
  • :app:crapReport 因 Kotlin lambda 编号漂移失败:同步 crap-scope.json 中 11 处方法名。
  • :common:lintDebug 因 home_bili_music_* 缺少中文翻译失败:补齐 values-zh。

清单 / Checklist

  • ./gradlew :app:compileDebugKotlin 通过(JDK 21)
  • ./gradlew :app:assembleDebug 通过,产物已真机安装测试(截图即来自该构建)
  • 新增单元测试覆盖百科解析、仓库缓存、ViewModel 状态、评论预览准入与更多面板预测性关闭
  • 未新增第三方依赖
  • connectedDebugAndroidTest(本机无连接设备,由云端 instrumented shard 0 / 1 / External Lyrics host 覆盖)

其他信息 / Additional Information

  • 基线:cwuom/NeriPlayer:master @ b0da29cff(已含 PR fix(ui): stabilize playlist reordering and selection actions #542 / test(ui): stabilize preview theme prompt back dismissal #543);本 PR 共 28 commits / 60 files。
  • 新增文件:SongEncyclopedia.kt、NeteaseEncyclopediaParser.kt、NeteaseSongEncyclopediaRepository.kt、SongEncyclopediaMemoryCache.kt、SongEncyclopediaRepository.kt、SongEncyclopediaUiState.kt、EncyclopediaSongFactory.kt、SongEncyclopediaContent.kt、SongEncyclopediaSectionAction.kt、SongEncyclopediaSectionCard.kt、SongEncyclopediaSectionsLegacy.kt、SongEncyclopediaSongDetailSection.kt、SongEncyclopediaArtistSection.kt、SongEncyclopediaArtistFollowButton.kt、SongEncyclopediaListeningSection.kt、SongEncyclopediaCommentPreview.kt、SongEncyclopediaSimilarSongsSection.kt、SongEncyclopediaRelatedPlaylistsSection.kt、HalfExpandableSheetPanel.kt、BiliMusicModels.kt、BiliVideoIdCodec.kt、BiliMusicHomeSections.kt、BiliMusicDetailScreens.kt、HomeScrollKeys.kt、BiliMusicHomeRepository.kt。
  • 对既有播放链路零侵入:不改 PlayerManager、Media3/ExoPlayer、播放队列、音源解析与音频缓存。
  • 验证方式:B 站数据源与 av→bv 换算以真实接口全量校验(aidToBvid 与官方 x/web-interface/view 返回的 bvid 全部匹配;2000 次随机 aid 往返 0 失败);百科解析用真实接口响应做全量字段校验。
  • HomeScreen.kt 仍受 OwnedMainSourceLineBudgetTest(< 2000 物理行)约束,合并后为 1983 行。
  • 上游 np-submodule/miuix 指针随合并前移,本分支未单独改动其内容。

- 首页新增「Bilibili 音乐榜」与「Bilibili 音乐热门」两个模块,位置排在网易云「私人雷达 / 雷达歌单」之后、「热力飙升 / 我的推荐」之前
- 音乐榜支持热门榜 / 原创榜切换,详情页在期数旁展示更新时间
- 音乐热门改用移动端分区榜接口 app.bilibili.com/x/v2/rank/region,Web 端 ranking/v2 分区榜数据已冻结废弃
- 新增 BiliVideoIdCodec 做 av→bv 本地换算(移动端接口只返回 aid)
- 卡片样式与 B 站收藏夹一致:16:9 缩略图、统一缩略图参数、名次 / 时长角标
- Coalesce Bilibili startup loads so the international bootstrap path no
  longer cancels/restarts the in-flight rank and hot requests.
- Count the Bilibili sections in the home visibility check, so they still
  render when the Netease card switches are all off.
- Propagate offlineMode to the shared HomeViewModel from both detail
  screens and gate refresh entry points accordingly.
- Drop the root-level BackHandler in both detail screens so the host
  predictive back transition keeps working.
- Add KDoc to the new public/internal declarations to satisfy the
  docstring coverage check.
The rank type switch read the cache only when online and always set
loading = true, so an offline switch briefly showed a spinner before
loadBiliMusicRank took its cache-only branch.
- BiliMusicHomeRepository: 官方 MV 补全改为按 4 条一批串行推进,
  避免冷启动/强制刷新时一次性打出几十个 WBI view 请求触发 412 风控
- BiliMusicDetailScreens: 两个详情列表底部内边距改为 24.dp + LocalMiniPlayerHeight,
  修复播放中迷你播放器遮挡最后一行与「打开原视频」按钮的问题
- config/quality/domain-dependencies.json:把新增的 B 站音乐模型类
  (BiliMusicHotVideo / BiliMusicRankPeriod / BiliMusicRankSong)登记进
  platform-bilibili-client 域的 allowed_classes,修复 :platform:verifyDomainDependencies
  的 6 条违规(该任务在 CI 的 Gradle tests 里失败)。
- BiliMusicModels:新增 BiliMusicPlaybackSource.UNAVAILABLE,当官方 MV 与
  榜单授权投稿都不完整时如实标注「暂无可播放音频」,不再误报为投稿视频。
- BiliMusicHomeRepository:MV 补全按批推进时,若某一批全部失败(典型是风控 412
  或超时)就停止后续批次,立即把剩余曲目原样返回,避免榜单长时间加载。
:app:testDebugUnitTest 中 OwnedMainSourceLineBudgetTest 失败:
HomeScreen.kt 达 2032 物理行,超过 LocalManagementLineBudget
MAX_EXCLUSIVE = 2000 的预算。

将 14 个 HomeScrollKey* 常量与 7 个 home*ScrollKey 辅助函数
整体移入新增的 HomeScrollKeys.kt,均为纯字符串拼装逻辑,
不改变任何运行时行为。声明用 internal(Kotlin 无包私有,
两文件同包可直接引用)。

同时清理两个失效 import:
- androidx.compose.ui.text.font.FontWeight(本就无使用点)
- moe.ouom.neriplayer.ui.viewmodel.tab.NeteaseHomePlaylistSource
  (提取后类型名不再显式出现,仅经类型推断)

HomeScreen.kt: 2032 → 1982 行,两文件未使用 import 均为 0。
提取滚动锚点 key 到 HomeScrollKeys.kt 时漏了 favoriteId 扩展函数的
import,导致 CI :app:compileDebugKotlin 报 Unresolved reference 'favoriteId'。
favoriteId 是 YouTubeMusicPlaylist 的扩展函数(YouTubeMusicModels.kt:28),
只需补 import,无需改 key 语义。

顺带在 gradle-tests artifact 里加上 **/build/reports/problems/**,
让下次 CI 失败能直接拿到 problems report 的 file:line。
评论1 持久化榜单类型:HomeSelectedItem.BiliMusicRank 从 object 改为携带
rankType 的 data class,saveState/restore 都把 rankType 编进 savedState;
BiliMusicRankDetailScreen 新增 restoredRankType 参数,进程恢复时用它对齐
共享 HomeViewModel(否则恢复后展示默认 HOT 而非用户上次选的榜单)。

评论2 离线重进重启空加载:BiliMusicRankDetailScreen 自己追踪 wasOffline,
从离线切回在线且当前该榜为空时强制重载一次。首页不再组合时 HomeScreen 的
wasOffline 恢复刷新不会抵达这个详情目的地,故在详情页内补齐。
- lint: modules/common 新增的 15 条 home_bili_music_* 只有 values/ 与 values-en/
  两份,values-zh/ 缺失,触发 :common:lintDebug 的 MissingTranslation(默认
  severity=error),打挂 CI 的 Gradle tests job。补上对应的中文翻译。
- P2-A: 榜单详情页不再把 BiliMusicRankType 编进导航身份。原先 HomeHostScreen
  硬编码 HomeSelectedItem.BiliMusicRank(BiliMusicRankType.HOT),首页选中原创榜后
  打开详情会被 restore effect 立刻切回热门榜;详情内切榜也不会回写,进程恢复后
  仍回到热门榜。改为 BiliMusicRank 恢复为 object,类型单独用 rememberSaveable
  持久化在宿主侧:首页「查看全部」时经 onBiliMusicRankMore(type) 记住当前
  ui.biliMusicRank.type,详情内切榜经 onRankTypeSelected 回写。类型与导航身份
  解耦后,切榜不会改变 AnimatedContent 的 targetState,避免多余转场动画,也不会
  重跑 LaunchedEffect(selected) 破坏返回时的滚动恢复。
- P2-B: BiliMusicHotDetailScreen 镜像榜单详情页的 wasOffline 空载恢复。原先该页
  的 LaunchedEffect(offlineMode) 只同步离线标志,进入或恢复时若首屏加载因离线被
  跳过,回到在线后不会重启,页面会一直停在「暂无内容」直到手动刷新。
在播放页更多选项面板中加入「歌曲百科」二级页,数据全部来自网易云
公开接口,不推断、不占位:

- model:新增 SongEncyclopedia 数据族与 encyclopediaSongKey(),
  用于跨层统一的歌曲标识(platform 不能依赖 local 的 stableKey)
- platform:NeteaseClient 新增两个匿名 GET 接口
  (/api/song/play/about/block/page 与 /api/song/creators);
  新增解析器、内存缓存(10 分钟 TTL / 32 条 LRU)与仓库实现,
  按 songKey 缓存,CancellationException 向上传播
- viewmodel:NowPlayingViewModel 新增百科状态机,用 RequestGeneration
  票据 + Job 取消防串台,切歌后旧响应不会覆盖新歌
- ui:新增 SongEncyclopediaContent 二级页(加载/空/不支持/失败重试/
  成功五态),入口挂在更多选项主列表
- 资源:新增三套 strings_encyclopedia.xml
- 门禁:platform-netease-repository 域白名单补入 encyclopedia 包
- 测试:新增解析器、缓存、仓库三个单测
在「更多」面板新增「歌曲百科」二级页,接入网易云歌曲百科数据源。

数据层(modules/platform/netease/encyclopedia)
- NeteaseClient: 新增 getSongEncyclopediaSummaryCancellable / getSongCreatorsCancellable
  (/api/song/play/about/block/page 与 /api/song/creators,API 模式匿名 GET)
- NeteaseEncyclopediaParser: 解析 SONG_PLAY_ABOUT_SONG_BASIC 板块的曲风/推荐标签/
  语种/BPM/获奖/综艺收录,以及创作者角色表
- NeteaseSongEncyclopediaRepository: 严格按 songKey 匹配、内存 LRU+TTL 缓存、
  CancellationException 向上传播、创作团队失败单独降级不影响整页
- SongEncyclopediaMemoryCache: 同步 LRU 缓存,10 分钟 TTL / 32 条
- SongEncyclopedia(modules/model/encyclopedia): 数据模型 + encyclopediaSongKey()

界面层(app/.../ui/screen/nowplaying/actions/,分片薄函数)
- SongEncyclopediaSongDetailSection: 曲风/专辑/语种/发行日期/BPM/乐器/制作团队
- SongEncyclopediaListeningSection: 本机听歌记录(首听时间/累计时长/播放次数),
  明确标注非账号完整历史
- SongEncyclopediaArtistSection: 艺人头像/名称/专辑与单曲数/可展开简介;
  不渲染关注按钮与粉丝数(数据源无匿名来源,避免误导)
- SongEncyclopediaSimilarSongsSection / RelatedPlaylistsSection / CommentEntry
- SongEncyclopediaContent: 状态机承载(Success 携带 songKey 二次校验防串台)

接线
- MoreOptionsPage 新增 ENCYCLOPEDIA 枚举与分派,经 navigation 复用既有
  艺人/歌单路由;NowPlayingScreen 传 onEnterArtist / onEnterPlaylist
- NowPlayingViewModel: encyclopediaState + load/cancel/retry,
  RequestGeneration 代际票据丢弃旧请求,禁止 fetch-then-set
- domain-dependencies.json: platform-netease-repository 追加 encyclopedia 模型白名单

测试
- modules/platform: 57 项(解析器 23 / 仓库 22 / 缓存 12)
- app: 9 项(状态机、代际防串台、重复请求跳过、取消传播)
- :app:verifyDomainDependencies 0 violations;
  :app:crapToolTests 为既有 javac EXCEPTION_ACCESS_VIOLATION 问题,需 -x 跳过

未实现(数据源不存在,不伪造)
- 模块四 AI 解读(「小宝」在原项目无对应服务)
- 模块九 相关播客(MUSIC_MEMORY 板块匿名不可用)
- 粉丝数 / 关注按钮 / 乐谱上传(无匿名来源或成本过高)
UI 隔离
- 新增 SongEncyclopediaSectionCard:每个模块独立 Card 容器,
  单模块的空态/失败态/内容高度变化不再影响其它模块边界
- SongEncyclopediaBody 改为逐模块包裹卡片:歌曲详情、听歌记录、
  关于艺人、评论预览、相似歌曲、相关歌单、获奖收录

评论预览(仅网易云)
- 新增 SongEncyclopediaCommentPreview:复用 CommentViewModel 与
  CommentItem,展示前 3 条评论、评论总数与「查看全部评论」入口
- 使用独立 key 的 CommentViewModel,避免与完整 CommentSheet 共用实例
  导致一方离开组合时清掉另一方状态
- 只读预览:点赞/回复/楼中楼全部禁用,不在百科页产生副作用操作
- 仅 commentSource.platform == NETEASE 时渲染;B 站/本地/其它来源不显示
- 完整评论列表仍由既有 CommentSheet 提供

登录态关注
- 新增 SongEncyclopediaArtistFollowButton:仅网易云登录态为 Valid 时渲染,
  未登录完全隐藏(关注无法与账号同步时显示按钮会误导)
- 关注动作复用项目既有的 FavoritePlaylistRepository 与
  toggleCreatorFollow,与艺人详情页同一套本地收藏逻辑
- 关注中显示加载态,失败在按钮下方显示错误,不伪造成功

字符串
- values / values-zh / values-en 各新增 2 条:
  encyclopedia_comment_preview_failed、encyclopedia_comment_show_all

测试
- 新增 SongEncyclopediaCommentPreviewPolicyTest(4 项):
  网易云可预览、B 站不可预览、本地歌曲无来源、resourceId 非正不可预览
- 既有 platform 57 项百科测试、app 9 项状态机测试、4 项 MoreOptions 测试全部通过
- :app:verifyDomainDependencies 0 violations(需 -x :app:crapToolTests)
- :app:assembleDebug 通过
崩溃原因
- SongEncyclopediaCommentPreview 使用 LazyColumn,而百科页外层是
  Column(Modifier.verticalScroll(...))。垂直可滚动组件被测量为无限高度
  约束时 Compose 直接抛 IllegalStateException。
- 真机堆栈: LazyListKt.rememberLazyListMeasurePolicy ->
  checkScrollableContainerConstraints -> throwIllegalStateException

修复
- 预览只有 3 条,不需要懒加载,改为普通 Column + forEachIndexed + key
- 移除 LazyColumn / itemsIndexed 导入,新增 key 导入

约束记录
- 百科页内任何模块都不得再引入 LazyColumn / LazyRow /
  LazyVerticalGrid / verticalScroll,外层滚动是唯一滚动容器
1. 模块区分度:抽取 SongEncyclopediaSectionCard 带标题栏(图标+标题+分隔线),
   卡片下沉到各 section 内部,有内容才渲染,卡片之间统一 12dp 间距;
   删除各 section 内部的 SubsectionTitle 与 SongEncyclopediaContent 的重复标题。
2. 半屏:把评论面板内联的半屏逻辑抽成公共组件 HalfExpandableSheetPanel,
   评论与歌曲百科共用同一套拖拽/吸附实现;百科页标题栏新增勾柄与全屏切换。
3. 评论跳转:「查看全部评论」改为先收起更多面板,再由宿主打开原生评论面板,
   不再在百科页之上叠一层 CommentSheet。
4. 预测性返回:更多面板主页返回时整个面板跟手缩小+圆角+滑出(复用
   PredictiveBackDismiss 基建),子页返回主页仍走原有转场;
   ModalBottomSheetProperties(shouldDismissOnBackPress = false) 交给面板自管。

本机仅验证 :app:compileDebugKotlin;单测与 CRAP/域依赖门禁交由 GitHub CI。
在 MoreOptionsSheetNavigation 实参里新增 onOpenComments lambda 后,NowPlayingScreen 内
lambda 编译编号整体 +1(152 → 153),crap-scope.json 的 method_scopes 仍按旧名字声明,
:app:crapReport 因而报 "Method scopes have no measured methods"。按 2cec540 的既有做法
跟随编号更新。
1. 新增 SongEncyclopediaSectionAction.kt:把百科各模块的「展开/收起」
   「查看全部评论」「重试」「播放全部」统一收敛到 HapticTextButton 实现,
   图标、尺寸、颜色、触觉、间距一致,替换原来四处各写一遍的文字按钮。
2. 评论预览的加载态改用公共组件 CommentLoadingBlock,与评论面板保持一致。
3. MoreOptionsEncyclopediaPage 的双层嵌套 lambda 改为调用命名私有函数
   openEncyclopediaComments,把圈复杂度从 3 降到 2,与同文件其余
   7 个 MoreOptions*Page 持平。
4. 补 4 条 MoreOptionsSheetBackTargetTest 用例覆盖
   MoreOptionsSheetOwner.canPredictiveDismiss,把该 getter 覆盖率
   拉到 100%,以通过 :app:verifyCrap 门禁。

本机仅验证编译;单测与 CRAP/域依赖门禁交由 GitHub CI。
合并上游 02611a5「feat(ui): add Clean preview theme and refresh
app interactions (cwuom#538)」,取得 UiStyle(Material/Clean) 双风格基建。

手工解决 3 个冲突:
- MoreOptionsMainContent.kt:保留我的歌曲百科入口与一起听入口,
  改用上游 MoreOptionsItem 组件(hue 分别用 MoreHueViolet /
  MoreHueIndigo),跟随上游 Clean 视觉。
- HomeScreen.kt:上游把 HomeScrollKey* / HomePhoneGridCells /
  key 辅助函数挪回本文件,与我从 HomeScreen.kt 抽出到
  HomeScrollKeys.kt 的行数预算修复重叠。删除冲突块,继续使用
  HomeScrollKeys.kt 作为唯一定义处,并把 HomePhoneGridCells 一并
  放进该文件。
- crap-scope.json:取上游 method_scopes(合并后源码以上游为准),
  source_patterns 取上游(HEAD 无独有条目)。

在此基建上开始 Clean 适配:B 站音乐首页卡片、音乐榜/音乐热门详情页、
歌曲百科的封面与关注按钮改用 neriRoundedShape(),使两种风格各自合适。
合并上游 Clean 主题解冲突时,listen_together 与 encyclopedia 两个入口
被写成两组重复项,且第一组末尾误删 MoreOptionsGroup 的闭合括号,
花括号失衡导致编译期语法错误。

恢复为两项共用一个 MoreOptionsGroup:listen_together(MoreHueIndigo)
+ encyclopedia(MoreHueViolet)。括号平衡校验 116/116。
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-18T07:43:59.270899Z f5b92bb PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: cwuom/NeriPlayer/.coderabbit.yaml
  • Review profile: QUIET
  • Plan: Advanced
  • Run ID: 1c0d4873-cc2c-49b8-9ba2-6fce188eec07
📥 Commits

Reviewing files that changed from the base of the PR and between f5b92bb and facf779.

📒 Files selected for processing: 1
  • app/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/NowPlayingSecondaryActions.kt

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 6 remain after this review.

📝 Walkthrough

新增网易云歌曲百科的数据模型、请求解析、缓存、加载状态和展示页面,并将入口接入播放选项面板。新增 Bilibili 音乐榜单与热门视频的数据加载、首页区块、详情页面及播放导航。评论面板改用共享的半屏面板组件。CI 报告上传范围和质量依赖白名单也有更新。

Priority: ➖ Normal

Merge Risk: 🔵 Low · up to facf7

The PR is broadly mergeable, but these localized attribution, presentation, and test-maintenance issues should receive follow-up.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 35.42% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 288 functions across 44 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed 标题准确概括了主要变更:新增歌曲百科,并适配上游 Clean 双主题。
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments: 4
modules/model/src/main/java/moe/ouom/neriplayer/data/model/bilibili/music/BiliMusicModels.kt: 1

152-161: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

官方 MV 条目的 uploaderMid 与实际播放视频不一致

hasOfficialMv 为 true 时,uploader 使用 playbackUploader,也就是 MV 账号名。uploaderMid 却仍然是 creation_up,也就是授权投稿 UP 主的 mid。因此,同一个 BiliVideoItem 中的名称和 mid 属于两个不同账号。

如果播放页或后续流程按 uploaderMid 打开 UP 主空间或执行关注操作,用户会看到 MV 账号名,但会进入投稿 UP 主的空间。这与 KDoc 中“上传者与实际播放的那支视频保持一致”的约定不符。

BiliMusicHomeRepository.enrichOfficialMv 已经获取了 VideoBasicInfo,其中包含 ownerMid,但该值没有写回模型。建议新增 mvUploaderMid,并在官方 MV 分支中使用它。

🐛 建议修复
     /** 官方 MV 的上传账号名(补全时带回) */
     val mvUploader: String = "",
+    /** 官方 MV 的上传账号 mid(补全时带回) */
+    val mvUploaderMid: Long = 0L,
             uploader = playbackUploader,
-            uploaderMid = uploaderMid,
+            uploaderMid = if (mvUploaderMid > 0L) mvUploaderMid else uploaderMid,
             coverUrl = videoCover,

在 BiliMusicHomeRepository.enrichOfficialMv 中同步写入:

song.copy(
    mvAid = info.aid,
    mvDurationSec = info.durationSec,
    mvUploader = info.ownerName,
    mvUploaderMid = info.ownerMid,
    mvCoverUrl = normalizeVideoCover(info.coverUrl)
)

如果 mvUploader 为空,playbackUploader 会回退到投稿 UP 主名称。此时 mid 也应保持为投稿 UP 主的 mid,以保证名称和 mid 属于同一账号。

app/src/test/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaCommentPreviewPolicyTest.kt: 1

53-57: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

测试校验的是本地副本逻辑,与生产代码不一致。

isPreviewEligible 是测试内私有实现,要求 resourceId > 0L。生产代码 SongEncyclopediaBody 只判断 commentSource?.platform == CommentPlatform.NETEASE,没有 resourceId 检查。因此 netease song without positive resource id is not eligible 通过,但生产代码会为 resourceId = 0 的来源渲染评论预览。生产判断以后改动时,这组测试也不会失败。

请把判断提取为 internal fun isEncyclopediaCommentPreviewEligible(source: CommentSource?),在 SongEncyclopediaBody 和本测试中共用。

依据路径指令:"Flag a test only when it can pass despite a user-visible regression"。

Source: Path instructions

app/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaContent.kt: 2

402-408: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

播放量单位硬编码为「万」,英文界面显示中文字符。

formatEncyclopediaPlayCount 固定拼接 "万"。英文资源 encyclopedia_playlist_play_count 为 %1$s plays,英文用户会看到 681万 plays。请把单位放入字符串资源,或按 Locale 选择格式(例如英文使用 K/M)。

🌐 修复示例
-internal fun formatEncyclopediaPlayCount(playCount: Long): String {
-    return if (playCount >= 10_000L) {
-        "${playCount / 10_000L}万"
-    } else {
-        playCount.toString()
-    }
-}
+internal fun formatEncyclopediaPlayCount(context: Context, playCount: Long): String {
+    return if (playCount >= 10_000L) {
+        context.getString(CoreCommonR.string.encyclopedia_play_count_ten_thousand, playCount / 10_000L)
+    } else {
+        playCount.toString()
+    }
+}

304-304: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

同一字段在页面上重复显示。

SongEncyclopediaSongDetailSection 已展示曲风、语种、BPM、专辑、作词、作曲、编曲、制作人。SongEncyclopediaLegacySections 在同一页面再次展示「基本信息」「音乐属性」「创作团队」卡片。用户会看到曲风、BPM、专辑、作词等同一数据出现两次。

请只保留一处数据源:从 SongEncyclopediaLegacySections 中去掉已由歌曲详情卡片覆盖的字段,或只保留 otherRoles、标签、获奖与收录这类歌曲详情卡片未覆盖的内容。

Also applies to: 345-345


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: cwuom/NeriPlayer/.coderabbit.yaml
  • Review profile: QUIET
  • Plan: Advanced
  • Run ID: 5d0597bf-5965-4b93-a4e7-87d1c2e2e011
📥 Commits

Reviewing files that changed from the base of the PR and between 02611a5 and f5b92bb.

📒 Files selected for processing: 52
  • .github/workflows/android_ci.yml
  • app/src/main/java/moe/ouom/neriplayer/core/di/AppContainer.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/NeriApp.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/component/comment/CommentSheet.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/component/sheet/HalfExpandableSheetPanel.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/screen/host/HomeHostScreen.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/NowPlayingScreen.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/NowPlayingSecondaryActions.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/EncyclopediaSongFactory.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/MoreOptionsMainContent.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaArtistFollowButton.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaArtistSection.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaCommentPreview.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaContent.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaListeningSection.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaRelatedPlaylistsSection.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaSectionAction.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaSectionCard.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaSectionsLegacy.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaSimilarSongsSection.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaSongDetailSection.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/screen/tab/home/BiliMusicDetailScreens.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/screen/tab/home/BiliMusicHomeSections.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/screen/tab/home/HomeScreen.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/screen/tab/home/HomeScrollKeys.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/viewmodel/NowPlayingViewModel.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/viewmodel/SongEncyclopediaUiState.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/viewmodel/tab/HomeViewModel.kt
  • app/src/main/java/moe/ouom/neriplayer/ui/viewmodel/tab/bili/BiliMusicHomeRepository.kt
  • app/src/test/java/moe/ouom/neriplayer/ui/screen/nowplaying/MoreOptionsSheetBackTargetTest.kt
  • app/src/test/java/moe/ouom/neriplayer/ui/screen/nowplaying/actions/SongEncyclopediaCommentPreviewPolicyTest.kt
  • app/src/test/java/moe/ouom/neriplayer/ui/viewmodel/NowPlayingViewModelEncyclopediaTest.kt
  • config/quality/domain-dependencies.json
  • modules/common/src/main/res/values-en/strings_encyclopedia.xml
  • modules/common/src/main/res/values-en/strings_home.xml
  • modules/common/src/main/res/values-zh/strings_encyclopedia.xml
  • modules/common/src/main/res/values-zh/strings_home.xml
  • modules/common/src/main/res/values/strings_encyclopedia.xml
  • modules/common/src/main/res/values/strings_home.xml
  • modules/model/src/main/java/moe/ouom/neriplayer/data/model/bilibili/music/BiliMusicModels.kt
  • modules/model/src/main/java/moe/ouom/neriplayer/data/model/encyclopedia/SongEncyclopedia.kt
  • modules/platform/src/main/java/moe/ouom/neriplayer/platform/bilibili/api/client/BiliClient.kt
  • modules/platform/src/main/java/moe/ouom/neriplayer/platform/bilibili/api/client/BiliVideoIdCodec.kt
  • modules/platform/src/main/java/moe/ouom/neriplayer/platform/netease/api/client/NeteaseClient.kt
  • modules/platform/src/main/java/moe/ouom/neriplayer/platform/netease/encyclopedia/NeteaseEncyclopediaParser.kt
  • modules/platform/src/main/java/moe/ouom/neriplayer/platform/netease/encyclopedia/NeteaseSongEncyclopediaRepository.kt
  • modules/platform/src/main/java/moe/ouom/neriplayer/platform/netease/encyclopedia/SongEncyclopediaMemoryCache.kt
  • modules/platform/src/main/java/moe/ouom/neriplayer/platform/netease/encyclopedia/SongEncyclopediaRepository.kt
  • modules/platform/src/test/java/moe/ouom/neriplayer/platform/netease/encyclopedia/NeteaseEncyclopediaExtendedParserTest.kt
  • modules/platform/src/test/java/moe/ouom/neriplayer/platform/netease/encyclopedia/NeteaseEncyclopediaParserTest.kt
  • modules/platform/src/test/java/moe/ouom/neriplayer/platform/netease/encyclopedia/NeteaseSongEncyclopediaRepositoryTest.kt
  • modules/platform/src/test/java/moe/ouom/neriplayer/platform/netease/encyclopedia/SongEncyclopediaMemoryCacheTest.kt

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.

@cwuom

cwuom commented Oct 18, 2026

Copy link
Copy Markdown
Owner

补充一下截图

@cwuom

cwuom commented Oct 18, 2026

Copy link
Copy Markdown
Owner

等 #542 合并就处理你的 PR

liaolon and others added 4 commits October 18, 2026 16:11
CodeRabbit Major 意见:百科页的 onEnterArtist / onEnterPlaylist 直接透传到
NeriApp 的 navigateToNeteaseArtist / navigateToNeteasePlaylist,这两个函数只做
navController.navigate,不会关闭播放页覆盖层;而更多面板此时仍然展开。结果是
导航发生在覆盖层下方,用户看不到任何变化,必须手动关闭面板和播放页。

改为与同文件 MoreOptionsMainActions.onAlbum 一致的处理:owner.dismiss 先收起
更多面板,收起完成后再导航并调用 navigation.onNavigateUp() 退出播放页。
新增 onEnterPlaylist 参数使 Kotlin 编译器生成的 lambda 编号整体后移,
NowPlayingScreen$lambda$154$* 已不存在,CRAP 门禁报
"Method scopes have no measured methods" 导致 :app:crapReport 失败。
同步为实际编译产物中的 lambda$155$*。
MoreOptionsEncyclopediaPage 内联了四个捕获 owner/navigation 的 lambda 字面量,
Compose 编译器为它们生成记忆化分支,使该 composable 的圈复杂度达到 5;
该方法在 CI 覆盖率报告中覆盖率为 0,CRAP = 5^2 + 5 = 30,超过门禁阈值 9。

按同文件 MoreOptionsSearchActions / MoreOptionsMainActions 的既有惯例,把四个
回调提成 MoreOptionsEncyclopediaActions 类字段,composable 内只传字段引用。
复杂度 5 -> 2(0% 覆盖率下 CRAP = 6),行为与顺序完全不变。
@cwuom

cwuom commented Oct 18, 2026

Copy link
Copy Markdown
Owner

clean 主题没有适配好

@liaolon

liaolon commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

clean 主题没有适配好

回头再适配

@liaolon

liaolon commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

clean 主题没有适配好

回头再适配

等会适配,现在有事

CI 的 :app:lintDebug 报 LocalContextGetResourceValueCall 共 7 处:
在 composable 里通过 LocalContext.current + context.getString() 取资源
会在配置变化/语言切换时漏掉重组。改为直接调用 stringResource。

- SongEncyclopediaCommentPreview:评论总数
- SongEncyclopediaListeningSection:听歌时长三档 + 播放次数,顺带去掉了
  不再需要的 remember(stringResource 本身随组合重组)
- SongEncyclopediaRelatedPlaylistsSection:歌单播放量
- SongEncyclopediaSimilarSongsSection:相似歌曲序号
- SongEncyclopediaArtistSection:艺人作品数文案,buildEncyclopediaArtistMeta
  改为 @composable 并在内部用 stringResource
上游 cwuom#538 引入 Clean 预览主题后,本 PR 新增的歌曲百科与 B 站音乐 UI
没有跟着接入 NeriDesign tokens,Clean 下会出现卡片与宿主面板糊在一起、
区块标题层级与全 app 其余区块不同源等问题。

- SongEncyclopediaSectionCard:卡片在 Clean 下改用墨色 5% 半透明底
  (surfaceContainerLow 为纯白、surfaceTint 被置空后 elevation 染色失效,
  原配色对比度仅约 1.07:1),模块标题改用 onSurface + SemiBold、分隔线改
  墨色 hairlineAlpha;SongEncyclopediaSectionSpacing 改读 tokens.sectionSpacing
- DensityScaledModalBottomSheet:默认 shape 显式接 tokens.sheetShape。
  material3 的 ExpandedShape 派生自 MaterialTheme.shapes.extraLarge 的
  top-only 版本(SheetBottomTokens.DockedContainerShape = CornerExtraLargeTop),
  Clean 下两者本就等价,此处只是把圆角来源显式化、不依赖 material3 内部
  映射,Material/Clean 下行为均不变
- CommentItem:评论卡片在 Clean 下改墨色 4%,与 sheet 底拉开层次
- SongEncyclopediaContent:面板水平内边距接 tokens.pageHorizontalPadding
- BiliMusicHomeSections:区块标题加 Clean 分支(去图标、onSurface + Bold),
  榜单类型切换改 NeriChoiceChip,并收回 30dp 图标缩进
- BiliMusicDetailScreens:同上切换 chip;两个详情页 TopAppBar 与 Scaffold
  透明化,自定义背景图下不再实心遮挡
@liaolon
liaolon force-pushed the feat/song-encyclopedia branch from 7fb080c to e1e03c8 Compare October 18, 2026 13:59
上一提交把 Card 换成 Surface 时带进了三处与原始观感不一致的地方,
均按 material3 1.4.0 的默认值逐项核对后修正:

- shadowElevation 原本写成 Material 下 1dp:CardDefaults.cardElevation()
  的默认值是 FilledCardTokens.ContainerElevation = ElevationTokens.Level0
  = 0dp,两套主题原本都没有阴影,改回 0dp
- 标题字重原本在 Material 下覆盖成 Normal(400):titleSmall 的 M3 默认
  字重是 Medium(500),覆盖会变细,改为 Material 分支原样沿用
- 补 contentColor = onSurface:原 Card 走 contentColorFor(surfaceContainerLow)
  = onSurface,而 Clean 的墨色半透明不在色板内,不显式指定会退化为继承
  外层 LocalContentColor
SongEncyclopediaSectionSpacing 改读 NeriDesign.tokens.sectionSpacing 后,
Material 主题下模块间距从原来的 12dp 被抬到 20dp(Clean 才是 28dp),
把非 Clean 主题的观感也改掉了。按 Clean 分支读 token、Material 沿用 12dp
的写法还原,保证 Material 零回归。
@liaolon

liaolon commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

clean 主题没有适配好

。我看了最新发行版本的播放页面,里面最新主题的适配和我现在适配的基本上差不多。还是说,播放界面就是没有适配新主题

@liaolon

liaolon commented Oct 18, 2026

Copy link
Copy Markdown
Contributor Author

已按你说的重新适配 Clean 主题,麻烦再看一眼。

一共 6 个文件,都沿用了仓库里现有的 neriRoundedShape / NeriDesign.isClean 做法,没有另起一套:

  • SongEncyclopediaSectionCard.kt — 百科卡片切 Surface,Clean 下用连续圆角 + 墨色 5% 容器 + 6% 发丝线;Material 下逐项对齐原 Card 默认值(形状、0 阴影、onSurface 内容色、titleSmall 自带字重、12dp 间距),不改变现有观感
  • BiliMusicHomeSections.kt / BiliMusicDetailScreens.kt — 标题去图标改用 Bold 字重、FilterChip 换 NeriChoiceChip、详情页顶栏透明
  • DensityScaledOverlays.kt / SongEncyclopediaContent.kt / CommentItem.kt — 形状与留白接 NeriDesign token

同时合并了 upstream master(含 #542 / #543,零冲突),并修掉 :app:lintDebug 报的 LocalContextGetResourceValueCall。当前 ace338324,CI 全绿。

之前那版变化不明显,可能和我把重点放在首页 B 站模块、播放页这边动得少有关。如果还有不满意的位置,告诉我具体是哪一块,我继续调。

@cwuom

cwuom commented Oct 19, 2026

Copy link
Copy Markdown
Owner

clean 主题没有适配好

。我看了最新发行版本的播放页面,里面最新主题的适配和我现在适配的基本上差不多。还是说,播放界面就是没有适配新主题

你的主页 clean 主题的标题还是带图标的 我后面帮你改一下吧

@liaolon

liaolon commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

clean 主题没有适配好

。 我看了最新发行版本的播放页面,里面最新主题的适配和我现在适配的基本上差不多。 还是说,播放界面就是没有适配新主题

你的主页 clean 主题的标题还是带图标的 我后面帮你改一下吧

嗯,好的,主要是我有点不知道这个主题的设计风格

@cwuom

cwuom commented Oct 19, 2026

Copy link
Copy Markdown
Owner

clean 主题没有适配好

。 我看了最新发行版本的播放页面,里面最新主题的适配和我现在适配的基本上差不多。 还是说,播放界面就是没有适配新主题

你的主页 clean 主题的标题还是带图标的 我后面帮你改一下吧

嗯,好的,主要是我有点不知道这个主题的设计风格

Screenshot_2026-10-19-21-45-38-44_334bffdc113a6a9 这一块的可预测性返回你没有检查到它缺了一角 我后面会修的 合并可能得过段时间

cwuom and others added 2 commits October 19, 2026 22:30
更多菜单的面板在预测性返回时会被切掉底部一角:本地在
ModalBottomSheet 外叠加了自定义的 predictiveDismissTransform,
它的 RoundedCornerShape(cornerRadius) 会把四个角一起圆,
而 sheet 自身只有顶部圆角,底部就被多切掉一层。

改为与云端(cwuom/NeriPlayer)一致:删掉
Modifier.predictiveDismissTransform / rememberPredictiveDismissState /
PredictiveDismissHandler / ModalBottomSheetProperties /
MoreOptionsSheetOwner.canPredictiveDismiss 及其测试,面板关闭交给
material3 自带的预测性返回,子页返回主页仍由
MoreOptionsAnimatedPage 的 rememberHostPredictiveBackTransition 处理。
@liaolon
liaolon force-pushed the feat/song-encyclopedia branch from 99d560e to e997ae9 Compare October 19, 2026 18:48
cwuom added 4 commits October 20, 2026 19:54
Align artist metadata and follow controls, and add a persistent Bilibili Home visibility setting.

Block failed playlist loading behind safe retry dialogs while preserving offline caches and canceling stale requests.

Restore anchored More sheet predictive back, proportional content scaling, and cancellation recovery with regression coverage.
Integrate the PR author update while retaining the validated sheet-specific transform, independent cancellation recovery, and proportional content scaling.
Carry the official MV uploader ID through enrichment and playback mapping. Keep encyclopedia metadata in one section, reuse localized play counts, and cover comment preview eligibility through the real UI entry point.
@cwuom
cwuom merged commit e694ffb into cwuom:master Oct 21, 2026
15 of 16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants