| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
修(设置): 设置解析全面容错,坏一个字段不再把整份设置重置为默认 timetable_settings.dart 的 fromJson 里有 185 处 s String? / s bool? 之类硬 转换:任一字段类型不符(手改备份 / 旧版本存档 / 云同步 / 局域网传输包)就抛 TypeError,冒到 TimetableProfile.fromJsonLenient 的顶层 catch,把整份设置回落 defaults —— 学期起始日、节次表、主题、超级岛开关一起清零,再经 AppGlobalSettingsService.syncFrom 扩散成设备级键(模型组 I1,本轮最大剩余风险)。 - 新增 settings/lenient_json_readers.dart:统一口径「有值就收、收不下按缺键 处理」——字符串 toString、bool 只认真 bool 与 'true'/'false'(不猜 '1'/1)、 数字字符串 tryParse、List/Map 类型不符退 null; - 185 处逐点替换(String 100 / bool 82 / int 10 / double 26 / List 2 + safeStringList 3),合法输入逐字节不变,toJson 格式零改动; - 语言标签规范化函数挪到 utils/app_locale_tag.dart(纯字符串工具,不该占模型 体量也不能留在 models 层); - 主文件 2685 → 2614 行、逻辑单元 3725 → 3654,均回到 god-file 棘轮基线以内 (2621 / 3661),没有抬基线。 测试:新增 test/models/timetable_settings_lenient_parsing_test.dart 37 条 (每类坏值 + 多类同时坏 + round-trip 逐字节相等 + 各类辅助口径);连同 settings/屏幕存量用例共 140 条、test/models+test/domain 835 条全过。 | 17 天前 | |
收紧(工程化): lint 收紧至 strict-casts/strict-raw-types + 40 条扩展规则并全量清零,analyze --fatal-infos 门禁,CJK 硬编码审计棘轮接入 CI——analysis_options 启用 use_build_context_synchronously/cancel_subscriptions/avoid_print 等正确性规则与风格集(dart fix 批量修 1432 处 + 手工修 14 error/17 warning),两个高噪音规则 cascade_invocations(127)/discarded_futures(195) 注释保留待第二批;CI/安卓构建 analyze 收紧为 --fatal-infos 防劣化;audit_cjk_hardcode.py 新增 --baseline 棘轮模式(回归失败/改进自动下调),基线 68 文件 1070 行入库,CI 新增 CJK hardcode audit 步骤;附带 strict_raw_type 显式泛型、lan_edit_api_handlers ArgumentError.message 类型收窄、avoid_slow_async_io 24 处 exists()→existsSync()。flutter analyze 0 issues,services/providers/architecture/models/domain/utils/logging/data 测试全绿(壁纸 7 例失败为主分支既有,与本批无关)。 | 2 个月前 | |
修(顶栏): 切到月视图后更新红点凭空消失——加跨页面共享登记 用户:「为什么切换到月视图,右上角的球会闪一下,本来有更新红点,闪一下又不 显示了」。 根因不是动画,是**红点的值在独立页面里读不到**: - 红点画在「更多」那颗玻璃球上(HomeMoreActionIcon.showUpdateDot); - 值来自 UpdateCheckController.hasUpdate,而那个 controller **只有首页创建**; - 独立页此前一律传 showUpdateDot: false(注释理由是"不另起一份检查、宁可少一个 点")—— 那个顾虑对,但结论错了:红点不是首页专属装饰,它是"有更新"这个 **事实**的表示。从首页切过去它凭空消失,用户读成"球闪一下、点丢了"。 改法:新增 AppUpdateDotShare(极薄的静态登记,与 HomeWallpaperLuminanceShare / HomeStartupVisualPrimer 同一套取舍): - 首页 _handleUpdateChanged 里 publish 结果(检查仍只由首页发起); - 独立页两处(anchorContent 与常驻球 moreIcon)都读 hasUpdate。 这两处**必须同一份值**:不一致时菜单开合交接那一瞬会露出来。 三态语义(本文件的核心,也是测试钉的东西):没查过 / 查过没有 / 查过有。 不做区分的话独立页只能二选一(一律画点或一律不画),两种都错。 验证:新增 app_update_dot_share_test.dart 4 例。 咬合探针:把 hasUpdate 改成恒 false → 立即红 (Expected true / Actual false,reason 直指"切到月视图红点还在")。 我负责的测试 64 例全绿;dart analyze --fatal-infos 干净。 未真机验证:球的闪动是否只剩切换页面时的重建(那是两套页面栈各自画球导致的, 与红点无关);若仍可见,需要另做一层跨页常驻球,属更大改动。 | 16 天前 | |
修(安全): 捐赠名单只认官方直连,镜像不再回答展示型 JSON support_creator_service 把朋友名单(donors.json)也放进了镜像竞速池: 镜像对响应有完全控制权,可整份替换名单内容,而这条链路没有任何摘要校验 (安全组 W1)。 - async_utils 新增通用 isOfficialDirectUrl(GitHub/GitCode 直连判定,含子域与 FLUTTER_TEST loopback 豁免),isTrustedManifestUrl 改为委托它,语义不变; - 名单候选按它过滤:GitCode 直连仍在前面优先尝试,拿不到时只剩 GitHub 直连, 不再退到镜像; - 两个钉旧语义的用例改写:镜像即便能返回数据也不应被请求;直连全挂时如实失败, 不退到镜像拿一份「能显示」的名单。 | 17 天前 | |
新增(壁纸): Bing 每日壁纸库——可挑可选、可每天自动换 设置页新增「Bing 每日壁纸」入口:拉取 Bing 最近 8 天的每日壁纸,浏览时只看缩略图 省流量,点开或应用才下大图。画质三档竖屏(1080×1920 / 1080×2400 / 1440×3200), 浏览页一排胶囊可选;Bing 当天没提供某档时自动退到相邻档并如实说明,不会只丢一句 「下载失败」。选完图复用现成的位置编辑页(拖动取景 / 双指缩放 / 重置),应用后 照旧进「最近使用」。 另有「每天自动换」开关(设备级偏好,与每课表各自的壁纸正交):冷启动与每次回到 前台各判一次,同一天只换一次;网络失败一律静默并双写日志,不影响启动。 关键取舍(细节见 .agents/notes/implemented/feature/2026-10-05-bing-daily-wallpaper-library.md): - 落盘复用现成的受管壁纸目录常量,于是删除、缓存失效、历史与淘汰全部原样生效, 「应用」这件事零新代码。 - 自动换**不**补记「最近使用」历史:历史只 10 条且淘汰会删文件,每天补记的话 10 天后用户自己从相册存的图会被挤掉并且文件被删。 - 下载台账放独立的 device-global prefs(不进 TimetableSettings),缓存上限 12 条 并由台账自己封顶清理;台账记着的路径一律视为「被引用」,否则「看过但没确认 应用」那张会在退出图库时当垃圾删掉。 - 新增 5 个文件对 TimetableProvider 的扇入为 0,Provider 扇入棘轮(54,只减不增) 不动。 顺带把两条新日志键接上文案(log_bing_wallpaper_request_failed / log_bing_wallpaper_auto_apply_failed):localizer 的兜底是 _ => message, 漏了会让用户在「应用日志」页和导出 txt 里看到原始蛇形串。回归钉在六种语言上。 回归:Bing 相关 7 个测试文件 + arb 重复键 + 日志本地化共 122 例全绿; analyze --fatal-infos 干净。 | 30 天前 | |
新增(判墨测试): 补上判墨核心与接线契约的 33 条用例(上一条提交漏带新文件) 上一条 e71c3b26 用路径限定提交,只带走了 10 个已跟踪文件,这条把同批的 5 个新文件补上: - lib/utils/card_surface_ink.dart 判墨唯一入口(卡面亮度合成 + 对比度选色) - lib/utils/wallpaper_luminance_profile.dart 壁纸逐行亮度剖面 - lib/widgets/timetable/day_view/measured_screen_rect.dart 量卡片屏幕矩形(量化后重建) - test/utils/card_surface_ink_test.dart 27 例纯逻辑(含两张截图的实测数值反例) - test/utils/day_agenda_ink_wiring_test.dart 6 例接线契约 内容与 e71c3b26 的说明一致,不重复。全部 33 例通过,flutter analyze 无问题。 | 16 天前 | |
style: 清掉本轮引入的两条 analyzer info,回到上游 3 条基线 clock_time.dart 的 ClockTime(24, 0) 补 const;测试里 DateTime(2026, 10, 3, 0, 0) 的时分是默认值,去掉后与断言等价。 | 1 个月前 | |
修(天气): 坐标取整把「同一城市」判定打穿——升级用户永远丢缓存、日视图反复请求 ## 问题 8d81dd0c(隐私清单 #3)让出网与落盘坐标统一取整到 2 位小数。但 WeatherLocation. samePlaceTolerance 仍是 1e-4(≈11 米),而取整误差最大 0.005(≈550 米)—— 差 50 倍。 内存里的位置(initialize() 从 prefs 原样读回,升级用户是 5 位小数)与缓存预报里的 坐标(取整后的 2 位)直接相减,差值 0.00365 > 1e-4,于是 matchesLocation / isSamePlaceAs 恒判「不是同一个城市」。 后果不是理论上的: 1. 升级用户每次冷启动都丢弃磁盘缓存(_forecast 被置 null),白等一次网络; 2. _shouldRefresh() 里有一条 !forecast.matchesLocation(_location) 就 return true, 判否即恒为真 —— 日视图每张卡重建都可能重发请求,一天 8 次的 TTL 设计被绕开。 8d81dd0c 注释里「多刷一次、一次自愈」的假设只对「用户重新选一次城市」成立; 对「什么都不做只升级」不成立 —— 那条路径上没有任何东西会把旧位置改写成取整精度。 顺带解释 weather_provider_test 那 6 条常红:4 条是断言取整前的值(测试滞后,行为本身 正确),2 条正是这个 bug(同城判定失效导致请求次数变多)。之前被统一记成「测试没跟上 实现」,掩盖了后半截。 ## 改法 在比较处统一精度(不改存储、不放大容差): - coarsenCoordinate 提到横切层 lib/utils/coordinate_precision.dart(唯一真源), WeatherService.coarsenCoordinate 保留为一行转发的公开别名,调用点/测试无感。 放 utils 的理由:models 要用它,而 models→services 是反向依赖会被守卫拦。 - 新增 WeatherLocation.coarsened()。 - isSamePlaceAs() / matchesLocation() 改为两边都取整后再比(三种精度组合都成立)。 - WeatherProvider.initialize() 读回位置后取整;setLocation() 入口先规范化, 比对与落盘都用规范化后的那份。 不选「放大容差到 0.02」:那是把「同城」放宽到 2 公里,切校区不刷新,是新的功能错误。 ## 验证 - weather_provider_test 6 红 → 43 全绿;新增 test/models/weather_location_precision_test (6 条)钉住跨精度判定。 - 天气相关 5 个测试文件 146 条全绿;test/architecture/ 50 条全绿;analyze 改动文件 0 issue。 - 咬合探针:把 isSamePlaceAs / matchesLocation 的比较分别改回裸值,对应用例立即红 (Expected: true / Actual: <false>),还原后干净。 - 未验:真机「升级用户首次冷启动即有天气」(需一份旧格式 prefs)、日视图长时间挂起后 的请求次数(需日志计数)。 | 16 天前 | |
液态玻璃收口(一):删掉第三方包,全部表面改走自带折射着色器 - 移除 liquid_glass_widgets 依赖与全部用法(GlassTabBar/GlassButton/ LiquidGlassWidgets.wrap/initialize/HyperosLiquidGlassLayer);底栏药丸改由 SoftGlassTabBar + 注入式 surfaceBuilder 承担,四种材质共用同一套拖拽/弹簧实现。 - 旧 refraction/ 目录整体删除(glass_surface_shader.dart、 refraction_glass_surface.dart),统一到 lib/ui/hyperos/liquid/ 下的 LiquidGlassSurfaceShader / LiquidGlassStyle / LiquidGlassSurface。 - 数据层合并:FrostedGlassMode.refractionGlass 并入 liquidGlass(持久化字符串 'refractionGlass' 读取时迁移);GlassModeChoice 收成四档; RefractionGlassTuning 并入 LiquidGlassTuning,旋钮由十项换成折射七项。 - CourseCardSurfaceStyle.refraction 更名 liquidGlass('refraction' 读作新档), SurfaceMaterial.refractionGlass 与 liquidGlass 合并为一个枚举值。 - 文案统一:6 语言 arb 里 refractionGlass* 一族与 frostedGlassModeRefraction 下线,改用 liquidGlass* 键;课程卡片档位名改为「液态玻璃」。 test/ 侧尚未同步(下一步单独提交)。 | 1 个月前 | |
refactor(timetable): 主库瘦身——课程分组模型出库、今日/当前/下一节查询搬入实时活动服务 为下一刀(导入导出族出库,parts 归零)腾出棘轮空间,顺手把两处放错层的代码搬走: - CourseGroup / CourseConflict 是纯模型(同名课聚合、冲突对),不该躺在状态管理 主文件里。搬去 lib/utils/course_group.dart,分组逻辑收成 CourseGroup.buildFrom; 主库保留一个委托 getter + 一条 xport,现有 import 方零改动。 - getTodayCourses / getCurrentCourse / getNextCourse 与 getCoursesInProgress 同族:都依赖「今天属于哪一周 / 此刻钟点落在哪一节」的实时口径。三个实现搬进 TimetableLiveActivityService,主库只留委托;钟点仍走 compareClockText, 架构棘(双向字典序)继续绿。 - 假端口单测补 3 条:周次口径跟 currentDateWeek 走、设了开学日期改走真实日历周、 getCurrentCourse 用分钟序判定在上的那一节(共 19 条)。 主文件 3841 → 3751 行。定向回归(钟点棘 / 日程钟点复核 / 日视图 / 实时活动 / 架构棘)全绿。 | 17 天前 | |
修复(配色历史): seed 分支裸 cast 补类型守卫——colorGroupId/assignMatchingTextColor 曾用 as String?/as bool? 裸 cast,磁盘上类型垃圾(int 等)抛 TypeError 穿透到 _loadSchemes 整体 catch,一条坏种子记录就静默清空全部历史,与 b178077f 消灭的快照条目损害路径同源。改为与 snapshot 条目同口径:类型不符返回 null 丢弃该条,字段缺失仍兜底默认值(kCourseColorGroupAllId/false)。测试补 utils 条目级(两类垃圾各自丢弃+缺字段兜底保留)与服务层「坏种子记录不再连带清空整份历史」两层回归 | 2 个月前 | |
优(壁纸): Bing 原图加 qlt=95 质量参数 + 落盘后验尺寸、静默降尺寸自动重试 实测调查(两天图交叉验证): * 源图像素已到顶(_UHD.jpg 3840×2160 就是最大预生成档,更大后缀全 404), 像素数再大屏幕也显示不出来; * 但 Bing 默认把 JPEG 压得很狠:同尺寸 1440×3200 默认 890KB,qlt=95 约 2MB —— 像素不变、块状伪影肉眼可见地减少,是 1:1 观感的主要来源之一; * 顺带发现既有隐患:resize 接口部分组合会静默降尺寸(回高≤2592 的图, 带不带 qlt 都发生),「App 不再二次放大」的保证一直是软的。 改动: * resizeUrl 加 highQuality 开关(原图带 qlt=95,缩略图不带,小图不值得流量翻倍); * expectedSizeFor 与 fullUrl 同口径声明期望尺寸; * _downloadAt 落盘后读编码头部验尺寸(不整图解码),不够就换 c=16 重试一次 (实测 c=4 降尺寸时 c=16 常能给精确尺寸);重试仍不够或失败就回退用第一张。 测试:mock 必须用真 JPEG(手拼最小头被引擎判 Bogus marker length、校验假绿), fixture 用 System.Drawing 预生成三张。壁纸链路 + 架构守卫 131 例全绿。 | 22 天前 | |
修复(隐私): 外部分享导入的表格副本消费完就删,不再长期留在应用缓存 别的 App「用其他应用打开」把表格递进来时,MainActivity.kt:1546-1548 先把它复制成 <cacheDir>/external_imports/<millis>_<safeName>,:1358-1362 把绝对路径交给 Dart; getPendingExternalImport(:797-799) 在交接那一刻就把内存里的 pending 清空了 —— 也就是说交接之后只有 Dart 这一份引用。而消费方 course_import_screen.dart 的 _importFromExternalFile(:679 起,全仓唯一入口,由 :585 的 initialFilePath 触发) 读完字节就结束了,finally 里只复位 _isImporting。 grep 全仓 external_imports 只有这一个写入口、没有任何删除点;lib 里 6 处 .delete() 分别指向日志/壁纸/APK/托管图片,没有一条是它。 后果:一份含课程·教师·教室的明文副本长期躺在应用缓存里,用户每分享一次就多一份 (毫秒前缀不覆盖),只有系统存储紧张清缓存、或用户手动「清除缓存」才消失。 新增 utils/external_import_cleanup.dart:isExternalImportCacheCopy 必须判定 /cache/external_imports/ 这一段(FilePicker 那条路拿到的是用户自己的文件,一律不许删), deleteExternalImportCopy 删除失败静默返回 false —— 导入已经结束,不能因为清理失败把成功报成失败。 course_import_screen 的 finally 里调用它。 回归钉:test/utils/external_import_cleanup_test.dart 五条(真临时文件;含反斜杠路径、 用户文件不动、目录名伪装、文件已不存在)。 | 1 个月前 | |
perf(诊断): 首帧探针补三处口径,并切开页面外壳 首轮真机数据把「不是构建、是布局」这条结论钉住了(构建段只有 1~3ms,布局段 48~114ms),但也暴露了探针自己的三个盲区,这一轮补齐: 1. 绘制落在第几帧。真 Navigator.push 的第一帧只做构建与布局、不做绘制, 绘制在第二帧 —— 首轮 paintMs 全是 - 不是口径漏了,是事实如此。改成逐帧 跟进(一次性帧回调自链,最多 4 帧),绘制一出现就结算,并报 paintFrame。 用自链是因为 SchedulerBinding 没有移除持久帧回调的 API。 2. 布局跑了几趟。采样点只报第一趟的跨度,另加 layoutPasses / paintPasses: 布局贵有两种成因(一趟特别慢 / 来回跑好几趟),只看跨度分不开。 3. 页面外壳的恒定开销落在哪。新增 headerShell / bodyShell 两个采样点,与已有的 header / body 配对 —— 首轮五条 push 里外壳都占 39~47ms 且与内容无关, 这笔钱还没定性,下一轮切开。 调用点从 hyperos_page.dart 的 _buildHeaderContent / _buildBody 扩到 _buildHeaderShell 与 _buildBody 的两个层次。测试 7 条全过(含挂载守卫, 它盯的正是首轮栽过的那类「采样点挂到了不会被使用的分支上」;以及一条把 「绘制迟到」这个口径钉住的用例)。 回归:test/ui/hyperos/ 全目录 + 两个相关测试文件 → 291 通过、3 条失败, 那 3 条与动手前完全相同的既有红(都在本次没碰的玻璃文件里)。 | 1 个月前 | |
加(诊断): 转场按层计数,定位「返回比进入还慢」 上一轮实测留下一个解释不了的事实:同一条转场代码、同一类页面,**返回比进入还慢** (进入 7.5–9ms,返回 8.5–10.5ms),而返回时没有任何新内容要建。 illPaints=0 已经把「页面里的壁纸填充逐帧重画」排除掉了,剩三个嫌疑混在总耗时里 分不开:页面层被重新光栅化 / 淡出+压暗叠层每帧全屏重涂 / 右侧补底条。这个提交只做 一件事 —— 把它们分开计数,下次日志直接指出是哪一层。 改动(全部是只加计数,不改任何画面): * FramePerfProbe 新增 noteLayerPaint(name) 与窗口快照,窗口行多一个字段 layers=page:41,overlay:41,bleed:0(只列本窗口涨过的层); * 新增 _LayerPaintProbe([RenderProxyBox] 直通代理,常挂在树上,不破结构恒定 红线;探针未安装时只多一次布尔判断),插在三层上: - page —— 放在 RepaintBoundary **之内**。这是关键判据:计数 ≈ 帧数说明 缓存没起作用、整页在逐帧重光栅化;计数 ≈ 1 说明页面像素是复用的、贵在别处。 放在边界外会恒等于帧数,读不出任何东西; - overlay —— 淡出 + 压暗合成的那一层全屏纯色; - bleed —— 右侧视差补底条。 测试:frame_perf_probe 18 条、hyperos_navigation 16 条全绿(新增 layers=page:2 断言),analyze 0。 | 18 天前 | |
防止: corrupted color and settings data from crashing runtime The runtime had multiple unguarded hex color parsers and one log-settings decode path that could throw when persisted data was malformed. This change centralizes tolerant color parsing, hardens log settings decoding, and makes update checks stop after the first successful source instead of continuing background requests. Constraint: Persisted settings and imported backups may contain malformed user data Rejected: Leave invalid data handling to upstream storage cleanup | still leaves crash paths during app startup and screen rendering Rejected: Keep concurrent update source fetches | wastes network after an early successful result Confidence: high Scope-risk: narrow Reversibility: clean Directive: Reuse the shared hex color parser for any future user-controlled color fields instead of adding new raw int.parse paths Tested: flutter analyze; flutter test Not-tested: Manual import of intentionally corrupted backup data on a physical device | 6 个月前 | |
修(月视图): 按专业美学评审逐项修可读性与版式(P0 三条 + P1 两条) 用户口径:「专业美学角度评价一下这个页面的设计」→「全部修」。 评审是拿真机截图逐像素量出来的,所以每一项都有实测数字与修法。 P0-1 判墨粒度:整屏一个数 → 逐行查剖面 旧实现用壁纸主体带(屏幕 22%~72%)的平均亮度判 42 个格子的黑白。真机上 卡面亮度自上而下从 0.56 走到 0.23,同一个黑字的实测对比度因此从 12:1 摆到 5:1,月外日期(再叠两层 alpha)落到 1.97:1 —— 物理上不可读。 PageWallpaperLuminanceSampler 改采逐行剖面(与三带同一次解码,不多花一次 解码),页面按 6 行算出各自亮度传给网格,每格按自己那张卡的真实卡面判墨。 P0-2 弱化 ≠ 变淡 月外日期仍然要弱,但手段从"乘两个凭感觉的 alpha"改成"按对比度弱化": 推到刚好还读得出(3.2:1)为止。满强度本来就够不着目标时原样返回, 不偷偷减对比度。 P0-2 圆点 5dp → 8dp 5dp 在 393dp 屏上只占 1.5%,压在纹理复杂的照片上认不出来:实测课程点 1.01:1(等于没有)。现在 8dp + 按对比度压明度保色相(≥3:1)+ 卡面墨色描边。 旧实现的 outlined: true 取 scaffoldBackgroundColor,而独立页外壳里那是 transparent —— 从来没画出来过。 P0-3 拉杆 1.22:1 → 满 alpha 黑/白 拉杆是全页唯一教用户"这张表能上滑"的控件。旧实现取主体带墨色再乘 0.32 alpha,在它实际压着的那段水面(0.09)上只剩 1.22:1。现在按拉杆自己那段 壁纸判满 alpha 的黑/白(黑白对任一亮度的对比度都 ≥4.1:1)。 P0-3 状态栏图标极性 0.5 → 0.179 黑白对比度相等点是 L≈0.179,不是中灰 0.5。旧实现把 0.179~0.5 整段判给 白图标,而白图标在那一段恰恰是差的那档:真机顶部带 0.356 → 判白图标, 在右侧亮云(局部 0.738)上实测 1.33:1,5G / 信号 / 电量整排读不出来。 改后同一张壁纸判黑图标,局部 15.8:1。 P1 字阶与左轴收口 一页 9 级字号(含 11.5 / 9.5 / 13.5 这类与任何一级都不成比例的值)→ 收成 四级主阶 + 三个专用值,全部登记在 month_metrics.dart。左边线四条 (14.1 / 13.5 / 11.7 / 17.2dp)→ 一条 axisInset = 14,卡片按光学对齐外扩 2dp。 顺带修周次轨:「第 17 周」在 34dp 列里折成两行(旧注释说"34 放得下"是错的, 真机实测折了),改 40dp + FittedBox 兜底 + 上限一行。 P1 概览卡变成真的卡 旧实现给概览卡传的底色就是 colorScheme.surface,与面板逐位相同(实测都是 250,249,254)—— 那张"卡"在视觉上不存在,三个数字悬空浮着。同时两条竖分隔线 用 VerticalDivider 但 Row 没有交叉轴约束,实测高度 0,一条线都没画出来。 现在概览卡与列表卡同族同底色、分隔线显式给高。 另外修两处 alpha 弱化(0 值数字与图例圆点):alpha 会把墨与卡面的亮度差一起 吃掉,实测「考试 0」的圆点只剩 1.42:1,读成禁用控件;改成向卡面混色。 P1 玻璃坞切开卡片 面板列表项被底部药丸从中间切开(「1月9日」下面那行只剩一半),读起来是渲染 错误。加一条 28dp 的底部渐隐(IgnorePointer,不吃面板的手势)。 P2 实测否决:品牌行让位 评审建议"收起时把品牌行折进玻璃带,把 44dp 还给网格"。实测(393×851): 格子行高按宽度定(目标正方形,(393-25-40)/7 = 47.43),按高度预算能给的也 正好是 47.43 —— 上限由屏宽决定,不由高度决定。所以把那 44dp 还给正文后行高 一点不变,多出来的全进面板(390 → 434dp),而面板本来就有 58% 的行没有内容。 同时会让独立页与首页的顶栏高度分家(那正是 2026-10-20 用户报「为什么和日视图 周视图的顶栏不一样高」的原事故)。故不做,结论与实测数字写进 month_metrics.dart 的文件头留档。 测试 · test/utils/month_cell_ink_test.dart(12 例):逐行剖面查询、4.5:1 正文门槛、 弱化 3.2:1、圆点 3:1、拉杆判色。期望值按 WCAG 公式自己算,不复用被测实现里 的对比度函数 —— 否则测的是自我一致性。 · test/services/month_view_aesthetic_contract_test.dart(9 例):钉参数透传 (rowLuminances 恒为空或 6 项)、单左轴、概览卡有实体。为了让它真的抓得住 "页面漏传逐行亮度",加了 MonthViewScreen.debugSeedLuminance 测试入口 (测试宿主机没有壁纸,否则两条路径都返回空表、删掉转发也不红)。 · test/utils/home_page_background_luminance_test.dart 补 3 例状态栏极性。 · 每条新断言都做过反向验证:把实现改坏(逐行改回整屏、弱化改回乘 alpha、 圆点不压明度、拉杆退回 0.32、状态栏退回 0.5、删掉页面转发),确认测试真的 变红,再还原。 验证范围:test/utils/ + test/domain/ + 月视图相关 services 共 1048 例通过; dart analyze 干净;copy_style / dependency / screens_size / god_file 四条架构守卫 通过。唯一红的一条 home_page_backdrop_sample_key_test 来自另一条泳道在 timetable_screen.dart 的在途改动(该文件本轮未被本提交触碰)。 未做真机验证(本会话不控制实体设备),视觉结论仍待用户在真机上确认。 | 15 天前 | |
修(壁纸): 玻璃表面也要跟着壁纸一起糊——卡片与玻璃带底下不再透出原图 用户报(2026-10-21):设了壁纸模糊之后,课程卡片的玻璃、各种玻璃的底,透出来的 还是原本那张清晰壁纸。 ## 根因 壁纸层和玻璃层是**两条独立的糊**: - 壁纸层( homePageBackdropImageWidget)贴的是 σ_wall 的成品; - 玻璃表面(课程卡 / 顶栏玻璃带 / 日视图摘要替身卡)**不画**这层,它们从 PreblurredWallpaperCache 取另一张"按玻璃自己的磨砂量、从**原图**烤好的整屏 位图",再挖自己那一块贴。 后者完全没经过壁纸那层,所以玻璃里透出的是清晰原图。上一个提交只改了前者, 这是我的漏改。 ## 修法 高斯核方差可加:先 σ₁ 再 σ₂ 等价于单次 sqrt(σ₁² + σ₂²)。新增 composePreblurSigmas,把壁纸那层折进玻璃的烤图量,于是玻璃只需取**一张**位图, 不二次采样、不多占显存。 - resolveHomePreblurSigma / resolveCourseCardPreblurSigma 加**必填**参数 wallSigma。刻意做成必填而不是可选默认 0:这次的 bug 就是"少接一处、静默 退回原图",必填能让编译器把每个调用点都逼出来(实际正好揪出 6 处)。 - 6 个调用点全部接上:运行时(home_chrome_spec)、启动预热(primer)、 设置页预览(week_preview,两处各接)。预热与预览必须与运行时**同参**, 否则烤出来的不是同一个缓存条目 —— 预热白做、预览与真首页不一致。 ## 边界语义(都有用例钉住) - 壁纸 0(没糊):原样返回玻璃那层 → 现有行为逐像素不变。 - 卡片 0「清档」+ 壁纸没糊:仍是 0(出原图、不跑高斯),不破 2026-09-22 那条口径。 - 卡片 0 清档 + 壁纸糊了:变成 wallSigma —— 卡片背后本来就是糊壁纸。 - 合成结果仍夹在 0~24,滑杆上限不变。 - 2026-10-19「液态 chrome 优先于 gaussianCardsDrive」那条判定顺序不动(有用例防回归)。 实时那条路(LiquidGlassSurface 的 BackdropFilterLayer)采样的是屏幕上真实像素, 本来就带着壁纸那层模糊,不需要也不该再折一次。 ## 验证 - 新增 test/utils/preblur_sigma_compose_test.dart 11 例,期望值全部手推 (3/4→5、15/20→25、同值→sqrt2·σ)。 - **咬合探针已做**:把 home resolver 里的 wallSigma 改成 0(即复现漏改前行为), 两条用例立刻红;还原后全绿。 - 存量 home_startup_visual_primer_test.dart 5 处调用补 wallSigma: 0 (它们钉的就是"壁纸没开模糊"时的既有口径)。 - 受影响回归全过:primer 6 例、preblur 缓存 24 例、壁纸渲染 5 例、 周预览 15 例、合成 11 例;四条改动文件 analyze 无 issue。 - 未验:真机观感(玻璃里现在应当看到糊壁纸),本机无设备。 | 16 天前 | |
修(视觉): 墨色极性不许因为「只有视口变了」退回按主题猜 用户 2026-10-19 报:进设置时首页星期栏与「轻屿课表」标题闪一下白再闪回来, 不是每次都出现、一次之后就再也不出现。 根因:那几处 chrome 的墨色由「壁纸顶部那条带有多亮」决定,而亮度是异步采样 出来的(几十到几百毫秒)。采样键里带着**视口尺寸 + 取景 + 倍率**,视口一变 键就不命中,调用方随之回落到「按主题猜极性」—— 猜错就是一次可见的墨色翻面, 等新采样落地再淡回来(SoftGlassPolarityFade 的 280ms 交叉淡入)。上一次之后就 不再出现,因为采样结果被这份共享记住了。 改法:给共享加 resolve(key):精确键优先;不命中时**忽略视口**再比一次。 取景(alignX/alignY)与倍率仍然要求相等 —— 那两维会真的改变带子的平均值, 旧值必须作废(既有用例守着这条)。调用点行数保持不变(棘轮零余量)。 验证:dart analyze 干净;test/utils/home_wallpaper_luminance_share_test.dart 新增 3 条(只有视口变 → 沿用;换壁纸/改取景/改倍率 → 作废;没登记 → null), 连同 preblurred_wallpaper_glass、home_page_region_blur、test/architecture/ 共 96 条全绿。真机观感待用户确认。 | 17 天前 | |
补齐导入体积上限:备份恢复路径也加上,并抽出可测的共用读取 自查发现上一轮只修了 xlsx/表格一条路,data_transfer_screen 的备份恢复仍然是 withData: true 无上限——而备份是明文课表 JSON,比单个表格更容易超大。 抽出 lib/utils/import_file_reader.dart: - readImportFileBytes 先 File.length() 量大小,超限抛 ImportFileTooLarge, 之后才读字节。这是唯一能真正拦住的地方,因为 Android 内存不足是直接杀进程。 - 两条路径(表格、备份)现在共用同一段逻辑,不再各写一遍。 - formatByteBudget 统一文案里的体积显示。 测试补上真实断言:之前只测了判断函数,没测「读之前真的被拦住」。现在用真实 临时文件验证边界(恰好等于上限通过、超一字节拒绝)、并确认被拒文件在盘上 未被改动。 删掉 SpreadsheetImportService.exceedsFileSizeLimit / formatMaxFileSize: 职责已移到共用读取里,两处并存会让人以为还有第二条拦截路径。 | 1 个月前 | |
功能(课表配色): 导入后随时一键重刷全课表颜色的核心管线——课程不再只能靠重新导入换色。新增 course_recolor.dart 纯逻辑层: 重刷分组键改按共享课程名(与课程组编辑页同口径,维持「同名同色」不变式,不再沿用导入期的 name+teacher 键)、固定种子批次(同种子同课程列表逐色一致,是「切回上一套」的确定性基础)、导入原色快照记录(逐组存 color/textColor,含手工挑过的颜色,永远有路可回);applyRandomImportCourseColors 增加 groupKeyBuilder 可选参(默认不变,导入路径零影响)。新增按课表 profile 作用域隔离的方案历史持久化服务(种子/快照两类记录+导航指向,上限 20 套丢最旧并同步前移指向,坏 JSON/坏记录兜底)。TimetableProvider 新增 applyCourseRecolors 按 id 对位覆盖 color/textColor 并持久化+通知+更新桌面部件,无变化返回 0 不通知,其余字段不动。测试: 同名同色/种子确定性/文字色开关/深色组生效/快照往返含新课程保色/JSON 往返与坏数据、历史往返/作用域隔离/超限前移/坏数据跳过、provider 覆盖与持久化重读共 21 项 | 2 个月前 | |
全面国际化:轻屿课表文案、服务消息与运行日志多语言 补全中英日韩及繁体 ARB,新增服务消息/日志/导入结果本地化模块; 界面与原生诊断日志统一走 l10n,并补充 i18n 维护脚本。 Co-authored-by: Cursor <cursoragent@cursor.com> | 3 个月前 | |
修(超级岛): 展开详情列表对齐真机渲染——拖拽下移错格、课前假死开关、手柄误触 - 拖拽落位改直用 onReorderItem 的已校正索引,修向下拖一格等于没动 - 课前页收走阶段/进度/状态三个永不渲染的字段,列表与预览、真机一致 - 列表默认顺序改按提升态行序(与原生 promotedExpandedDetailDefaultOrder 同) - 拖动手柄铺满热区并吞掉单击,点手柄不再误切开关 - 说明文案按课前/课中拆分(六语言新增 liveExpandedDetailHint* 两键) - 字段可见集抽到 utils 层并补 7 项纯逻辑测试(独立期望值) | 22 天前 | |
fix(l10n): 「跟随系统」清空 Intl.defaultLocale,日期不再沿用上一个语言 syncIntlDefaultLocale(null) 原来直接 return。应用内从固定语言切回「跟随系统」时, MaterialApp.locale 回到 null(UI 用系统语言),但进程级 Intl.defaultLocale 仍是 上一个 App 语言,add_exam_screen 三处与 lan_edit_screen 一处的无 locale DateFormat 继续按旧语言输出——正是 2026-10-20 体检 #34 要修的那个 bug 的镜像(UI 中文、 日期英文)。清空全局值后 DateFormat 回落 systemLocale,与「locale = null 即系统」 口径一致。 测试:null 用例由「保持 ko」(把错误语义写成契约)改为断言 isNull;新增一条端到端 回路用例(systemLocale → ko → 跟随系统),断言复位后 DateFormat.yMMM() 的输出回到 systemLocale 那一份。定向 5/5 通过,analyze 0 issue。 | 17 天前 | |
fix(CI): 新闸门里那句中文 assert 文案触发了 CJK 棘轮 lib/utils/luminance_sample_gate.dart 的 assert 文案写成中文,而 tool/audit_cjk_hardcode.py 数的是**非注释行的 CJK 字面量**(assert 文案也算), 于是它以 NEW FILE ...: 1 判红 —— CI 会挂在这一步。 改成英文:仓里既有 assert 文案本来就是英文(如 hyperos_chip_row.dart:55 的 HyperosChipRow requires at least one label), assert 也永远不会出现在用户面前,没有本地化的必要。 验证:audit_cjk_hardcode.py --baseline 回到 **退出码 0**; 闸门 5 条单测仍全绿。 | 29 天前 | |
test(壁纸): 补 downloadToManagedImage client 生命周期回归测试 三条用例钉住上一次修复的语义:自建 client 必须流读完才关(含 send 失败 出口)、共享 client 绝不能被下载函数关。为让默认分支(client == null) 在测试里可达,给 managed_image_storage 加了 @visibleForTesting 的 managedImageOwnedHttpClientFactory 注入口——flutter_test 的假 HttpOverrides 会把真实 http.Client() 全部打成 400,没有这个口子 该分支在测试环境里不可达。 | 17 天前 | |
修(月视图): 补齐按行判墨的配套文件 —— 601b25dc 带进来的代码此前编译不过 601b25dc 是月视图的性能修复,但同一文件里混着另一条泳道「按行判墨」的改动, 路径限定提交无法按块挑选,于是把对方**未完成的代码**一起带进了仓库:那一版 month_grid.dart 已经调用 MonthDayCell(rowLuminance: ...),而 month_day_cell.dart 与它的两个新依赖(month_metrics.dart / utils/month_cell_ink.dart)都还没进仓库。 后果:HEAD 单独拿出来编译不过(月视图整片报错)。430b3e07 补了第一个文件, 本提交补齐剩下两件。 顺带修掉对方代码里三处会拦 pre-commit 的机械问题(不改变行为): - monthDotContrast = 3.0 → 3(prefer_int_literals); - monthCellInkFor(outsideMonth: false) 删掉 —— 该参数默认值就是 false,等价; - 圆点描边:outlineColor! 改为先落局部变量再判空。原写法删掉 ! 会报 argument_type_not_assignable(public field 不做类型提升)。 ⚠️ 协作教训(与 430b3e07 同一条,这里补齐):发现自己的提交把对方未完成的功能 一起带走时,必须**把对方那部分依赖全部补齐到能编译**,或者直接暴露问题等对方 收尾;只补一半会留下一个编译不过的 HEAD。 | 15 天前 | |
修(月视图): 按专业美学评审逐项修可读性与版式(P0 三条 + P1 两条) 用户口径:「专业美学角度评价一下这个页面的设计」→「全部修」。 评审是拿真机截图逐像素量出来的,所以每一项都有实测数字与修法。 P0-1 判墨粒度:整屏一个数 → 逐行查剖面 旧实现用壁纸主体带(屏幕 22%~72%)的平均亮度判 42 个格子的黑白。真机上 卡面亮度自上而下从 0.56 走到 0.23,同一个黑字的实测对比度因此从 12:1 摆到 5:1,月外日期(再叠两层 alpha)落到 1.97:1 —— 物理上不可读。 PageWallpaperLuminanceSampler 改采逐行剖面(与三带同一次解码,不多花一次 解码),页面按 6 行算出各自亮度传给网格,每格按自己那张卡的真实卡面判墨。 P0-2 弱化 ≠ 变淡 月外日期仍然要弱,但手段从"乘两个凭感觉的 alpha"改成"按对比度弱化": 推到刚好还读得出(3.2:1)为止。满强度本来就够不着目标时原样返回, 不偷偷减对比度。 P0-2 圆点 5dp → 8dp 5dp 在 393dp 屏上只占 1.5%,压在纹理复杂的照片上认不出来:实测课程点 1.01:1(等于没有)。现在 8dp + 按对比度压明度保色相(≥3:1)+ 卡面墨色描边。 旧实现的 outlined: true 取 scaffoldBackgroundColor,而独立页外壳里那是 transparent —— 从来没画出来过。 P0-3 拉杆 1.22:1 → 满 alpha 黑/白 拉杆是全页唯一教用户"这张表能上滑"的控件。旧实现取主体带墨色再乘 0.32 alpha,在它实际压着的那段水面(0.09)上只剩 1.22:1。现在按拉杆自己那段 壁纸判满 alpha 的黑/白(黑白对任一亮度的对比度都 ≥4.1:1)。 P0-3 状态栏图标极性 0.5 → 0.179 黑白对比度相等点是 L≈0.179,不是中灰 0.5。旧实现把 0.179~0.5 整段判给 白图标,而白图标在那一段恰恰是差的那档:真机顶部带 0.356 → 判白图标, 在右侧亮云(局部 0.738)上实测 1.33:1,5G / 信号 / 电量整排读不出来。 改后同一张壁纸判黑图标,局部 15.8:1。 P1 字阶与左轴收口 一页 9 级字号(含 11.5 / 9.5 / 13.5 这类与任何一级都不成比例的值)→ 收成 四级主阶 + 三个专用值,全部登记在 month_metrics.dart。左边线四条 (14.1 / 13.5 / 11.7 / 17.2dp)→ 一条 axisInset = 14,卡片按光学对齐外扩 2dp。 顺带修周次轨:「第 17 周」在 34dp 列里折成两行(旧注释说"34 放得下"是错的, 真机实测折了),改 40dp + FittedBox 兜底 + 上限一行。 P1 概览卡变成真的卡 旧实现给概览卡传的底色就是 colorScheme.surface,与面板逐位相同(实测都是 250,249,254)—— 那张"卡"在视觉上不存在,三个数字悬空浮着。同时两条竖分隔线 用 VerticalDivider 但 Row 没有交叉轴约束,实测高度 0,一条线都没画出来。 现在概览卡与列表卡同族同底色、分隔线显式给高。 另外修两处 alpha 弱化(0 值数字与图例圆点):alpha 会把墨与卡面的亮度差一起 吃掉,实测「考试 0」的圆点只剩 1.42:1,读成禁用控件;改成向卡面混色。 P1 玻璃坞切开卡片 面板列表项被底部药丸从中间切开(「1月9日」下面那行只剩一半),读起来是渲染 错误。加一条 28dp 的底部渐隐(IgnorePointer,不吃面板的手势)。 P2 实测否决:品牌行让位 评审建议"收起时把品牌行折进玻璃带,把 44dp 还给网格"。实测(393×851): 格子行高按宽度定(目标正方形,(393-25-40)/7 = 47.43),按高度预算能给的也 正好是 47.43 —— 上限由屏宽决定,不由高度决定。所以把那 44dp 还给正文后行高 一点不变,多出来的全进面板(390 → 434dp),而面板本来就有 58% 的行没有内容。 同时会让独立页与首页的顶栏高度分家(那正是 2026-10-20 用户报「为什么和日视图 周视图的顶栏不一样高」的原事故)。故不做,结论与实测数字写进 month_metrics.dart 的文件头留档。 测试 · test/utils/month_cell_ink_test.dart(12 例):逐行剖面查询、4.5:1 正文门槛、 弱化 3.2:1、圆点 3:1、拉杆判色。期望值按 WCAG 公式自己算,不复用被测实现里 的对比度函数 —— 否则测的是自我一致性。 · test/services/month_view_aesthetic_contract_test.dart(9 例):钉参数透传 (rowLuminances 恒为空或 6 项)、单左轴、概览卡有实体。为了让它真的抓得住 "页面漏传逐行亮度",加了 MonthViewScreen.debugSeedLuminance 测试入口 (测试宿主机没有壁纸,否则两条路径都返回空表、删掉转发也不红)。 · test/utils/home_page_background_luminance_test.dart 补 3 例状态栏极性。 · 每条新断言都做过反向验证:把实现改坏(逐行改回整屏、弱化改回乘 alpha、 圆点不压明度、拉杆退回 0.32、状态栏退回 0.5、删掉页面转发),确认测试真的 变红,再还原。 验证范围:test/utils/ + test/domain/ + 月视图相关 services 共 1048 例通过; dart analyze 干净;copy_style / dependency / screens_size / god_file 四条架构守卫 通过。唯一红的一条 home_page_backdrop_sample_key_test 来自另一条泳道在 timetable_screen.dart 的在途改动(该文件本轮未被本提交触碰)。 未做真机验证(本会话不控制实体设备),视觉结论仍待用户在真机上确认。 | 15 天前 | |
加(诊断): 一次性补齐转场各层 + 各玻璃面的逐帧计数 上一轮测出的真差别(同一套转场代码):从**设置列表**进页面 3.5~5.8ms/帧, 从**首页菜单**进页面 8.4~10.5ms/帧,两边的 page 都是 1(缓存有效)。 差别只剩「底下压着的是首页」这一条。为一次测完,本轮把所有剩余疑点一并装上, 不再一轮一轮加: * 新增 lib/utils/paint_probe.dart 的 PaintProbe(公共直通代理,探针未装时 只多一次布尔判断)。带 enabled 开关:零面积/全透明的层不该被计入 —— 上一轮 bleed 恒等于帧数就是这个坑(零宽色块照样走 paint); * 转场分桶:pageMoving / pageStill、overlayMoving / overlayStill、 bleedMoving / bleedStill。判据是「本页的 enter 是否在走」,用来直接回答 「静止的被覆盖页那层全屏蒙层是不是每帧都在重涂」; * 玻璃面:glassFrost(磨砂背板)、glassCard(课程卡)、glassLiquid (液体玻璃自绘层)、glassBand(顶栏模糊带)。用来回答「首页的玻璃是不是 在转场里逐帧重算」—— 设置列表页只有 glassFrost,首页还有 liquid/band。 读法:frames= 是这段有多少帧,某个计数 ≈ frames 就是它在逐帧重画;为 0 或 1 就是没动。全部只加计数,不改任何画面。 测试:hyperos_navigation + preblurred_wallpaper_glass + glass_capture_ratio 共 42 条全绿,analyze 0。 | 18 天前 | |
修(性能探针): 第二轮真机实测暴露两个口径 bug——预算被采成 6.2ms(-26% 阈值)、首页拆成两行 第二轮巡检数据完整了(11 页 / 2397 帧),反而把两个更隐蔽的口径问题照了出来。 两条都已修,并各补用例 + 咬合变异(现在 28/28 有牙齿)。 一、实测 vsync 采到面板不存在 6.2ms,预算被压 26% 判据块打出 budgetMs=6.2 nominalMs=8.3 observedVsyncMs=6.2。设备实际支持 60/90/120/144Hz(周期 16.67/11.11/8.33/6.94ms),6.2ms ≈ 161Hz **不是任何 一个真实模式** —— 而预算因此从 8.33 收紧到 6.20,掉帧阈值(预算×1.25)从 10.41ms 压到 7.75ms,把合格帧成批判成掉帧。那一轮 69.9% 的掉帧率大半是 这么来的假警报。 改法:PerfPageLedger 收一份 supportedRefreshRatesHz,只采纳「落在某个 支持模式周期 ±8% 内」的样本;空表 = 不知道,此时不加过滤(多一条规则就多 一处能猜错的地方,「不知道」不该被当成「都不像」)。门面把上报刷新率与 常见档位一起注入,容纳面板在档位间切换。 二、首页拆成 AppEntryScreen + home:day 两行 真机读数里 AppEntryScreen 单独占一行(802 帧 / 40s / fps=20),真正的首页 内容在 home:day(598 帧 / 8.3s)—— 同一页被拆成两行,两版本还未必按同样 比例分裂,那是假增减;更要紧的是启动期那一大段被记到了一个谁都不认识的 类名下。AppEntryScreen 正是 MaterialApp.home 建出来的根 widget。 改法:canonicalPageId 把它也收敛到 home:<view>(未接视图追踪器时退化成 home,不丢)。 顺带:三条 vsync 用例从 perf_probe_test 搬进 perf_page_ledger_test —— 它们是 **账本的**采样口径,放在门面层时咬合探针改坏账本根本跑不到它们(这条判据因此 一度是「改坏也全绿」的摆设,是咬合探针抓出来的)。 验证:探针用例 111 条全绿;架构守卫 61 条全绿;bash scripts/perf-probe-teeth.sh 28 条变异全部产出运行期红灯;dart analyze --fatal-infos 零问题;CJK 棘轮干净。 本轮仍只修探针自身,未改任何绘制/动画行为。 | 12 天前 | |
修(性能探针): 第二轮真机实测暴露两个口径 bug——预算被采成 6.2ms(-26% 阈值)、首页拆成两行 第二轮巡检数据完整了(11 页 / 2397 帧),反而把两个更隐蔽的口径问题照了出来。 两条都已修,并各补用例 + 咬合变异(现在 28/28 有牙齿)。 一、实测 vsync 采到面板不存在 6.2ms,预算被压 26% 判据块打出 budgetMs=6.2 nominalMs=8.3 observedVsyncMs=6.2。设备实际支持 60/90/120/144Hz(周期 16.67/11.11/8.33/6.94ms),6.2ms ≈ 161Hz **不是任何 一个真实模式** —— 而预算因此从 8.33 收紧到 6.20,掉帧阈值(预算×1.25)从 10.41ms 压到 7.75ms,把合格帧成批判成掉帧。那一轮 69.9% 的掉帧率大半是 这么来的假警报。 改法:PerfPageLedger 收一份 supportedRefreshRatesHz,只采纳「落在某个 支持模式周期 ±8% 内」的样本;空表 = 不知道,此时不加过滤(多一条规则就多 一处能猜错的地方,「不知道」不该被当成「都不像」)。门面把上报刷新率与 常见档位一起注入,容纳面板在档位间切换。 二、首页拆成 AppEntryScreen + home:day 两行 真机读数里 AppEntryScreen 单独占一行(802 帧 / 40s / fps=20),真正的首页 内容在 home:day(598 帧 / 8.3s)—— 同一页被拆成两行,两版本还未必按同样 比例分裂,那是假增减;更要紧的是启动期那一大段被记到了一个谁都不认识的 类名下。AppEntryScreen 正是 MaterialApp.home 建出来的根 widget。 改法:canonicalPageId 把它也收敛到 home:<view>(未接视图追踪器时退化成 home,不丢)。 顺带:三条 vsync 用例从 perf_probe_test 搬进 perf_page_ledger_test —— 它们是 **账本的**采样口径,放在门面层时咬合探针改坏账本根本跑不到它们(这条判据因此 一度是「改坏也全绿」的摆设,是咬合探针抓出来的)。 验证:探针用例 111 条全绿;架构守卫 61 条全绿;bash scripts/perf-probe-teeth.sh 28 条变异全部产出运行期红灯;dart analyze --fatal-infos 零问题;CJK 棘轮干净。 本轮仍只修探针自身,未改任何绘制/动画行为。 | 12 天前 | |
加(性能探针): 常驻页面级探针网——每页帧率 + 能耗代理 + 启动分段 + A/B 差分 用户反馈这一版新增的东西让真实体验大范围下降,要求「在所有页面加上探针」。 原有三件诊断件都答不了这个问题:frame_perf_probe 要人肉打点且按设计是临时件、 first_frame_probe 只看首帧、内存页只量内存;且没有能耗口径、没有两版本对比工具。 详情见 .agents/notes/implemented/process/2026-10-24-perf-page-probe-network.md。 做法: - 页面归属**零手工打点**:挂 NavigatorObserver,~40 条路由入口自动进表,以后新增页面 也不用记得打点。只补两处**不经过 Navigator** 的切换(首页日/周/月、玻璃坞内嵌页), 走监听持久化的 timetableHomeViewMode —— 因此不用往 2,815 行的热文件 timetable_screen.dart 里插打点。 - 匿名路由(本仓大量 HyperosPageRoute<void>)在首帧之后从 subtreeContext 有界回溯 改名,不让占位行吃掉最贵的首帧账;改名带代际校验,只改「当前仍开着的」那一段。 - **行名收敛**:首页经路由回来给 home、经视图切换给 home:week,两套名字会让差分表 把一页拆成两行、两版本还未必按同样比例分裂 → canonicalPageId 一律归到 home:<view>。 - **能耗代理而非功率**:未 root 实测 /sys/class/power_supply/*/current_now 与 Mali 频率全部 Permission denied,故给进程 CPU 时间 + RSS 增量 + 帧成本;报表与导出文本 都写明这不是瓦数/mAh。 - **探针不制造它要找的东西**:不调 getprop(那是主线程起进程)、不同步列目录、 无新帧不写盘、CPU/RSS 按 250ms 采样而非每帧读。 - **正式版零开销在装配处**:install 在 release 直接 return,navigatorObservers 用 条件元素 —— 那一侧连对象都不建,不是「调用很快」。 - 出口:报表页(排行 / 启动分段 / 判据三栏 / 导出 JSON+TXT)。两条入口都避开正在被 并行会话改写的 6 份 ARB:调试深链 settings/live/perf-report + 已门控的诊断页; 报表页文案**全英文**,以不顶爆 CJK 硬编码棘轮(新文件加中文会顶爆 CI)。 - tool/perf_diff.dart:两份快照按页配对出差分表(文本 + CSV),带三条防骗口径 —— 样本下限(小样本不进恶化名单)、缺读数为 null 而非 0、两侧判据不一致时先出警告。 验证: - 94 条纯逻辑用例全绿(test/utils/perf_{page_ledger,proc_reader,startup,probe,diff}_test.dart)。 - **咬合探针 22 条变异全部产出运行期红灯**(bash scripts/perf-probe-teeth.sh): 逐条改坏判据(fps 分子、vsync 收紧、comm 解析、行名收敛、样本下限、缺读数…), 每条都必须红;三档判定区分「有牙齿 / 改坏还全绿 / 编译错不算证据」。 过程中它抓到两处真问题:跨度判据与单侧分栏各有一条没牙齿,已补用例。 - 动画时长棘轮给 lib/utils/perf_probe.dart +1(那处是**采样周期**,属逻辑时间; 跟着转场倍率缩放会让 2.5× 时每 100ms 读一次 /proc,诊断件反过来给自己加负载)。 该棘轮改动做了反向验证:配额改 0 → 守卫红,还原 → 绿。 - test/architecture/ 61 条全绿;dart analyze --fatal-infos 对本批全部文件零问题。 真机采集与出包未在本轮进行(用户明令「先把探针加上」;构建由 Windows 侧执行)。 | 12 天前 | |
加(性能探针): 常驻页面级探针网——每页帧率 + 能耗代理 + 启动分段 + A/B 差分 用户反馈这一版新增的东西让真实体验大范围下降,要求「在所有页面加上探针」。 原有三件诊断件都答不了这个问题:frame_perf_probe 要人肉打点且按设计是临时件、 first_frame_probe 只看首帧、内存页只量内存;且没有能耗口径、没有两版本对比工具。 详情见 .agents/notes/implemented/process/2026-10-24-perf-page-probe-network.md。 做法: - 页面归属**零手工打点**:挂 NavigatorObserver,~40 条路由入口自动进表,以后新增页面 也不用记得打点。只补两处**不经过 Navigator** 的切换(首页日/周/月、玻璃坞内嵌页), 走监听持久化的 timetableHomeViewMode —— 因此不用往 2,815 行的热文件 timetable_screen.dart 里插打点。 - 匿名路由(本仓大量 HyperosPageRoute<void>)在首帧之后从 subtreeContext 有界回溯 改名,不让占位行吃掉最贵的首帧账;改名带代际校验,只改「当前仍开着的」那一段。 - **行名收敛**:首页经路由回来给 home、经视图切换给 home:week,两套名字会让差分表 把一页拆成两行、两版本还未必按同样比例分裂 → canonicalPageId 一律归到 home:<view>。 - **能耗代理而非功率**:未 root 实测 /sys/class/power_supply/*/current_now 与 Mali 频率全部 Permission denied,故给进程 CPU 时间 + RSS 增量 + 帧成本;报表与导出文本 都写明这不是瓦数/mAh。 - **探针不制造它要找的东西**:不调 getprop(那是主线程起进程)、不同步列目录、 无新帧不写盘、CPU/RSS 按 250ms 采样而非每帧读。 - **正式版零开销在装配处**:install 在 release 直接 return,navigatorObservers 用 条件元素 —— 那一侧连对象都不建,不是「调用很快」。 - 出口:报表页(排行 / 启动分段 / 判据三栏 / 导出 JSON+TXT)。两条入口都避开正在被 并行会话改写的 6 份 ARB:调试深链 settings/live/perf-report + 已门控的诊断页; 报表页文案**全英文**,以不顶爆 CJK 硬编码棘轮(新文件加中文会顶爆 CI)。 - tool/perf_diff.dart:两份快照按页配对出差分表(文本 + CSV),带三条防骗口径 —— 样本下限(小样本不进恶化名单)、缺读数为 null 而非 0、两侧判据不一致时先出警告。 验证: - 94 条纯逻辑用例全绿(test/utils/perf_{page_ledger,proc_reader,startup,probe,diff}_test.dart)。 - **咬合探针 22 条变异全部产出运行期红灯**(bash scripts/perf-probe-teeth.sh): 逐条改坏判据(fps 分子、vsync 收紧、comm 解析、行名收敛、样本下限、缺读数…), 每条都必须红;三档判定区分「有牙齿 / 改坏还全绿 / 编译错不算证据」。 过程中它抓到两处真问题:跨度判据与单侧分栏各有一条没牙齿,已补用例。 - 动画时长棘轮给 lib/utils/perf_probe.dart +1(那处是**采样周期**,属逻辑时间; 跟着转场倍率缩放会让 2.5× 时每 100ms 读一次 /proc,诊断件反过来给自己加负载)。 该棘轮改动做了反向验证:配额改 0 → 守卫红,还原 → 绿。 - test/architecture/ 61 条全绿;dart analyze --fatal-infos 对本批全部文件零问题。 真机采集与出包未在本轮进行(用户明令「先把探针加上」;构建由 Windows 侧执行)。 | 12 天前 | |
加(发版门禁): 「本次重点」必须能认回条目,否则 tag 前就红 发 v2.1.4.1 时我把重点第三条写成「转场掉帧」,条目里其实是「转场更顺」。解析器 认不回来时**不会报错**,只是把这条降级成 kind=other、label 为空——弹窗与更新页上 就成了一条没有分类标签的灰色重点。而当时的门禁只 grep '^>.*本次重点' 查「有没有 写」,照样 PASS,所以问题是我发布之后才被用户发现的。 根因两条: 1. 重点行我按自己的理解另起说法,而不是抄条目里的原话; 2. 检查只覆盖「写了没」,没覆盖「认不认得回来」。 这次把它变成门禁,三处兜住: - 解析器新增 ReleaseNotesDigest.unresolvedHighlights(唯一真源: kind == other && text == headline 即认不回来),文档写明降级后果; - 新增 tool/check_release_highlights.dart:用**同一个解析器**复核当前 pubspec 对应的说明文件,认不回来逐条打印原文并 exit 1;release_preflight.sh 接上这条 (dart 不在 PATH 时降级 WARN); - test/docs/release_notes_format_test.dart 加断言:当前版本的每个重点都必须 认回条目(CI 全程兜住,不依赖人记得跑 preflight)。 顺带修 preflight 里一个我自己埋的坑:把 dart run 接进管道后 $? 取的是 sed 的 退出码,门禁会永远 PASS——现在单独取退出码再展示。 回归:test/utils/release_notes_digest_test.dart 加 2 条(「转场掉帧」必须被判为 认不回来;换成「转场更顺」后 unresolvedHighlights 为空)。实测好文件 exit 0、 坏文件 exit 1 并点名「转场掉帧」;release_preflight.sh 输出 「PASS 「本次重点」3 条全部认回条目:…」。26 个相关用例全绿,analyze 0 issue。 docs/RELEASE.md 的写法规则同步:重点行优先抄条目原话,并给出自检命令。 | 17 天前 | |
修禁用按钮字/底撞色:字色改为从实际底色推导(跨全部主题色) 用户报「正在使用」那个灰按钮上的字几乎看不见。实测确认是配色缺陷,非个案: 禁用底色随主题色走(resolveThemeSeedDisabled,seed 向白/向 #242424 混 0.7), 禁用字色却不跟——次要/破坏性按钮用写死的 Miuix 灰 #B2B2B2,主色按钮用 resolveThemeSeedDisabledInk(浅色返回近白 #FCFCFC,那条本是给开关 thumb 调的)。 两个不同源的颜色必然撞色,中性灰/近黑 seed 撞得最狠。浅色模式实测: #3482FF → #C2DAFF 上 1.49:1 #5CA502 → #CEE4B3 上 1.55:1 #F5C518 → #FCEEBA 上 1.82:1 #808080 → #D9D9D9 上 1.50:1 #2A2A2A → #BFBFBF 上 1.15:1(用户所用) #000000 → #B3B3B3 上 1.01:1 主色按钮更差:近白压在上述底色上仅 1.33–1.79:1。影响全 App 灰掉的按钮。 改法:新增 disabledInkOn(background, {targetContrast = 4.5})——从实际底色推字色, 取刚好达标的那一档(底色亮往黑混、暗往白混,到 4.5:1 就停),既可读又保留 muted 感,不会一律压成纯黑。HyperosButton 三个实底 variant(primary / secondary 非 frosted / destructive)的 disabledFg 改为现算。 两个实现要点: - 必须两个方向都试。第一版按 luminance>0.5 选黑/白,在 #B3B3B3(luminance 0.451) 上选白,往白混只会更近,怎么混都到不了 4.5,最后回落纯白 2.11:1。改为 toward(black) ?? toward(white) 依次尝试、各取最小达标混比,最坏 4.51:1。 - 附带好处:字色从带色底色混出,带着主题色相(蓝 seed 得偏蓝灰 #525B6B), 不是与主题无关的 foreign gray。 毛玻璃上的两个禁用按钮未改:底色是半透明白、合成色取决于背后壁纸,没有可推导的 实底,代码里已注释标明以免后人误当漏改。 测试:33/33。新增两条 testWidgets 直接走生产路径(ThemeSeedScope + HyperosColors.disabledSecondary),覆盖 11 个 seed × 明暗两模式断言 ≥4.5:1。 特意不在测试里重抄 accent→null 的回落(深色近黑 seed 按设计返回 null)——第一版 就是这么写的,直接 NPE 漏掉了该分支,改为复用生产取色入口。 另有一条回归锚点把旧固定灰的 <3:1 钉住。 | 1 个月前 | |
fix(平台通道): 71 处原生调用加默认超时,主线程卡住不再永久挂起 平台通道的回包由原生主线程派发(官方口径:setMethodCallHandler 回调跑在 主线程)。主线程一卡——冷启动、GC、原生 handler 内跑重活——Dart 侧的 await 就永远不完成,且没有超时、没有取消、没有恢复路径。实测本仓 71 处 invokeMethod / invokeListMethod / invokeMapMethod 全部裸调,零超时。 fair_memory 那条尤其危险:它是反向的(原生等 Dart 回执),Dart 卡住等于 Android 低内存 killer 流程卡住。 超时做在通道这一层而不是 71 个调用点上:调用点逐个补 .timeout() 的 diff 巨大且易漏,而通道声明只有 21 处。新增 TimedMethodChannel(MethodChannel 子类)覆写三个 invoke 方法,全仓一次覆盖,以后新写的通道用它就自动带上限。 超时时抛 PlatformException(code: 'TIMEOUT') 而不是 TimeoutException: 抛 TimeoutException 会让只捕获 PlatformException 的调用点漏接(实测 18 处), 另有 13 处调用点就近没有 catch —— 那会把「安静卡住」换成「凭空多出 31 个 未处理异步错误」。从 Dart 视角看超时与「原生调用失败」是同一件事:没拿到 可用回包,抛 PlatformException 让既有 catch 原样生效,零改动适配。 默认 10s:真机上这些调用是毫秒级到几百毫秒,留 10s 是为了让「原生真卡了」 与「设备正忙」分得开,几十毫秒阈值会把正常调用误杀。 测试用「永不完成的 Future」把「安静卡死」变成确定性失败,覆盖正常回传、 原生报错不被改写、回 null 不算超时、三个方法都有上限,以及那条 「不抛 TimeoutException」的约定。 | 1 个月前 | |
删(壁纸): Wallhaven 图源整体移除;画质不再可选,恒按最高档下载 用户拍板「去掉这个渠道只要 bing」「清晰度只保留最高,不要留下选项」。 * Wallhaven 三件套(模型/服务/图库页)、图源与换源枚举、图源世代号、 Wallhaven 台账与缓存挂靠、六语言词条全部删除。 * BingWallpaperResolution 降级为缺档保底阶梯:画质恒按 high(1440×3200 下限 + 按屏抬升),setResolution/分辨率存档键删除;缺档退一档重试的 韧性原样保留,退档提示文案随选项一起删。 * DailyWallpaperService 保留(吞意外 + 结局翻译两个职责仍在),内部分发 收敛为 Bing 单路;按天判据退回纯 lastAutoAppliedDate。 * 旧数据不迁移:台账里的 wh: 条目随封顶清理自然挤出,prefs 旧键不再被读。 * 设置页图源胶囊与图库页画质胶囊消失;六语言词条纯行删除(-193 行, 不重排其余内容)。 测试:删 2 个 wallhaven 测试文件;重写 daily_wallpaper_service_test (单源口径);改 5 个测试的档位/端点断言。壁纸链路 10 文件 + 架构守卫 共 133 例全绿。 | 22 天前 | |
修复(壁纸最近使用): 批量补记丢掉缩放,切回时放大被静默重置 rememberWallpaperHistoryBatch 逐条转调 pushWallpaperHistory 时只传了 key/alignX/alignY/usedAt,没有 scale —— 而 pushWallpaperHistory 的签名里 根本没有 scale 参数,新建条目一律取默认值 1。 生产路径就在 settings_home_backdrop_flow.dart:224:切换壁纸时要同时补记 「被换下的那一张」和「新选中的那一张」,两条都走这个批量口, 于是历史里存的 scale 永远是 1。 后果与模型层的承诺直接冲突: - WallpaperHistoryEntry.scale 的文档写着「切回最近使用时连缩放一起还原 —— 它是当时那个取景的一部分,只还原位置不还原缩放等于取景被改了一半」; - applyWallpaperHistoryEntry 也照做(homePageWallpaperScale: entry.scale), 读的是一个恒为 1 的值。 用户放大过的壁纸一旦被换下,再点回来看的就是 1:1 的取景, 而且界面上没有任何提示说明缩放被改了。 pushWallpaperHistory 增加 scale 形参(默认 1,与旧数据缺省一致), 批量补记透传 entry.scale。新增用例断言批量后 scale/usedAt/对齐都保留。 | 1 个月前 | |
新增(判墨测试): 补上判墨核心与接线契约的 33 条用例(上一条提交漏带新文件) 上一条 e71c3b26 用路径限定提交,只带走了 10 个已跟踪文件,这条把同批的 5 个新文件补上: - lib/utils/card_surface_ink.dart 判墨唯一入口(卡面亮度合成 + 对比度选色) - lib/utils/wallpaper_luminance_profile.dart 壁纸逐行亮度剖面 - lib/widgets/timetable/day_view/measured_screen_rect.dart 量卡片屏幕矩形(量化后重建) - test/utils/card_surface_ink_test.dart 27 例纯逻辑(含两张截图的实测数值反例) - test/utils/day_agenda_ink_wiring_test.dart 6 例接线契约 内容与 e71c3b26 的说明一致,不重复。全部 33 例通过,flutter analyze 无问题。 | 16 天前 | |
修复(小组件): 「色条+文字」的文字压暗档 0.10→0.13,收紧色条与课程名的亮度差(用户反馈「字和色块不是一个颜色」) 浅色卡片上色条按相对亮度 Y≤0.24 压暗、文字按 Y≤0.10 压得更暗,同一色相被摆到两个亮度档:104 色预设里 98 色出现「条比字亮」,最多差 2.45 倍(例 #5EEAD4 → 色条 #149883 / 文字 #0D6457)。旧档对状态胶囊底还有 5.79:1、对白卡 7.00:1,而 AA 只要 4.5:1,多出来的余量全变成了观感代价。 放宽到 0.13:胶囊底(#E0EAFF,最严的那个底)4.78:1,白卡 5.83:1,条/字最大亮度差收窄到 1.89 倍;再抬到 0.14 只剩 4.52:1、0.16 就跌破 4.5,没有继续放宽的余地。 Dart 与 Kotlin 两侧常量同步;两侧各加一条防退化守卫(条/字亮度差 ≤2 倍,旧档 0.10 会红)。深色底与渐变底的同色问题方向相反、差距较小,本批不动。 | 1 个月前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 17 天前 | ||
| 2 个月前 | ||
| 16 天前 | ||
| 17 天前 | ||
| 30 天前 | ||
| 16 天前 | ||
| 1 个月前 | ||
| 16 天前 | ||
| 1 个月前 | ||
| 17 天前 | ||
| 2 个月前 | ||
| 22 天前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 18 天前 | ||
| 6 个月前 | ||
| 15 天前 | ||
| 16 天前 | ||
| 17 天前 | ||
| 1 个月前 | ||
| 2 个月前 | ||
| 3 个月前 | ||
| 22 天前 | ||
| 17 天前 | ||
| 29 天前 | ||
| 17 天前 | ||
| 15 天前 | ||
| 15 天前 | ||
| 18 天前 | ||
| 12 天前 | ||
| 12 天前 | ||
| 12 天前 | ||
| 12 天前 | ||
| 17 天前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 22 天前 | ||
| 1 个月前 | ||
| 16 天前 | ||
| 1 个月前 |