本次更新包含: 1. 新增Windows平台全流程适配:后台任务、通知服务、电池服务兼容处理 2. 国行设备扫码降级方案:GMS检测+ZXing原生扫码fallback 3. Android 15 edge-to-edge 适配与废弃属性兼容 4. 液态玻璃渲染性能优化,Android端降级为standard模式 5. 工作台模式统一适配全平台,移除鸿蒙端冗余判断 6. 更新依赖与插件配置,新增sqlite3_flutter_libs支持 7. 修复通知服务初始化问题,完善Windows平台配置 8. 优化数据管理服务,新增Windows平台跳过逻辑 9. 修复隐私协议页面样式与文案更新 10. 升级应用版本号至6.6.29+2606272
2836 lines
193 KiB
Markdown
2836 lines
193 KiB
Markdown
# Changelog
|
||
|
||
所有重要变更均记录于此文件。格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/)。
|
||
|
||
> 保留最近 14 个版本(v6.101.0 ~ v6.120.0)。更早版本(v6.87.0 ~ v6.100.0)的特性已合并进软件特性功能文档,详见文末归档概览。
|
||
|
||
***
|
||
|
||
## [v6.149.0] - 2026-06-30
|
||
|
||
### 🔒 修复 Google Play 数据安全表单账号删除链接被拒问题
|
||
|
||
#### 背景
|
||
Google Play 审核反馈:数据安全表单中填写的账号删除链接 `https://s2ss.com/agreements/privacy-rights.html?lang=en&tab=delete` 未链接到有效页面,且页面未明确提及应用或 Google Play 商品详情中指定的实体(应用名称、开发者/公司)。
|
||
|
||
#### 改动内容
|
||
1. **优化 `docs/toolsapi/public/agreements/privacy-rights.html` 顶部头部区域**
|
||
- 将标题图标从 emoji 🛡️ 替换为应用图标 `icon_120x120.png`,提升品牌识别度
|
||
- 新增开发者名称展示:**`Micro storm`**(中文显示“开发者:Micro storm”,英文显示“Developer: Micro storm”)
|
||
- 更新应用名称展示:
|
||
- 中文:`闲言 - 创作卡片·笔记分享`
|
||
- 英文:`Xianyan - Notes and records`
|
||
- 顶部合规提示文案中同步明确标注开发者实体 `Micro Storm(微风暴工作室)` 及新的应用名称
|
||
- 头部默认展示应用名称、包名 `apps.xy.xianyan` 及开发者名称,确保 Google Play 审核可直接识别应用实体
|
||
|
||
2. **在“状态查询”标签页增加隐私政策链接**
|
||
- 新增提示文案,引导用户查看《隐私政策》/ Privacy Policy
|
||
- 链接指向同目录下的 `privacy-policy.html`,在新标签页打开
|
||
- 支持中英双语切换
|
||
|
||
3. **修复 `vogov.cn` 页面 API 接口指向问题**
|
||
- `vogov.cn` 为纯静态站点,无后端 API,页面原有的相对路径请求会指向 `vogov.cn/api/...` 导致报错
|
||
- 修改 `API_BASE`:当页面托管在 `vogov.cn` / `www.vogov.cn` 时,自动将请求转发到 `https://s2ss.com`
|
||
- `s2ss.com` / `tools.wktyl.com` 下仍使用相对路径,保持同域请求避免 `__token__` 响应头读取问题
|
||
- 已验证 `s2ss.com` 接口已配置 `Access-Control-Allow-Origin: https://vogov.cn`,支持跨域
|
||
|
||
4. **保留并优化“注销账号”标签页(`tab=delete`)**
|
||
- 继续使用 `https://vogov.cn/agreements/privacy-rights.html?lang=en&tab=delete` 作为 Google Play 数据安全表单账号删除链接
|
||
- 页面已包含账号登录注销、状态查询、邮件申请注销等完整功能
|
||
- 明确说明注销后果及删除的数据范围
|
||
|
||
5. **移除独立账号注销页面及相关入口**
|
||
- 删除 `docs/toolsapi/public/agreements/account-deletion.html`
|
||
- 移除 `privacy-rights.html` 和 `index.html` 中指向 `account-deletion.html` 的链接
|
||
|
||
6. **部署到 `vogov.cn` 域名**
|
||
- 将更新后的 `privacy-rights.html`、`index.html` 及应用图标 `icon_120x120.png` 同步到 `/www/wwwroot/www.vogov.cn/agreements/`
|
||
- 从 `vogov.cn/agreements/` 目录删除 `account-deletion.html`
|
||
- 验证 `https://vogov.cn/agreements/privacy-rights.html?lang=en&tab=delete` 返回 HTTP 200,图标资源可正常访问
|
||
|
||
#### 后续操作
|
||
- 请登录 Google Play 管理中心,将数据安全表单中的账号删除链接更新为:
|
||
`https://vogov.cn/agreements/privacy-rights.html?lang=en&tab=delete`
|
||
(`vogov.cn` 域名在 Google Play 自动检测中可正常识别)
|
||
- 已同步部署到 `vogov.cn/agreements/` 目录并验证可访问
|
||
|
||
***
|
||
|
||
## [v6.148.0] - 2026-06-29
|
||
|
||
### 🔧 修复 Windows 平台运行时错误 + 数据库迁移修复
|
||
|
||
#### 背景
|
||
编译运行 Windows 桌面端后,日志中出现多个运行时错误:
|
||
1. `SqliteException: table sentences has no column named is_liked` 反复出现(约20次/分钟)
|
||
2. `PlatformException: GetSystemPowerStatus() failed` — BatteryInfoService/BatteryOptimizationService 在无电池的 Windows 桌面崩溃
|
||
3. `MissingPluginException: No implementation found for method checkPendingManageStorage` — DataManagementService 未适配 Windows
|
||
4. `UnimplementedError: No implementation found for workmanager on this platform` — BackgroundTaskService 在 Windows 端崩溃
|
||
5. `Invalid argument(s): Windows settings must be set when targeting Windows platform` — 本地通知服务缺少 WindowsInitializationSettings
|
||
|
||
#### 任务1:修复数据库 V8 迁移 is_liked 列名错误
|
||
|
||
##### 根因
|
||
V8 迁移使用 Dart 字段名 `isLiked` 作为 SQL 列名,但 Drift ORM 生成 SQL 列名 `is_liked`(snake_case)。导致:
|
||
- V8 实际创建了 `isLiked` 列而非 `is_liked`
|
||
- Drift 查询访问 `is_liked` 时抛出 SqliteException
|
||
|
||
##### 改动文件
|
||
**`lib/core/storage/database/app_database.dart`**
|
||
- V8 迁移:`isLiked` → `is_liked`(修正 SQL 列名)
|
||
- 新增 V22 迁移:修复已有数据库,检测遗留 `isLiked` 列并迁移数据到 `is_liked`,若 `is_liked` 不存在则添加
|
||
- `schemaVersion` 从 21 升级到 22
|
||
- `onUpgrade` 中添加 V22 迁移调用
|
||
|
||
#### 任务2:修复 Windows 平台服务初始化
|
||
|
||
##### 改动文件
|
||
**`lib/core/services/device/battery_info_service.dart`**
|
||
- `init()` 添加 `pu.isWindows` 平台跳过,避免 `GetSystemPowerStatus()` 异常
|
||
|
||
**`lib/core/services/device/battery_optimization_service.dart`**
|
||
- `init()` 添加 `pu.isWindows` 平台跳过
|
||
|
||
**`lib/app/services/data_management_service.dart`**
|
||
- `initialize()` 守卫条件新增 `pu.isWindows`,避免 MissingPluginException
|
||
|
||
**`lib/core/services/background/background_task_service.dart`**
|
||
- `init()` 添加 `pu.isWindows` 平台跳过,避免 workmanager UnimplementedError
|
||
|
||
#### 任务3:修复本地通知服务 Windows 初始化
|
||
|
||
##### 改动文件
|
||
**`lib/core/services/notification/notification_init_stub.dart`**
|
||
- `buildNotificationInitSettings` 新增 `windowsSettings` 可选参数
|
||
- `InitializationSettings` 构造传入 `windows` 参数
|
||
|
||
**`lib/core/services/notification/local_notification_service.dart`**
|
||
- 新增 `WindowsInitializationSettings`(appName='闲言', appUserModelId='apps.xy.xianyan', guid=...)
|
||
|
||
**`lib/core/services/notification/notification_service.dart`**
|
||
- 同上,新增 `WindowsInitializationSettings`
|
||
|
||
**`lib/features/file_transfer/services/notification_service.dart`**
|
||
- 同上,新增 `WindowsInitializationSettings`
|
||
|
||
#### 影响范围
|
||
- Windows 桌面端所有运行时异常已消除
|
||
- 数据库 is_liked 列修复对所有平台生效
|
||
- Android/iOS/macOS 行为不受影响
|
||
|
||
***
|
||
|
||
## [v6.147.0] - 2026-06-27
|
||
|
||
### 🎨 句子卡片点赞/收藏按钮重设计 + 修复 Android GlassQuality 性能预算超限
|
||
|
||
#### 背景
|
||
用户反馈主页句子广场卡片存在以下问题:
|
||
1. 收藏按钮已收藏态用橙色填充,视觉过重,与 iOS 26 液态玻璃风格不统一
|
||
2. 点赞/收藏按钮偏大,占用过多横向空间
|
||
3. 未点赞/未收藏时按钮无明显边框轮廓,用户难以辨识可点击区域
|
||
4. 运行 Android 真机时报错 `[ERR-MQVJCQ4I] GlassQuality.premium performance budget exceeded`
|
||
5. 卡片统计行中点赞数/收藏数 icon 与右侧操作按钮、详情 Sheet 重复显示
|
||
|
||
#### 任务1:收藏按钮改为透明毛玻璃风格
|
||
|
||
##### 改动文件
|
||
**`lib/features/home/presentation/home_sentence_card.dart`**
|
||
- `LiquidGlassActionButton` 组件新增两个字段:
|
||
- `useGlassWhenActive`(bool):激活态是否使用透明毛玻璃风格(而非渐变填充)
|
||
- `inactiveBorderColor`(Color?):未激活态边框颜色(可选)
|
||
- build 方法支持两种激活态风格:
|
||
- `useGlassWhenActive=false`(默认):渐变填充(原行为,点赞按钮)
|
||
- `useGlassWhenActive=true`:透明毛玻璃(收藏按钮),背景透明 + 启用 BackdropFilter + activeColor 边框/前景
|
||
- 收藏按钮调用方修改:
|
||
- `activeColor` 从橙色 `Color(0xFFFF9F0A)` 改为 `ext.textSecondary`(次要色)
|
||
- 传入 `useGlassWhenActive: true`
|
||
- 传入 `inactiveBorderColor: ext.textSecondary.withValues(alpha: 0.6)`
|
||
|
||
##### 影响范围
|
||
- 收藏按钮已收藏态:透明毛玻璃 + 次要色边框/前景,视觉轻盈统一
|
||
- 收藏按钮未收藏态:次要色边框轮廓,清晰可辨识
|
||
|
||
#### 任务2:点赞/收藏按钮缩小 + 左移
|
||
|
||
##### 改动文件
|
||
**`lib/features/home/presentation/home_sentence_card.dart`**
|
||
- 按钮 padding:`horizontal 13 → 9, vertical 7 → 4`
|
||
- icon size:`14 → 12`
|
||
- icon 与文字间距:`6 → 4`
|
||
- 文字字号:`12 → 11`
|
||
- 按钮间距:`AppSpacing.sm → AppSpacing.xs`(两处)
|
||
|
||
##### 影响范围
|
||
- 按钮更紧凑,留出更多空间给作者名和工具图标
|
||
- 整体操作行视觉更精致
|
||
|
||
#### 任务3:点赞按钮增加未激活态边框
|
||
|
||
##### 改动文件
|
||
**`lib/features/home/presentation/home_sentence_card.dart`**
|
||
- 点赞按钮调用方传入 `inactiveBorderColor: ext.textSecondary.withValues(alpha: 0.6)`
|
||
- 配合 v6.144.0 已加粗的未激活态边框(1.2px),用户可清晰看到未点赞按钮的可点击区域
|
||
|
||
#### 任务4:修复 GlassQuality.premium performance budget exceeded 报错
|
||
|
||
##### 问题分析
|
||
- `liquid_glass_widgets` 包内置 `GlassPerformanceMonitor`,在 debug/profile 模式下监控光栅帧时长
|
||
- 当 `GlassQuality.premium` 持续超过 16ms 光栅预算(60 帧 ≈ 1 秒),抛出 `FlutterError`
|
||
- Android 设备 GPU 性能参差不齐,真机/模拟器均可能无法达到 premium 预算
|
||
- 原有 `GlassQualityResolver` 仅对 Intel Mac 降级,Android 保持 premium
|
||
|
||
##### 解决方案
|
||
Android 端默认降级为 `GlassQuality.standard`(轻量着色器),视觉效果接近 premium,但渲染开销降低 5-10 倍。
|
||
|
||
##### 改动文件
|
||
**`lib/core/utils/platform/glass_quality_resolver.dart`**
|
||
- `quality` getter 新增 Android 分支:`isAndroid` → `GlassQuality.standard`
|
||
- `isHighPerf` getter 更新:`!isAndroid && !_isIntelMac`
|
||
- 文件头部注释补充 2026-06-27 更新说明
|
||
|
||
##### 影响范围
|
||
- **Android 真机/模拟器**:使用 standard 质量,流畅运行无性能预算告警
|
||
- **Intel MacBook**:保持 standard(原行为)
|
||
- **Apple Silicon (arm64) / iOS**:保持 premium(原行为)
|
||
- release 模式下 `GlassPerformanceMonitor` 不注册回调,零开销
|
||
|
||
#### 任务5:移除卡片统计行中点赞/收藏 icon+数值
|
||
|
||
##### 问题
|
||
卡片统计行(`_buildStatsRow`)中显示点赞数/收藏数 icon+数值,但:
|
||
1. 右侧操作按钮(`LiquidGlassActionButton`)已显示点赞数
|
||
2. 详情 Sheet 中已显示完整点赞/收藏数据
|
||
3. 卡片信息冗余,占用纵向空间
|
||
|
||
##### 改动文件
|
||
**`lib/features/home/presentation/home_sentence_card.dart`**
|
||
- `_buildStatsRow` 移除点赞数/收藏数 icon+数值
|
||
- 仅保留评论数(评论按钮未直接显示在卡片上)
|
||
- 空判断条件简化为 `sentence.commentCount <= 0`
|
||
|
||
##### 影响范围
|
||
- 卡片更简洁,减少信息冗余
|
||
- 评论数仍可快速查看
|
||
|
||
#### 验证
|
||
- `flutter analyze` 通过:`No issues found! (ran in 15.7s)`
|
||
- Android 真机(22127RK46C, Android 16 API 36)release 模式运行正常,无 `budget exceeded` 报错
|
||
- 应用正常启动并加载首页
|
||
|
||
***
|
||
|
||
## [v6.146.0] - 2026-06-27
|
||
|
||
### 🚀 国行设备扫码 ZXing 原生 fallback + 管理空间一键清理改用原生 API
|
||
|
||
#### 背景
|
||
承接 v6.145.0 的 ML Kit unbundled 切换:
|
||
1. 切换到 unbundled 后,无 Google Play Services 的国行设备扫码功能完全丧失
|
||
2. "应用信息 → 管理空间 → 一键清理" 之前用 Dart 端手动删除各目录,无法清除 WebView/系统级缓存,且无法恢复到"安装前全新状态"
|
||
|
||
#### 任务1:集成 ZXing 原生扫码作为国行设备 fallback
|
||
|
||
##### 问题分析
|
||
- `mobile_scanner` unbundled 模式依赖 GMS 运行时加载 ML Kit
|
||
- 国行设备无 GMS,`MobileScannerController.start()` 会失败
|
||
- v6.145.0 仅做了"不可用提示",未提供替代扫码能力 → 用户完全无法扫码
|
||
|
||
##### 解决方案
|
||
集成 `com.journeyapps:zxing-android-embedded:4.3.0` 作为纯原生(不依赖 GMS)的扫码 fallback。
|
||
|
||
##### 改动文件
|
||
|
||
**1. `android/app/build.gradle.kts`**
|
||
- 添加 `com.journeyapps:zxing-android-embedded:4.3.0` 依赖(纯原生 ZXing 扫码,不依赖 GMS)
|
||
|
||
**2. `android/app/src/main/kotlin/apps/xy/xianyan/MainActivity.kt`**
|
||
- 新增 `zxingScanLauncher`:基于 `ScanContract` Activity Result API 注册扫码启动器
|
||
- 新增 `pendingScanResult: MethodChannel.Result?`:保存待返回的扫码结果
|
||
- 新增 `startZxingScan(result)` 方法:构建 `ScanOptions`(QR_CODE、横竖屏自由、不响蜂鸣、返回条码图像),通过 `zxingScanLauncher.launch(options)` 启动原生扫码 Activity
|
||
- 扫码完成回调:`result.contents == null` → 用户取消返回 null;否则返回 `mapOf("contents" to ..., "formatName" to ...)` 给 Dart 端
|
||
- 注册 MethodChannel `xianyan/zxing_scanner` 处理 `startZxingScan` 调用
|
||
|
||
**3. `lib/core/services/device/zxing_scanner_service.dart`(新建)**
|
||
- Dart 端 ZXing 扫码服务,封装 MethodChannel `xianyan/zxing_scanner` 调用
|
||
- `ZxScanResult` 数据类:`contents` + `formatName`
|
||
- `startScan()` 静态方法:返回 `Future<ZxScanResult?>`,用户取消返回 null
|
||
- 仅 Android 平台可用,其他平台返回 null
|
||
|
||
**4. 5 个扫码页面集成 ZXing fallback**
|
||
|
||
| 文件 | 集成方式 |
|
||
|------|---------|
|
||
| `lib/shared/widgets/qrcode_scanner_page.dart` | 通用扫码页:GMS 不可用时显示"点击扫码"按钮,点击调用 `ZxingScannerService.startScan()`,识别成功后走原 `_handleScanResult` 流程 |
|
||
| `lib/features/auth/presentation/qrcode_login_page.dart` | 扫码登录:GMS 不可用视图加"📷 点击扫码"按钮,调用 ZXing 扫码后解析 code 参数触发 `confirmLogin` |
|
||
| `lib/features/ctc/presentation/pages/ctc_qr_scan_page.dart` | CTC 扫码开笔记:GMS 不可用视图加"📷 点击扫码"按钮,ZXing 扫码后提取笔记 key 跳转编辑页 |
|
||
| `lib/features/file_transfer/presentation/pages/qr_code_tab.dart` | 文件传输扫码配对:GMS 不可用时直接调用 ZXing 扫码,识别后走 `_processQrPayload` |
|
||
| `lib/features/file_transfer/services/discovery/qr_pairing_service.dart` | QR 配对服务:`startScan()` 检测到 GMS 不可用时进入 `_startZxingScanLoop()` 循环扫码,直到找到设备或用户取消 |
|
||
|
||
##### 影响范围
|
||
- **有 GMS 的设备**:使用 mobile_scanner + ML Kit(实时摄像头识别)
|
||
- **无 GMS 的国行设备**:自动降级到 ZXing 原生扫码 Activity(每次扫码需点击按钮触发,非实时连续识别)
|
||
- **iOS/macOS**:不受影响(ML Kit 直接打包,不依赖 GMS)
|
||
|
||
#### 任务2:管理空间一键清理改用 API 28+ 原生 API
|
||
|
||
##### 问题分析
|
||
- v6.144.0 之前 `ManageSpaceActivity.performClearAllData()` 使用 Dart 端手动删除各目录(`cacheDir`/`filesDir`/`shared_prefs`/`databases`/`app_flutter`/`code_cache`/`app_webview`)
|
||
- 手动删除无法覆盖所有应用数据(如外部缓存、系统级 AccountManager 数据、备份等)
|
||
- 无法真正恢复"安装前全新状态"
|
||
- `ActivityManager.clearApplicationUserData()` 无参版本已弃用
|
||
|
||
##### 解决方案
|
||
改用 API 28+ 的 `ActivityManager.clearApplicationUserData(listener, intent)` 异步原生 API,等同于系统"应用信息 → 清除数据"。
|
||
|
||
##### 改动文件
|
||
|
||
**`android/app/src/main/kotlin/apps/xy/xianyan/ManageSpaceActivity.kt`**
|
||
|
||
新增/修改方法:
|
||
- `performClearAllData()`:
|
||
- 清除前读取 `xianyan_prefs` 中的 `agreement_accepted` 协议同意状态并保存到内存变量
|
||
- API 28+ → 调用 `clearViaActivityManagerAsync(agreementAccepted)`
|
||
- API < 28 → 回退到 `clearAllDataManual()` 手动删除
|
||
- `clearViaActivityManagerAsync(agreementAccepted)`:
|
||
- 获取 `ActivityManager` 系统服务
|
||
- 构建 `restartIntent`(指向 `SplashActivity`,`FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK`)
|
||
- 调用 `activityManager.clearApplicationUserData(listener, restartIntent)`:
|
||
- 系统级清除所有应用数据(内部存储、外部存储、数据库、SharedPreferences、缓存、WebView、AccountManager 等)
|
||
- `listener` 在清除完成后回调:成功 → 恢复协议状态 → 启动 restartIntent → `finishAffinity()` → `killProcess` 杀掉当前进程;失败 → 回退手动删除
|
||
- `intent` 参数:系统在 listener 调用后自动启动此 Intent(双保险)
|
||
- `restoreAgreementState(agreementAccepted)`:
|
||
- `clearApplicationUserData` 会清除所有 SharedPreferences,需要重新写入 `xianyan_prefs` 中的协议同意状态
|
||
- 仅当清除前 `agreementAccepted == true` 时才恢复,避免误开启协议
|
||
|
||
##### 影响范围
|
||
- **API 28+(Android 9+)设备**:一键清理后应用完全恢复到安装前全新状态(包括 WebView、AccountManager、外部缓存等手动无法清除的数据)
|
||
- **API < 28 设备**:保持原有手动删除逻辑
|
||
- **协议状态保留**:清除数据后自动恢复用户已同意的协议状态,避免用户重新同意
|
||
|
||
#### 任务3:修复 use_build_context_synchronously 警告(举一反三)
|
||
|
||
##### 问题
|
||
`flutter analyze` 在 `ctc_qr_scan_page.dart:67` 报 `use_build_context_synchronously` info 警告:`await ZxingScannerService.startScan()` 后使用 `Navigator.of(context)` 跨异步间隙。
|
||
|
||
##### 举一反三修复
|
||
检查所有调用 `ZxingScannerService.startScan()` 的 Widget 文件,统一在 `await` 后添加 `if (!mounted) return;` 检查:
|
||
|
||
| 文件 | 修复位置 |
|
||
|------|---------|
|
||
| `lib/features/ctc/presentation/pages/ctc_qr_scan_page.dart` | `_startZxingScan()` 第63行 |
|
||
| `lib/features/auth/presentation/qrcode_login_page.dart` | `_startZxingScan()` 第107行 |
|
||
| `lib/features/file_transfer/presentation/pages/qr_code_tab.dart` | `_startZxingScan()` 第175行 |
|
||
| `lib/shared/widgets/qrcode_scanner_page.dart` | `_startZxingScan()` 第138行 |
|
||
|
||
注:`qr_pairing_service.dart` 是 Service 非 Widget,无 `mounted` 概念,已用 `if (!_deviceController.isClosed)` 保护,无需修改。
|
||
|
||
#### 验证
|
||
- `flutter analyze` 7 个相关 Dart 文件全部通过:`No issues found! (ran in 18.7s)`
|
||
- ZXing 扫码:在国行设备(无 GMS)上点击"📷 点击扫码"按钮应启动原生扫码界面,扫描二维码后正确返回内容
|
||
- 一键清理:在"应用信息 → 管理空间 → 一键清理"确认后,应用应完全恢复到安装前状态并自动重启到 SplashActivity,协议同意状态保留
|
||
|
||
#### 版本号
|
||
- pubspec.yaml: 6.6.28+2606271 → 6.6.29+2606272
|
||
- pubspec.ohos.yaml: 6.6.27+2606271 → 6.6.29+2606272
|
||
- pubspec.macos.yaml: 6.6.27+2606271 → 6.6.29+2606272
|
||
|
||
***
|
||
|
||
## [v6.145.0] - 2026-06-27
|
||
|
||
### 🔧 修复 Android 15 edge-to-edge 无边框画面 + 弃用 API Google Play 警告
|
||
|
||
#### 问题现象
|
||
Google Play 上传 APK 时出现两项警告:
|
||
1. **无边框画面警告**:以 SDK 35 为目标平台的应用在 Android 15 上默认采用无边框画面,需处理边衬区
|
||
2. **弃用 API 警告**:应用使用了 `Window.setStatusBarColor`、`Window.setNavigationBarColor`、`Window.setNavigationBarDividerColor` 等 Android 15 已弃用的 API,来源包括项目自身代码和 ML Kit 库静态初始化块
|
||
|
||
#### 根因分析
|
||
1. **项目自身代码**:`styles.xml` 和 `styles-night.xml` 中使用了 `android:statusBarColor` 和 `android:navigationBarColor` 主题属性(Android 15 已弃用);`MainActivity` 和 `SplashActivity` 未调用 `enableEdgeToEdge()`
|
||
2. **ML Kit 库**:`mobile_scanner` 使用 bundled 版本 `com.google.mlkit:barcode-scanning:17.3.0`,其静态初始化块(`com.google.android.gms.internal.mlkit_vision_common.zzca.<clinit>`)调用了弃用 API。bundled 版本打包进 APK,Google Play 扫描时发现这些调用
|
||
3. **ML Kit 版本**:17.3.0 已是最新版本(2024-08-07 发布),无更新版本可升级修复
|
||
|
||
#### 修复方案
|
||
|
||
**1. Edge-to-Edge 适配**
|
||
- `android/app/build.gradle.kts`:添加 `androidx.activity:activity-ktx:1.9.3` 依赖(`enableEdgeToEdge()` 扩展函数需要 1.8.0+)
|
||
- `MainActivity.kt`:`onCreate` 中 `super.onCreate` 之前调用 `enableEdgeToEdge()`
|
||
- `SplashActivity.kt`:同上
|
||
- 新建 `values-v35/styles.xml` 和 `values-night-v35/styles.xml`:覆盖 Android 15+ 主题,移除弃用的 `statusBarColor` 和 `navigationBarColor` 主题属性(API < 35 仍保留原有透明设置)
|
||
|
||
**2. ML Kit 切换到 unbundled 版本**
|
||
- `android/gradle.properties`:添加 `dev.steenbakker.mobile_scanner.useUnbundled=true`
|
||
- 切换后 ML Kit 代码通过 Google Play Services 运行时加载,不打包进 APK,Google Play 扫描不再发现弃用 API 调用
|
||
- APK 体积减小约 3-10MB
|
||
- **注意**:国行设备(无 Google Play Services)扫码功能不可用,需降级处理
|
||
|
||
**3. Google Play Services 可用性检测**
|
||
- `MainActivity.kt`:新增 `DEVICE_CAPABILITY_CHANNEL` MethodChannel,提供 `isGooglePlayServicesAvailable` 方法,使用 `GoogleApiAvailability.isGooglePlayServicesAvailable()` 检测
|
||
- `android/app/build.gradle.kts`:添加 `com.google.android.gms:play-services-base:18.5.0` 依赖
|
||
- 新建 `lib/core/services/device/scanner_availability_service.dart`:Dart 端扫码可用性检测服务,封装 MethodChannel 调用,缓存检测结果,非 Android 平台直接返回可用
|
||
|
||
**4. 扫码页面降级处理**
|
||
5 个使用 `mobile_scanner` 的文件均添加 GMS 可用性检测:
|
||
- `lib/shared/widgets/qrcode_scanner_page.dart`:扫码前检测,不可用时显示"扫码功能暂不可用"视图 + 返回按钮
|
||
- `lib/features/auth/presentation/qrcode_login_page.dart`:扫码登录 tab 检测,不可用时显示提示视图
|
||
- `lib/features/ctc/presentation/pages/ctc_qr_scan_page.dart`:改为 `autoStart: false` + 检测,不可用时显示提示 + 手动输入钥匙入口
|
||
- `lib/features/file_transfer/presentation/pages/qr_code_tab.dart`:扫码按钮检测,不可用时 toast 提示 + 跳转手动输入配对码
|
||
- `lib/features/file_transfer/services/discovery/qr_pairing_service.dart`:`startScan()` 方法检测,不可用时跳过扫码
|
||
|
||
**5. status_bar_service.dart 注释完善**
|
||
- 添加 Android 15+ 行为说明注释:`statusBarColor`/`navigationBarColor` 等属性在 Android 15+ 被忽略,保留仅向下兼容;图标亮度属性仍有效
|
||
|
||
#### 影响范围
|
||
- **Android 15+ 设备**:默认启用 edge-to-edge,状态栏/导航栏透明,内容延伸到屏幕边缘
|
||
- **Android 14 及以下**:保持原有透明状态栏/导航栏行为
|
||
- **有 Google Play Services 的设备**:扫码功能正常(通过 GMS 加载 ML Kit)
|
||
- **国行设备(无 GMS)**:扫码功能不可用,显示降级提示,CTC 扫码页可手动输入钥匙,文件传输可手动输入配对码
|
||
|
||
#### 验证建议
|
||
- Android 15 设备:检查状态栏/导航栏区域内容是否正确显示,无遮挡
|
||
- 国行设备:检查扫码页面是否显示"功能暂不可用"提示,手动输入入口是否可用
|
||
- Google Play Console:重新上传 APK 后确认两项警告消失
|
||
|
||
***
|
||
|
||
## [v6.144.0] - 2026-06-27
|
||
|
||
### 🐛 修复句子广场已收藏句子重复出现且未显示状态 + 按钮轮廓不清
|
||
|
||
#### 问题现象
|
||
1. 句子广场中已收藏的句子在循环加载时重复出现,且未显示"已收藏"状态
|
||
2. 未点赞/未收藏时按钮轮廓过淡(边框 0.5px + 25% white),用户无法辨识可点击区域
|
||
|
||
#### 根因分析
|
||
1. **服务端根因**:`/api/feed/list` 接口 `_batchAttachInteractionCounts` 只回填聚合计数(like_count 等),不返回当前用户的 `is_liked`/`is_favorited` 状态。客户端 `HomeSentence.fromFeedItem` 解析时降级为 false,导致已收藏句子重复出现时显示"未收藏"。
|
||
2. **客户端根因**:`fetchRefreshSentences`/`fetchNewSentences` 直接使用服务端返回的 `isLiked`/`isFavorited`,未合并本地 DB 的互动状态。未登录用户服务端始终返回 false,本地 DB 是唯一状态来源但未被读取。
|
||
3. **循环加载根因**:`cycleRound >= 3` 时 `allSeenIds.clear()` 清空所有已见 ID,已收藏句子的去重记录也一并清除,导致循环加载时已收藏内容重复出现。
|
||
4. **按钮样式根因**:`LiquidGlassActionButton` 未激活态边框仅 0.5px + 25% white 透明度,视觉上几乎不可见。
|
||
|
||
#### 服务端修复
|
||
**`docs/toolsapi/application/api/controller/Feed.php`**
|
||
- `_batchAttachInteractionCounts(&$items, $userId = 0)`:新增 `$userId` 参数,当用户已登录时批量查询 `feed_interaction` 表中该用户的 like/favorite 记录,回填 `is_liked`/`is_favorited` 字段。
|
||
- `list()` 方法:调用处从 `$this->_batchAttachInteractionCounts($items, $userId)` 改为 `$this->_batchAttachInteractionCounts($items, $this->_getUserId())`(修复未定义变量 `$userId`)。
|
||
- `_batchLoadFeedItems()` 方法:同样传入 `$this->_getUserId()`。
|
||
- **缓存隔离**:缓存命中后重新调用 `_batchAttachInteractionCounts` 按当前用户重查 per-user 字段;缓存写入前剥离 `is_liked`/`is_favorited` 为 false,避免不同用户串状态。
|
||
|
||
#### 客户端修复
|
||
**`lib/core/storage/database/app_database.dart`**
|
||
- 新增 `getSentencesByIds(List<String> ids)` 批量查询方法,返回 `Map<String, Sentence>`,用于合并本地 DB 互动状态。
|
||
|
||
**`lib/features/home/providers/home_feed_mixin.dart`**
|
||
- 新增 `_mergeLocalInteractionState(sentences)` 私有方法:批量查询本地 DB,采用 OR 合并策略(`local true wins`)——服务端 true 或本地 true 均为 true。覆盖未登录场景(服务端始终 false,本地 DB 是唯一来源)和登录场景(本地兜底未同步的离线操作)。
|
||
- `fetchRefreshSentences`:`result.list.map(HomeSentence.fromFeedItem)` 后调用 `_mergeLocalInteractionState` 合并本地状态。
|
||
- `fetchNewSentences`:同上,生成 `mergedSentences` 供后续去重/循环加载使用。
|
||
- `cycleRound >= 3` 循环加载:清空 `allSeenIds`/`allSeenTexts` 前保留已收藏句子的 ID 和文本,恢复到去重集合中;`recentIds`/`recentTexts` 合并已收藏内容,确保循环加载时跳过已收藏句子。
|
||
|
||
**`lib/features/home/presentation/home_sentence_card.dart`**
|
||
- `LiquidGlassActionButton` 未激活态:边框宽度 0.5px → 1.2px;背景透明度加深(深色 8%→15% white,浅色 18%→28% white);边框透明度加深(深色 12%→30% white,浅色 25%→50% white)。
|
||
|
||
#### 验证
|
||
- `flutter analyze` 三个修改文件 0 issues
|
||
- curl 验证服务端:`limit=8` 非缓存路径返回 `"is_liked":false,"is_favorited":false`(未登录正确);缓存命中路径 `"成功(cache)"` 同样返回字段(重新附加 per-user 状态)
|
||
- 缓存隔离验证:缓存写入前剥离 per-user 字段,命中后按当前用户重查,杜绝跨用户串状态
|
||
|
||
#### 不足与建议
|
||
- **缓存命中额外查询**:每次缓存命中调用 `_batchAttachInteractionCounts` 会重新查询聚合计数(冗余),可后续拆分为仅查询 per-user 状态的轻量方法。
|
||
- **跨设备同步延迟**:未登录用户依赖本地 DB,登录用户首次加载时服务端返回正确状态,但缓存命中时 per-user 状态依赖 `_batchAttachInteractionCounts` 重查,若 DB 负载高可能增加延迟。
|
||
- **循环加载已收藏保留策略**:当前保留全部已收藏 ID 在 `allSeenIds` 中,若用户收藏量极大(>300),`_buildSeenIdList` 会截断为最近 300 条,早期收藏可能再次出现。可考虑按收藏时间分页保留。
|
||
|
||
***
|
||
|
||
## [v6.143.0] - 2026-06-27
|
||
|
||
### 🐛 修复 lite 模式点赞/取消点赞后 like_count 不更新的问题
|
||
|
||
#### 问题现象
|
||
- 主页句子广场(lite 模式,limit=5)点赞或取消点赞后,like_count 不变化
|
||
- curl 实测:unlike 后 lite list 返回 `msg=成功(cache)`,like_count 仍为旧值
|
||
- 非 lite 模式(limit=10/20)正常,仅 lite 模式 + limit=5 异常
|
||
|
||
#### 根因分析(三层 bug)
|
||
1. **真正根因(核心)**:`_clearFeedCache` 的 `$limits = [10, 20, 30, 50]` **不包含 5**,而客户端 lite 模式请求用 limit=5,导致 `feed_list_hitokoto_newest_1_5_lite` 这个 cacheKey 从未被清理。互动操作后旧缓存仍然命中,返回过期数据。
|
||
2. **潜在 bug(已兜底)**:`Cache::rm` 在某些环境(opcache 缓存旧代码 / ThinkPHP complex 驱动异常)下不删除文件。诊断脚本中 `Cache::rm` 返回 true 但文件仍在。
|
||
3. **历史遗漏**:`_clearFeedCache` 之前未覆盖 `_lite` 后缀的 cacheKey(v6.142.0 已加,但 limit=5 才是关键阻塞点)。
|
||
|
||
#### 服务端修复
|
||
**`docs/toolsapi/application/api/controller/Feed.php`**
|
||
- `_clearFeedCache`:`$limits` 从 `[10, 20, 30, 50]` 扩展为 `[5, 10, 15, 20, 25, 30, 40, 50]`,覆盖客户端所有可能的 limit 值(含 5)。同步修复 `_clearWeightCache` 的同名数组。
|
||
- 新增 `_unlinkCacheFile($cacheKey)` 私有方法:在 `Cache::rm` 之后兜底,按 ThinkPHP 5 File 驱动路径规则(`CACHE_PATH/前2位md5/剩余30位md5.php`)直接 `unlink` 缓存文件,绕过 opcache 缓存旧代码 / complex 驱动异常。
|
||
- `_clearFeedCache` 所有 `Cache::rm` 调用后均追加 `$this->_unlinkCacheFile()` 兜底,覆盖 singleKeys / list 列表 / trending / 平台后缀 全部缓存 key。
|
||
- 移除 v6.142.0 排查期间遗留的 7 处临时调试代码(`file_put_contents` 写入 unlike_debug/clearcache_debug/feed_cache_debug 日志)。
|
||
|
||
#### 验证
|
||
自动化测试脚本 `/tmp/test_lite_cache.py` 全流程验证通过:
|
||
```
|
||
[stepA] 预热: msg='成功' feed_id=8812 like_count=3
|
||
[stepB] like: msg='操作成功'
|
||
[stepC] like后: msg='成功' like_count=4 期望=4 cache命中=False [OK]
|
||
[stepD] unlike: msg='操作成功'
|
||
[stepE] unlike后: msg='成功' like_count=3 期望=3 cache命中=False [OK]
|
||
=== 全部通过: lite 模式缓存清理修复成功 ===
|
||
```
|
||
关键验证点:
|
||
- stepC/stepE 的 `msg` 不再包含 `cache`,证明缓存已被清理、强制重新查库
|
||
- like_count 正确递增(3→4)和递减(4→3)
|
||
|
||
#### 排查过程经验
|
||
- 最初误判"unlike 不递减",实际是缓存竞态。加 sleep 2 秒重测发现 unlike 本身工作正常。
|
||
- 调试日志(file_put_contents)全部为空,误导排查方向。根因是 opcache 缓存了旧 Feed.php(不含调试日志的版本),PHP-FPM 重启后 opcache 未彻底清理。通过 `opcache_invalidate(Feed.php, true)` + `opcache_reset()` 解决。
|
||
- 最终通过在 `_unlinkCacheFile` 中加 trace 日志,对比 `newest_1_5_lite` 和 `newest_1_10_lite` 的记录,发现 limit=5 的 key 从未被处理,定位到 `$limits` 数组遗漏。
|
||
|
||
#### 文档同步
|
||
- `API_FEED_DOC.md` 头部版本号 v2.4 → v2.5
|
||
- 修正 v6.142.0 中关于 unlike 的错误结论(标注为已修复,指向本版本)
|
||
|
||
#### 不足与建议
|
||
- **$limits 仍为枚举**:若未来客户端新增 limit=35 等值,需同步更新数组。建议后续改为 `range(5, 50, 5)` 或用 glob 扫描缓存文件前缀的方式彻底规避。
|
||
- **opcache 自动清理依赖**:当前依赖 `validate_timestamps=1` + `revalidate_freq=3`,文件更新后最多 3 秒延迟。若部署后 3 秒内有请求命中旧 opcache,仍可能用旧代码。建议部署脚本中调用 `opcache_reset` 或重启 PHP-FPM。
|
||
- **Cache::rm 与 unlink 双写**:当前每次互动操作执行约 320 次 `Cache::rm + unlink`,性能可接受但略显冗余。可考虑用 `Cache::clear()` 清空整个缓存目录,但会影响其他模块缓存命中率。
|
||
|
||
***
|
||
|
||
## [v6.142.0] - 2026-06-27
|
||
|
||
### 🐛 修复主页句子广场点赞按钮显示 0 的问题
|
||
|
||
#### 根因分析(双重 bug)
|
||
1. **服务端根因**:`/api/feed/list` 接口 `_formatItem` 默认不返回 `like_count/favorite_count/comment_count/share_count` 字段,只有 `/api/feed/detail` 才附加。客户端 `FeedItem.fromJson` 解析 `null` → 默认 0 → 按钮显示 0。
|
||
2. **客户端根因**:`toggleLike`/`toggleFavorite` 乐观更新时只翻转 `isLiked/isFavorited`,未递增/递减 `likeCount/favoriteCount`,导致即使点击点赞数字也不变。
|
||
|
||
#### 服务端修复
|
||
**`docs/toolsapi/application/api/controller/Feed.php`**
|
||
- 新增 `_batchAttachInteractionCounts(&$items)` 私有方法:
|
||
- 收集所有 `(feed_type, feed_id)` 对
|
||
- 按 feed_type 分组批量查询 `feed_interaction` 表,`GROUP BY feed_id, action` 一次性获取 like/favorite/comment/share 计数
|
||
- 回填到 items 的 `like_count/favorite_count/comment_count/share_count` 字段
|
||
- 避免 N+1 查询,列表接口性能影响极小
|
||
- `list()` 方法末尾调用,`_batchLoadFeedItems()` 末尾也调用(覆盖 favorites/likes/history/readlater 列表)
|
||
- 查询失败时降级填充 0,保证字段始终存在(客户端不再解析 null)
|
||
|
||
#### 客户端修复
|
||
**`lib/features/home/providers/home_interaction_mixin.dart`**
|
||
- `toggleLike` 乐观更新:除翻转 `isLiked` 外,同步递增/递减 `likeCount`(点赞 +1,取消点赞 -1,下限 0)
|
||
- `toggleFavorite` 乐观更新:除翻转 `isFavorited` 外,同步递增/递减 `favoriteCount`(举一反三,同类问题)
|
||
- `_persistLikeLocally` 兜底插入分支:调用新增的 `setLikesCount` 同步写入本地 DB 的 `likes` 字段
|
||
|
||
**`lib/core/storage/database/app_database.dart`**
|
||
- 新增 `setLikesCount(id, count)` 方法:
|
||
- 由于 `.g.dart` 未重新生成(`build_runner` 受 freezed/analyzer 版本冲突阻塞),`SentencesCompanion` 缺少 `likes` 命名参数
|
||
- 改用 `customStatement` 直接执行 SQL:`UPDATE sentences SET likes = ?, updated_at = ? WHERE id = ?`
|
||
- 自动 clamp 负数为 0
|
||
|
||
**`lib/features/home/providers/home_sentence_model.dart`**
|
||
- `HomeSentence.fromDb` 添加注释说明 `likes` 字段未读取原因(Sentence 类缺 `likes` getter,.g.dart 未重新生成)
|
||
- 兜底场景保持 likeCount=0,主场景由服务端返回真实数据
|
||
|
||
#### 验证
|
||
- curl 实测 `/api/feed/list` 修复前:`like_count= MISSING favorite_count= MISSING`
|
||
- curl 实测 `/api/feed/list` 修复后:`like_count= 4 favorite_count= 3 comment_count= 0`(真实数据)
|
||
- 端到端验证:登录 → 查询 detail(like_count=4, is_liked=True)→ list 接口返回一致 like_count=4
|
||
- `flutter analyze` 三个修改文件零警告
|
||
|
||
#### 文档同步
|
||
- `API_FEED_DOC.md` 头部版本号 v2.3 → v2.4
|
||
- 3.4 节响应字段说明新增 v2.4 更新提示
|
||
|
||
#### 不足与建议
|
||
- ~~**服务端 unlike 潜在 bug**:curl 实测 unlike 后 list 接口的 like_count 仍为 4,没有递减。可能 feed_interaction 表的 unlike 操作未真正删除记录(待排查 action 方法逻辑)。~~ **[v6.143.0 已修复]** 实际根因是 `_clearFeedCache` 的 `$limits` 数组不含 5,导致 lite 模式 limit=5 的缓存未被清理,详见 v6.143.0。
|
||
- **`build_runner` 阻塞**:导致 `SentencesCompanion`/`Sentence` 类缺少 `likes` 字段,本地兜底场景 likeCount 显示 0。建议解决 freezed 3.2.5/analyzer 12.1.0 版本冲突后重新生成。
|
||
- **可扩展**:`_batchAttachInteractionCounts` 可进一步附加 `is_liked`/`is_favorited` 字段(需登录用户ID),让 list 接口也能返回用户个人互动状态,避免客户端依赖 detail 接口。
|
||
|
||
***
|
||
|
||
## [v6.141.0] - 2026-06-27
|
||
|
||
### ✨ LiquidGlassActionButton 深度升级 + 触觉音效 + 长按交互
|
||
|
||
#### 背景
|
||
v6.140.0 完成句子卡片按钮重设计与收藏 Bug 修复后,识别出 5 项不足。本版本逐一解决。
|
||
|
||
#### 改进清单
|
||
|
||
##### 1. 真正的 BackdropFilter 液态玻璃
|
||
**`lib/features/home/presentation/home_sentence_card.dart`**
|
||
- `LiquidGlassActionButton` 未激活态改用真正的 `BackdropFilter` + `ImageFilter.blur`
|
||
- sigma 计算:`GlassTokens.baseBlur * ext.glassBlurMultiplier`(按钮较小用 base 层 10.0)
|
||
- 性能降级链路:
|
||
- 鸿蒙端 `OhosDeviceCapabilities.supportsBackdropFilter` 判断
|
||
- `PerformanceOptimizer.instance.shouldEnableBackdropFilter` 省电模式降级
|
||
- `PerformanceOptimizer.instance.suggestedBlurSigma()` 低端设备 sigma 缩放
|
||
- `RepaintBoundary` 隔离重绘,避免列表滚动重复模糊
|
||
- 激活态仍用渐变填充(无需模糊)
|
||
|
||
##### 2. 统一动画驱动
|
||
- 移除 `AnimatedScale` + `ScaleTransition` 双重叠加
|
||
- 改为单一 `AnimatedBuilder` + `Transform.scale` 统一缩放整个按钮(icon + 文字)
|
||
- 动画曲线 `Curves.easeInOutBack`,时长 400ms
|
||
|
||
##### 3. 跨模块文案依赖修复
|
||
- `t.discover.base.favorite/favorited` → `t.home.sentenceDetail.favorite/favorited`
|
||
- home 模块不再依赖 discover 模块翻译,符合 AGENTS.md 架构约束
|
||
- `THomeBase` 无 favorite 字段,但 `TSentenceDetail`(home.sentenceDetail)已有完整字段
|
||
|
||
##### 4. 稍后读同类 Bug 排查(无需修复)
|
||
- 调查发现 `_persistReadLaterLocally` **不写 sentences 表**
|
||
- 稍后读状态通过 `ChatMessageService.sendReadLaterSentence` 写入 ChatMessage 表(会话 ID `'readlater'`)
|
||
- 取消时调 `db.softDeleteChatMsgRecord` 软删除
|
||
- 已有 try-catch + `inserted = true` 兜底,逻辑鲁棒
|
||
- **结论**:稍后读不存在 UPDATE 静默失败问题,无需修复
|
||
|
||
##### 5. HapticService + SfxService 触觉音效接入
|
||
**`lib/features/home/presentation/home_sentence_card.dart`**
|
||
- 新增 `HapticType` 枚举(like / favorite)
|
||
- `LiquidGlassActionButton` 新增 `hapticType` 参数
|
||
- 激活态:`HapticService.medium()` + `SfxService.instance.play(SfxType.like/favorite)`
|
||
- 取消态:`HapticService.light()` + `SfxService.instance.play(SfxType.unlike/unfavorite)`
|
||
- 反馈在 `didUpdateWidget` 中根据状态变化触发,与动画同步
|
||
|
||
##### 6. 长按卡片触发重冲击
|
||
**`lib/core/utils/ui/interaction_animations.dart`**
|
||
- `PressableCard` 新增 `onLongPress` 参数
|
||
- 长按触发时自动回弹(避免停留在按压态)
|
||
**`lib/features/home/presentation/home_sentence_card.dart`**
|
||
- `SentenceCard` 接入长按:`HapticService.heavy()` + 触发详情面板
|
||
- 与点击效果一致,但提供差异化触觉反馈(重冲击 vs 无冲击)
|
||
|
||
##### 7. 服务端 /api/feed/favorites 验证
|
||
- 服务端代码审查:`Feed.php::_formatItem` 第 2186 行明确返回 `'feed_type' => $type`
|
||
- 客户端解析:`FeedItem.fromJson` 第 295 行 `feedType: json['feed_type'] as String? ?? ''`
|
||
- 接口在线验证:curl 返回 401(未登录,非 404),接口正常
|
||
- **结论**:feed_type 字段链路完整,无需修复
|
||
|
||
#### 验证
|
||
- `flutter analyze` 通过,0 error / 0 warning
|
||
- 2 个修改文件的 IDE 诊断全部通过
|
||
|
||
#### 改动文件
|
||
| 文件 | 改动 |
|
||
|------|------|
|
||
| [home_sentence_card.dart](file:///Users/wushu/Documents/trae_projects/project/xianyan/lib/features/home/presentation/home_sentence_card.dart) | LiquidGlassActionButton 重构 + HapticType + 长按接入 + 文案改用 home.sentenceDetail |
|
||
| [interaction_animations.dart](file:///Users/wushu/Documents/trae_projects/project/xianyan/lib/core/utils/ui/interaction_animations.dart) | PressableCard 新增 onLongPress 支持 |
|
||
|
||
---
|
||
|
||
## [v6.140.0] - 2026-06-27
|
||
|
||
### 🐛 修复句子广场收藏在「我的收藏」页面不显示的 Bug + 卡片按钮重设计
|
||
|
||
#### 背景
|
||
用户反馈主页「句子广场」的句子卡片点击收藏后,在「我的收藏」页面没显示收藏的句子。同时要求重新设计点赞/收藏按钮为长方形 icon+文字 样式,去掉分享 icon 按钮。
|
||
|
||
#### Bug 根因(多重故障叠加)
|
||
1. **本地数据库 UPDATE 静默失败**:`home_interaction_mixin._persistFavoriteLocally` 直接调用 `interactionDb.toggleFavorite(id)` UPDATE,若 sentences 表中不存在该 id 的记录(Feed 列表未缓存/缓存被清),UPDATE 静默失败,本地 isFavorite 字段从未被设置。
|
||
2. **Legacy API 硬编码 `targetType: 'article'`**:`favorite_repository._loadLegacyFavorites` 中硬编码 `targetType: 'article'`,但句子广场的 feedType 是 `hitokoto`/`chengyu`/`hanzi`/`poetry` 等,降级 Legacy API 永远查不到句子收藏。
|
||
3. **`mergeWithLocalDb` 去重 key 不鲁棒**:仅用 `content+author` 作为去重 key,空 content 时退化为 `|title`,多条空 content 条目互相误去重;且未利用 id 信息,服务端和本地同一句子(id 相同但 content 微小差异)会被当作两条。
|
||
4. **降级条件过激**:`_loadFeedFavorites` 中 `result.list.isEmpty && result.total == 0 && page == 1` 触发立即降级 Legacy API,在服务端 bug / 缓存延迟 / 新用户无收藏等场景下误降级。
|
||
|
||
#### 修复
|
||
| 文件 | 修复 |
|
||
|------|------|
|
||
| `lib/features/home/providers/home_interaction_mixin.dart` | `_persistFavoriteLocally` 改为先查再决定 `setFavoriteFlag` 或 `insertOrUpdateSentence` upsert 兜底;举一反三同步修复 `_persistLikeLocally` 相同问题 |
|
||
| `lib/core/storage/database/app_database.dart` | 新增 `setLikeFlag(id, value)` 方法(与 `setFavoriteFlag` 对称),避免 `toggleLike` 在记录不存在时静默失败 |
|
||
| `lib/features/home/favorite_repository.dart` | `_loadLegacyFavorites` 移除硬编码 `targetType: 'article'`,让服务端返回所有类型收藏 |
|
||
| `lib/features/home/favorite_repository.dart` | `mergeWithLocalDb` 改为双重去重策略:主 key 用 `targetType\|targetId`,副 key 用 `content\|title`(空 content 不参与去重) |
|
||
| `lib/features/home/favorite_repository.dart` | `_loadFeedFavorites` 空结果时先检查本地数据库,本地有则兜底返回,避免误降级 Legacy API |
|
||
|
||
#### 卡片按钮重设计(方案 C · 液态玻璃)
|
||
**`lib/features/home/presentation/home_sentence_card.dart`**:
|
||
- 移除分享 icon 按钮(分享功能保留在左滑手势和详情面板入口)
|
||
- 移除 `_shareAsImage` / `_shareAsText` / `_isSharing` / `_repaintKey` 及相关 import(dart:io / dart:ui / path_provider / share_plus / kIsWeb)
|
||
- 新增 `LiquidGlassActionButton` 组件(iOS 26 Liquid Glass 风格):
|
||
- 长方形 icon + 文字 按钮
|
||
- 未激活:半透明玻璃材质 + 微高光 + 中性色
|
||
- 已激活:渐变填充 + 内嵌高光 + 投射阴影 + 白色 icon/文字
|
||
- 点击触发弹跳动画(AnimationController + easeInOutBack 曲线)
|
||
- 自动跟随 `AppThemeExtension` 切换明/暗/AMOLED/主色
|
||
- 点赞按钮:❤️ + 数字(用 `NumberFormatter.formatCount`),激活态 iOS systemRed 渐变
|
||
- 收藏按钮:★ + 文字"收藏"/"已藏"(用 `t.discover.base.favorite/favorited`),激活态 iOS systemOrange 渐变
|
||
- 新增 `_ToolIconButton` 抽出拼音/翻译/TTS 工具图标共用组件,添加无障碍语义标签
|
||
- HTML 设计原型:`docs/prototypes/sentence_card_redesign.html`(4 种方案 + 动态主题切换预览)
|
||
|
||
#### 验证
|
||
- `flutter analyze` 通过,0 error / 0 warning
|
||
- 4 个修改文件的 IDE 诊断全部通过
|
||
|
||
#### 不足与扩展建议
|
||
详见本次会话末尾的「审计验收」总结。
|
||
|
||
---
|
||
|
||
## [v6.139.0] - 2026-06-26
|
||
|
||
### 🧹 macOS 编译验证 + 静态分析清理
|
||
|
||
#### 背景
|
||
编译运行 macOS 端验证应用状态,确认无构建错误和运行时异常。同时清理 `flutter analyze` 报告的低风险静态问题,并将 `PermissionService.openSettings` 透传权限名到原生日志,提升 macOS 权限跳转日志可读性。
|
||
|
||
#### 编译运行验证
|
||
- `flutter run -d macos` 编译成功,应用正常启动
|
||
- 网络请求正常(`/api/user_center/index`、`/api/webapi/ip`、`/api/feed/favorites` 返回 200)
|
||
- 菜单栏语言切换、GoRouter 路由跳转、权限被拒后引导跳转系统设置均工作正常
|
||
- 运行日志中观察到的 `macOS photos 权限请求失败: permanentlyDenied` 属预期行为(用户在 TCC 弹窗拒绝),应用正确引导用户前往系统设置
|
||
|
||
#### 静态分析清理(25 → 20,0 error / 0 warning)
|
||
| 文件 | 修复 |
|
||
|------|------|
|
||
| `lib/features/reading_report/presentation/reading_report_page.dart` | 移除未使用的 `go_router` import |
|
||
| `lib/core/router/app_nav_extension.dart` | 移除冗余的 `flutter/widgets.dart` import(cupertino.dart 已覆盖) |
|
||
| `test/l10n/translation_coverage_test.dart` | 添加 `ignore_for_file: avoid_print`(测试诊断输出) |
|
||
|
||
#### 权限跳转日志增强
|
||
**`lib/core/services/auth/permission_service.dart`**:
|
||
- `openSettings()` 新增可选参数 `AppPermission? permission`
|
||
- macOS 分支透传 `permission.macosPermissionName` 到 `MacosPlatformService.openPermissionSettings()`
|
||
- 原生日志从「权限: nil」改善为「权限: photos」等具名输出(macOS 无法深链到具体权限子页,仅用于日志可读性)
|
||
|
||
**调用点同步更新**(4 处):
|
||
- `_showSettingsDialog`:`openSettings(perm)`
|
||
- `_showDeniedDialog`:`openSettings(perm)`
|
||
- `permission_management_page.dart`:`openSettings(p)`
|
||
- `theme_sections_style.dart`:`openSettings(AppPermission.photos)`
|
||
|
||
#### 验证
|
||
- `flutter analyze` 通过,0 error / 0 warning,剩余 20 个 info(async gap、`bytes` 废弃、参数冗余)按需后续处理
|
||
|
||
---
|
||
|
||
## [v6.138.0] - 2026-06-26
|
||
|
||
### 🔧 全量迁移应用数据至 Application Support(v6.137.0 后续)
|
||
|
||
#### 背景
|
||
v6.137.0 完成了数据库文件和 Debug 沙盒的迁移,但项目中仍有 13 处使用 `getApplicationDocumentsDirectory()` 存储应用内部数据(Hive、聊天文件、字体等)。本次统一迁移所有应用内部数据到 `getApplicationSupportDirectory()`,并修复 `clearCache()` 中与 FactoryReset 同样的危险清空逻辑。
|
||
|
||
#### 新增工具函数
|
||
**`lib/core/utils/platform/path_provider_native.dart`**:
|
||
- 新增 `getAppDataDirectory()`:返回 `getApplicationSupportDirectory()`,推荐用于应用内部数据
|
||
- 新增 `migrateAppDataFromDocumentsToSupport()`:一次性迁移函数
|
||
- 迁移已知应用子目录:`image_cache`、`readlater_sync`、`chat_audio`、`chat_attachments`、`chat_trash`、`cloud_cache`、`file_transfer`、`fonts`、`backups`
|
||
- 迁移 Hive 文件:`*.hive`、`*.lock`
|
||
- 使用 copy 而非 rename,复制成功后才删除旧文件
|
||
- 若新路径已存在同名文件/目录,跳过(避免覆盖)
|
||
- 进程内缓存迁移状态,避免重复执行
|
||
|
||
**`lib/core/utils/platform/platform_utils.dart`**:
|
||
- 新增 `safeAppDataPath` getter:暴露 `getAppDataDirectory()`
|
||
- 新增 `migrateAppData()` 函数:暴露迁移逻辑
|
||
|
||
**`lib/main.dart`**:
|
||
- 在 `KvStorage.init()` 之前调用 `pu.migrateAppData()`,确保存储初始化时使用新路径
|
||
|
||
#### 替换的使用点(13 处)
|
||
|
||
| 文件 | 修改内容 |
|
||
|------|----------|
|
||
| `hive_safe_access.dart` | Hive.init 从 `Hive.initFlutter()` 改为 `Hive.init(applicationSupportPath)`,降级方案保留 Documents |
|
||
| `image_cache_metadata_service.dart` | 索引重建扫描新路径,兼容旧路径残留 |
|
||
| `readlater_device_sync_service.dart` | 同步文件保存到新路径,扫描兼容新旧路径 |
|
||
| `chat_audio_service.dart` | 录音文件存储到新路径 |
|
||
| `chat_file_service.dart` | 附件存储到新路径,路径解析兼容新旧 |
|
||
| `cloud_cache_service.dart` | 云端缓存存储到新路径 |
|
||
| `cache_manager_service.dart` | 文件传输缓存存储到新路径 |
|
||
| `font_download_service.dart` | 字体文件存储到新路径 |
|
||
| `font_management_notifier.dart` | 字体扫描新路径 |
|
||
|
||
#### 修复 clearCache() 危险清空逻辑
|
||
**`lib/features/settings/providers/general_settings_provider.dart`**:
|
||
|
||
原 `clearCache()` 方法存在与 FactoryReset 同样的危险逻辑:遍历 `getApplicationDocumentsDirectory()` 返回目录的所有子项并递归删除。在 macOS/Windows/Linux 非沙盒下会删除用户 `~/Documents` 下所有内容。
|
||
|
||
修复方案:
|
||
1. **临时目录**:直接清空内容(应用专属,安全)
|
||
2. **Application Support**:带路径校验的清空(要求路径包含 `apps.xy.xianyan` 或 `xianyan`)
|
||
3. **Documents**:仅清理已知应用子目录(`image_cache`、`chat_audio`、`chat_attachments` 等 9 个),不删除根内容
|
||
4. 新增 `_safeClearDirContents()` 辅助方法,支持路径校验
|
||
|
||
#### 保留的使用点(用户可见文件)
|
||
以下使用点保留 `getApplicationDocumentsDirectory()`,因为存储的是用户可见文件:
|
||
- `data_management_export_mixin.dart`:数据导出 `.xypk` 文件(导出后通过 share_plus 分享,路径不重要临时文件)
|
||
|
||
#### backup_service.dart 备份迁移(补充)
|
||
**`lib/core/services/data/backup_service.dart`**:
|
||
- `backupDirPath` 从 `safeAppDirPath`(Documents)改为 `safeAppDataPath`(Application Support)
|
||
- `getBackupList()` 兼容新旧两个路径:同时扫描 Application Support 和 Documents,合并结果并按文件名去重
|
||
- 新增 `_scanBackupDir()` 私有方法,提取目录扫描逻辑
|
||
|
||
**`lib/core/utils/platform/path_provider_native.dart`**:
|
||
- 修正迁移子目录列表:`backups` → `xianyan_backups`(与 `backup_service.dart` 的 `_backupDirName` 保持一致)
|
||
|
||
**`lib/features/settings/presentation/more_settings_page.dart`** 和
|
||
**`lib/features/settings/providers/general_settings_provider.dart`**:
|
||
- Documents 目录清理列表新增 `xianyan_backups` 子目录
|
||
- FactoryReset 的 `_safeClearAppFilesystemData()` 新增步骤 6.5:清理 Documents 下已知应用子目录
|
||
|
||
#### 验证
|
||
- `flutter analyze` 所有修改文件 → No issues found
|
||
- 迁移逻辑在应用启动时自动执行,用户无感知
|
||
- 兼容旧路径残留文件,迁移失败不影响使用
|
||
|
||
---
|
||
|
||
## [v6.137.0] - 2026-06-26
|
||
|
||
### 🔧 数据库路径迁移 + Debug 沙盒启用(v6.136.0 后续优化)
|
||
|
||
#### 背景
|
||
v6.136.0 修复了 FactoryReset 误删项目源代码的严重 bug,但暴露出两个深层问题:
|
||
1. 数据库文件位置(`getApplicationDocumentsDirectory()`)在桌面非沙盒下不理想
|
||
2. Debug 模式未启用沙盒,导致开发环境与生产环境路径行为不一致(这是 bug 复现的根源)
|
||
|
||
本版本实施两个优化建议,从根因层面避免类似问题再次发生。
|
||
|
||
---
|
||
|
||
### 建议 1:数据库文件迁移至应用专属目录
|
||
|
||
#### 修改文件
|
||
**`lib/core/storage/database/database_connection/native.dart`**:
|
||
|
||
#### 路径变更
|
||
|
||
| 平台 | 旧路径 | 新路径 |
|
||
|------|--------|--------|
|
||
| macOS 非沙盒 | `~/Documents/xianyan.db` | `~/Library/Application Support/apps.xy.xianyan/xianyan.db` |
|
||
| macOS 沙盒 | `~/Library/Containers/.../Data/Documents/xianyan.db` | `~/Library/Containers/.../Data/Library/Application Support/xianyan.db` |
|
||
| iOS | `<app>/Documents/xianyan.db` | `<app>/Library/Application Support/xianyan.db` |
|
||
| Windows | `~/Documents/xianyan.db` | `%APPDATA%/<vendor>/<app>/xianyan.db` |
|
||
| Linux | `~/Documents/xianyan.db` | `~/.local/share/<app>/xianyan.db` |
|
||
| Android | `/data/data/<pkg>/files/xianyan.db` | `/data/data/<pkg>/files/xianyan.db`(路径相同,无影响) |
|
||
|
||
#### 自动迁移逻辑
|
||
新增 `_resolveDatabaseFile()` 方法,在 `LazyDatabase` 初始化阶段执行:
|
||
|
||
1. **新路径已存在** → 直接使用(迁移已完成或全新安装)
|
||
2. **旧路径存在数据库文件** → 复制到新路径(含 `-wal`/`-shm`/`-journal` 辅助文件),删除旧文件
|
||
3. **都不存在** → 使用新路径(首次安装)
|
||
4. **迁移失败** → 回退到旧路径,保证数据可访问
|
||
|
||
#### 安全性保障
|
||
- 使用 `copy` 而非 `rename`,复制成功后才删除旧文件,避免迁移中断导致数据丢失
|
||
- 迁移在 `LazyDatabase` 初始化阶段执行,此时数据库未打开,无并发风险
|
||
- 详细的日志记录迁移过程,便于排查问题
|
||
|
||
#### 联动修改
|
||
**`lib/features/settings/presentation/more_settings_page.dart`**:
|
||
- `_safeClearAppFilesystemData()` 方法第 6.1 步扩展:同时清理新旧两个路径下的数据库文件
|
||
- 新路径:`getApplicationSupportDirectory()/xianyan.db`
|
||
- 旧路径:`getApplicationDocumentsDirectory()/xianyan.db`(兼容历史版本残留)
|
||
|
||
---
|
||
|
||
### 建议 2:启用 Debug 沙盒
|
||
|
||
#### 修改文件
|
||
**`macos/Runner/DebugProfile.entitlements`**:
|
||
- `com.apple.security.app-sandbox`:`false` → `true`
|
||
|
||
#### 决策过程
|
||
启用 Debug 沙盒与建议 1 存在冲突:
|
||
- 沙盒内应用**无法访问沙盒外路径**
|
||
- 若先迁移数据库到沙盒外的 `~/Library/Application Support/apps.xy.xianyan/`,再启用沙盒,迁移逻辑会尝试从沙盒外旧路径复制,但沙盒内应用无权访问 → 迁移失败
|
||
|
||
经用户确认,选择"启用 Debug 沙盒 + 接受数据丢失"方案:
|
||
- ✅ 项目源代码不会丢失(IDE 进程不受沙盒影响)
|
||
- ⚠️ Debug 模式下应用内数据(数据库、收藏、历史等)会丢失,开发者需重新登录/创建
|
||
- ✅ 长期收益:开发环境与生产环境路径行为一致,避免类似 bug 复现
|
||
|
||
#### 沙盒启用后路径变化(Debug 模式)
|
||
|
||
| API | 启用前(非沙盒) | 启用后(沙盒) |
|
||
|-----|------------------|----------------|
|
||
| `getApplicationDocumentsDirectory()` | `~/Documents` | `~/Library/Containers/apps.xy.xianyan/Data/Documents` |
|
||
| `getApplicationSupportDirectory()` | `~/Library/Application Support/apps.xy.xianyan` | `~/Library/Containers/apps.xy.xianyan/Data/Library/Application Support` |
|
||
| `getTemporaryDirectory()` | `~/Library/Caches/apps.xy.xianyan` | `~/Library/Containers/apps.xy.xianyan/Data/Library/Caches` |
|
||
|
||
#### 影响范围评估
|
||
- ✅ Hive 存储:迁移到沙盒内 Documents,安全
|
||
- ✅ 图片缓存元数据:迁移到沙盒内 Documents,安全
|
||
- ✅ 聊天音频/文件:迁移到沙盒内 Documents,安全
|
||
- ✅ 字体下载服务:迁移到沙盒内 Documents,安全
|
||
- ✅ LocalSend 文件接收:存储在应用专属目录,不依赖 `~/Downloads`,安全
|
||
- ✅ 稍后读同步:迁移到沙盒内 Documents,安全
|
||
- ✅ Flutter 调试:Dart VM Service 通过 localhost 通信,沙盒允许 localhost 网络访问
|
||
|
||
#### 用户须知
|
||
- Debug 模式下首次启动会显示空数据库,需重新登录账户
|
||
- 旧 Debug 数据库文件残留在沙盒外路径,可手动清理:
|
||
- `~/Documents/xianyan.db`
|
||
- `~/Library/Application Support/apps.xy.xianyan/xianyan.db`
|
||
|
||
---
|
||
|
||
### 举一反三
|
||
1. **其他使用 `getApplicationDocumentsDirectory()` 的位置**:项目中仍有 13 处使用此 API 存储应用数据(Hive、图片缓存、聊天文件、字体、稍后读等)。本次未一并迁移,因为:
|
||
- 启用沙盒后,这些路径自动变为应用专属(沙盒内 Documents),不再有污染问题
|
||
- 桌面非沙盒场景下仍有潜在风险,但优先级低于数据库
|
||
- 后续可考虑统一迁移到 `getApplicationSupportDirectory()`
|
||
|
||
2. **数据库迁移日志监控**:建议在 Release 模式下监控 `Database migration:` 日志,了解用户迁移情况,及时发现问题
|
||
|
||
3. **沙盒启用后的回归测试**:建议在 Debug 模式下测试以下功能:
|
||
- 数据库创建/读写
|
||
- 文件下载(字体、图片)
|
||
- LocalSend 文件接收
|
||
- 相机/麦克风权限
|
||
- 定位功能
|
||
|
||
---
|
||
|
||
## [v6.136.0] - 2026-06-26
|
||
|
||
### 🚨 严重 Bug 修复(数据丢失 - macOS/Windows/Linux 非沙盒模式)
|
||
|
||
#### 背景
|
||
用户反馈:macOS Debug 模式下,进入「更多设置 → 重置与清理 → 清空软件所有数据」输入"重置"确认后,**整个项目源代码文件全部丢失**。
|
||
|
||
#### 根因
|
||
`lib/features/settings/presentation/more_settings_page.dart` 的 `_executeFactoryReset` 方法第 6 步缓存清理逻辑存在严重缺陷:
|
||
|
||
```dart
|
||
// ❌ 原 dangerous 代码
|
||
final dirs = <Directory>[];
|
||
dirs.add(await getTemporaryDirectory());
|
||
dirs.add(await getApplicationDocumentsDirectory()); // macOS 非沙盒 → ~/Documents
|
||
dirs.add(await getApplicationSupportDirectory());
|
||
|
||
for (final dir in dirs) {
|
||
await for (final entity in dir.list()) {
|
||
if (entity is Directory) {
|
||
await entity.delete(recursive: true); // 递归删除子目录!
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
- macOS Debug 模式下 [DebugProfile.entitlements](file:///Users/wushu/Documents/trae_projects/project/xianyan/macos/Runner/DebugProfile.entitlements) 中 `com.apple.security.app-sandbox = false`(未启用沙盒)
|
||
- 非沙盒模式下 `getApplicationDocumentsDirectory()` 返回**用户级 `~/Documents`**(不是应用容器目录)
|
||
- 数据库连接 [native.dart#L27-L28](file:///Users/wushu/Documents/trae_projects/project/xianyan/lib/core/storage/database/database_connection/native.dart#L27-L28) 将 `xianyan.db` 放在 `~/Documents/xianyan.db`
|
||
- 代码遍历 `~/Documents` 下所有子项并递归删除 → `~/Documents/trae_projects` 被递归删除 → **整个项目源代码丢失**
|
||
- 同样的危险在 Windows/Linux 非沙盒下都存在
|
||
|
||
#### 修复方案
|
||
**`lib/features/settings/presentation/more_settings_page.dart`**:
|
||
|
||
1. **新增 `_safeClearAppFilesystemData()` 方法**,遵循三大安全原则:
|
||
- 永不递归删除 `getApplicationDocumentsDirectory()` 等返回目录的根内容
|
||
- 仅删除已知应用专属路径(数据库文件、应用标识子目录)
|
||
- 路径校验:清理子目录前验证路径包含 `apps.xy.xianyan` 或 `xianyan`,否则跳过
|
||
|
||
2. **精确删除数据库文件**:仅删除 `xianyan.db`、`xianyan.db-wal`、`xianyan.db-shm`、`xianyan.db-journal`,不遍历父目录
|
||
|
||
3. **`_safeClearDirectoryContents()` 通用方法**:
|
||
- `requireAppIdentifier` 参数:要求路径包含应用标识才清理
|
||
- 仅清空目录的直接子项,不删除目录本身
|
||
- 单个文件删除失败不影响整体清理
|
||
- 详细的日志记录
|
||
|
||
4. **分层清理策略**:
|
||
- 数据库文件:精确文件删除(无条件)
|
||
- 临时目录:直接清空(应用专属,安全)
|
||
- 应用支持目录:带路径校验的清空
|
||
- 应用缓存目录:直接清空(应用专属,安全)
|
||
- Flutter ImageCache:内存缓存清理
|
||
|
||
#### 影响范围
|
||
- ✅ macOS 非沙盒(Debug 模式):修复项目源代码丢失问题
|
||
- ✅ Windows 非沙盒:修复 `Documents` 目录被清空问题
|
||
- ✅ Linux 非沙盒:修复用户主目录被清空问题
|
||
- ✅ iOS/Android 沙盒:原行为正常,现增加路径校验作为防御
|
||
- ✅ macOS Release 沙盒模式:原行为正常,现增加路径校验作为防御
|
||
|
||
#### 举一反三
|
||
- 数据库连接文件位置(`native.dart`)使用 `getApplicationDocumentsDirectory()` 在桌面非沙盒下不理想,建议未来迁移至 `getApplicationSupportDirectory()`(应用专属,更安全)
|
||
- 「数据管理页面」的 `_clearAllData` 方法仅清理数据库表,不涉及文件系统删除,安全
|
||
- 「缓存管理页面」的清理操作通过 `CacheService` 调用,使用应用专属路径,安全
|
||
|
||
---
|
||
|
||
## [v6.135.0] - 2026-06-26
|
||
|
||
### 🧹 仓库瘦身(历史大文件清理)
|
||
|
||
#### 背景
|
||
远程仓库体积膨胀至 598 MB(Gitea 服务端统计),本地 `.git` 277 MB。历史中累积了大量第三方二进制、服务端运行产物与调试截图,导致单次 `git push` 触发 Nginx HTTP 413(请求体过大)。
|
||
|
||
#### 操作(`git-filter-repo` 改写全部历史)
|
||
从所有历史提交中移除以下路径:
|
||
|
||
| 路径 | 类型 | 说明 |
|
||
|---|---|---|
|
||
| `windows/local_packages/sqlite3/nuget_test/` | 第三方二进制 | NuGet 测试包(多平台 .so/.dll/.dylib),Flutter 项目不该提交 |
|
||
| `docs/toolsapi/public/compress/` | 服务端产物 | 图片压缩缓存 |
|
||
| `docs/toolsapi/public/uploads/` | 用户上传 | 服务端运行时数据,绝对不该入库 |
|
||
| `docs/toolsapi/public/base64/` | 服务端产物 | base64 编码图片 |
|
||
| `packages/liquid_glass_easy/showcases/` | 展示资源 | gif 演示文件 |
|
||
| `debug_*.png`(根目录 6 个) | 调试截图 | 误提交的调试截图 |
|
||
|
||
#### 保留(构建/运行依赖)
|
||
- `windows/local_packages/sqlite3/prebuilt/sqlite3.dll`(3.1 MB):`windows/CMakeLists.txt:116` 引用,Drift 数据库 Windows 端依赖,已从备份恢复
|
||
- `docs/toolsapi/public/qqwry.dat` / `file.json`:服务端运行依赖,本次保留待评估
|
||
- `assets/images/empty/rz.png` / iOS AppIcon:建议后续压缩替换,不从历史删除
|
||
|
||
#### `.gitignore` 新增
|
||
- `/docs/toolsapi/public/{compress,uploads,base64}/`
|
||
- `/debug_*.png`、`/ohos_run_log.txt`、`/build_log.txt`
|
||
|
||
#### 效果
|
||
- 本地 `.git`:277 MB → 60 MB(降 78%)
|
||
- 改写 113 个提交,HEAD 由 `6c8c2e24` → `d01863d0`
|
||
- 备份:`/tmp/xianyan-backup-20260626-074855.bundle`(164 MB)
|
||
- ⚠️ 协作者需重新 clone(历史已改写,force push 覆盖远程)
|
||
|
||
---
|
||
|
||
## [v6.134.0] - 2026-06-26
|
||
|
||
### 🛠 修复(App Store 审核合规 - Guideline 2.4.5(i) / 2.1(a))
|
||
|
||
#### 背景
|
||
App Store 审核驳回 v6.6.25 (2606260),提出 3 项问题:
|
||
1. **2.4.5(i)** 三个 entitlement 缺少匹配功能:`files.downloads.read-write` / `files.downloads.read-only` / `network.server`
|
||
2. **2.4.5(i)** 沙盒缺少必需 entitlement:`device.camera`(相机功能)/ `personal-information.location`(定位功能)
|
||
3. **2.1(a)** 权限管理页点击「相册/存储/麦克风请求」时显示错误(`MissingPluginException`)
|
||
|
||
#### 1. Entitlements 文件修订(macOS 沙盒合规)
|
||
**`macos/Runner/Release.entitlements` + `DebugProfile.entitlements`**:
|
||
|
||
| 操作 | Entitlement | 说明 |
|
||
|---|---|---|
|
||
| ➕ 新增 | `com.apple.security.device.camera` | 相机:扫码/拍照生成卡片/OCR(AVCaptureDevice via PermissionManager.swift) |
|
||
| ➕ 新增 | `com.apple.security.device.microphone` | 麦克风:语音录制/转文字(AVCaptureDevice via PermissionManager.swift) |
|
||
| ➕ 新增 | `com.apple.security.personal-information.location` | 定位:permission_handler 依赖链引用 CoreLocation 符号,沙盒需此 entitlement |
|
||
| ➖ 删除 | `com.apple.security.files.downloads.read-only` | 代码无 `getDownloadsDirectory` 调用,确属冗余 |
|
||
| ➖ 删除 | `com.apple.security.files.downloads.read-write` | 同上 |
|
||
| ✅ 保留 | `com.apple.security.network.server` | LocalSend 局域网文件传输实际使用:`HttpServer.bindSecure` / `ServerSocket.bind` |
|
||
|
||
#### 2. Info.plist 补全 Usage Description
|
||
- 新增 `NSLocationUsageDescription`:声明定位用途(IP 大致区域,不获取精确 GPS)
|
||
- 已存在:`NSCameraUsageDescription` / `NSMicrophoneUsageDescription` / `NSPhotoLibraryUsageDescription`
|
||
|
||
#### 3. 权限管理 Bug 修复(替代 permission_handler_apple 的 macOS 空实现)
|
||
**根因**:`permission_handler_apple 9.4.9` 仅支持 iOS,无 macOS 实现,调用 `Permission.camera.status` 等方法时抛出 `MissingPluginException`,UI 显示错误提示。
|
||
|
||
**修复方案**:新增原生权限管理器,通过 MethodChannel 暴露给 Dart 端。
|
||
|
||
- **➕ `macos/Runner/PermissionManager.swift`(新文件)**
|
||
- 支持 4 类权限:`camera` / `microphone` / `photos` / `notification`
|
||
- 基于 AVFoundation / Photos / UserNotifications framework
|
||
- 状态字符串与 Dart 侧 `AppPermissionStatus` 枚举对齐:`notDetermined` / `granted` / `permanentlyDenied` / `restricted`
|
||
- `openSystemSettings` 跳转「隐私与安全性」面板(macOS 无法深链到子页面)
|
||
|
||
- **`macos/Runner/AppDelegate.swift`**
|
||
- 注册 MethodChannel `apps.xy.xianyan/macos.app` 三个方法:
|
||
- `checkPermission` → `PermissionManager.checkStatus`
|
||
- `requestPermission` → `PermissionManager.requestPermission`
|
||
- `openPermissionSettings` → `PermissionManager.openSystemSettings`
|
||
|
||
- **`macos/Runner.xcodeproj/project.pbxproj`**
|
||
- 注册 `PermissionManager.swift` 到 Runner target(PBXBuildFile + PBXFileReference + Sources)
|
||
|
||
- **`lib/core/services/auth/permission_service.dart`**
|
||
- `AppPermission.macosPermissionName` 新增属性:返回原生权限名称字符串
|
||
- `AppPermission.localServer` 新增虚拟权限:用于 UI 解释 `network.server` entitlement 对应的 LocalSend 功能
|
||
- `AppPermission.isPlatformRelevant` 全平台适配:macOS/Windows/Linux/Web 各自过滤
|
||
- `PermissionService.checkStatus` macOS 分支:调用 `MacosPlatformService.checkPermission`
|
||
- `PermissionService.requestPermission` macOS 分支:调用 `MacosPlatformService.requestPermission`
|
||
|
||
- **`macos/Runner/MainFlutterWindow.swift`**
|
||
- Impeller 开关修复:setenv 方式已废弃,改为 `FlutterDartProject.commandLineArguments` 传递 `--enable-impeller`/`--no-enable-impeller`
|
||
|
||
#### 4. App Store Connect 回复要点
|
||
- `network.server`:LocalSend 局域网文件传输(HttpServer.bindSecure / ServerSocket.bind)
|
||
- **用户入口路径**:① "我的" → "我的设备" → "文件传输助手"卡片
|
||
② "发现" tab → "文件传输助手"会话(📁 emoji)
|
||
③ Spotlight 搜索 "文件传输" → 跳转
|
||
④ 二维码扫描 `xianyan://transfer` scheme
|
||
- `downloads.read-*`:已删除(无匹配功能)
|
||
- `device.camera` / `personal-information.location`:已添加
|
||
- `device.microphone`:补加(PermissionManager.swift 用到 AVCaptureDevice audio)
|
||
- Bug 修复:`permission_handler_apple` 无 macOS 实现,已替换为原生 `PermissionManager`
|
||
|
||
#### 5. 防御性决策记录
|
||
- **麦克风 entitlement(补加)**:Apple 仅点名 camera,但 `PermissionManager.swift` 同时调用 `AVCaptureDevice.requestAccess(for: .audio)`,没麦克风 entitlement 同样会被 TCC 拒绝。一并加上避免下次因麦克风被拒再次返工。
|
||
- **定位 entitlement(防御性添加)**:闲言 APP 实际不用 GPS,仅因 `permission_handler` 二进制含 CoreLocation 符号被 Apple 静态扫描到。更彻底的做法是 fork 该插件移除 location 部分,但成本高,添加 entitlement 是性价比最高的方案。
|
||
- **LocalSend 入口路径**:用户可通过 4 个入口触发文件传输功能(详见上方"用户入口路径"),向 Apple 解释 `network.server` 的实际使用场景。
|
||
|
||
#### 影响范围
|
||
- ✅ macOS 端权限管理页面可正常请求相机/麦克风/相册/通知权限
|
||
- ✅ 沙盒合规,所有 entitlement 都有匹配功能或代码引用
|
||
- ✅ 不影响 iOS / Android / 鸿蒙 / Windows / Linux 端
|
||
- ⚠️ 注意:删除 downloads entitlement 后,若未来需要"保存到下载目录"功能需重新添加
|
||
|
||
***
|
||
|
||
## [v6.133.0] - 2026-06-26
|
||
|
||
### ✨ 新增(隐私政策多端对齐 + 关于页 DUNS 入口 + GDPR 欧盟代表澄清)
|
||
|
||
#### 背景
|
||
v6.132.0 在隐私政策中新增了「主体信息」章节披露 D-U-N-S 编号,但遗留 4 个待办:① 关于页未同步展示 DUNS,多端不一致;② 「十二、国际数据保护」声明已指定欧盟代表但未披露具体身份,违反 GDPR Art.27;③ 网页英文区比中文区少 8 个章节(历史遗留不对齐);④ App 内隐私政策简版(9 章)与网页详版(16+ 章)内容详细程度不一致。本次一次性解决全部 4 项。
|
||
|
||
#### 1. 任务A:关于页同步展示 D-U-N-S
|
||
**新增 2 个翻译字段到 `t_about.dart`**:
|
||
- `entityInfoMenu` / `entityInfoMenuDesc` — 用于关于页「法律信息」区块的新菜单项
|
||
- 14 个语言文件全部补全(zh_CN/zh_TW/ja/ko/de/it/es/fr/pt/ru/ar/bn/hi/en)
|
||
- 法语/意大利语译文含撇号,使用双引号包裹
|
||
|
||
**`about_page.dart` _LegalSection 新增菜单项**:
|
||
- 图标 `CupertinoIcons.building_2_fill` — 主体信息入口
|
||
- 点击触发 `_showEntityInfoDialog` 弹窗
|
||
- 弹窗用 `CupertinoAlertDialog + SingleChildScrollView + Column`,展示 7 个字段(主体名称/DBA/DUNS/统一社会信用代码/注册地址/行业/邮箱),中英对照,底部说明 Dun & Bradstreet 认证
|
||
- 所有标签使用 `AppTypography.caption2`,标签 `w600` 加粗,值正常字重,符合 iOS HIG
|
||
|
||
#### 2. 任务B:GDPR 欧盟代表改为"暂无代表"
|
||
GDPR Art.27 要求非欧盟主体在面向欧盟用户提供服务时须指定 EU 代表。本工作室暂未指定,但原政策声明"已指定欧盟代表"与事实不符,可能被欧盟监管机构质疑。
|
||
|
||
**修改位置**:
|
||
- 网页中文区 12.1 GDPR 子章节 — 「我们已指定欧盟代表,联系方式可通过本政策列明的邮箱获取」改为「我们暂未指定欧盟代表(GDPR Art.27 代表)。如有需要,您可通过本政策列明的邮箱与我们联系」
|
||
- 网页英文区 XIII. International Data Protection GDPR 子章节同步表述为「We have not yet designated an EU Representative under GDPR Art.27. If needed, you may contact us via the email listed in this policy.」
|
||
- App 内隐私政策简版新增「12. 国际数据保护」章节,欧盟代表表述与网页一致
|
||
|
||
#### 3. 任务C:网页英文区补全 8 个缺失章节
|
||
英文区章节由 10 个补齐至 18 个(Zero-XIV),与中文区 17 个章节基本对齐(英文区多「IX. Deceased Person's Information」死者信息章节,是英文区原有保留项)。
|
||
|
||
| 新增英文章节 | 行号 | 对应中文章节 |
|
||
|---|---|---|
|
||
| Zero. Definitions(由 `<p>` 改为 `<h2>`) | 708 | 零、定义 |
|
||
| II.5. Business Functions and Personal Information Mapping Table(含 14 行表格) | 805-903 | 二点五、业务功能与个人信息映射表 |
|
||
| VI. Cookie and Similar Technologies | 1047 | 六、Cookie 及同类技术 |
|
||
| VII. Third-Party SDKs(含 SDK 表格) | 1049 | 七、第三方SDK |
|
||
| VII.5. Automated Decision-Making | 1079 | 七点五、自动化决策 |
|
||
| VIII.5. Personal Information Security Incident Response | 1105 | 八点五、个人信息安全事件处置 |
|
||
| X.5. Personal Information Protection Impact Assessment | 1142 | 九点五、个人信息保护影响评估 |
|
||
| XIII. International Data Protection(含 13.1 GDPR/13.2 CCPA/13.3 COPPA) | 1186 | 十二、国际数据保护 |
|
||
|
||
**章节号顺延**:原 VI→VIII、VII→IX、VIII→X、IX→XI、X→XII、XI→XIV,所有子章节号(6.x→8.x、7.x→9.x、8.x→10.x、11.x→14.x)同步顺延。
|
||
|
||
**翻译质量**:严格保留 HTML 标签结构(`<h2>`/`<h3>`/`<p>`/`<ul>`/`<li>`/`<table>`/`<span class="highlight">`),表格(`<table><thead><tbody>`)严格保留。翻译符合法律文书风格。
|
||
|
||
#### 4. 任务D:App 内隐私政策简版补全为详版
|
||
App 内 `privacy_policy_page.dart` 章节由 10 章补全至 17 章,与网页详版章节结构对齐(用户选择"补全章节保持简版"方案,不引入表格 widget)。
|
||
|
||
| 新增章节 | 内容摘要 |
|
||
|---|---|
|
||
| 0. 定义 | 9 个术语定义(个人信息/敏感信息/处理者/匿名化/去标识化等) |
|
||
| 2.5 业务功能与个人信息映射表 | 5 项业务功能 × 个人信息映射(纯文本,网页详版含表格) |
|
||
| 7. 第三方 SDK | 当前未集成声明 + 未来集成承诺 |
|
||
| 7.5 自动化决策 | 个性化推荐说明 + 退出权 + 禁止用户画像承诺 |
|
||
| 8.5 个人信息安全事件处置 | 5 步应急响应(72h 评估/通知/报告/记录 3 年) |
|
||
| 9.5 个人信息保护影响评估 | 5 种触发情形 + 评估内容 + 3 年保留 |
|
||
| 12. 国际数据保护 | GDPR/CCPA/COPPA 简版,欧盟代表声明为"暂无代表" |
|
||
|
||
保持现有 `_buildSection + 纯文本` UI 风格,不引入表格 widget。文件头注释按规范更新。**隐私政策正文"最后更新日期"保持 2026年5月7日 不变**(用户要求)。
|
||
|
||
#### 5. 涉及文件
|
||
- `lib/l10n/types/t_about.dart` — 新增 entityInfoMenu/entityInfoMenuDesc 字段(构造/字段/toMap/fromMap 四处)
|
||
- `lib/l10n/languages/*.dart` — 14 个语言文件补全 entityInfoMenu/entityInfoMenuDesc 翻译
|
||
- `lib/features/profile/presentation/about_page.dart` — _LegalSection 新增主体信息菜单项 + _showEntityInfoDialog 弹窗 + _entityLine 渲染辅助方法
|
||
- `lib/features/settings/presentation/privacy/privacy_policy_page.dart` — 章节补全(0/2.5/7/7.5/8.5/9.5/12)+ 文件头更新
|
||
- `docs/toolsapi/public/agreements/privacy-policy.html` — 英文区补全 8 章节 + 章节号顺延 + GDPR 欧盟代表表述修正(中英两处)
|
||
- `CHANGELOG.md`
|
||
|
||
#### 6. 验证
|
||
- `flutter analyze lib/l10n/languages/ lib/l10n/types/t_about.dart lib/features/profile/presentation/about_page.dart lib/features/settings/presentation/privacy/privacy_policy_page.dart` — No issues found!(8.2s)
|
||
- 网页版通过 SFTP 上传至 `https://tools.wktyl.com/agreements/privacy-policy.html`,curl 验证新增章节已上线
|
||
|
||
#### 7. 不足与建议
|
||
- App 内隐私政策简版补全后,表格(如业务功能映射表)用纯文本代替,信息密度低于网页版。如未来需要更丰富展示,可考虑用 `Table` widget 重构
|
||
- 网页英文区比中文区多「IX. Deceased Person's Information」章节(英文区原有保留),未来可考虑中文区也补这一章
|
||
- 关于页弹窗内容仍为硬编码中英对照,未来如需深度多语言,可提取到 `t_about.dart` 字段
|
||
|
||
---
|
||
|
||
***
|
||
|
||
## [v6.132.0] - 2026-06-26
|
||
|
||
### ✨ 新增(隐私政策披露 D-U-N-S 编号 + 主体信息章节 — 适配全球上架合规)
|
||
|
||
#### 背景
|
||
工作室已取得邓白氏码(D-U-N-S Number: 586261192),App 计划在 Apple App Store / Google Play 全球上架。原隐私政策(App 内 + 网页版)仅披露公司名、统一社会信用代码、ICP 备案,缺少国际通用的企业身份标识,不利于海外监管机构、合作方与用户进行主体身份核验。GDPR 第 13/14 条要求"数据控制者"身份清晰披露,D-U-N-S 作为全球企业唯一标识可显著提升合规性并降低审核争议。
|
||
|
||
#### 1. App 内隐私政策页(privacy_policy_page.dart)
|
||
- 在「8. 政策更新」与「9. 联系我们」之间新增「9. 主体信息」章节
|
||
- 原「9. 联系我们」顺延为「10. 联系我们」
|
||
- 「9. 主体信息」披露:主体名称(中英)、经营别称(DBA)、D-U-N-S 编号、统一社会信用代码、注册地址(中英)、所属行业、隐私事务联系邮箱
|
||
- 数据源:Dun & Bradstreet 官网 + 工商注册信息
|
||
- 文件头注释按规范更新「更新时间 / 上次更新内容」
|
||
- **隐私政策正文"最后更新日期"保持 2026年5月7日 不变**(用户要求)
|
||
|
||
#### 2. 网页版隐私政策(privacy-policy.html)
|
||
- **中文区**:在「十、联系我们」前插入「十、主体信息」;原「十、联系我们」顺延为「十一、联系我们」;原「十一、国际数据保护」顺延为「十二、国际数据保护」,其子章节 11.1/11.2/11.3 同步顺延为 12.1/12.2/12.3
|
||
- **英文区**:在「IX. Contact Us」前插入「IX. Entity Information」;原「IX. Contact Us」顺延为「X. Contact Us」;原「X. Legal Application and Dispute Resolution」顺延为「XI. Legal Application and Dispute Resolution」,其子章节 10.1/10.2 同步顺延为 11.1/11.2
|
||
- **footer**:在「统一社会信用代码」与「ICP 备案号」之间新增 D-U-N-S 一行;地址统一更新为 D-U-N-S 官网注册地址(中国云南省红河哈尼族彝族自治州弥勒市朋普镇朋普社区朋肖路49号 邮编:652301)
|
||
- **DATA 对象**:新增 `duns` 字段(中英);更新 `contact` 地址为 D-U-N-S 官网注册地址(中英);英文公司名由原"…Weifengbao…"修正为 D-U-N-S 官网登记的"Mile Pengpu Town Micro Storm Network Technology Studio"
|
||
- **switchLang 函数**:新增 `footer-duns` 元素的同步更新逻辑
|
||
- **联系我们章节的"通信地址"同步更新**为 D-U-N-S 官网注册地址(中英),保证整篇文档地址一致性
|
||
|
||
#### 3. 关键决策与发现
|
||
- **地址不一致问题**:原 footer 与「联系我们」中填写的"云南省昆明市西山区滇池度假区(碧鸡街道车家壁513号)"与 D-U-N-S 官网登记的注册地址(红河州弥勒市朋普镇)不符。统一社会信用代码 92532526MA6PCX153W 中的 532526 行政区划码对应弥勒市,印证 D-U-N-S 官网地址为工商注册地址。本次以 D-U-N-S 官网地址为准统一全文,避免 Apple/Google 审核时主体信息核对出现偏差
|
||
- **法定代表人字段移除**:初版主体信息含「法定代表人:李振阳(Zhenyang Li)」字段,应要求移除(App 内 + 网页中英三处均已删除)。隐私政策主体信息聚焦"组织身份"而非"自然人身份",更符合数据最小化原则
|
||
- **英文公司名修正**:原 DATA.en.company 为"Mile City Pengpu Town Weifengbao Network Technology Studio",与 D-U-N-S 官网登记"Mile Pengpu Town Micro Storm Network Technology Studio"不一致。本次以 D-U-N-S 官网为准修正,确保 Apple Developer Program 主体核验通过
|
||
|
||
#### 4. 合规建议(供后续维护参考)
|
||
- **App Store Connect**:在 App 信息 → "开发者信息"处填写 D-U-N-S 586261192,确保与隐私政策披露一致
|
||
- **Google Play Console**:在"应用内容 → 隐私政策"URL 提交本页面,主体信息保持一致
|
||
- **欧盟 GDPR 代表**:当前"国际数据保护"章节声明已指定欧盟代表,但未具体披露代表身份。若目标用户含欧盟且无实体,建议后续在主体信息或国际数据保护章节明确 EU 代表身份与联系方式(GDPR Art.27)
|
||
- **CCPA"请勿出售"入口**:当前声明"不出售个人信息"已合规;若后续商业模式变更需设立专门"请勿出售我的个人信息"链接
|
||
- **关于页同步**:建议检查 App 内「关于」页面(t_about.dart)是否同步展示 D-U-N-S,保持多端一致(本次未涉及,留待后续)
|
||
|
||
#### 涉及文件
|
||
- `lib/features/settings/presentation/privacy/privacy_policy_page.dart`(新增「9. 主体信息」章节 + 章节顺延 + 文件头更新)
|
||
- `docs/toolsapi/public/agreements/privacy-policy.html`(中文区/英文区新增「主体信息」章节 + 章节顺延 + footer 加 D-U-N-S + DATA 加 duns 字段 + 地址统一为 D-U-N-S 官网注册地址 + 英文公司名修正 + switchLang 加 footer-duns 同步)
|
||
- `CHANGELOG.md`
|
||
|
||
---
|
||
|
||
***
|
||
|
||
## [v6.131.0] - 2026-06-26
|
||
|
||
### ✨ 新增(工作台仪表盘全面国际化 + 动态主题/样式 + 交互增强)
|
||
|
||
#### 背景
|
||
工作台模式右栏默认面板 `OverviewDashboard` 存在 23 处硬编码中文(问候语/区块标题/快捷操作标签/空状态/统计单位),不支持多语言;未消费 `AppThemeExtension` 的 `fontScale`/`fontWeight` 动态样式;动画 duration 硬编码不响应无障碍 `reduceAnimations` 降级;问候语在 build 内取 `DateTime.now().hour`,跨午时停留不刷新;`favoriteState.total` 等字段缺空指针防护。
|
||
|
||
#### 1. 新增翻译模块 TDashboard
|
||
- 新建 `lib/l10n/types/t_dashboard.dart` — 定义 `TDashboard` 类型,27 个字段覆盖:时段问候语(5)、问候区(1)、今日推荐(4)、快捷操作(9)、最近浏览(2)、数据统计(6),含 `toMap`/`fromMap`/`fallback` 回退
|
||
- `lib/l10n/types/t_root.dart` — 注入 `dashboard` 字段(构造函数/字段/toMap/fromMap 四处)
|
||
- `lib/l10n/types/t.dart` — 导出 `t_dashboard.dart`
|
||
- 14 语言文件补全 `dashboard: TDashboard(...)` 段落:zh_CN/en 手写,zh_TW/ja/ko/de/it/es/fr/pt/ru/ar/bn/hi 12 种补全,`T.withFallback` 自动回退英语
|
||
|
||
#### 2. 重构 overview_dashboard.dart
|
||
- **ConsumerStatefulWidget + Timer**:每分钟校验小时变更,跨午时(12:00)/傍晚(18:00)/深夜(22:00)自动刷新问候语与图标,`dispose` 取消定时器
|
||
- **全文案多语言**:`ref.watch(translationsProvider)` 接入,23 处硬编码替换为 `t.dashboard.xxx`
|
||
- **动态主题/样式**:字号统一 `× ext.fontScale` 缩放,正文字重跟随 `ext.fontWeight`;`GlassContainer` 自动消费 `cardStyleId`/`cornerRadiusId`/`glassBlurMultiplier`
|
||
- **触觉反馈**:快捷操作/最近浏览点击加 `HapticFeedback.selectionClick()`
|
||
- **骨架屏**:今日推荐加载中(`homeState.isLoading && recommends.isEmpty`)显示 `Shimmer` 占位,深浅色自适应
|
||
- **无障碍降级**:响应 `generalSettingsProvider.reduceAnimations`,动画 `duration` 降为 `Duration.zero`,`AppIcon.animate` 关闭,`SlideAnimation.verticalOffset` 归零
|
||
- **空指针防护**:`authState.user?.signinDays ?? 0`,`streakDays` 单位用 `db.streakDayUnit` 拼接(i18n)
|
||
- **作者引用 i18n**:`'$db.authorPrefix${sentence.author ?? db.anonymousAuthor}'`,`author` 空字符串回退佚名
|
||
|
||
#### 3. 举一反三(附带发现 → 已同步修复)
|
||
- **`t_root.dart` 的 `toMap()` 缺少 `'quickCard': quickCard.toMap()` 键**(现有 bug),导致 `T.withFallback` 对 `quickCard` 模块失效——非中文/英语语言的 quickCard 空字段无法回退英语。**本次已补上该键**,并审查 `t_settings_advanced` 的 5 个 impeller 翻译键(v6.129.0 新增)的 toMap/fromMap 完整性,确认无遗漏
|
||
- **`translation_io_service.dart` 的 `importFromJson` 构造 `T(...)` 缺少 `dashboard` 参数**(v6.130.0 给 T 加 `required this.dashboard` 后未同步更新,会导致编译错误)。**本次已补上 `dashboard: fallback.dashboard`**,参照 `quickCard` 的处理模式(dashboard 属于非用户可编辑导出模块,直接用 fallback)
|
||
- **设计说明**:`tToMap`/`exportAllTranslations` 是"导出用户可编辑翻译"方法,设计上只含 nav/common/profile/settings/note/beta/submit/studyPlan/correction/leisure 9 个核心模块,不含 quickCard/dashboard——这是预期行为,无需改动
|
||
- **覆盖率检测自动包含**:`translation_coverage.dart` 通过 `zhCN.toMap()` 自动遍历所有 section,toMap 补全 quickCard 键后,覆盖率报告会自动包含 quickCard 模块的字段统计
|
||
|
||
#### 涉及文件
|
||
- `lib/l10n/types/t_dashboard.dart`(新增)
|
||
- `lib/l10n/types/t_root.dart`、`lib/l10n/types/t.dart`(注入导出 + 修复 toMap 漏写 quickCard 键)
|
||
- `lib/l10n/translation_io_service.dart`(修复 importFromJson 的 T 构造缺 dashboard 参数)
|
||
- `lib/l10n/languages/*.dart`(14 个语言文件补全 dashboard 段落)
|
||
- `lib/app/layout/overview_dashboard.dart`(重构)
|
||
- `CHANGELOG.md`
|
||
|
||
---***
|
||
|
||
## [v6.130.0] - 2026-06-26
|
||
|
||
### ✨ 新增(macOS 权限动态申请 — 替代 permission_handler_apple 无 macOS 实现的问题)
|
||
|
||
#### 背景:macOS 端权限无法动态申请
|
||
|
||
- **Issue**: `permission_handler_apple 9.4.9` 仅支持 iOS,无 macOS 实现。此前 macOS 端权限管理页面直接返回 `granted`,用户点击"请求权限"按钮不会弹出系统授权对话框,实际授权发生在首次访问资源时由系统自动触发(被动模式)。用户无法在权限管理页面主动触发授权。
|
||
- **方案**: 新建 `PermissionManager.swift` 原生权限管理器,通过 `apps.xy.xianyan/macos.app` MethodChannel 暴露给 Flutter,实现 macOS 原生权限动态申请。
|
||
- **支持的权限**:
|
||
| 权限 | 原生 API | 状态查询 | 请求方式 |
|
||
|---|---|---|---|
|
||
| 相机 | AVFoundation | `AVCaptureDevice.authorizationStatus(for: .video)` | `AVCaptureDevice.requestAccess(for: .video)` |
|
||
| 麦克风 | AVFoundation | `AVCaptureDevice.authorizationStatus(for: .audio)` | `AVCaptureDevice.requestAccess(for: .audio)` |
|
||
| 相册 | Photos framework | `PHPhotoLibrary.authorizationStatus(for:)` | `PHPhotoLibrary.requestAuthorization(for:)` |
|
||
| 通知 | UserNotifications | `UNUserNotificationCenter.getNotificationSettings` | `UNUserNotificationCenter.requestAuthorization` |
|
||
- **权限状态映射**:
|
||
- `notDetermined` → 未决定(可请求)
|
||
- `granted` → 已授权(含 `.authorized` / `.limited` / `.provisional` / `.ephemeral`)
|
||
- `permanentlyDenied` → 已拒绝(macOS 拒绝后不再次弹窗,需去系统设置)
|
||
- `restricted` → 受限(如家长控制)
|
||
- **实现**:
|
||
1. 新建 `macos/Runner/PermissionManager.swift` — 原生权限管理器(AVFoundation/Photos/UserNotifications)
|
||
2. `AppDelegate.swift` 的 `registerAppChannel` 中新增 3 个 MethodChannel 方法:
|
||
- `checkPermission` — 查询权限状态(异步)
|
||
- `requestPermission` — 请求权限(触发系统 TCC 弹窗)
|
||
- `openPermissionSettings` — 打开系统设置 - 隐私与安全性
|
||
3. `macos/Runner.xcodeproj/project.pbxproj` — 将 PermissionManager.swift 添加到 Xcode 项目
|
||
4. `MacosPlatformService` 新增 `checkPermission` / `requestPermission` / `openPermissionSettings` 三个方法
|
||
5. `PermissionService` 修改 macOS 分支:
|
||
- `checkStatus` — 调用原生 `checkPermission` 查询 TCC 状态
|
||
- `requestPermission` — 调用原生 `requestPermission` 触发系统弹窗(先显示说明对话框,用户确认后触发)
|
||
- `openSettings` — 调用原生 `openPermissionSettings` 跳转系统设置
|
||
6. `AppPermission` 枚举新增 `macosPermissionName` getter — 返回 macOS 原生权限名称映射
|
||
- **举一反三**:
|
||
- macOS TCC 权限模型与 iOS 不同:一旦用户拒绝,再次调用 `requestAccess` 不会弹窗(需用户去系统设置重置)。因此 macOS 上 `denied` 映射为 `permanentlyDenied`
|
||
- 通知权限是例外:可以多次请求(`requestAuthorization` 每次都会返回当前状态,但不弹窗)
|
||
- `UNUserNotificationCenter.getNotificationSettings` 是异步的,统一所有权限查询为异步回调模式
|
||
- **涉及文件**:
|
||
- `macos/Runner/PermissionManager.swift`(新增)
|
||
- `macos/Runner/AppDelegate.swift` — channel handler 新增 3 个 case
|
||
- `macos/Runner.xcodeproj/project.pbxproj` — 添加文件引用
|
||
- `lib/core/services/device/macos_platform_service.dart` — 新增权限管理方法
|
||
- `lib/core/services/auth/permission_service.dart` — macOS 分支改为调用原生 API
|
||
|
||
---
|
||
|
||
## [v6.129.0] - 2026-06-26
|
||
|
||
### 🐛 修复(macOS 渲染引擎 — Impeller 开关不生效 + x86 警告 + Apple Silicon 提示)
|
||
|
||
#### 问题:通用设置中 Impeller 开关无论打开或关闭,实际渲染引擎始终是 Skia
|
||
|
||
- **Issue**: 用户在「通用设置 → 高级 → Impeller 渲染引擎」切换开关并重启应用后,实际渲染引擎始终为 Skia,开关完全失效
|
||
- **根因**: `MainFlutterWindow.swift` 的 `awakeFromNib()` 使用 `setenv("FLUTTER_ENGINE_SWITCH_0", ...)` 试图通过环境变量传递 `--enable-impeller`/`--no-enable-impeller` 给 Flutter 引擎,但 **macOS 桌面 embedder (FlutterMacOS) 不读取 `FLUTTER_ENGINE_SWITCH_<N>` 环境变量**(该机制仅适用于 mobile embedder iOS/Android)。同时 `Info.plist` 中的 `FLTEnableImpeller` key 已被早期版本移除,引擎无任何信号可读取,只能走 SDK 默认值(Skia)。`currentImpellerEnabled` 缓存的是预期值而非实际值,进一步掩盖了问题,导致 UI 显示与引擎实际状态不符
|
||
- **方案**: 改用 `FlutterDartProject` + `commandLineArguments` 方式传递命令行参数,这是 macOS 桌面 embedder 唯一可靠的引擎开关控制方式,命令行参数优先级高于 Info.plist 的 `FLTEnableImpeller` 与 SDK 默认值
|
||
- **实现**:
|
||
1. `MainFlutterWindow.awakeFromNib()` — 移除 `setenv("FLUTTER_ENGINE_SWITCH_0", ...)`,改为:
|
||
```swift
|
||
let dartProject = FlutterDartProject()
|
||
dartProject.commandLineArguments = [impellerArg]
|
||
let flutterViewController = FlutterViewController(project: dartProject)
|
||
```
|
||
2. 保留 `currentImpellerEnabled` 静态变量缓存当前引擎实际运行状态(修复后此值与引擎实际状态一致)
|
||
3. 默认值策略不变:Apple Silicon 默认开启 Impeller,Intel Mac 默认关闭(Metal 驱动有渲染资源累积 bug)
|
||
- **举一反三**:
|
||
- `FLUTTER_ENGINE_SWITCH_<N>` 环境变量机制仅适用于 mobile embedder(iOS/Android),桌面 embedder(macOS/Windows/Linux)应使用 `FlutterDartProject.commandLineArguments` 或 Info.plist 的 `FLTEnableImpeller` key
|
||
- 引擎开关的"实际运行状态"应通过引擎 API 查询,不能直接复用"预期值"作为缓存,否则会掩盖开关不生效的问题
|
||
- Info.plist 中移除 `FLTEnableImpeller` 后必须确保有运行时替代方案(命令行参数 / FlutterDartProject),否则引擎走 SDK 默认值
|
||
- **涉及文件**:
|
||
- `macos/Runner/MainFlutterWindow.swift` — `awakeFromNib()` 改用 `FlutterDartProject.commandLineArguments`
|
||
- `macos/Runner/AppDelegate.swift` — 注释同步更新(实际逻辑未变)
|
||
|
||
---
|
||
|
||
#### 增强:x86 端开启 Impeller 前增加警告对话框
|
||
|
||
- **Issue**: Intel Mac (x86_64) 上 Metal 驱动有渲染资源累积 bug,开启 Impeller 可能导致色差/字体割裂/闪烁。用户在不知情情况下开启 Impeller 可能造成体验问题
|
||
- **方案**: 在 `_onImpellerToggle(value)` 中检测架构,若为 x86_64 且 `value == true`,先弹出二次确认警告对话框,用户确认后才写入设置;用户取消则恢复开关状态
|
||
- **实现**:
|
||
1. 新增 `_showX86ImpellerWarningDialog(T t)` 方法 — 返回 `Future<bool>`,用户确认返回 true,取消返回 false
|
||
2. 对话框包含警告图标(橙色三角形)+ 标题 + 详细描述(说明 Intel Mac 上的风险)+ 取消/仍要开启 两个按钮
|
||
3. 用户取消时通过 `setState` 恢复 `_impellerEnabled = false`,不写入 UserDefaults
|
||
- **Apple Silicon 提示**: 在重启对话框中新增 arm64 架构专属提示卡片(蓝色背景 + sparkles 图标),说明"Apple Silicon 上 Impeller 基于 Metal 性能更好,推荐开启"
|
||
- **新增翻译键**(5 个,覆盖全部 14 种语言):
|
||
- `impellerX86WarningTitle` — Intel Mac 兼容性警告标题
|
||
- `impellerX86WarningDesc` — 警告描述(说明风险 + Apple Silicon 推荐使用 Skia)
|
||
- `impellerX86WarningConfirm` — 仍要开启
|
||
- `impellerX86WarningCancel` — 取消
|
||
- `impellerAppleSiliconTip` — Apple Silicon 推荐开启提示
|
||
- **涉及文件**:
|
||
- `lib/features/settings/presentation/general/general_settings_page.dart` — `_onImpellerToggle` / `_showX86ImpellerWarningDialog` / `_buildImpellerDialogContent`
|
||
- `lib/l10n/types/t_settings_advanced.dart` — 新增 5 个字段 + toMap + fromMap
|
||
- `lib/l10n/languages/*.dart` — 14 种语言全部添加新键
|
||
|
||
---
|
||
|
||
## [v6.128.0] - 2026-06-26
|
||
|
||
### 🐛 修复(macOS 运行时 — permission_handler MissingPluginException + WebRTC 构建冲突)
|
||
|
||
#### 问题一:permission_handler_apple 无 macOS 实现 — 权限管理页点击报错(App Store 2.1(a) 直接原因)
|
||
|
||
- **Issue**: macOS 端权限管理页点击「相册」「麦克风」「相机」请求时抛出 `MissingPluginException(No implementation found for method checkPermissionStatus on channel flutter.baseflow.com/permissions/methods)`,点击「去设置」按钮同样报错。这是 v6.127.0 未覆盖的运行时问题,是 App Store 审核被拒 Guideline 2.1(a) 的直接原因
|
||
- **根因**: `permission_handler_apple 9.4.9` 的 `pubspec.yaml` 仅声明 `ios` 平台,**无 macOS 实现**:
|
||
```yaml
|
||
flutter:
|
||
plugin:
|
||
implements: permission_handler
|
||
platforms:
|
||
ios: # ⚠️ 仅 ios,无 macos
|
||
pluginClass: PermissionHandlerPlugin
|
||
```
|
||
- `permission_handler_apple-9.4.9/` 目录下只有 `ios/`,无 `macos/`
|
||
- macOS 端 `GeneratedPluginRegistrant.swift` 不注册 `PermissionHandlerApplePlugin`
|
||
- 调用 `Permission.camera.status` / `Permission.photos.request()` / `openAppSettings()` 均抛出 `MissingPluginException`
|
||
- **方案**: macOS sandbox 下权限由 **entitlement + Info.plist 用法说明** 自动管理,系统在首次访问受保护资源时弹出授权对话框(由 OS 触发,不由 App 调用)。`permission_service.dart` 添加 macOS 早返回逻辑,跳过 `permission_handler` 调用
|
||
- **实现**:
|
||
1. `checkStatus()` — macOS 端直接返回 `granted`,避免调用未注册的方法通道
|
||
2. `requestPermission()` — macOS 端直接返回 `true`,权限由系统 sandbox + entitlement 自动管理
|
||
3. `openSettings()` — 新增 macOS 原生跳转:`Process.run('open', ['x-apple.systempreferences:com.apple.preference.security?Privacy'])`,打开「系统设置 > 隐私与安全性」
|
||
4. `_showSettingsDialog` / `_showDeniedDialog` — 将 `openAppSettings()` 调用改为 `openSettings()`,确保 macOS 端「去设置」按钮可用
|
||
5. `checkStatus()` / `requestPermission()` 新增 `on MissingPluginException` 兜底,Linux 等无 `permission_handler` 实现的桌面端视为已授权
|
||
- **举一反三**:
|
||
- Flutter 联邦插件(federated plugin)的平台实现可能只覆盖部分平台,使用前需检查 `pub-cache` 中实际包的 `pubspec.yaml` 平台声明
|
||
- macOS sandbox 下权限模型与 iOS 不同:iOS 需 App 主动调用 `request()`,macOS 由 OS 在首次访问时自动弹出,entitlement 是「App 级授权」,OS 弹框是「用户级授权」
|
||
- `MissingPluginException` 不应仅靠 `try-catch` 静默吞掉,需在入口处按平台显式短路,否则权限管理页状态显示为「未决定」具有误导性
|
||
- **涉及文件**:
|
||
- `lib/core/services/auth/permission_service.dart` — `checkStatus` / `requestPermission` / `openSettings` / `_showSettingsDialog` / `_showDeniedDialog`
|
||
|
||
---
|
||
|
||
#### 问题二:flutter_webrtc 1.4.0 与 macOS WebRTC-SDK 144.7559.09 版本冲突
|
||
|
||
- **Issue**: macOS `pod install` 报错 `CocoaPods could not find compatible versions for pod "WebRTC-SDK"`,`Podfile.lock` 锁定 `144.7559.09`,但 `flutter_webrtc 1.4.0` 的 macOS podspec 依赖 `144.7559.01`(`.01` 版本)
|
||
- **根因**: commit 667f3e49 添加本地 `macos/WebRTC-SDK.podspec.json` 声明 `.09` 版本(Intel Mac 渲染修复补丁),但 `flutter_webrtc` 保持在 1.4.0(pins `.01`),两者冲突
|
||
- **方案**: 升级 `flutter_webrtc` 1.4.0 → 1.5.2(其 macOS podspec 已对齐声明 `s.dependency 'WebRTC-SDK', '144.7559.09'`)
|
||
- **验证**: `flutter analyze` 通过,`screen_share_page.dart` / `webrtc_service.dart` / `screen_share_provider.dart` 无 API 变更
|
||
- **鸿蒙端**: 本地包 `packages/flutter_webrtc`(v1.4.0-ohos.1)无法直接升级,待后续同步到 `1.5.2-ohos`;两端 pubspec 独立互不影响
|
||
- **涉及文件**:
|
||
- `pubspec.yaml` — `flutter_webrtc: ^1.4.0` → `^1.5.2`
|
||
- `pubspec.macos.yaml` — 同上
|
||
- `pubspec.ohos.yaml` — 更新 flutter_webrtc 注释(远程端已升至 1.5.2)
|
||
- `pubspec.lock` — flutter_webrtc sha256 更新
|
||
- `iOS_macOS_Developer_Guide.md` — 差异对照表更新 + 新增 §2.8.9 flutter_webrtc 特殊包说明 + 新增 §2.8.10 permission_handler_apple macOS 缺失实现说明
|
||
|
||
---
|
||
|
||
## [v6.127.0] - 2026-06-26
|
||
|
||
### 🐛 修复(macOS App Store 审核被拒 — Guideline 2.4.5(i) / 2.1(a))
|
||
|
||
#### macOS 审核三大问题修复 — entitlement 配置 + 权限管理全平台适配
|
||
|
||
> **注**: 本条目覆盖 entitlement 配置与权限卡片平台过滤;运行时 `MissingPluginException` 修复见 v6.128.0。
|
||
|
||
- **Issue**: macOS 版本 6.6.25 (2606260) 提交 App Store 被拒,三条审核意见:
|
||
1. **Guideline 2.4.5(i)** — 声明了未使用的 entitlement:`files.downloads.read-only`、`files.downloads.read-write`、`network.server`
|
||
2. **Guideline 2.4.5(i)** — 有功能但缺对应 entitlement:相机缺 `com.apple.security.device.camera`,位置缺 `com.apple.security.personal-information.location`
|
||
3. **Guideline 2.1(a)** — 在权限管理页点击「相册&存储」「麦克风」请求时显示错误信息
|
||
- **根因**:
|
||
1. `Release.entitlements` / `DebugProfile.entitlements` 声明了 `files.downloads.*` 但全代码库无 `getDownloadsDirectory` 调用(字体下载用的是 `getApplicationDocumentsDirectory`),属于声明未使用
|
||
2. 扫码 / 拍照 / OCR / 语音录制 / 语音转文字功能在共享代码中无平台守卫,macOS 端实际可用,但未声明 `device.camera` / `device.audio-input` / `personal-information.photos-library` entitlement,且 `Info.plist` 缺 `NSCameraUsageDescription` / `NSMicrophoneUsageDescription` 用法说明
|
||
3. `permission_service.dart` 的 `isPlatformRelevant` 对 macOS 无任何过滤(鸿蒙端有过滤,macOS 落到 `return true`),导致权限管理页在 macOS 展示相机/相册/位置/附近设备/麦克风卡片;点击请求时因 entitlement + Info.plist 双缺,`permission_handler` 在 sandbox 下请求失败弹出错误对话框
|
||
4. 位置(GPS)实际全平台未使用(仅 IP 定位 + 手动文本输入),却展示位置权限卡片具有误导性
|
||
- **方案**:
|
||
1. macOS entitlements:移除未用的 `files.downloads.*`;新增 `device.camera` / `device.audio-input` / `personal-information.photos-library`;保留 `network.server`(LocalSend 文件传输本地服务器必需)
|
||
2. macOS `Info.plist`:新增 `NSCameraUsageDescription` / `NSMicrophoneUsageDescription` 用法说明
|
||
3. 重写 `isPlatformRelevant` 为全平台适配:macOS/Windows/Linux/Web 各自过滤;位置卡片仅移动端展示(桌面端/Web 只用 IP 定位);附近设备仅移动端;相册仅 macOS + 移动端(Windows/Linux/Web 用文件选择器)
|
||
4. 新增 `localServer` 虚拟权限卡片(仅桌面端展示),对应 `network.server` entitlement,向用户透明声明 LocalSend 文件传输本地服务器能力
|
||
- **实现**:
|
||
1. **entitlements** — `DebugProfile.entitlements` + `Release.entitlements` 同步修改:移除 2 个 downloads 键,新增 3 个 device/photos 键
|
||
2. **Info.plist** — 新增相机 / 麦克风用法说明(中文)
|
||
3. **permission_service.dart** — `AppPermission` 枚举新增 `localServer` 虚拟权限;`label/description/usageScenes/denialImpact` 4 个 switch 补全 case;`isPlatformRelevant` 重写为「特殊权限单独处理 + 鸿蒙/桌面/Web/移动端分支」结构
|
||
4. **l10n** — `t_settings_permission.dart` 新增 `permLocalServer*` 4 字段(构造/字段/toMap/fromMap);14 个语言文件新增 4 条翻译
|
||
- **举一反三**:
|
||
- entitlement 应遵循「最小权限集」原则,声明即需有对应功能,否则触发审核质疑;后续新增 entitlement 必须同步在权限管理页向用户声明用途
|
||
- 跨平台权限过滤不能只覆盖单一平台(鸿蒙),所有桌面端 / Web 都需显式过滤,否则同一 bug 会在其他平台复现
|
||
- macOS sandbox 下 `permission_handler` 依赖 entitlement + Info.plist usage description 双重配置才能弹系统授权框,缺一即失败
|
||
- **审核回复要点**(提交 App Store Connect 时使用):
|
||
- `network.server` 用于 LocalSend 局域网文件传输(`HttpServer.bind` / `ServerSocket.bind`),见 `localsend_service.dart` / `tcp_socket_service.dart`
|
||
- 已移除未使用的 `files.downloads.*` entitlement
|
||
- 已补全相机 / 麦克风 entitlement 与用法说明,权限管理页请求功能正常
|
||
- **涉及文件**:
|
||
- `macos/Runner/DebugProfile.entitlements` — 移除 downloads,新增 camera/audio-input/photos-library
|
||
- `macos/Runner/Release.entitlements` — 同上
|
||
- `macos/Runner/Info.plist` — 新增 NSCameraUsageDescription / NSMicrophoneUsageDescription
|
||
- `lib/core/services/auth/permission_service.dart` — 新增 localServer 枚举 + 全平台 isPlatformRelevant
|
||
- `lib/l10n/types/t_settings_permission.dart` — 新增 permLocalServer 4 字段
|
||
- `lib/l10n/languages/*.dart`(14 个语言文件)— 新增 permLocalServer 4 条翻译
|
||
|
||
---
|
||
|
||
## [v6.126.0] - 2026-06-25
|
||
|
||
### 🐛 修复(键盘事件)
|
||
|
||
#### RawKeyboard 断言错误修复
|
||
|
||
- **Issue**: `[ERR-MQSQKCXW]` Flutter framework 断言失败:`'event is! RawKeyDownEvent || _keysPressed.isNotEmpty'`,出现在 `raw_keyboard.dart` 第 863 行
|
||
- **根因**: Flutter 框架已知 bug([#109677](https://github.com/flutter/flutter/issues/109677)、[#116530](https://github.com/flutter/flutter/issues/116530))。`RawKeyboard`(已废弃)与 `HardwareKeyboard` 同时接收键盘事件,当修饰键标志未正确设置时,`RawKeyboard` 内部 `_keysPressed` 为空,收到 `RawKeyDownEvent` 时断言失败
|
||
- **方案**: 在 `main()` 中 `WidgetsFlutterBinding.ensureInitialized()` 之后添加 `HardwareKeyboard.instance.addHandler((_) => false)` 处理器,确保键盘事件正确传播
|
||
- **涉及文件**:
|
||
- `lib/main.dart` — 第 58-63 行新增 `HardwareKeyboard` 处理器
|
||
|
||
---
|
||
|
||
## [v6.125.0] - 2026-06-25
|
||
|
||
### 🐛 修复(macOS 双标题栏)
|
||
|
||
#### macOS 窗口"双标题栏"问题 — 隐藏系统红黄绿灯按钮
|
||
|
||
- **Issue**: macOS 端软件窗口显示两个窗口栏,红黄绿灯那一栏上方还有一栏(Flutter 自绘的 `DesktopWindowTitleBar`),需去掉系统的红黄绿灯栏
|
||
- **根因**:
|
||
1. Flutter 侧 `DesktopWindowTitleBar` 已自绘 macOS 风格的红黄绿三圆点按钮(`_MacOSTrafficButton`)
|
||
2. 原生侧 `awakeFromNib` 仅设置了 `titlebarAppearsTransparent = true` / `titleVisibility = .hidden` / `fullSizeContentView`,**未隐藏系统 `standardWindowButton`**
|
||
3. 隐藏系统按钮的逻辑仅写在 `setDarkMode` 方法中(`MainFlutterWindow.swift` 第 402-404 行),而 `setDarkMode` 经 `MacosPlatformService.syncTheme` 调用时有去重机制(`if (_themeInitialized && _lastIsDark == isDark) return`),仅主题变化时触发一次
|
||
4. 因此应用启动初期、全屏退出后、主题未变化时,系统红黄绿灯按钮始终可见,与 Flutter 自绘标题栏形成"双标题栏"
|
||
- **方案**: 提取公共方法 `hideSystemTrafficButtons(of:)`,在三个时机统一隐藏系统红黄绿灯按钮
|
||
- **实现**:
|
||
1. **新增 `hideSystemTrafficButtons(of window: NSWindow? = nil)`** — 隐藏 `closeButton`/`miniaturizeButton`/`zoomButton`;参数为 nil 时遍历所有窗口(兼容多窗口),指定窗口时仅操作该窗口(启动时性能更优)
|
||
2. **`awakeFromNib`** — 在设置透明属性后立即调用 `hideSystemTrafficButtons(of: self)`,确保启动时即隐藏(Intel Mac 也调用,因 Flutter 自绘标题栏在所有桌面平台一致显示)
|
||
3. **`setDarkMode`** — 将原内联的三行 `standardWindowButton?.isHidden = true` 替换为 `hideSystemTrafficButtons()` 调用,消除重复代码
|
||
4. **`handleFullScreenExit`** — 全屏退出后重新调用 `hideSystemTrafficButtons(of: self)`,防止系统重置按钮可见性
|
||
- **举一反三**:
|
||
- 此前 `setDarkMode` 承担了"主题切换 + 隐藏按钮"两个职责,按钮隐藏依赖主题切换时机触发,属于隐式依赖。现提取为独立方法后,职责清晰,可在任意时机显式调用
|
||
- 类似的"启动时未初始化"问题排查方向:所有依赖 Dart 侧 MethodChannel 回调才执行的原生初始化逻辑,都应在 `awakeFromNib` 中有默认值或主动初始化,避免通道未就绪时显示异常
|
||
- **涉及文件**:
|
||
- `macos/Runner/MainFlutterWindow.swift` — 新增 `hideSystemTrafficButtons(of:)` 方法;`awakeFromNib`/`setDarkMode`/`handleFullScreenExit` 三处调用;更新文件头注释
|
||
|
||
---
|
||
|
||
## [v6.124.0] - 2026-06-25
|
||
|
||
### ✨ 增强(Impeller 开关对话框 — 架构/实时状态/业务提示)
|
||
|
||
#### Impeller 开关对话框增强 — 显示 CPU 架构与引擎实时运行状态
|
||
|
||
- **Issue**: 通用设置页 Impeller 开关对话框存在三个问题
|
||
1. "稍后重启"仅关闭对话框,无业务提示和状态反馈
|
||
2. "立即重启"只关闭应用未启动新实例(`NSApp.terminate` 执行过快)
|
||
3. 对话框缺少 CPU 架构和引擎实时运行状态信息
|
||
- **方案**:
|
||
1. **原生侧新增 `currentImpellerEnabled` 静态缓存** — 区分"用户设置值"(UserDefaults,可能已修改未生效)和"引擎实际运行状态"(应用启动时固定)
|
||
2. **新增 `getImpellerRunningStatus` channel** — 返回当前引擎实际是否在用 Impeller
|
||
3. **新增 `getCpuArchitecture` channel** — 返回 `arm64`/`x86_64`,用于对话框显示
|
||
4. **`restartApp` 改进** — 添加 800ms 延迟、错误检查、日志;先启动新实例再终止当前实例
|
||
5. **对话框增强** — 显示三行状态:当前架构 / 当前引擎(实际运行)/ 重启后
|
||
6. **稍后重启增强** — 关闭对话框后显示 Toast 提示"设置已保存,重启后生效"
|
||
7. **副标题增强** — 用户修改设置未重启时,副标题显示"待重启生效"标记
|
||
- **实现**:
|
||
1. **MainFlutterWindow.swift**: 新增 `currentImpellerEnabled` 静态变量,在 awakeFromNib 中缓存引擎实际运行状态
|
||
2. **AppDelegate.swift**: 新增 `getImpellerRunningStatus`/`getCpuArchitecture` channel 方法;`restartApp` 添加延迟+错误检查+日志
|
||
3. **MacosPlatformService.dart**: 新增 `getImpellerRunningStatus()`/`getCpuArchitecture()` 方法;`restartApp()` 改为返回 `Future<bool>`
|
||
4. **general_settings_page.dart**: 新增 `_impellerPendingRestart`/`_cpuArch`/`_impellerRunning` 状态变量;`_loadImpellerEnabled` 并行加载三个值;重写 `_onImpellerToggle` 增强对话框;新增 `_buildImpellerDialogContent`/`_buildStatusRow` 辅助方法
|
||
5. **general_settings_sections.dart**: 新增 `impellerPendingRestart` 参数,副标题动态显示"待重启生效"标记
|
||
6. **i18n**: 为 14 个语言文件添加 5 个新翻译键(impellerSavedToast/impellerPendingRestart/impellerDialogArch/impellerDialogRunning/impellerDialogAfterRestart)
|
||
- **涉及文件**:
|
||
- `macos/Runner/MainFlutterWindow.swift` — 新增 currentImpellerEnabled 静态缓存
|
||
- `macos/Runner/AppDelegate.swift` — 新增 getImpellerRunningStatus/getCpuArchitecture channel,改进 restartApp
|
||
- `lib/core/services/device/macos_platform_service.dart` — 新增 getImpellerRunningStatus/getCpuArchitecture 方法
|
||
- `lib/features/settings/presentation/general/general_settings_page.dart` — 增强对话框显示架构+实时状态,稍后重启显示 Toast
|
||
- `lib/features/settings/presentation/general/general_settings_sections.dart` — 副标题显示待重启标记
|
||
- `lib/l10n/types/t_settings_advanced.dart` — 新增 5 个翻译键定义
|
||
- `lib/l10n/languages/*.dart` — 14 个语言文件添加新翻译键
|
||
|
||
---
|
||
|
||
## [v6.123.0] - 2026-06-25
|
||
|
||
### 🐛 修复(Intel Mac 渲染色差/字体割裂)
|
||
|
||
#### Impeller 渲染引擎开关 — 默认值策略 + 立即重启 + i18n
|
||
|
||
- **Issue**: Intel Mac 上页面跳转动画闪烁、字体模糊割裂、色差(前几次跳转正常,累积后出现)
|
||
- **根因**: Flutter Impeller 渲染引擎在 Intel Mac 的 Metal 驱动上有渲染资源累积 bug,导致色差/字体割裂
|
||
- **验证方法**:
|
||
1. 原生 AppKit demo(纯 Cocoa 渲染)排除硬件问题 — 字体显示正常,无异常色彩
|
||
2. 禁用 Impeller 后问题消失,确认根因在 Impeller
|
||
- **方案**: 默认值策略 — Apple Silicon 开启 Impeller(性能更好),Intel Mac 关闭(避免色差)。用户可在「通用设置 → 高级」中手动覆盖,修改后需重启应用生效
|
||
- **实现**:
|
||
1. **Info.plist**: 移除 `FLTEnableImpeller` 声明,由原生代码根据用户设置动态控制
|
||
2. **MainFlutterWindow.swift**:
|
||
- 手动创建 `FlutterDartProject`,通过 KVC 设置 `commandLineArguments`(`--enable-impeller`/`--no-enable-impeller`)
|
||
- 命令行参数优先级高于 Info.plist,可动态控制 Impeller 开关
|
||
- 新增 `shouldEnableImpeller()` 方法,通过 `object(forKey:)` 判断用户是否已显式设置:
|
||
- 已设置:返回用户设置的值
|
||
- 未设置(首次启动):Apple Silicon 默认开启,Intel Mac 默认关闭
|
||
3. **AppDelegate.swift**:
|
||
- 新增 `getImpellerEnabled`/`setImpellerEnabled` MethodChannel 方法
|
||
- `getImpellerEnabled` 使用 `getImpellerEnabledWithDefault()` 保持与 `shouldEnableImpeller()` 逻辑一致
|
||
- 新增 `restartApp` 原生方法 — 通过 `NSWorkspace.openApplication` 启动新实例后 `NSApp.terminate` 终止当前实例
|
||
4. **MacosPlatformService.dart**: 新增 `getImpellerEnabled()`/`setImpellerEnabled()`/`restartApp()` 方法
|
||
5. **general_settings_sections.dart**: 高级分组新增 Impeller 开关(仅 macOS),使用 i18n 翻译键
|
||
6. **general_settings_page.dart**: 加载/切换 Impeller 状态,切换后弹出重启提示对话框(含"立即重启"和"稍后重启"两个按钮)
|
||
7. **i18n**: 为 14 个语言文件添加 8 个 Impeller 翻译键(zh_cn/zh_tw/en/ja/ko/fr/de/es/pt/ru/ar/hi/bn/it)
|
||
- **涉及文件**:
|
||
- `macos/Runner/Info.plist` — 移除 FLTEnableImpeller
|
||
- `macos/Runner/MainFlutterWindow.swift` — 手动创建引擎传递命令行参数 + 默认值策略
|
||
- `macos/Runner/AppDelegate.swift` — Impeller MethodChannel + restartApp 方法
|
||
- `lib/core/services/device/macos_platform_service.dart` — Impeller 开关 + restartApp 方法
|
||
- `lib/features/settings/presentation/general/general_settings_sections.dart` — 高级分组 Impeller 开关
|
||
- `lib/features/settings/presentation/general/general_settings_page.dart` — 加载/切换 Impeller 状态 + 重启对话框
|
||
- `lib/l10n/types/t_settings_advanced.dart` — 新增 8 个 Impeller 翻译键定义
|
||
- `lib/l10n/languages/*.dart` — 14 个语言文件添加 Impeller 翻译
|
||
|
||
---
|
||
|
||
## [v6.122.0] - 2026-06-24
|
||
|
||
### 🔧 维护(pro_image_editor 版本锁定)
|
||
|
||
#### 1. pro_image_editor 版本锁定为 12.4.4
|
||
- **Issue**: 本地暂存修改将 `pro_image_editor` 从 12.4.4 改为 ^12.4.8,与远程代码产生冲突
|
||
- **原因**: 12.5.x 版本与 Flutter 3.33 运行时不兼容,需锁定到 12.4.4 确保稳定性
|
||
- **修复**:
|
||
- `pubspec.macos.yaml`: 锁定 `pro_image_editor: 12.4.4`
|
||
- `pubspec.ohos.yaml`: 锁定 `pro_image_editor: 12.4.4`
|
||
- 注释说明 12.5.x 与 Flutter 3.33 运行时不兼容
|
||
- **涉及文件**: `pubspec.macos.yaml`, `pubspec.ohos.yaml`
|
||
|
||
---
|
||
|
||
## [v6.121.0] - 2026-06-24
|
||
|
||
### 🐛 修复(macOS 启动期闪退)
|
||
|
||
#### 闪退根因分析
|
||
- **Issue**: macOS 端启动后 3.5 秒闪退,崩溃栈与 v6.119.0 修复前完全一致
|
||
- **崩溃栈**:
|
||
- Thread 0 (main): `macos_window_utils` → `[NSWindow setBackgroundColor:]` → `NSThemeFrame._updateBackdropView` → `-[NSView removeFromSuperview]`
|
||
- Thread 8 (io.flutter.raster): `impeller::Canvas::SetupRenderPass()` — `KERN_INVALID_ADDRESS at 0x0`
|
||
- **根因**:
|
||
- `macos_window_utils` 插件的 `MainFlutterWindowManipulator.start()` 方法内部会调用 `showTitle()` / `makeTitlebarOpaque()` / `disableFullSizeContentView()` / `setWindowBackgroundColorToDefaultColor()` 四个方法
|
||
- `setWindowBackgroundColorToDefaultColor()` 会触发 `[NSWindow setBackgroundColor:]` → `NSThemeFrame._updateBackdropView` → `removeFromSuperview`
|
||
- Dart 侧 `WindowManipulator.initialize()` → 原生 `reset()` → 当 `mainFlutterWindow == nil` 时走 `start(nil)` 分支 → 触发上述竞态
|
||
- v6.119.0 的修复仅跳过了 Dart 侧 `applyEffect` 中的冗余调用,但**未跳过** `WindowManipulator.initialize()` 内部的 `reset()` → `start(nil)` 路径
|
||
- **修复**:
|
||
1. 在 `MainFlutterWindow.awakeFromNib` 中提前调用 `MainFlutterWindowManipulator.start(mainFlutterWindow: self)`,让 `mainFlutterWindow` 静态属性先被设置
|
||
2. 之后 `WindowManipulator.initialize()` 的 `reset()` 会因 `mainFlutterWindow != nil` 走 `passthroughViewHandler.start()` 分支,不再触发 `setWindowBackgroundColorToDefaultColor()`
|
||
3. `start()` 会覆盖 `awakeFromNib` 预设的透明属性,因此在 `start()` 之后重新设置(此时 Flutter 引擎未启动,Impeller raster 线程未运行,无竞态风险)
|
||
- **涉及文件**:
|
||
- `macos/Runner/MainFlutterWindow.swift` — 导入 `macos_window_utils`,提前调用 `start`,之后重新设置透明属性
|
||
|
||
### 🎨 优化(macOS 窗口标题栏 + 系统菜单栏)
|
||
|
||
#### 1. macOS 系统菜单栏支持多语言
|
||
- **Issue**: macOS 系统菜单栏(应用菜单/编辑菜单/视图菜单/窗口菜单/帮助菜单)项目仅显示英文,未跟随系统语言
|
||
- **根因**:
|
||
- `MainMenu.xib` 只有 `Base.lproj` 版本,无本地化 `.strings` 文件
|
||
- `project.pbxproj` 的 `knownRegions` 仅包含 `en` 和 `Base`
|
||
- **修复**:
|
||
1. **创建本地化文件**:为 5 种主要语言创建 `MainMenu.strings`:
|
||
- `en.lproj/MainMenu.strings` — 英语
|
||
- `zh-Hans.lproj/MainMenu.strings` — 简体中文
|
||
- `zh-Hant.lproj/MainMenu.strings` — 繁体中文
|
||
- `ja.lproj/MainMenu.strings` — 日语
|
||
- `ko.lproj/MainMenu.strings` — 韩语
|
||
2. **项目配置**(`project.pbxproj`):
|
||
- `knownRegions` 添加 `zh-Hans`/`zh-Hant`/`ja`/`ko`
|
||
- 创建 `MainMenu.strings` 的 `PBXVariantGroup` 和 `PBXFileReference`
|
||
- 将 `MainMenu.strings` 添加到 `PBXResourcesBuildPhase`
|
||
- **效果**: 系统菜单栏跟随 macOS 系统语言显示对应文案
|
||
- **涉及文件**:
|
||
- `macos/Runner/en.lproj/MainMenu.strings` — 新增
|
||
- `macos/Runner/zh-Hans.lproj/MainMenu.strings` — 新增
|
||
- `macos/Runner/zh-Hant.lproj/MainMenu.strings` — 新增
|
||
- `macos/Runner/ja.lproj/MainMenu.strings` — 新增
|
||
- `macos/Runner/ko.lproj/MainMenu.strings` — 新增
|
||
- `macos/Runner.xcodeproj/project.pbxproj` — 添加本地化支持
|
||
|
||
#### 2. 去掉视图菜单下的 "Enter Full Screen" 按钮
|
||
- **Issue**: macOS 系统菜单栏 → 视图菜单下有 "Enter Full Screen" 按钮,与 Flutter 侧自绘的绿灯按钮(弹 window size dialog)功能重复
|
||
- **修复**: 删除 `MainMenu.xib` 中 View 菜单下的 "Enter Full Screen" 菜单项
|
||
- **涉及文件**:
|
||
- `macos/Runner/Base.lproj/MainMenu.xib` — 删除 "Enter Full Screen" 菜单项
|
||
|
||
#### 3. 软件内切换语言后 macOS 系统菜单栏同步更新
|
||
- **Issue**: 软件内切换语言后,macOS 系统菜单栏( 右边的按钮:应用菜单/编辑菜单/视图菜单/窗口菜单/帮助菜单)未跟随切换,仍显示原语言
|
||
- **根因**:
|
||
- macOS 启动时根据系统语言偏好加载 `MainMenu.strings`,软件内切换语言仅更新 Flutter 侧 UI,未通知原生侧重新加载菜单标题
|
||
- `generalSettingsProvider.setLanguage` 方法只更新内存状态 + 持久化 KvStorage + 写日志,未调用任何 MethodChannel
|
||
- XIB 加载后 `NSMenuItem.identifier` 默认为 nil,无法通过 XIB objectId 直接定位菜单项
|
||
- **修复**(三层联动):
|
||
1. **原生侧 — 建立 objectId ↔ menuItem 映射**(`MainFlutterWindow.swift`):
|
||
- 新增 `menuItemIds` 静态数组,按深度优先顺序记录 XIB 中所有 menuItem 的 objectId(与 `MainMenu.xib` 声明顺序一致,跳过 separatorItem)
|
||
- 新增 `assignMenuItemIdentifiers()` 方法,在 `awakeFromNib` 中递归遍历 `NSApp.mainMenu`,按顺序将 objectId 赋值给对应 `menuItem.identifier`
|
||
2. **原生侧 — 切换菜单语言**(`MainFlutterWindow.swift`):
|
||
- 新增 `setMenuLanguage(languageId:)` 方法:
|
||
- 根据 languageId 解析 lproj 目录名(`system` → `Bundle.preferredLocalizations(from:)` 跟随系统;`zh-CN`/`zh-Hans` → `zh-Hans`;`zh-TW`/`zh-Hant` → `zh-Hant`;`en`/`ja`/`ko` → 对应 lproj)
|
||
- 通过 `Bundle.main.path(forResource:ofType:forLocalization:)` 加载对应语言的 `MainMenu.strings` 为 `[String: String]`
|
||
- 递归遍历 `NSApp.mainMenu`,根据 `menuItem.identifier`(XIB objectId)查找 `.strings` 中的 key(`"id.title"`)并更新标题
|
||
- 替换 `APP_NAME` 占位符为应用名(`CFBundleName`)
|
||
- MethodChannel 新增 `case "setMenuLanguage"` 处理,调用 `self.setMenuLanguage(languageId:)`
|
||
3. **Dart 侧 — 通知原生侧切换**:
|
||
- `MacosPlatformService.setMenuLanguage(String languageId)` — 新增静态方法,通过 `apps.xy.xianyan/macos` 通道调用原生侧
|
||
- `generalSettingsProvider.setLanguage` — 在更新状态后调用 `MacosPlatformService.setMenuLanguage(id)`
|
||
- `DesktopServicesManager._initMenuLanguage` — 新增方法,应用启动时根据用户设置的语言同步菜单栏(macOS 启动时按系统语言加载 `.strings`,需根据用户设置覆盖)
|
||
- **效果**:
|
||
- 软件内切换语言后,系统菜单栏所有菜单项标题立即更新为目标语言
|
||
- 应用启动时菜单栏语言与用户设置一致(而非跟随系统语言)
|
||
- **涉及文件**:
|
||
- `macos/Runner/MainFlutterWindow.swift` — 新增 `menuItemIds`/`assignMenuItemIdentifiers`/`setMenuLanguage`,awakeFromNib 调用 `assignMenuItemIdentifiers`
|
||
- `lib/core/services/device/macos_platform_service.dart` — 新增 `setMenuLanguage` 静态方法
|
||
- `lib/features/settings/providers/general_settings_provider.dart` — `setLanguage` 调用 `MacosPlatformService.setMenuLanguage`
|
||
- `lib/app/services/desktop_services_manager.dart` — 新增 `_initMenuLanguage`,启动时同步菜单栏语言
|
||
|
||
### 📦 打包(macOS App Store 提交包重新打包)
|
||
|
||
#### 1. 生成 App Store 提交用 pkg(x86_64 + arm64 通用二进制)
|
||
- **背景**: v6.121.0 修复完成后需重新打包提交 App Store
|
||
- **流程**:
|
||
1. `flutter clean` + `flutter pub get` + `pod install`
|
||
2. `flutter build macos --release` — 构建 universal binary (x86_64 + arm64),201.0MB
|
||
3. `xcodebuild archive` — 生成 xcarchive(Automatic 签名,Apple Development 证书)
|
||
4. `xcodebuild -exportArchive` — 导出 App Store pkg,使用 Apple Distribution 证书重新签名
|
||
- **产物**:
|
||
- `build/Runner.xcarchive` — 归档文件
|
||
- `build/Runner/闲言.pkg` — 110MB,App Store 提交包
|
||
- **验证**:
|
||
- 主可执行文件:universal (x86_64 + arm64)
|
||
- pkg 签名:3rd Party Mac Developer Installer: li zhenyang (5V9NVUU6K5)
|
||
- app 签名:Apple Distribution: li zhenyang (5V9NVUU6K5)
|
||
- Info.plist 包含 `ITSAppUsesNonExemptEncryption=false` 加密合规声明
|
||
- **新增文件**:
|
||
- `macos/ExportOptions.plist` — App Store 导出配置(method=app-store, teamID=5V9NVUU6K5)
|
||
- **上传方式**: 通过 Xcode Organizer 手动上传(`open -a Xcode-beta build/Runner.xcarchive` → Distribute App → App Store Connect → Upload)
|
||
- **注意事项**:
|
||
- Release 配置保持 `CODE_SIGN_IDENTITY = "Apple Development"` + `CODE_SIGN_STYLE = Automatic`,exportArchive 阶段会自动使用 Apple Distribution 证书重新签名
|
||
- 若直接将 CODE_SIGN_IDENTITY 改为 "Apple Distribution" 会与 Automatic 签名冲突,导致构建失败
|
||
|
||
---
|
||
|
||
## [v6.120.0] - 2026-06-24
|
||
|
||
### 📦 打包(macOS Universal Binary 提交 App Store)
|
||
|
||
#### 1. macOS 包支持 x86_64 + arm64 双架构,用于 App Store 提交
|
||
- **Issue**: 原 macOS Release 配置强制 `ARCHS = arm64`,仅生成 arm64 单架构包,x86_64 Mac 无法原生运行;且 Flutter SDK `xcode_backend.dart` 中存在强制 arm64 的"修复"代码,阻止 universal binary 构建
|
||
- **根因**:
|
||
- `macos/Runner.xcodeproj/project.pbxproj` Release 配置中 `ARCHS = arm64` 限制只构建 arm64
|
||
- Flutter SDK `packages/flutter_tools/bin/xcode_backend.dart` 第 646-648 行:当 `ARCHS` 包含空格(多架构)时强制改为 `arm64`,导致 `flutter assemble` 只生成 arm64 的 FlutterMacOS.framework
|
||
- Flutter SDK `flutter_tools.snapshot` 未包含 `darwin.dart` 中 `thinFramework` 的逐架构验证修复(snapshot 未随源码更新重新编译)
|
||
- **修复**:
|
||
1. **项目配置**(`macos/Runner.xcodeproj/project.pbxproj`):Release 配置 `ARCHS` 从 `arm64` 改为 `$(ARCHS_STANDARD)`,支持 universal binary
|
||
2. **Flutter SDK 修复**(`packages/flutter_tools/bin/xcode_backend.dart`):移除 `if (platform == TargetPlatform.macos && archs.contains(' ')) archs = 'arm64'` 强制 arm64 逻辑,保留多架构传入 `flutter assemble`
|
||
3. **Snapshot 重建**:修改 `bin/cache/flutter_tools.stamp` 触发 `flutter_tools.snapshot` 重新编译,使 `darwin.dart` 的逐架构 `lipo -verify_arch` 验证修复生效
|
||
- **构建结果**:
|
||
- `build/macos/Build/Products/Release/xianyan.app` — universal (x86_64 + arm64),200.9MB
|
||
- `build/xianyan.xcarchive` — universal archive,用 Apple Distribution 证书签名
|
||
- `build/pkg/闲言.pkg` — 110MB,用 3rd Party Mac Developer Installer 证书签名
|
||
- **验证**:
|
||
- 主可执行文件、FlutterMacOS.framework、App.framework 均为 universal (x86_64 + arm64)
|
||
- 签名正确:TeamIdentifier=5V9NVUU6K5,Format=app bundle with Mach-O universal
|
||
- Info.plist 包含 `ITSAppUsesNonExemptEncryption=false` 加密合规声明
|
||
- **上传方式**: 通过 Xcode Organizer 手动上传(open build/xianyan.xcarchive → Distribute App → App Store Connect → Upload)
|
||
- **涉及文件**:
|
||
- `macos/Runner.xcodeproj/project.pbxproj` — Release 配置 ARCHS 改为 universal
|
||
- Flutter SDK `packages/flutter_tools/bin/xcode_backend.dart` — 移除强制 arm64 逻辑(SDK 本地修改)
|
||
- Flutter SDK `bin/cache/flutter_tools.stamp` — 触发 snapshot 重建
|
||
- **注意事项**:
|
||
- Flutter SDK 的 `xcode_backend.dart` 修改是本地的,`flutter upgrade` 或重新安装 Flutter SDK 后需重新应用
|
||
- 后续 macOS App Store 打包可直接执行 `flutter build macos --release` + `xcodebuild archive` + `xcodebuild -exportArchive`
|
||
|
||
---
|
||
|
||
## [v6.119.0] - 2026-06-24
|
||
|
||
### 🐛 修复(macOS Release 模式启动闪退)
|
||
|
||
#### 1. macOS Release 模式启动 ~3.5 秒后 EXC_BAD_ACCESS 空指针崩溃
|
||
- **Issue**: macOS Release 模式启动后约 3.5 秒闪退,Debug 模式正常
|
||
- **崩溃栈**:
|
||
- Thread 8 (io.flutter.raster): `impeller::Canvas::SetupRenderPass()` (canvas.cc:1344) — `KERN_INVALID_ADDRESS at 0x0`
|
||
- Thread 0 (main): `macos_window_utils` → `[NSWindow setBackgroundColor:]` → `NSThemeFrame _updateBackdropView` → `-[NSView removeFromSuperview]`
|
||
- **根因**: 启动期 `applyEffect()` 通过 PostFrame 回调调用 `setWindowBackgroundColorToClear()`,触发 `[NSWindow setBackgroundColor:]` 修改视图层级(`removeFromSuperview`),与 Impeller raster 线程的 `SetupRenderPass` 产生竞态。Release 模式时序更紧凑,竞态更易触发;Debug 模式有 JIT 开销,时序较松散故未复现
|
||
- **修复**(四层防御):
|
||
1. **原生侧前移不变属性**(`MainFlutterWindow.awakeFromNib`):在 Flutter 引擎启动前预设 `isOpaque=false` + `backgroundColor=NSColor.clear` + `titlebarAppearsTransparent=true` + `titleVisibility=.hidden` + `fullSizeContentView`,消除渲染期 `setBackgroundColor:` 调用
|
||
2. **Dart 侧跳过冗余调用**(`MacosWindowEffectService.applyEffect`):移除 `makeTitlebarTransparent()` / `enableFullSizeContentView()` / `hideTitle()` / `setWindowBackgroundColorToClear()` 四个已在 awakeFromNib 设置的调用,仅保留主题相关的 `overrideMacOSBrightness` 和 `NSVisualEffectView` 操作
|
||
3. **互斥锁串行化**(`MacosWindowEffectService`):添加 `Completer<void>` 互斥锁,防止 `applyEffect()` 并发执行导致原生视图层级竞争
|
||
4. **延迟首次应用**(`DesktopServicesManager._initWindowEffect`):首次 `applyEffect()` 延迟 500ms,等待 Impeller 完成初始渲染
|
||
5. **跳过首次主题同步**(`ThemeSyncService.syncThemeToDesktop`):首次 build 的窗口特效同步由 `_initWindowEffect` 统一处理,避免 build() 与 PostFrame 回调并发触发 `applyEffect()`
|
||
- **涉及文件**:
|
||
- `macos/Runner/MainFlutterWindow.swift` — awakeFromNib 中预设窗口透明属性
|
||
- `lib/core/services/desktop/implementations/macos_window_effect_service.dart` — 互斥锁 + 跳过冗余调用
|
||
- `lib/app/services/desktop_services_manager.dart` — 延迟首次 applyEffect
|
||
- `lib/app/services/theme_sync_service.dart` — 跳过首次窗口特效同步
|
||
|
||
### 🔧 维护(pubspec.yaml 同步鸿蒙端模板)
|
||
|
||
#### 1. pubspec.yaml 版本号与依赖同步鸿蒙端模板
|
||
- **Issue**: `pubspec.yaml` 版本落后(6.6.22+2606213),且头部注释错误标注为"鸿蒙端模板",与 `pubspec.ohos.yaml` 模板(6.6.25+2606241)不同步
|
||
- **原因**: 拉取 git 代码后未重新生成 `pubspec.yaml`,导致主文件与鸿蒙端模板版本号不一致
|
||
- **修复**:
|
||
- 基于 `pubspec.ohos.yaml` 模板重新生成 `pubspec.yaml`
|
||
- 版本号升级:6.6.22+2606213 → 6.6.25+2606241
|
||
- 头部注释修正为"主 pubspec.yaml(基于鸿蒙端模板生成)"
|
||
- 所有依赖项、本地包引用、dependency_overrides 完全对齐 `pubspec.ohos.yaml`
|
||
- 保留 `msix` dev_dependency 和 `msix_config` 配置(Windows 端 Microsoft Store 打包专用)
|
||
- **涉及文件**: `pubspec.yaml`
|
||
- **验证**: `git diff --no-index pubspec.ohos.yaml pubspec.yaml` 确认差异仅为 msix 配置和头部注释
|
||
|
||
---
|
||
|
||
## [v6.118.0] - 2026-06-24
|
||
|
||
### 🐛 修复(macOS 全屏退出后窗口背景变黑)
|
||
|
||
#### 1. macOS 原生全屏退出后窗口背景显示黑色而非桌面
|
||
- **Issue**: 通过「视图 → Enter Full Screen」进入原生全屏后,点击绿灯退出全屏(选择小窗),窗口缩小后背景变为黑色,无法透过窗口看到桌面
|
||
- **原因**:
|
||
- `MacosWindowEffectService.applyEffect()` 调用 `WindowManipulator.setWindowBackgroundColorToClear()` 设置了 `isOpaque=false` + `backgroundColor=NSColor.clear`
|
||
- 但全屏模式下 macOS **强制**窗口不透明(`isOpaque=true`, `backgroundColor=黑色`)
|
||
- 退出全屏后这些属性未自动恢复,导致窗口背景保持黑色
|
||
- 同时 `titlebarAppearsTransparent` 也可能被系统重置
|
||
- **修复**:
|
||
- **原生侧**(`MainFlutterWindow.swift`):监听 `NSWindow.didExitFullScreenNotification`,全屏退出后立即重新设置 `isOpaque=false` + `backgroundColor=NSColor.clear` + `titlebarAppearsTransparent=true`,并触发窗口重绘
|
||
- **Dart 侧**(`MacosPlatformService`):新增 `initEventListeners()` 和 `onFullScreenExited` 回调,接收原生侧的全屏退出事件
|
||
- **服务层**(`DesktopServicesManager`):注册全屏退出回调,退出后重新调用 `applyEffect()` 恢复完整窗口特效(NSVisualEffectView 毛玻璃、标题栏融合等)
|
||
- **涉及文件**:
|
||
- `macos/Runner/MainFlutterWindow.swift` — 添加全屏退出通知监听 + `handleFullScreenExit` 方法
|
||
- `lib/core/services/device/macos_platform_service.dart` — 新增原生事件监听机制
|
||
- `lib/app/services/desktop_services_manager.dart` — 注册全屏退出回调 + 重新应用特效
|
||
|
||
---
|
||
|
||
## [v6.117.0] - 2026-06-24
|
||
|
||
### 🐛 修复(macOS 菜单栏打开页面绕过工作台模式)
|
||
|
||
#### 1. macOS 顶部「闲言」菜单项打开页面不符合工作台模式且无法返回
|
||
- **Issue**: macOS 端通过系统顶部菜单栏 右侧的「闲言/文件/视图/帮助」菜单选项打开页面时,页面全屏覆盖整个窗口,不符合工作台模式布局,且无法返回上一页
|
||
- **原因**: `MacosMenuBarWrapper._navigateTo` 直接调用 `appRouter.push(route)`,将页面 push 到根 GoRouter Navigator,绕过了工作台模式的右栏嵌套 Navigator,导致页面独占窗口
|
||
- **修复**:
|
||
- 参考 `DesktopTrayController._navigateTo` 的修复7.1方案,改为工作台模式感知导航
|
||
- 直接读取 `splitViewProvider.workbenchEnabled` 判断工作台模式(不依赖 MediaQuery,避免窗口状态切换时尺寸未就绪导致判断失败)
|
||
- 工作台模式开启且非全屏路由时,push 到右栏嵌套 Navigator(`rightPanelStackProvider`),页面显示在右栏并支持栈式返回
|
||
- 非工作台模式或全屏路由仍走 GoRouter push
|
||
- **涉及文件**: `lib/features/desktop/macos_menu_bar_wrapper.dart`
|
||
|
||
### 🔒 合规(App Store 加密出口合规声明)
|
||
|
||
#### 1. macOS Info.plist 补充加密出口合规声明
|
||
- **Issue**: App Store Connect 上传构建版本时提示「你的 App 采用了哪种类型的加密算法?」合规问卷
|
||
- **原因**: macOS 端 `Info.plist` 缺少 `ITSAppUsesNonExemptEncryption` 声明(iOS 端已有)
|
||
- **修复**: 在 `macos/Runner/Info.plist` 中添加 `ITSAppUsesNonExemptEncryption = false`,声明 App 仅使用 Apple 操作系统提供的标准加密(HTTPS/TLS),不使用非豁免加密,跳过每次上传的出口合规问卷
|
||
- **涉及文件**: `macos/Runner/Info.plist`
|
||
|
||
---
|
||
|
||
## [v6.116.0] - 2026-06-24
|
||
|
||
### 🐛 修复(情景诗词发送无回复 + 卡片堆积)
|
||
|
||
#### 1. 情景诗词发送预选词后无诗词回复
|
||
- **Issue**: 输入预选词发送后,页面没有回复诗词
|
||
- **原因**:
|
||
- `_buildMessages` 从 `poetrySessions` 历史列表构建消息,发送后新诗词未正确显示
|
||
- `_send` 方法 `isExactTag` 检查过于严格,输入同义词(如"下雨")无法触发发送
|
||
- 缺少用户消息反馈,发送后无视觉提示
|
||
- **修复**:
|
||
- 重构 `_buildMessages`:直接从 `state.jinrishiciContent` 构建当前诗词消息,不再遍历历史 sessions
|
||
- 新增 `state.currentTag` 字段追踪当前预选词,发送后显示用户消息气泡
|
||
- `_send` 方法支持同义词自动映射(如"下雨"→"雨")和单匹配自动发送
|
||
- 添加 API 错误提示(聊天中显示 ⚠️ 错误信息)
|
||
- `_selectSuggestion` 统一使用 `_doSend` 方法
|
||
- **涉及文件**: `weather_page.dart`, `weather_provider.dart`
|
||
|
||
#### 2. 情景诗词无限下滑显示多个相关诗词气泡卡片
|
||
- **Issue**: 发现页面 → 情景诗词一直下滑,出现无数个「相关诗词」气泡卡片
|
||
- **原因**: `_buildMessages` 循环为每个历史 session 都添加诗词卡片,历史 session 越多卡片越多
|
||
- **修复**: 仅显示最新 session 的诗词内容,发送一个预选词回应一首诗词,历史 session 不再展示
|
||
- **涉及文件**: `lib/features/weather/presentation/weather_page.dart`
|
||
|
||
#### 3. 情景诗词收藏/分享按钮点击无反应
|
||
- **Issue**: 相关诗词卡片中的收藏和分享按钮点击无任何响应
|
||
- **原因**: `_buildActionChip` 的 `GestureDetector` 缺少 `onTap` 回调,仅有 UI 渲染
|
||
- **修复**:
|
||
- `_buildActionChip` 新增 `onTap` 可选回调参数,点击时触发触感反馈
|
||
- 新增 `_favoritePoetry` 方法:收藏诗词到本地数据库(AppDatabase),支持切换收藏状态
|
||
- 新增 `_sharePoetry` 方法:调用 `ShareSheet` 弹出分享面板
|
||
- **涉及文件**: `lib/features/weather/presentation/weather_page.dart`
|
||
|
||
---
|
||
|
||
## [v6.115.0] - 2026-06-24
|
||
|
||
### 🐛 修复(布局溢出)
|
||
|
||
#### 1. 拾光栏设置面板工作台模式底部溢出 143px
|
||
- **Issue**: 工作台模式右栏打开「拾光栏设置」时,内容区超出可用高度导致底部溢出 143 像素
|
||
- **原因**: `DateConfigSheet` 使用固定 `maxHeight` 约束 + 不可滚动的 `Column` 布局,右栏高度(扣除48px顶栏)不足时溢出
|
||
- **修复**:
|
||
- 顶栏(拖拽指示器 + 关闭按钮 + 标题行)保持固定
|
||
- 内容区(角色卡片、日期行、显示项、轮播开关、Tips等)改为 `Expanded` + `SingleChildScrollView` 可滚动布局
|
||
- 同时修复了 `onTap` 回调的箭头函数语法问题(改为块体语法避免解析异常)
|
||
- **涉及文件**: `lib/features/home/presentation/date_config_sheet.dart`
|
||
|
||
---
|
||
|
||
## [v6.114.0] - 2026-06-23
|
||
|
||
### ✨ 增强(热力图重绘 + 智能预加载 + 动画系统 + 导航增强)
|
||
|
||
#### 1. 热力图年报水平滚动 + peakDay展示 + 月份标签 + 暗色模式
|
||
- **Issue**: 年报365天时单元格被压缩到8px下限,太小难以点击;peakDay已计算未展示;日期范围无月份分隔;暗色模式对比度过高
|
||
- **修复**:
|
||
- 年报模式(cellWidth<12px):使用 `SingleChildScrollView` 水平滚动,固定单元格14px
|
||
- 新增 peakDay chip:显示最活跃日期(M月D日格式)
|
||
- 新增月份标签行:自动检测月份变化点,标签与热力图周列对齐
|
||
- 暗色模式 `_levelColor` alpha降低:1-4级分别 0.15/0.3/0.5/0.8
|
||
- **涉及文件**: `lib/features/reading_report/presentation/reading_report_widgets.dart`
|
||
|
||
#### 2. appPushWidget 支持 extra 参数
|
||
- **Issue**: `appPushWidget` 仅支持 builder 模式,不支持 extra 参数传递
|
||
- **修复**: 新增 `Object? extra` 可选参数,工作台模式下传入 `RightPanelEntry`
|
||
- **涉及文件**: `lib/core/router/app_nav_extension.dart`
|
||
|
||
#### 3. 句子广场智能预加载
|
||
- **Issue**: 预加载阈值固定16,快速滚动时加载不及时
|
||
- **修复**: 根据滚动速度(items/second)动态调整阈值:快速滚动(>3 items/s)时阈值翻倍(上限40),慢速滚动(<1 items/s)时恢复默认16
|
||
- **涉及文件**: `lib/features/home/providers/home_provider.dart`
|
||
|
||
#### 4. Beta水印屏幕尺寸动态密度
|
||
- **Issue**: 水印组数密度系数固定1.5,不同屏幕宽高比密度不一致
|
||
- **修复**: 根据屏幕宽高比动态调整密度系数:超宽屏(>1.8)用1.8,竖屏(<0.75)用1.2,标准屏1.5
|
||
- **涉及文件**: `lib/shared/widgets/beta_testing_overlay.dart`
|
||
|
||
#### 5. 动画强度感知 — 仅首次入场动画开关
|
||
- **Issue**: 用户设置"无动画"后入场动画仍播放
|
||
- **修复**: 入场动画读取 `themeSettingsProvider.animationIntensity`,`durationMultiplier<=0` 时跳过动画,其他强度按倍率调整时长和曲线
|
||
- **涉及文件**: `lib/features/home/presentation/widgets/home_sentence_list_section.dart`
|
||
|
||
#### 6. 句子卡片退出动画 + 列表项删除动画
|
||
- **Issue**: 屏蔽内容时卡片直接消失,无过渡动画
|
||
- **修复**:
|
||
- `HomeState` 新增 `exitingIds` 字段标记退出中的卡片
|
||
- `blockContent` 先标记退出中,延迟300ms后从列表移除
|
||
- 退出动画:fadeOut + slideX(0→0.3) + scale(1.0→0.9)
|
||
- **涉及文件**: `lib/features/home/providers/home_state.dart`、`lib/features/home/providers/home_interaction_mixin.dart`、`lib/features/home/presentation/widgets/home_sentence_list_section.dart`
|
||
|
||
#### 7. 创作卡片/编辑此句返回主页动画
|
||
- **Issue**: 从编辑器返回主页时无重新聚焦动画
|
||
- **修复**: 新增 `markReturnedFromEditor()` 方法,清空入场动画历史,编辑器返回后句子卡片重新播放入场动画
|
||
- **涉及文件**: `lib/features/home/providers/home_provider.dart`、`lib/features/home/presentation/providers/sentence_detail_sheet.dart`、`lib/features/home/presentation/panels/sentence_detail_actions.dart`
|
||
|
||
***
|
||
|
||
## [v6.113.0] - 2026-06-23
|
||
|
||
### 🐛 修复(句子广场循环加载 + 热力图重绘 + 入场动画增强)
|
||
|
||
#### 1. 句子广场骨架卡死修复(任务6)
|
||
- **Issue**: 主页句子广场滑到底部时显示骨架无法滑动,列表不增长
|
||
- **根因**: `fetchNewSentences` 去重耗尽后使用 fallback 但 `hasMore=true`,导致骨架持续显示但无新数据追加;`_buildListBottom` 在 `hasMore=false` 时返回空 `SizedBox.shrink()`
|
||
- **修复**:
|
||
- `home_feed_mixin.dart`: 去重3轮后无新数据时,重置 `allSeenIds`/`allSeenTexts`(保留最近20条避免立即重复),用本次返回数据继续填充列表,实现循环加载
|
||
- `home_sentence_list_section.dart`: `_buildListBottom` 在 `hasMore=false` 且非离线时,显示"已经看完所有内容"提示 + 重新加载按钮(带入场动画)
|
||
- 新增 `resetAndReload()` 方法:清空已见集合,从第1页重新拉取
|
||
- **涉及文件**: `lib/features/home/providers/home_feed_mixin.dart`、`lib/features/home/providers/home_provider.dart`、`lib/features/home/presentation/widgets/home_sentence_list_section.dart`、`lib/features/home/presentation/home_page.dart`
|
||
|
||
#### 2. 句子卡片入场动画增强(任务6.1)
|
||
- **Issue**: 句子载入时入场动画较单一(仅 fadeIn + slideY)
|
||
- **修复**:
|
||
- 增强 `_SentenceCardWithSkeleton` 入场动画:fadeIn + slideY(0.15) + scale(0.95→1.0, easeOutBack) + blur(4px→0, 聚焦效果) + shimmer(accent色微妙高光)
|
||
- 错峰延迟从 50ms 提升到 60ms,最多 12 个卡片参与级联
|
||
- 骨架屏 `_LoadingSkeletonCard` 增加 fadeIn + scale 入场动画
|
||
- **涉及文件**: `lib/features/home/presentation/widgets/home_sentence_list_section.dart`
|
||
|
||
#### 3. 使用报告活跃热力图重绘(任务7)
|
||
- **Issue**: 热力图卡片左边是热力图,右边空白,未铺满卡片
|
||
- **修复**:
|
||
- 使用 `LayoutBuilder` 响应式计算单元格尺寸,`Expanded` 铺满卡片宽度
|
||
- 新增统计 chip 行:连续活跃天数、活跃天数/总天数、活跃率百分比
|
||
- 新增图例行:"活跃度分布" 标签 + 少→多 渐变图例
|
||
- 新增日期范围标注:底部显示起止日期(月.日格式)
|
||
- 单元格尺寸根据周数动态计算(clamp 8-18px),高度略大于宽度
|
||
- **涉及文件**: `lib/features/reading_report/presentation/reading_report_widgets.dart`
|
||
|
||
***
|
||
|
||
## [v6.112.0] - 2026-06-23
|
||
|
||
### ♻️ 重构(emoji完全迁移 + 窗口控制去重 + 鸿蒙端冗余简化)
|
||
|
||
#### 1. _CloseActionTile 的 actionEmoji 迁移到 CupertinoIcons(任务10扩展)
|
||
- **Issue**: _CloseActionTile 的 actionEmoji 仍使用 emoji(🔴🟡❓),与任务8的 emoji→icon 迁移不一致
|
||
- **修复**:
|
||
- actionEmoji 从字符串 emoji 改为 IconData(CupertinoIcons.xmark_circle_fill/minus_circle_fill/question_circle_fill)
|
||
- 图标颜色使用语义色:关闭=errorColor(红)、最小化=warningColor(黄)、询问=textHint(灰)
|
||
- 描述文本和分段控件子项改为 Row(Icon + Text) 布局
|
||
- optionText 函数移除 emoji,改为纯文本(Semantics value 应为描述性文本)
|
||
- **涉及文件**: `lib/features/settings/presentation/workbench/workbench_settings_page.dart`
|
||
|
||
#### 2. AdaptiveNavBar 水平模式移除窗口控制按钮(任务10扩展)
|
||
- **Issue**: floatingNavBar 为 null 时回退到 AdaptiveNavBar 水平模式,会显示 - 口 X 按钮,与任务8"顶部去掉窗口按钮"要求矛盾
|
||
- **根因**: DesktopWindowTitleBar 已在顶部提供完整窗口控制(macOS红黄绿灯/Windows方按钮),AdaptiveNavBar 水平模式的窗口控制按钮是重复显示
|
||
- **修复**:
|
||
- 移除 _buildHorizontal 中的 `if (isTop && pu.isDesktop) _buildDesktopWindowControls(context, ext)` 条件
|
||
- 删除 _buildDesktopWindowControls 方法、_showWindowSizeMenu 方法、_WindowControlBtn 类
|
||
- 删除不再需要的 imports(window_manager/WindowCloseHandler/WindowSizePopup/platform_utils)
|
||
- 删除未使用的 isRight、isTop、splitState 变量
|
||
- **涉及文件**: `lib/app/layout/adaptive_nav_bar.dart`
|
||
|
||
#### 3. 确认顶部窗口控制不缺失(任务10扩展)
|
||
- **Issue**: 任务8去掉顶部 - 口 X 按钮后,用户可能无法通过标准窗口控件关闭窗口
|
||
- **确认**:
|
||
- macOS 端:DesktopWindowTitleBar 左侧提供红黄绿三圆点按钮(关闭/最小化/最大化),系统原生风格
|
||
- Windows 端:DesktopWindowTitleBar 右侧提供方形按钮(最小化/最大化/关闭),Windows 原生风格
|
||
- DesktopWindowTitleBar 在 app_shell.dart 中始终显示在桌面端顶部(第78-85行),窗口控制不缺失
|
||
- **涉及文件**: 无修改(确认现有实现已满足需求)
|
||
|
||
#### 4. GlassBottomBar 鸿蒙端防御性冗余简化(任务10扩展)
|
||
- **Issue**: floatingNavBar 构建时判断 `AppShell._isOhos && !liquidGlassReady`,但工作台模式已在 build() 中通过 `!pu.isOhos` 过滤鸿蒙端,此判断是防御性冗余
|
||
- **修复**:
|
||
- floatingNavBar 直接构建 _buildGlassBottomBar,不再判断鸿蒙端
|
||
- 添加注释说明工作台模式已过滤鸿蒙端
|
||
- floatingNavBar 类型从 `Widget?` 改为 `Widget`(不再为 null)
|
||
- **涉及文件**: `lib/app/layout/app_shell.dart`
|
||
|
||
***
|
||
|
||
## [v6.111.0] - 2026-06-23
|
||
|
||
### ✨ 新增与增强(情景诗词预选词 + 工作台导航栏图标化 + 滑动性能优化)
|
||
|
||
#### 1. 情景诗词发送框改为输入预选框(任务5)
|
||
- **Issue**: 情景诗词页面发送框可输入任意文本,与"情景关联"语义不符
|
||
- **增强**:
|
||
- 发送框改为预选词输入栏,只能发送预选的字词
|
||
- 输入时自动匹配预选词(支持同义词和拼音匹配),匹配不上时发送按钮禁用
|
||
- 发送预选词后根据预选字词调用 jinrishici API 获取对应标签的诗词
|
||
- 失焦时若仅1条建议自动填充,空输入+聚焦时显示常用标签
|
||
- 记录标签使用频率用于智能排序(getMostUsedTags/recordTagUsage)
|
||
- **涉及文件**: `lib/features/weather/presentation/weather_page.dart`、`lib/features/weather/weather_provider.dart`
|
||
|
||
#### 2. 情景诗词设置城市联动 + 动态主题按钮字体不可见修复(任务5.1)
|
||
- **Issue**: 情景诗词设置页城市修改后页面不显示对应城市诗词;部分按钮字体色和按钮色一致导致选中时看不到字体
|
||
- **修复**:
|
||
- WeatherSettingsNotifier 持久化到 KvStorage,城市修改后联动 weatherProvider.loadPoetryByTag(city)
|
||
- 新增"应用城市"按钮和 _onCityChanged 处理
|
||
- AppThemeExtension 新增 computeContrastColor 静态方法(WCAG 相对亮度公式)和 computedTextOnAccent getter
|
||
- _buildSegmentedRow 选中态文字颜色从 ext.textOnAccent 改为 _computeContrastColor(ext.accent)
|
||
- 任务9审计修复:原 _pow 方法对小数指数处理有 bug(0.4.toInt()=0 导致循环0次返回1.0),改用 dart:math 的 pow 确保计算精度
|
||
- **涉及文件**: `lib/features/weather/presentation/weather_settings_page.dart`、`lib/core/theme/app_theme.dart`
|
||
|
||
#### 3. 诗词下方"第x首推荐"改为"相关诗词",只推荐一首(任务5.2)
|
||
- **Issue**: 诗词下方显示"第x首推荐",推荐多首诗词
|
||
- **修复**: 改为"相关诗词"标题,matchedPoems 使用 .take(1) 只取1首
|
||
- **涉及文件**: `lib/features/weather/presentation/weather_page.dart`
|
||
|
||
#### 4. macOS 托盘打开页面独占窗口修复(任务7.1)
|
||
- **Issue**: macOS 端工作台模式从托盘打开页面时独占一个窗口,导致工作台布局失效
|
||
- **根因**: _navigateTo 方法使用 MediaQuery.sizeOf 判断工作台模式,窗口恢复时 MediaQuery 返回0导致判断失败
|
||
- **修复**: 直接读取 splitViewProvider.workbenchEnabled 判断,工作台模式时 push 到 rightPanelStackProvider,完全绕过 MediaQuery
|
||
- **涉及文件**: `lib/features/desktop/desktop_tray_controller.dart`
|
||
|
||
#### 5. 托盘切换工作台/导航栏位置概率不生效修复(任务7.2)
|
||
- **Issue**: macOS 端从托盘点击工作台模式开启/关闭或切换导航栏位置时概率不生效
|
||
- **根因**: 未聚焦窗口 + 同步 updateMenu 与原生菜单回调冲突
|
||
- **修复**: _onToggleWorkbench 和 _onSetNavBarPosition 添加 _focusMainWindow() 调用,_updateMenu 改为 Future.microtask 延迟执行
|
||
- **涉及文件**: `lib/features/desktop/desktop_tray_controller.dart`
|
||
|
||
#### 6. 导航栏位置卡片 emoji 改 icon + 顶底用 GlassBottomBar 悬浮 tap 栏(任务8)
|
||
- **Issue**: 导航栏位置卡片使用 emoji 不统一;顶/底 tap 栏占位而非悬浮
|
||
- **增强**:
|
||
- NavBarPosition 新增 icon 属性(CupertinoIcons.arrow_left/right/up/down)和 glassLabelIcon 属性(top/bottom 返回 rectangle_stack)
|
||
- 设置页分段控件和 Picker 弹窗从 emoji+label 改为 Row(Icon + Text)
|
||
- WorkbenchLayout 新增 floatingNavBar 参数,水平位置(顶/底)改用 Stack 布局实现悬浮不占位
|
||
- AppShell 提取 _buildGlassBottomBar 方法供窄屏布局和工作台顶/底布局复用
|
||
- 顶部去掉 - 口 X 窗口按钮,将 app 模式的 tap 栏调整到上方
|
||
- **涉及文件**: `lib/core/providers/split_view_provider.dart`、`lib/features/settings/presentation/workbench/workbench_settings_page.dart`、`lib/features/settings/presentation/general/general_settings_pickers.dart`、`lib/core/layout/workbench/workbench_layout.dart`、`lib/app/layout/app_shell.dart`
|
||
|
||
#### 7. 工作台模式句子广场滑动卡顿优化(任务7)
|
||
- **Issue**: 工作台模式主页句子广场滑动时卡顿不丝滑
|
||
- **优化**:
|
||
- 预加载检查(onCheckPreload/onLoadMore)从 Future(() => ...) 改为 WidgetsBinding.instance.addPostFrameCallback,避免在 build 中触发副作用
|
||
- SliverChildBuilderDelegate 设置 addRepaintBoundaries: false(已在子项手动添加 RepaintBoundary)和 addAutomaticKeepAlives: false(句子卡片无需保持存活状态)
|
||
- LongPressDraggable 的 feedback 改为轻量占位卡片(仅显示句子文本和作者),childWhenDragging 改为简单图标占位符,避免构建完整 SentenceCard
|
||
- CustomScrollView 添加 cacheExtent: 800,预渲染视口外的卡片,减少滚动时的重建
|
||
- **涉及文件**: `lib/features/home/presentation/widgets/home_sentence_list_section.dart`、`lib/features/home/presentation/home_page.dart`
|
||
|
||
#### 8. 任务9审计修复:weather_provider 空指针风险
|
||
- **Issue**: loadPoetryByTag 方法中 state.weather! 和 state.mood! 使用非空断言,天气未加载完成时可能抛出空指针异常
|
||
- **修复**: 直接从 jinrishiciData 提取数据,不再创建 WeatherPoetryResult 对象,避免 state.weather!/state.mood! 空指针风险
|
||
- **涉及文件**: `lib/features/weather/weather_provider.dart`
|
||
|
||
***
|
||
|
||
## [v6.110.3] - 2026-06-23
|
||
|
||
### 🐛 修复(窗口尺寸选择菜单 clamp 崩溃 + 屏幕尺寸获取不可靠)
|
||
|
||
#### 1. WindowSizePopup 选择尺寸时 clamp 抛出 Invalid argument(s): 360.0(严重)
|
||
- **Issue**: macOS 端点击窗口栏绿灯按钮,选择窗口尺寸预设时抛出 `Invalid argument(s): 360.0`,菜单无法应用尺寸
|
||
- **根因**:
|
||
- `_applySize` 中 `targetH.clamp(minH, screen.height - 16)` 要求 `lowerLimit <= upperLimit`
|
||
- 当 `_screenWorkArea` 异步初始化未完成时,回退到 `MediaQuery.sizeOf(context)`,而 MediaQuery 在桌面端返回的是**窗口尺寸**(可能很小,如 300×200)
|
||
- 当 `screen.height - 16 < 360`(即窗口高度 < 376)时,`clamp(360.0, <360)` 抛出 `Invalid argument(s): 360.0`
|
||
- **修复**:
|
||
- 使用 `math.max(minW, screen.width - 16)` 确保 clamp 上限不小于下限
|
||
- `_applySize` 中若 `_screenWorkArea` 为 null,先 `await _refreshScreenWorkArea()` 同步刷新一次,避免回退到窗口尺寸
|
||
- 添加安全默认值 `Size(1280, 800)` 作为最终回退
|
||
- **涉及文件**: `lib/shared/widgets/window_size_popup.dart`
|
||
|
||
#### 2. _refreshScreenWorkArea 屏幕尺寸获取不可靠
|
||
- **Issue**: `PlatformDispatcher.views.first.physicalSize` 在窗口初始化阶段可能为 `Size.zero`,导致计算出的屏幕尺寸为 0
|
||
- **根因**: Flutter 视图未完成首帧布局时 `physicalSize` 未初始化
|
||
- **修复**:
|
||
- 检测 `physical.isEmpty || dpr <= 0`,回退到 MediaQuery
|
||
- 添加防御性检查:若 workArea 为空或异常小(<100px),使用安全默认值 `Size(1280, 800)`
|
||
- catch 块同样添加防御性检查
|
||
- **涉及文件**: `lib/shared/widgets/window_size_popup.dart`
|
||
|
||
***
|
||
|
||
## [v6.110.2] - 2026-06-23
|
||
|
||
### 🐛 修复(收藏页面瀑布流视图 RenderFlex 布局崩溃)
|
||
|
||
#### 1. FavoriteGridCard 在瀑布流中 Expanded 无界高度约束崩溃(严重)
|
||
- **Issue**: macOS 运行时进入"我的收藏"页面,切换到瀑布流视图(MasonryGridView)后抛出 `RenderFlex children have non-zero flex but incoming height constraints are unbounded`,连锁触发 `RenderBox was not laid out: RenderFlex` / `RenderPadding` / `RenderDecoratedBox` / `RenderRepaintBoundary` / `RenderSemanticsGestureHandler`,以及 `sliver_multi_box_adaptor.dart: child.hasSize` 断言失败和 `Null check operator used on a null value`,导致整个收藏页面无法渲染
|
||
- **根因**:
|
||
- `FavoriteGridCard` 同时用于网格视图(`GridView.builder` + `childAspectRatio: 0.85`,高度有界)和瀑布流视图(`MasonryGridView.count`,高度无界 0~Infinity)
|
||
- `Column` 内使用 `Expanded` 包裹文本内容,`Expanded` 要求父级提供有限高度约束
|
||
- 瀑布流中父级高度约束为 `0~Infinity`(无界),`Expanded` 与 shrink-wrap 互斥,触发断言
|
||
- `Column` 的 `mainAxisSize` 默认为 `MainAxisSize.max`,在无界约束下尝试扩展到无限大小
|
||
- **修复**:
|
||
- 将 `Expanded` 改为 `Flexible`(默认 `FlexFit.loose`),在有界约束下可扩展填充剩余空间,在无界约束下可自然收缩到子组件固有大小
|
||
- 显式设置 `Column` 的 `mainAxisSize: MainAxisSize.min`,让 Column 收缩包裹子组件,避免在无界约束下尝试扩展
|
||
- `_buildHighlightedText` 已有 `maxLines: 5` + `overflow: TextOverflow.ellipsis` 限制,可保证文本不溢出
|
||
- **涉及文件**: `lib/features/home/presentation/favorite/favorite_card.dart`
|
||
|
||
***
|
||
|
||
## [v6.110.1] - 2026-06-23
|
||
|
||
### 🐛 修复(Beta 水印蒙版布局崩溃 + 情景诗词标题溢出 + 框架错误弹窗骚扰)
|
||
|
||
#### 1. BetaTestingOverlay 无限约束导致 RenderBox 布局失败(严重崩溃)
|
||
- **Issue**: macOS 运行时抛出 `BoxConstraints forces an infinite width and infinite height` + `RenderBox was not laid out: RenderTransform`,导致 4 个实验性页面(工具中心/编辑器/句子来源/桌面小部件)无法正常渲染
|
||
- **根因**:
|
||
- `OverflowBox` 使用 `maxWidth/maxHeight: double.infinity` 传播无限约束
|
||
- `Container` 设置 `width/height: double.infinity` 在无限约束下无法计算自身尺寸
|
||
- `Transform.rotate` 包裹无限大小的子组件,导致 RenderBox 无法布局
|
||
- **修复**:
|
||
- 移除 `OverflowBox` 的 `double.infinity`,改用屏幕对角线 × 1.5 作为有限容器尺寸(保证旋转后仍覆盖全屏)
|
||
- 移除 `Container` 的 `width/height: double.infinity`,改用 `Padding` 避免无限尺寸传播
|
||
- **涉及文件**: `lib/shared/widgets/beta_testing_overlay.dart`
|
||
|
||
#### 2. BetaTestingOverlay ParentData 传递失败(Positioned 必须直接挂在 Stack 下)
|
||
- **Issue**: macOS 运行时抛出 `Incorrect use of ParentDataWidget`,`Positioned` 无法找到 `Stack` 祖先
|
||
- **根因**: `IgnorePointer` 包裹了 `Positioned.fill`,阻断了 `StackParentData` 的传递(`Positioned` 必须是 `Stack` 的直接子组件,中间不能有其他 RenderObjectWidget)
|
||
- **修复**: 将 `Positioned.fill` 移到最外层作为根组件,`IgnorePointer` 放在其内部
|
||
- **涉及文件**: `lib/shared/widgets/beta_testing_overlay.dart`
|
||
|
||
#### 3. 情景诗词推荐列表标题 2 像素右侧溢出
|
||
- **Issue**: 情景诗词页面推荐 tab 中的诗词卡片元信息行(作者 + 标题 + 关键词)溢出 2 像素
|
||
- **根因**:
|
||
- 作者 `Text` 和标题 `Text` 未包裹 `Flexible`,按固有宽度渲染不收缩
|
||
- `Spacer()` 强制占用剩余空间,与未收缩的文本冲突
|
||
- 当作者名 + 《标题》+ 关键词 + 间距超过 Row 可用宽度时产生溢出
|
||
- **修复**:
|
||
- 作者和标题 `Text` 用 `Flexible` 包裹,添加 `maxLines: 1` + `overflow: TextOverflow.ellipsis`
|
||
- 移除 `Spacer()`,改用固定 `SizedBox(width: AppSpacing.sm)` 间距,避免与 `Flexible` 冲突
|
||
- **涉及文件**: `lib/features/weather/presentation/weather_page.dart`
|
||
|
||
#### 4. IOSScrollViewFlingVelocityTracker 时间戳乱序错误弹窗骚扰(Flutter 框架 bug 静默处理)
|
||
- **Issue**: macOS 上滚动时偶发 `The position being added ... has a smaller timestamp ... than its predecessor` 错误,触发 Catcher2 弹窗打扰用户
|
||
- **根因**: Flutter 框架 `IOSScrollViewFlingVelocityTracker.addPosition` 断言时间戳单调递增,但 macOS/iOS 触摸事件时间戳偶尔乱序(框架已知 bug,非应用代码问题)
|
||
- **修复**:
|
||
- `Catcher2ConfigService`: 新增 `_silentFrameworkErrorPatterns` 统一过滤框架内部已知非致命错误
|
||
- `CopyableDialogReportMode`: 匹配静默模式时自动确认不弹窗
|
||
- `_ConsoleLogHandler`: 静默错误降级为 `Log.w`,不记录到 `CrashLogService`
|
||
- `GlobalErrorHandler`: 同步增加 `smaller timestamp` 静默处理,保持双层防护一致性
|
||
- **涉及文件**: `lib/core/services/catcher2_config_service.dart`、`lib/core/services/error/global_error_handler.dart`
|
||
|
||
***
|
||
|
||
## [v6.110.0] - 2026-06-23
|
||
|
||
### ✨ 新增(Beta 测试全局水印蒙版 + 诗词匹配增强 + 动画可交互)
|
||
|
||
#### 1. Beta 测试全局透明水印蒙版(Issue 2 Flutter 实施)
|
||
- **Issue**: 工具中心/编辑器/句子来源/桌面小部件 4 个实验性页面需添加透明网格水印蒙版,标识 Beta 测试状态与设备溯源信息
|
||
- **修改**:
|
||
- 新增 `BetaTestingOverlay` 共享组件:透明网格水印,3 行内容(主标识/状态提示/溯源信息),字体统一 12px,斜向 -22° 排列,水印贯穿两侧(OverflowBox + Transform.rotate + Wrap 平铺)
|
||
- 3 行内容:① 闲言APP beta testing(加粗)② 实验中的功能(斜体)③ IP · 机型 · 时间 · 版本 · 随机码(等宽字体)
|
||
- 异步并行加载设备信息(DeviceInfoService.getDeviceName)与 IP 地址(IpLocationService.queryMyIp),加载完成前不显示蒙版避免闪烁
|
||
- 浅色/深色主题自适应颜色,IgnorePointer 包裹不阻挡页面交互
|
||
- 偶数组额外显示 `xianyan · beta` 标识行增加视觉层次
|
||
- **水印密度动态调整**:LayoutBuilder 根据屏幕尺寸计算 itemCount(屏幕面积 / 单组面积 × 1.2 密度系数),clamp 到 [6, 30],小屏稀疏大屏饱满
|
||
- **本地 IP 回退**:三级回退策略(queryMyIp 24h缓存 → KvStorage 永不过期上次IP → 未知IP),离线时仍可显示上次 IP
|
||
- **涉及文件**:
|
||
- 新增 `lib/shared/widgets/beta_testing_overlay.dart`
|
||
- 修改 `lib/features/home/presentation/home_tool_center.dart`(Stack 顶层叠加)
|
||
- 修改 `lib/editor/pages/editor/editor_page.dart`(ProEditorPage 与加载态均叠加)
|
||
- 修改 `lib/features/source/presentation/source_page.dart`(CupertinoPageScaffold 外层 Stack 叠加)
|
||
- 修改 `lib/features/widget/presentation/widget_management_page.dart`(CupertinoPageScaffold 外层 Stack 叠加)
|
||
|
||
### 🐛 修复(诗词匹配精度 + API 标签限制 + 搜索历史 + 动画交互 + 功耗 tips + 去重池 + 加载动画)
|
||
|
||
#### 2. 情景诗词匹配精度有限(拼音/同义词无法匹配)
|
||
- **Issue**: `findMatchingTags` 使用 contains 模糊匹配,输入"下雨"无法匹配"雨",拼音/同义词无法匹配
|
||
- **修改**:
|
||
- 新增 60+ 条同义词/近义词映射表 `kSynonymMap`(如"下雨"→["雨"]、"南京"→["金陵"]、"梅"→["梅花"])
|
||
- 新增 3 层匹配策略:① 同义词映射 ② 中文包含匹配 ③ 拼音匹配(PinyinHelper.getShortPinyin/getPinyin)
|
||
- 新增拼音缓存 `_pinyinCache` 避免重复计算
|
||
- 匹配结果按使用频率智能排序(`_sortByUsageFrequency`)
|
||
- **涉及文件**: `lib/features/poetry/presentation/poetry_tags.dart`
|
||
|
||
#### 3. 无搜索历史记录与智能排序
|
||
- **Issue**: 用户频繁使用的标签未记录,无智能排序
|
||
- **修改**:
|
||
- 新增 `getTagUsageHistory`/`recordTagUsage`/`getMostUsedTags` 基于 KvStorage 持久化标签使用历史
|
||
- 存储格式 `tag\u0000count`,保留前 30 条高频标签
|
||
- `PoetryInputBar` 发送/选择建议时调用 `recordTagUsage` 记录
|
||
- 输入框聚焦且为空时显示"常用"标签行(最多 6 个),点击直接发送
|
||
- **涉及文件**:
|
||
- `lib/features/poetry/presentation/poetry_tags.dart`
|
||
- `lib/features/poetry/presentation/poetry_widgets.dart`
|
||
|
||
#### 4. API 标签限制(fetchSentenceByTag 重试仍可能不匹配)
|
||
- **Issue**: jinrishici API 不支持客户端传 tag 参数,matchTags 重试最多 3 次仍可能不匹配
|
||
- **修改**:
|
||
- 新增内存缓存 `_tagCache`(5 分钟 TTL),命中缓存直接返回避免重复请求
|
||
- 重试次数从 3 次增至 5 次,每次间隔 500ms
|
||
- 第 3 次仍不匹配时再次清除 Token 换推荐种子
|
||
- 5 次都不匹配时返回最后结果并缓存(仍可展示)
|
||
- 所有请求失败时回退到无标签的 `fetchSentence()`
|
||
- **涉及文件**: `lib/core/services/data/jinrishici_sdk_service.dart`
|
||
|
||
#### 5. 动画为静态循环(无法响应用户交互)
|
||
- **Issue**: 工作台/分屏动画演示为纯演示性质,无法点击切换方向
|
||
- **修改**:
|
||
- 新增 `_paused1`/`_paused2` 状态与 `_togglePause1()`/`_togglePause2()` 方法
|
||
- 点击动画卡片切换暂停/继续,暂停时显示半透明遮罩 + play 图标
|
||
- AnimationController 通过 `stop()`/`repeat(reverse: true)` 控制暂停/继续
|
||
- **涉及文件**: `lib/features/settings/presentation/workbench/workbench_animation_demos.dart`
|
||
|
||
#### 6. 动画尺寸固定(小屏拥挤)
|
||
- **Issue**: `_AnimationCard` 高度固定 60px,小屏下可能拥挤
|
||
- **修改**:
|
||
- 改用 LayoutBuilder 自适应高度:`animHeight = (maxW * 0.5).clamp(50.0, 80.0)`
|
||
- 宽度与高度均根据父容器约束动态计算
|
||
- **涉及文件**: `lib/features/settings/presentation/workbench/workbench_animation_demos.dart`
|
||
|
||
#### 7. 功耗 tips 文案单一(未区分移动端/桌面端)
|
||
- **Issue**: 功耗提示未区分移动端/桌面端的功耗差异说明
|
||
- **修改**:
|
||
- `PowerConsumptionTip` 根据 `pu.isMobile`/`pu.isDesktop` 显示不同文案
|
||
- 移动端:"移动端同时打开多个页面会增加功耗与内存占用"
|
||
- 桌面端:"桌面端多页面并行渲染会占用更多显存与 CPU 资源"
|
||
- 其他平台:"同时打开多个页面会增加系统资源占用"
|
||
- **涉及文件**: `lib/features/settings/presentation/workbench/workbench_animation_demos.dart`
|
||
|
||
#### 8. 日签去重池仅 20 条(高频换一句仍可能重复)
|
||
- **Issue**: 去重池容量仅 20 条,高频换一句后仍可能重复
|
||
- **修改**: `_maxHistorySize` 从 20 扩容至 50,降低高频换一句时的重复概率
|
||
- **涉及文件**: `lib/features/daily_card/daily_card_provider.dart`
|
||
|
||
#### 9. 换一句无加载动画(内容突然替换)
|
||
- **Issue**: 换一句过程中无过渡动画,内容突然替换
|
||
- **修改**:
|
||
- AnimatedSwitcher 的 key 加入 `content.text.hashCode`,内容变化时触发过渡
|
||
- 新增 `transitionBuilder`:FadeTransition + ScaleTransition(0.95→1.0)
|
||
- 新增 `isRefreshing` 半透明遮罩 + CupertinoActivityIndicator 居中加载指示器
|
||
- IgnorePointer 包裹遮罩避免阻挡交互
|
||
- **涉及文件**: `lib/features/daily_card/presentation/daily_card_page.dart`
|
||
|
||
***
|
||
|
||
## [v6.109.0] - 2026-06-23
|
||
|
||
### ✨ 新增(情景诗词输入预选框 + 工作台动画演示)
|
||
|
||
#### 1. 情景诗词发送框改为输入预选框
|
||
- **Issue**: 用户要求发送框只能发送预选词,输入时自动匹配预选词,根据预选词更换诗词
|
||
- **修改**:
|
||
- 新增 `PoetryInputBar` 组件:输入框+发送按钮,实时匹配预选词建议列表,仅允许发送精确匹配的预选词
|
||
- 新增 `kAllPoetryTags` 扁平化标签列表、`findMatchingTags` 模糊匹配、`isExactTag` 精确匹配辅助函数
|
||
- 增强 `fetchSentenceByTag`:增加 matchTags 匹配重试(最多3次),提升标签与诗词相关性
|
||
- 增强城市显示:从纯文本改为带图标的卡片样式,显示当前城市+天气+温度
|
||
- **涉及文件**:
|
||
- `lib/features/poetry/presentation/poetry_tags.dart`
|
||
- `lib/features/poetry/presentation/poetry_widgets.dart`
|
||
- `lib/features/poetry/presentation/poetry_page.dart`
|
||
- `lib/core/services/data/jinrishici_sdk_service.dart`
|
||
|
||
#### 2. 工作台模式设置页增加动画演示图
|
||
- **Issue**: 工作台模式和分屏功能开关下方需增加动画演示图,导航栏位置卡片需增加功耗提示
|
||
- **修改**:
|
||
- 新增 `WorkbenchModeAnimationDemos`:工作台↔App模式切换动画(三栏收缩/展开)
|
||
- 新增 `SplitViewAnimationDemos`:竖屏↔横屏切换动画(上下/左右分栏淡入淡出)
|
||
- 新增 `PowerConsumptionTip`:功耗内存占用提示标签
|
||
- `_SliderTile` 增加 `trailing` 参数支持右下角附加组件
|
||
- **涉及文件**:
|
||
- 新增 `lib/features/settings/presentation/workbench/workbench_animation_demos.dart`
|
||
- 修改 `lib/features/settings/presentation/workbench/workbench_settings_page.dart`
|
||
|
||
### 🐛 修复(窗口尺寸 + 横屏布局 + 路由匹配 + 骨架超时)
|
||
|
||
#### 3. macOS/Win窗口尺寸选择异常
|
||
- **Issue**: 12-13寸笔记本选择1920×1080后窗口比屏幕大;4K/2K大显示器选择800×600后窗口很小
|
||
- **修改**:
|
||
- 将绝对像素预设改为屏幕比例预设(50%/65%/75%/85%/95%/100%)
|
||
- 新增 `_refreshScreenWorkArea` 检测屏幕工作区(macOS扣28px菜单栏,Win扣48px任务栏)
|
||
- `_applySize` 按比例计算目标尺寸并 clamp 到屏幕安全区,应用后居中
|
||
- UI 显示比例标签+动态像素值+屏幕尺寸
|
||
- **涉及文件**: `lib/shared/widgets/window_size_popup.dart`
|
||
|
||
#### 4. 移动端横屏左侧空白
|
||
- **Issue**: 横屏窄屏(600<width<768)时使用 Center+ConstrainedBox 导致两侧空白
|
||
- **修改**: 改为 `Row[_buildVerticalTabBar(), Expanded(content)]` 布局,左侧显示垂直tab栏
|
||
- **涉及文件**: `lib/app/layout/app_shell.dart`
|
||
|
||
#### 5. 工作台切换App模式后按钮残留选中态
|
||
- **Issue**: 从工作台模式切换成app模式后,已打开页面的按钮仍显示选中着重色
|
||
- **修改**:
|
||
- `activeRightPanelRouteProvider` 增加工作台模式守卫,非工作台模式返回 null
|
||
- `RightPanelStackNotifier` 新增 `clearAll()` 方法清空所有Tab右栏栈
|
||
- `setWorkbenchEnabled` 关闭工作台模式时调用 `clearAll()`
|
||
- **涉及文件**:
|
||
- `lib/core/layout/workbench/right_panel_navigator.dart`
|
||
- `lib/core/providers/split_view_provider.dart`
|
||
|
||
#### 6. 嵌套子路由匹配失败
|
||
- **Issue**: 闲情逸致页面点击设置icon无法跳转到设置页面
|
||
- **根因**: `_findInChildren` 比较 `def.path == path`,但子路由路径是相对的(如`settings`),搜索路径是绝对的(如`/leisure/settings`)
|
||
- **修改**: 新增 `_joinPath` 辅助函数拼接父路径和子路径,`_findInChildren` 和 `_findInChildrenParam` 增加 `parentPath` 命名参数
|
||
- **涉及文件**: `lib/core/router/route_registry.dart`
|
||
|
||
#### 7. 闲情逸致骨架持续显示
|
||
- **Issue**: 工作台模式下闲情逸致页面有时一直显示骨架不显示内容
|
||
- **根因**: `_init()` 中的数据加载操作没有超时保护,可能挂起导致 `isLoading` 永远为 true
|
||
- **修改**: 为每个 `loadIndex()` 和 `loadMonth()` 调用添加3秒超时;`build()` 中添加5秒安全兜底强制结束 loading
|
||
- **涉及文件**: `lib/features/tool_center/leisure/providers/leisure_timeline_provider.dart`
|
||
|
||
#### 8. 日签卡片换一句无效
|
||
- **Issue**: 点击换一句后卡片内容未更换
|
||
- **根因**: `refreshCurrentType()` 优先调用 `_fetchByType`(每日API),同一天返回相同内容
|
||
- **修改**: 改为优先使用 `_fetchFromChannel`(带去重+重试的频道随机接口),失败时回退到每日API;新增内容相同检测,若相同再尝试一次
|
||
- **涉及文件**: `lib/features/daily_card/daily_card_provider.dart`
|
||
|
||
#### 9. 导出个人信息页面布局错误
|
||
- **Issue**: `[ERR-MQPK2MM9]` proxy_box.dart `!debugNeedsLayout` 断言错误
|
||
- **根因**: `Flexible` + `shrinkWrap: true` ListView 在 `Column` 中导致布局问题
|
||
- **修改**: 改为 `Expanded`(移除 `shrinkWrap`),提供固定高度约束
|
||
- **附加**: 列表底部新增 tips 气泡提示,URL 使用强调色可点击复制
|
||
- **涉及文件**: `lib/features/settings/presentation/account/account_export_info_sheet.dart`
|
||
|
||
### 📝 文案修改
|
||
|
||
#### 10. 了解我们页面文案更新
|
||
- "官网首页"改为"在 闲言App",增加副标题"在其他设备上下载闲言app"
|
||
- URL 改为 `https://s2ss.com/app.html`
|
||
- 团队签名更新:无书的书→"不会写代码的小书",伯乐不相马→"这个bug有力气👍"
|
||
- **涉及文件**: `lib/features/profile/presentation/learn_us_sections.dart`, `lib/features/profile/presentation/learn_us_widgets.dart`, `lib/l10n/languages/zh_cn.dart`
|
||
|
||
#### 11. 工作台模式文案更新
|
||
- "微信PC"改为"studio"
|
||
- **涉及文件**: `lib/l10n/languages/zh_cn.dart`, `lib/l10n/languages/zh_tw.dart`
|
||
|
||
### 🎨 新功能预告
|
||
|
||
#### 12. 新版本功能介绍增加预告视图
|
||
- 新增预告卡片:"下个版本,即将支持在应用内加载网页url"
|
||
- **涉及文件**: `lib/features/home/presentation/widgets/new_features_dialog.dart`
|
||
|
||
***
|
||
|
||
## [v6.108.0] - 2026-06-23
|
||
|
||
### 🐛 修复(布局溢出 + 运行时错误)
|
||
|
||
#### 1. 翻译助手页面底部溢出 29px
|
||
- **Issue**: 分栏模式下空状态内容(图标+文字+快捷翻译芯片)超出可用高度导致 BOTTOM OVERFLOWED BY 29 PIXELS
|
||
- **修改**: `_buildEmptyState` 包裹 `SingleChildScrollView(physics: NeverScrollableScrollPhysics)` 允许内容在受限空间内自适应收缩
|
||
- **涉及文件**: `lib/features/discover/presentation/pages/translate/translate_page.dart`
|
||
|
||
#### 2. 情景诗词页面右侧溢出 2px
|
||
- **Issue**: 诗词卡片 padding(AppSpacing.md) + ListView 水平 padding 叠加后总宽度超出 2px
|
||
- **修改**: 卡片水平 padding 从 `AppSpacing.md`(16) 缩减为 `AppSpacing.sm`(8),垂直保持不变
|
||
- **涉及文件**: `lib/features/poetry/presentation/poetry_page.dart`
|
||
|
||
#### 3. 发现页松手打开区域底部溢出 38px
|
||
- **Issue**: 下拉刷新指示器容器使用固定 `height` 但内部内容(气泡+精灵角色+进度条)实际高度超出容器
|
||
- **修改**: 容器从固定 `height` 改为 `constraints: BoxConstraints(minHeight:)`,允许内容撑开
|
||
- **涉及文件**: `lib/features/discover/presentation/pages/home/discover_page.dart`
|
||
|
||
#### 4. Quick Card 编辑器右侧溢出
|
||
- **Issue**: 预览区宽度计算 `screenWidth - 16` 未考虑父容器 `padding: AppSpacing.md`(双侧共32px),分栏模式下溢出
|
||
- **修改**: 预览最大宽度改为 `(screenWidth - 32).clamp(200, ∞)`,预留父容器双边距
|
||
- **涉及文件**: `lib/features/home/presentation/widgets/quick_card_preview.dart`
|
||
|
||
#### 5. 灵感页面关联推荐卡片底部溢出 12px
|
||
- **Issue**: 关联推荐横向列表容器 `SizedBox(height: 140)` 小于 `FeedItemChip(normal)` 模式实际内容高度
|
||
- **修改**: 容器高度从 140 增加到 156
|
||
- **涉及文件**: `lib/features/discover/presentation/pages/home/related_recommend_section.dart`
|
||
|
||
#### 6. RenderFractionalTranslation hitTestChildren 断言错误
|
||
- **Issue**: 灵感页面使用 `Transform.translate` 包裹 `CustomScrollView`,下拉刷新时 offset 动态变化触发布局断言 `!debugNeedsLayout`
|
||
- **修改**: 用 `Padding(padding: EdgeInsets.only(top: offsetY))` 替代 `Transform.translate(offset: Offset(0, offsetY))`,实现相同视觉效果且不触发断言
|
||
- **涉及文件**: `lib/features/discover/presentation/pages/home/inspiration_page.dart`
|
||
|
||
#### 7. Scrollbar ScrollController 无 ScrollPosition 错误
|
||
- **Issue**: 金币记录页 `CupertinoScrollbar` 未指定 controller 参数,默认使用 PrimaryScrollController 导致无 ScrollPosition 可绑定
|
||
- **修改**: 新增 `_scrollController` 并同时传给 `CupertinoScrollbar(controller:)` 和 `CustomScrollView(controller:)`
|
||
- **涉及文件**: `lib/features/user_center/presentation/coin_log_page.dart`
|
||
|
||
***
|
||
|
||
## [v6.107.0] - 2026-06-22
|
||
|
||
### 🏗️ 重构(应用根组件深度职责分离)
|
||
|
||
#### 1. 快捷操作处理器提取
|
||
- **Issue**: 搜索快捷方式逻辑(`_handleSearchShortcut`)和路由导航仍内联在根组件
|
||
- **修改**:
|
||
- 提取 `QuickActionsHandler`:封装快捷方式回调逻辑(搜索快捷方式 + 路由导航),处理白屏修复逻辑
|
||
- 根组件仅调用 `_quickActionsHandler.initialize()`
|
||
- **涉及文件**: 新增 `lib/app/services/quick_actions_handler.dart`,修改 `lib/app/app.dart`
|
||
|
||
#### 2. 小组件导航服务提取
|
||
- **Issue**: `_handlePendingWidgetNavigation` 仍留在根组件
|
||
- **修改**:
|
||
- 提取 `WidgetNavigationService`:封装小组件点击后的待处理导航逻辑
|
||
- 生命周期恢复时调用 `_widgetNavigationService.handlePendingNavigation()`
|
||
- **涉及文件**: 新增 `lib/app/services/widget_navigation_service.dart`,修改 `lib/app/app.dart`
|
||
|
||
#### 3. 应用锁生命周期服务提取
|
||
- **Issue**: `didChangeAppLifecycleState` 中的应用锁暂停/恢复逻辑仍内联
|
||
- **修改**:
|
||
- 提取 `AppLockLifecycleService`:封装应用锁在前后台切换时的处理逻辑
|
||
- 生命周期回调简化为 `_appLockLifecycleService.onAppPaused()`/`onAppResumed()`
|
||
- **涉及文件**: 新增 `lib/app/services/app_lock_lifecycle_service.dart`,修改 `lib/app/app.dart`
|
||
|
||
#### 4. ThemeSettingsState.toThemeData() 方法
|
||
- **Issue**: `AppTheme.buildFromSettings` 在 build 方法中被调用两次(light+dark),参数传递冗长
|
||
- **修改**:
|
||
- 在 `ThemeSettingsState` 上添加 `toThemeData()` 方法,封装主题构建逻辑
|
||
- build 方法中主题构建从 30+ 行参数简化为 3 行方法调用
|
||
- 支持 `isDark` 参数覆盖,用于构建 darkTheme
|
||
- **涉及文件**: 修改 `lib/features/settings/providers/theme_settings_provider.dart`
|
||
|
||
***
|
||
|
||
## [v6.106.0] - 2026-06-22
|
||
|
||
### 🏗️ 重构(应用根组件职责分离)
|
||
|
||
#### 1. 根组件职责过重问题
|
||
- **Issue**: `XianyanApp` 承担了10+个不同职责(主题同步、动画配置、数据管理、快捷方式、桌面服务等),违反单一职责原则,代码复杂度高、难以维护和测试
|
||
- **修改**:
|
||
- 提取 `ThemeSyncService`:管理主题状态同步到桌面端(macOS/Windows),包含窗口特效和托盘图标主题同步,内置去重机制
|
||
- 提取 `AnimationConfigService`:管理 flutter_animate 全局动画配置(时长、曲线),仅在设置变化时更新
|
||
- 提取 `DataManagementService`:管理原生数据管理通道(MethodChannel),处理系统存储清理、数据清除、页面导航
|
||
- 提取 `DesktopServicesManager`:统一管理桌面端服务初始化和生命周期(托盘、窗口特效、菜单栏)
|
||
- 重构 `app.dart`:使用4个服务类替代原有内联逻辑,代码量从874行降至689行(减少约21%)
|
||
- 提取 `_buildOhosApp`/`_buildStandardApp`/`_buildGlassThemeData`/`_buildOfflineBanner` 方法,提高 build 方法可读性
|
||
- **涉及文件**:
|
||
- 新增: `lib/app/services/theme_sync_service.dart`
|
||
- 新增: `lib/app/services/animation_config_service.dart`
|
||
- 新增: `lib/app/services/data_management_service.dart`
|
||
- 新增: `lib/app/services/desktop_services_manager.dart`
|
||
- 修改: `lib/app/app.dart`
|
||
|
||
***
|
||
|
||
## [v6.105.0] - 2026-06-22
|
||
|
||
### ✨ 增强(情景诗词交互重构 + 数据管理 + 功能补全)
|
||
|
||
#### 1. 预选词选择器重构
|
||
- **Issue**: 展开分组宽度变化造成视觉跳动,横向滚动单手操作不便,无搜索功能
|
||
- **修改**:
|
||
- 底部选择器改为纵向网格布局:搜索框 + 分组标题 + 标签芯片网格
|
||
- 搜索框实时过滤所有标签
|
||
- 分组标题仅视觉分隔,不可点击展开(消除跳动)
|
||
- 标签芯片直接点击选中/取消,无需展开/收起
|
||
- **涉及文件**: `poetry_widgets.dart`(PoetryTagSelector)
|
||
|
||
#### 2. 标签组合选择
|
||
- **Issue**: 只能选择单个标签
|
||
- **修改**:
|
||
- 支持同时选择多个标签(如「秋+雨」组合筛选)
|
||
- 已选标签显示在搜索框下方,支持点击取消/清除全部
|
||
- `PoetryState.selectedTags` 从 `String?` 改为 `List<String>`
|
||
- `PoetryNotifier` 新增 `clearSelectedTags()`/`removeSelectedTag()` 方法
|
||
- **涉及文件**: `poetry_provider.dart`, `poetry_widgets.dart`
|
||
|
||
#### 3. 诗词卡片长按菜单
|
||
- **Issue**: 底部快捷按钮占用空间且不符合 iOS 交互习惯
|
||
- **修改**:
|
||
- 使用 `CupertinoContextMenu` 包裹诗词卡片,长按弹出收藏/复制/分享/朗读菜单
|
||
- 删除底部快捷按钮(`_buildQuickActions`/`_buildQuickButton`)
|
||
- **涉及文件**: `poetry_page.dart`
|
||
|
||
#### 4. 手势操作
|
||
- **Issue**: 无法删除单条聊天记录或快速收藏
|
||
- **修改**:
|
||
- 诗词卡片使用 `Dismissible` 组件
|
||
- 右滑显示收藏图标(收藏不移除卡片)
|
||
- 左滑显示红色删除按钮(删除该条记录)
|
||
- `PoetryNotifier` 新增 `removeChatRecord(id)` 方法
|
||
- **涉及文件**: `poetry_page.dart`, `poetry_provider.dart`
|
||
|
||
#### 5. 聊天记录清空按钮
|
||
- **Issue**: 无清空聊天记录入口
|
||
- **修改**:
|
||
- 诗词页面导航栏添加垃圾桶图标按钮
|
||
- 设置页面「数据管理」卡片添加清空按钮
|
||
- 点击弹出确认对话框,确认后调用 `clearChatRecords()`
|
||
- **涉及文件**: `poetry_page.dart`, `poetry_settings_page.dart`
|
||
|
||
#### 6. 聊天记录上限提示
|
||
- **Issue**: 聊天记录上限 200 条但无提示
|
||
- **修改**:
|
||
- 当 `chatRecords.length > 180` 时,列表顶部显示黄色提示条
|
||
- 设置页面「数据管理」卡片显示 N/200 进度指示
|
||
- **涉及文件**: `poetry_page.dart`, `poetry_settings_page.dart`
|
||
|
||
#### 7. 诗词日历视图
|
||
- **Issue**: 无历史诗词查看入口
|
||
- **修改**:
|
||
- 导航栏添加日历图标按钮
|
||
- 点击弹出底部弹窗,按日期分组展示历史诗词
|
||
- 使用 `poetryState.poetryRecords` 获取所有诗词记录
|
||
- **涉及文件**: `poetry_widgets.dart`(PoetryCalendarSheet)
|
||
|
||
#### 8. 统一聊天记录系统
|
||
- **Issue**: `poetryHistory` 和 `chatRecords` 两套独立系统,数据冗余
|
||
- **修改**:
|
||
- 合并为统一的 `chatRecords`,删除 `poetryHistory` 字段
|
||
- `PoetryChatRecord` 直接存储 `JinrishiciPoetry` 对象(替代 `poetryJson` Map)
|
||
- 缓存 key 升级为 `poetry_cache_v2`
|
||
- **涉及文件**: `poetry_provider.dart`
|
||
|
||
#### 9. 功能补全
|
||
- **Issue**: 设置开关存在但 UI 未渲染(标签、译文、城市/天气)
|
||
- **修改**:
|
||
- 诗词气泡中根据 `showTags` 渲染标签芯片
|
||
- 诗词气泡中根据 `showTranslate` 渲染译文内容
|
||
- 聊天列表顶部显示城市/天气信息
|
||
- **涉及文件**: `poetry_page.dart`
|
||
|
||
#### 10. 设置页面增强
|
||
- **修改**:
|
||
- 新增「使用提示」卡片,包含 7 条功能说明
|
||
- 新增「数据管理」卡片,显示聊天记录统计和清空按钮
|
||
- **涉及文件**: `poetry_settings_page.dart`
|
||
|
||
#### 11. 文件拆分
|
||
- **修改**:
|
||
- `poetry_page.dart`(586 行)拆分出 `poetry_tags.dart`(标签定义)和 `poetry_widgets.dart`(选择器+日历组件)
|
||
- 所有文件均低于 1000 行限制
|
||
- **涉及文件**: 新增 `poetry_tags.dart`, `poetry_widgets.dart`
|
||
|
||
***
|
||
|
||
## [v6.104.0] - 2026-06-22
|
||
|
||
### ✨ 增强(情景诗词预选词选择器 + 城市设置 + 聊天记录持久化)
|
||
|
||
#### 1. 情景诗词输入框改为预选词芯片选择器
|
||
- **Issue**: 情景诗词页面原发送框为自由文本输入,无法有效按标签筛选诗词
|
||
- **修改**:
|
||
- 将底部输入框替换为预选词芯片选择器,只能选择预定义标签
|
||
- 7 个标签分组:气象🌤、时辰🕐、季节🌸、节日🎉、地理🏔、花木🌺、山水⛰
|
||
- 标签基于 jinrishici API 推荐关联标签体系(气象/时间/日期/地理/混合标签)
|
||
- 点击分组展开标签列表,选择标签后触发诗词刷新
|
||
- 当前选中标签高亮显示,支持清除
|
||
- **涉及文件**: `poetry_page.dart`
|
||
|
||
#### 2. 预选词选择后按标签刷新诗词
|
||
- **Issue**: 选择预选词后需根据标签获取对应诗词
|
||
- **修改**:
|
||
- `JinrishiciSdkService` 新增 `fetchSentenceByTag()` 方法
|
||
- `PoetryService` 新增 `fetchPoetryByTag()` 方法
|
||
- `PoetryNotifier` 新增 `loadPoetryByTag()` 方法
|
||
- 选择标签后清除旧 Token 重新请求,触发 API 生成新推荐
|
||
- 聊天界面显示用户选择的标签和系统返回的诗词
|
||
- **涉及文件**: `jinrishici_sdk_service.dart`, `poetry_service.dart`, `poetry_provider.dart`
|
||
|
||
#### 3. 设置页面城市选择,影响诗词页面显示
|
||
- **Issue**: 情景诗词设置页面城市功能为空壳,修改城市后诗词页面无变化
|
||
- **修改**:
|
||
- `PoetrySettings` 新增 `city` 字段,持久化到 KvStorage
|
||
- 设置页面新增「城市设置」分组卡片
|
||
- 城市选择弹窗提供 24 个古诗词名城(长安/洛阳/金陵/临安/姑苏/扬州等)
|
||
- 选择城市后同步到 `poetryProvider`,诗词页面显示对应城市
|
||
- 城市标签同时触发 `loadPoetryByTag()` 获取对应地域诗词
|
||
- **涉及文件**: `poetry_settings_page.dart`, `poetry_provider.dart`
|
||
|
||
#### 4. 聊天记录持久化保存
|
||
- **Issue**: 情景诗词页面聊天记录仅保存在内存,退出即丢失
|
||
- **修改**:
|
||
- `PoetryState` 新增 `chatRecords` 字段
|
||
- 新增 `PoetryChatRecord` 模型(type/content/time/poetryJson)
|
||
- 用户发送的标签、系统提示、返回的诗词全部持久化到缓存
|
||
- 最多保留 200 条记录,超出自动清理旧记录
|
||
- 下次打开软件可看到历史聊天记录,最新消息在最下面(类似微信/QQ)
|
||
- `PoetryNotifier` 新增 `addChatRecord()` 和 `clearChatRecords()` 方法
|
||
- **涉及文件**: `poetry_provider.dart`, `poetry_page.dart`
|
||
|
||
***
|
||
|
||
## [v6.103.0] - 2026-06-22
|
||
|
||
### ✨ 增强(快速卡片多语言全量补全 + 液态玻璃预设视觉实现 + 样式区翻译接入)
|
||
|
||
#### 1. 12 种语言 quickCard 翻译补全
|
||
- **Issue**: 12 种语言(zh_tw/ja/ko/fr/de/es/it/pt/ru/ar/hi/bn)的 quickCard 字段为英文占位,非中英用户看到英文
|
||
- **修复**:
|
||
- 12 个语言文件全部翻译为各自语言,覆盖 100+ 翻译键
|
||
- zh_tw 使用台湾繁体习惯(儲存/相簿/範本/重設/套用/字級)
|
||
- ja 使用日文习惯(保存/共有/キャンセル/リセット/テンプレート)
|
||
- ko 使用韩文习惯(저장/공유/취소/초기화/템플릿)
|
||
- fr/de/es/it/pt/ru/ar/hi/bn 均使用各自语言习惯
|
||
- 品牌名 `✨ Slight editor mini` 和 `Xianyan` 保持不变
|
||
- 中文字体名(宋体/楷体等)翻译为对应语言的字体描述
|
||
- **验证**: `flutter analyze` 14 个语言文件全部通过
|
||
|
||
#### 2. 液态玻璃预设视觉实现(iOS 26 风格)
|
||
- **Issue**: `CardPresetStyle.liquidGlass` 预设已存在但无液态玻璃视觉效果,仅显示普通渐变
|
||
- **修复**:
|
||
- `QuickCardPreview` 新增 `_buildCardContent()` 方法,当 `presetStyle == liquidGlass` 且平台支持时,使用 `AdaptiveLiquidGlassLayer` 包裹卡片内容
|
||
- `LiquidGlassSettings` 参数:blur=1.2 / refractiveIndex=1.45 / chromaticAberration=0.6 / lightIntensity=1.0 / ambientStrength=0.5
|
||
- 使用 `LiquidRoundedSuperellipse(borderRadius: 12)` 与卡片圆角统一
|
||
- 通过 `PlatformCapabilities.supports(CapabilityKey.liquidGlass)` 检测平台能力,不支持时降级为普通渐变
|
||
- `applyPreset(liquidGlass)` 补充 `gradientColors` 默认值和 `gradientBegin/End` 方向
|
||
- **效果**: 选择液态玻璃预设后,卡片预览区呈现 iOS 26 液态玻璃折射效果
|
||
|
||
#### 3. 样式调整区全量翻译接入
|
||
- **Issue**: `quick_card_style_sections.dart` 中 31 处硬编码中文(栏样式/信息/字体/色深/字号/大小/功能开关/朗读 等)
|
||
- **修复**:
|
||
- `QuickCardStyleSection` / `QuickCardToggleSection` / `QuickCardPresetSection` 新增 `TQuickCard t` 参数
|
||
- `_BarStyleRow` / `_BottomInfoRow` / `_FontRow` / `_ColorDepthRow` / `_FontSizeRow` / `_CardSizeRow` / `_TtsToggleItem` 全部接收 `t` 参数
|
||
- `_FontRow` 分离 `_fontKeys`(不可翻译的标识符)和 `_fontLabels`(翻译后的显示标签)
|
||
- `PresetCard` 新增 `t` 参数,`_label` 使用翻译键
|
||
- `quick_card_layouts.dart` 两处调用点传入 `t` 参数
|
||
|
||
- **文件**:
|
||
- lib/l10n/languages/zh_tw.dart(翻译补全)
|
||
- lib/l10n/languages/ja.dart(翻译补全)
|
||
- lib/l10n/languages/ko.dart(翻译补全)
|
||
- lib/l10n/languages/fr.dart(翻译补全)
|
||
- lib/l10n/languages/de.dart(翻译补全)
|
||
- lib/l10n/languages/es.dart(翻译补全)
|
||
- lib/l10n/languages/it.dart(翻译补全)
|
||
- lib/l10n/languages/pt.dart(翻译补全)
|
||
- lib/l10n/languages/ru.dart(翻译补全)
|
||
- lib/l10n/languages/ar.dart(翻译补全)
|
||
- lib/l10n/languages/hi.dart(翻译补全)
|
||
- lib/l10n/languages/bn.dart(翻译补全)
|
||
- lib/features/home/presentation/widgets/quick_card_preview.dart(液态玻璃效果)
|
||
- lib/features/home/presentation/widgets/quick_card_style_sections.dart(翻译接入)
|
||
- lib/features/home/presentation/widgets/quick_card_widgets.dart(PresetCard 翻译)
|
||
- lib/features/home/presentation/widgets/quick_card_state_logic.dart(liquidGlass 预设补全)
|
||
- lib/features/home/presentation/widgets/quick_card_layouts.dart(t 参数传递)
|
||
|
||
## [v6.102.0] - 2026-06-22
|
||
|
||
### ✨ 增强(快速卡片创作 Sheet 拆分 + 多语言全量接入 + A/B 对比模式 UI 补全)
|
||
|
||
#### 1. 文件拆分(quick_card_sheet.dart 1345行 → 5 个文件均 < 1000 行)
|
||
- **Issue**: `quick_card_sheet.dart` 单文件 1345 行,违反项目"文件不超过 1000 行"规则
|
||
- **修复**:
|
||
- `quick_card_sheet.dart`(921 行)— 主入口,保留状态字段、回调、构建方法
|
||
- `quick_card_layouts.dart`(705 行)— `QuickCardStackedLayout` + `QuickCardSplitLayout` 两种布局
|
||
- `quick_card_bars.dart`(349 行)— `QuickCardNavBar` + `QuickCardActionBar` + `QuickCardSplitTopBar`
|
||
- `quick_card_state_logic.dart`(194 行)— 纯逻辑:`stateToJson`/`stateFromJson`/`applyPreset`/`applyAutoPalette`/`resetState`/`resetHomeStyle`
|
||
- `quick_card_dialogs.dart`(362 行)— 文字编辑对话框 + 更多操作 Sheet + 结果提示
|
||
- **验证**: `flutter analyze` 9 个文件全部通过,No issues found!
|
||
|
||
#### 2. 多语言全量接入(TQuickCard 翻译类型)
|
||
- **Issue**: Sheet 内所有用户可见文案为硬编码中文,不支持多语言切换
|
||
- **修复**:
|
||
- 新增 `lib/l10n/types/t_quick_card.dart`(525 行)— 定义 100+ 翻译键
|
||
- `T` 根类新增 `quickCard` 字段,集成到 `T.fromMap`/`toMap`
|
||
- `zh_cn.dart` / `en.dart` 新增 `quickCard: TQuickCard(...)` 完整翻译
|
||
- 主 Sheet 新增 `_t` getter:`TQuickCard get _t => ref.read(translationsProvider).quickCard;`
|
||
- 18 处硬编码中文文案全部迁移至翻译键(保存失败/已保存到相册/已应用到主页/已复制/已粘贴/剪贴板为空/已清空/已重置/模板保存/模板加载/自动配色/A-B 进入/A-B 恢复/A-B 应用新样式 等)
|
||
- 布局组件 `QuickCardStackedLayout` / `QuickCardSplitLayout` / `QuickCardNavBar` / `QuickCardActionBar` / `QuickCardSplitTopBar` 全部接收 `TQuickCard t` 参数
|
||
- `QuickCardTextEditorDialog.show()` / `showQuickCardMoreSheet()` / `showQuickCardResult()` 全部接收 `TQuickCard t` 参数
|
||
- **动态主题**: 通过 `AppTheme.ext(context)` 获取主题扩展,所有颜色/圆角/阴影均来自主题系统
|
||
- **动态样式**: 通过 `ref.watch(translationsProvider)` 实现运行时切换语言无需重启
|
||
|
||
#### 3. A/B 对比模式 UI 补全(空壳功能完善)
|
||
- **Issue**: A/B 对比模式已保存快照,但缺少切换 A/B 视图的 UI 入口
|
||
- **修复**:
|
||
- 预览区新增长按手势 `_toggleAbView()`,切换显示 A 原样式 / B 新样式
|
||
- 预览右上角显示当前视图角标(A 原样式 / B 新样式),带动画切换
|
||
- `_LayoutCallbacks` 新增 `onToggleAbView` 回调
|
||
- 布局组件接收 `isAbMode` / `abShowOriginal` / `abSnapshotState` / `abSnapshotHomeStyle` 参数
|
||
|
||
- **文件**:
|
||
- lib/features/home/presentation/widgets/quick_card_sheet.dart(重写)
|
||
- lib/features/home/presentation/widgets/quick_card_layouts.dart(新建)
|
||
- lib/features/home/presentation/widgets/quick_card_bars.dart(新建)
|
||
- lib/features/home/presentation/widgets/quick_card_state_logic.dart(新建)
|
||
- lib/features/home/presentation/widgets/quick_card_dialogs.dart(修改)
|
||
- lib/l10n/types/t_quick_card.dart(新建)
|
||
- lib/l10n/types/t_root.dart(修改)
|
||
- lib/l10n/languages/zh_cn.dart(修改)
|
||
- lib/l10n/languages/en.dart(修改)
|
||
|
||
## [v6.101.2] - 2026-06-22
|
||
|
||
### 🐛 修复(托盘导航进入错误 Tab 栈)
|
||
|
||
#### 0. 托盘菜单点击跳转使用错误 context 导致页面进入错误 Tab 栈
|
||
- **Issue**: 托盘 pop 菜单点击跳转时,`DesktopTrayController._navigateTo` 使用 `_ref.context` 调用 `context.appPush(route)`。但 `_ref.context` 可能不属于当前活动 Tab(例如窗口失焦期间 Tab 已切换,但 controller 持有的 context 仍是旧 Tab 的),导致非工作台模式下 `GoRouter.push` 把页面压入错误 Tab 的 Navigator。
|
||
- **修复**:
|
||
- `app_router.dart` 新增公开函数 `tabNavigatorKeyFor(int tabIndex)`,按 Tab 索引(0=首页/1=发现/2=我的)返回对应的 `GlobalKey<NavigatorState>`,原先 `_homeNavigatorKey`/`_discoverNavigatorKey`/`_profileNavigatorKey` 仍为私有。
|
||
- `desktop_tray_controller.dart` 的 `_navigateTo` 方法改为:
|
||
1. 显式读取 `splitViewProvider.currentTab` 获取当前活动 Tab
|
||
2. 通过 `tabNavigatorKeyFor(currentTab)` 拿到当前活动 Tab 的 `navigatorKey.currentContext`
|
||
3. 优先使用该 context 进行 `appPush`,确保非工作台模式下 GoRouter.push 进入正确的 Tab 栈
|
||
4. 若 tabNavigatorKey 的 context 不可用或未 mounted,回退到 `_ref.context`(原逻辑),并打印警告日志
|
||
5. 新增 `Log.i` 记录导航目标和当前 Tab,便于追踪
|
||
- 工作台模式下 `appPush` 内部已通过 `splitViewProvider.currentTab` 决定右栏栈归属,本次修复主要覆盖非工作台模式 / 全屏路由场景
|
||
- **文件**:
|
||
- lib/core/router/app_router.dart(新增 `tabNavigatorKeyFor` 公开函数)
|
||
- lib/features/desktop/desktop_tray_controller.dart(_navigateTo 重构 + 文件头注释更新 + 新增 flutter/widgets.dart 导入)
|
||
|
||
## [v6.101.1] - 2026-06-22
|
||
|
||
### 🐛 修复(纯黑模式下 Mac 端托盘图标不可见)
|
||
|
||
#### 0. 纯黑模式(amoled)下 Mac 端托盘图标手动切换为白色 PNG
|
||
- **Issue**: Mac 端使用 `isTemplate: true` 让系统自动反色,但纯黑模式下系统可能将黑色图标保持黑色(因为背景也是黑色),导致托盘图标不可见
|
||
- **修复**:
|
||
- `DesktopTrayService.setIcon` 接口新增 `isAmoled` 参数(默认 false,向后兼容)
|
||
- `TrayManagerTrayService.setIcon` Mac 端分支逻辑更新:
|
||
- light 模式:`tray_icon_light.png`(黑色图标)+ `isTemplate: true`
|
||
- dark 模式:`tray_icon_light.png`(黑色图标)+ `isTemplate: true`(系统自动反色为白色)
|
||
- amoled 模式:`tray_icon_dark.png`(白色图标)+ `isTemplate: false`(手动指定白色,不依赖系统反色)
|
||
- `DesktopTrayController.onThemeChanged` 新增 `isAmoled` 参数透传
|
||
- `app.dart` 新增 `_lastDesktopAmoledState` 字段,单独跟踪 amoled 状态
|
||
(dark 和 amoled 的 `effectiveIsDark` 均为 true,需单独检测 dark↔amoled 切换)
|
||
- Windows/Linux 端逻辑保持不变
|
||
- **文件**:
|
||
- lib/core/services/desktop/desktop_tray_service.dart(接口签名 + Stub 实现)
|
||
- lib/core/services/desktop/implementations/tray_manager_tray_service.dart(Mac 端逻辑)
|
||
- lib/features/desktop/desktop_tray_controller.dart(onThemeChanged 透传 isAmoled)
|
||
- lib/app/app.dart(跟踪 amoled 状态并传入 onThemeChanged)
|
||
|
||
## [v6.101.0] - 2026-06-22
|
||
|
||
### 🐛 修复(鸿蒙端"编辑此句"按钮解除限制)
|
||
|
||
#### 0. 鸿蒙端"编辑此句"按钮解除限制
|
||
- **Issue**: 创作卡片页面的"编辑此句"按钮,鸿蒙端点击会显示"敬请期待",无法进入编辑器
|
||
- **修复**:
|
||
- 移除 `sentence_detail_actions.dart` 中 `if (pu.isOhos) { AppToast.showInfo('敬请期待'); return; }` 限制
|
||
- 移除 `sentence_detail_sheet.dart` 中相同的限制逻辑
|
||
- 清理未使用的 `platform_utils` 导入
|
||
- 鸿蒙端现在可以直接跳转到 `/editor?text=...` 进入编辑器
|
||
- **依据**: `/editor` 路由在鸿蒙端正常注册(`route_registry.dart:1130-1138`),`EditorPage` 无平台限制
|
||
- **文件**:
|
||
- lib/features/home/presentation/panels/sentence_detail_actions.dart(修改)
|
||
- lib/features/home/presentation/providers/sentence_detail_sheet.dart(修改)
|
||
|
||
### ✨ 增强(快速卡片创作 Sheet 功能升级 — 模板/壁纸/取色/A-B 对比/动效)
|
||
|
||
#### 1. 模板管理 UI 完善
|
||
- **Issue**: `_saveAsTemplate` 已实现保存,但"加载模板"在更多操作菜单中只是占位,未实现模板列表选择对话框
|
||
- **修复**:
|
||
- 新增 `quick_card_template_sheet.dart`(~400 行)— 模板管理底部面板
|
||
- 数据模型 `QuickCardTemplate`:key/stateJson/homeStyleJson/savedAt/isFavorite
|
||
- 列表排序:收藏优先 + 时间倒序
|
||
- 使用 `AppSlidable` 实现左滑收藏/取消收藏 + 右滑删除
|
||
- 模板项含预览缩略图(背景色/渐变 + 文字摘要)
|
||
- 点击模板项返回并还原 QuickCardState + DailyCardStyle
|
||
- 通过 `Hive.box<dynamic>('app').keys` 直接获取所有模板键
|
||
- **更多操作菜单新增**:`loadTemplate('📂 加载模板')`、`abCompare('⚖️ A/B 对比')`
|
||
|
||
#### 2. 颜色选择器升级(最近使用 + 吸管取色)
|
||
- **Issue**: 28 色预设 + HSL 滑块交互偏简陋,缺少吸管取色和最近使用记录
|
||
- **修复**:
|
||
- 新增 `quick_card_color_section.dart`(~480 行)— 独立颜色选择模块
|
||
- 顶部增加最近 6 色横条,持久化到 KvStorage(`quick_card_recent_colors` 键)
|
||
- 吸管取色:`image_picker` 选图 → `package:image` 解码 → 3x3 网格采样取平均色
|
||
- 颜色选择器对话框升级:28 色预设 + 最近使用 + HSL 三滑块
|
||
- 7 预设色 + 自定义入口 + 吸管取色按钮
|
||
- **修复 CupertinoIcons eyedropper 未定义**:改为 `Icons.colorize`(Material 图标)
|
||
|
||
#### 3. 壁纸库接入
|
||
- **Issue**: 背景图片选择路径单一,仅支持从相册选择,未支持从项目内置壁纸库选择
|
||
- **修复**:
|
||
- 新增 `quick_card_background_section.dart`(~560 行)— 独立背景选择模块
|
||
- `_WallpaperLibrarySheet` 底部面板:来源切换栏(12 源)+ 网格 + 长按预览
|
||
- 复用 `features/template` 的 `wallpaperProvider` + `WallpaperPreviewSheet`
|
||
- 背景选择行新增"从壁纸库选择"入口
|
||
- 网络壁纸 URL 通过 `imagePath` 字段存储
|
||
|
||
#### 4. 文件拆分(quick_card_style_sections.dart 917行 → 430行)
|
||
- **Issue**: `quick_card_style_sections.dart` 文件过大,违反单一职责
|
||
- **修复**:
|
||
- `_BackgroundRow` + `_GradientAngleChip` → `quick_card_background_section.dart`
|
||
- `_ColorRow` → `quick_card_color_section.dart`
|
||
- `QuickCardStyleSection` 新增 `onAutoPalette` 参数
|
||
- 移除 `image_picker` 和 `logger` 导入(已迁移到子文件)
|
||
|
||
#### 5. A/B 对比模式
|
||
- **Issue**: 调整样式时无法对比"原样式"与"新样式"
|
||
- **修复**:
|
||
- 新增 `_abSnapshotState`、`_abSnapshotHomeStyle`、`_isAbMode` 字段
|
||
- `_enterAbMode()`:保存当前样式快照
|
||
- `_exitAbMode({bool restore})`:恢复原样式或保留新样式
|
||
- 更多操作菜单新增 `⚖️ A/B 对比` 切换项
|
||
|
||
#### 6. adaptive_palette 自动配色
|
||
- **Issue**: 用户配色困难,需要手动调整
|
||
- **修复**:
|
||
- 背景选择行新增"自动配色"入口
|
||
- `_extractAutoPalette` 调用 `AdaptiveThemeService.extractFromProvider(provider)`
|
||
- 提取主色/辅助色/表面色应用到背景渐变 + 文字颜色
|
||
- `BackgroundLayerPipe` 扩展支持链式调用
|
||
|
||
#### 7. 交错入场动画(flutter_staggered_animations)
|
||
- 样式调整区各项使用 `AnimationLimiter` + `AnimationConfiguration.staggeredList`
|
||
- `SlideAnimation` + `FadeInAnimation` 实现交错入场
|
||
|
||
#### 8. Hero 过渡动画
|
||
- `SafePreview` 外包裹 `Hero(tag: 'quick_card_preview', flightShuttleBuilder: ...)`
|
||
- 从首页点击创建到 Sheet 弹出使用 Hero 过渡
|
||
|
||
#### 9. 触觉反馈增强
|
||
- 新增差异化触觉方法:
|
||
- `_fireLightHaptic()` — 滑块/微调(HapticService.light)
|
||
- `_fireMediumHaptic()` — 选择/确认(HapticService.medium)
|
||
- `_fireSuccessHaptic()` — 保存/完成(HapticService.success)
|
||
- 滑块类回调(字号/色深)调用 `_fireLightHaptic()`
|
||
- 分享调用 `_fireMediumHaptic()`,保存/应用调用 `_fireSuccessHaptic()`
|
||
|
||
#### 10. 网络图片背景支持
|
||
- **Issue**: `quick_card_preview.dart` 使用 `Image.file` 渲染背景,壁纸库返回的 URL 无法加载
|
||
- **修复**:
|
||
- 新增 `_buildImage(path, fallback)` 辅助方法
|
||
- 自动识别 `http://` / `https://` 前缀,分别使用 `Image.network` / `Image.file`
|
||
- 错误回退到纯色背景
|
||
|
||
- **文件**:
|
||
- lib/features/home/presentation/widgets/quick_card_sheet.dart(修改)
|
||
- lib/features/home/presentation/widgets/quick_card_style_sections.dart(修改)
|
||
- lib/features/home/presentation/widgets/quick_card_background_section.dart(新建)
|
||
- lib/features/home/presentation/widgets/quick_card_color_section.dart(新建)
|
||
- lib/features/home/presentation/widgets/quick_card_template_sheet.dart(新建)
|
||
- lib/features/home/presentation/widgets/quick_card_dialogs.dart(修改)
|
||
- lib/features/home/presentation/widgets/quick_card_preview.dart(修改)
|
||
|
||
***
|
||
|
||
## [v6.100.0] - 2026-06-22
|
||
|
||
### 🐛 修复(API 拦截器 401 错误处理优化)
|
||
|
||
#### 1. Token 静默刷新(提前 5 分钟)
|
||
- **Issue**: 之前移除了 token 自动刷新逻辑,用户长时间使用后 token 过期需手动重新登录,体验不好
|
||
- **修复**:
|
||
- `TokenService` 新增 `_cachedExpiresAt` 缓存字段,由 `checkToken`/`refreshToken` 在获取 `expires_in` 后自动更新
|
||
- `TokenService` 新增 `silentRefreshIfExpiringSoon()` 方法:基于缓存的过期时间判断是否即将过期(提前 5 分钟 = 300 秒),若即将过期则调用 `refreshToken` 静默刷新
|
||
- `ApiInterceptor.onRequest` 在每次请求前 fire-and-forget 调用静默刷新(不阻塞当前请求)
|
||
- 跳过 `/api/token/` 开头的请求,避免对 token 管理接口本身触发刷新(防止递归)
|
||
- 并发安全:复用 `TokenService._isRefreshing` 锁,多个请求同时触发只刷新一次
|
||
- 刷新失败不阻塞:原 Token 继续使用直到 401,由 401 处理逻辑引导用户重新登录
|
||
- 刷新成功后若响应未包含 `expires_in`,设置默认过期时间(2 小时)避免重复触发刷新
|
||
|
||
#### 2. 401 友好提示(Toast 替代对话框)
|
||
- **Issue**: 401 时无任何提示,用户不知道需要重新登录
|
||
- **修复**:
|
||
- `_handleAuthExpired` 新增 `AppToast.showWarning('登录已过期,请重新登录')` 友好提示
|
||
- 使用 Toast(bot_toast)而非对话框,不打断用户当前操作
|
||
- 带节流机制:3 分钟内不重复提示,避免多个并发请求同时 401 时弹出多次 Toast
|
||
- 保留 warning 级别日志(不恢复 error 级别)
|
||
- 不重试请求(避免死循环)
|
||
|
||
- **文件**:lib/core/network/api_interceptor.dart、lib/core/services/auth/token_service.dart
|
||
|
||
***
|
||
|
||
> v6.99.0 及更早版本(v6.87.0 ~ v6.99.0)已归档至软件特性功能文档。
|
||
> 主要特性概览:
|
||
> - **v6.99.0**: 快速卡片创作 Sheet 拆分(1547行→4文件)+ 空壳功能补全(11项:字号滑块/背景图片/渐变方向/自定义颜色器/字体扩展/更多操作菜单/导出格式区分/自动播放循环/TTS朗读指示/模板保存/重置确认)+ 拆分过程修复(触觉音效补回/滑块过度反馈/TTS订阅泄漏/色深滑块宽度)+ 文本编辑对话框升级
|
||
> - **v6.98.0**: 10项桌面端问题集中修复(窗口关闭对话框/托盘工作台模式/创作卡片侧栏/编辑器按钮/macOS窗口大小/托盘三段式深色/托盘icon/401错误)
|
||
> - **v6.97.1**: 7项问题修复后的体验优化扩展(密码强度检测/稍后读气泡卡片交互/灵感朗读/情景诗词滑动/设置交互/日签换一句/使用报告性能与CSV导出)
|
||
> - **v6.97.0**: 7项软件问题集中修复(密码修改前端/稍后读气泡卡片按钮/灵感朗读/情景诗词滑动空白/情景诗词设置/日签换一句/使用报告数据为空)
|
||
> - **v6.96.0**: Windows 桌面端窗口大小预设菜单(3×2 网格)+ 托盘菜单窗口大小子菜单 + 工作台模式导航栏位置切换
|
||
> - **v6.95.0**: 鸿蒙端专项修复(横屏翻转/复制粘贴/设备信息/键盘可见性/READ_PASTEBOARD 权限/正则崩溃)+ SDK 零修改原则
|
||
> - **v6.94.5**: 工作台设置页 Slider 崩溃修复(CupertinoSlider 替换)+ 移动端横屏工作台布局空白修复
|
||
> - **v6.94.4**: 工作台布局修复(拖拽 clamp 逻辑/双栏注释/pop canPop 检查)
|
||
> - **v6.94.3**: 工作台模式白屏修复(appPop/appCanPop 工作台感知)+ 闲情逸致页面 ref 卸载报错修复
|
||
> - **v6.93.1**: MethodChannel 命名风格统一为 `apps.xy.xianyan/{feature}` 风格
|
||
> - **v6.93.0**: 桌面端原生功能扩展第二批(剪贴板富文本HTML/侧边栏折叠记忆/分屏记忆/可拖拽导航栏/Windows Mica Alt 特效)
|
||
> - **v6.92.0**: macOS 原生功能扩展(Touch Bar/NSSharingService 共享面板/NSDockTile 徽章/NSStatusItem 菜单栏金句/CoreSpotlight 索引)
|
||
> - **v6.91.2**: 深度链接服务完善(xianyan:// scheme 跳转,note/sentence/readlater 路由映射)
|
||
> - **v6.91.1**: 笔记编辑页桌面端拖拽文件接入(图片 base64/文本追加/文件链接)
|
||
> - **v6.91.0**: 桌面端原生增强(系统托盘/macOS 菜单栏/自定义标题栏/Windows 原生能力/窗口特效/动态主题/工作台设置页)
|
||
> - **v6.90.1**: 工作台模式多语言支持 + 中栏宽度 KvStorage 持久化
|
||
> - **v6.90.0**: PC工作台布局重构(微信PC式三栏 WorkbenchLayout + RightPanelStackNotifier + 路由拦截)
|
||
> - **v6.89.1**: macOS MissingPluginException 修复 + Expanded 布局错误修复
|
||
> - **v6.89.0**: 评分弹窗商店名称多语言 + Beta问卷按钮隐藏跨平台修复
|
||
> - **v6.88.0**: 闲情逸致价格档位扩展(6档)+ 纠错历史本地缓存(drift)+ 学习计划详情页 + 闲情逸致全模块多语言
|
||
> - **v6.87.0**: 多平台应用商店统一服务(AppStoreService)+ 学习计划重构预告 + 闲情逸致"免费"改"平价" + 纠错页全面多语言
|