Files
xianyan/CHANGELOG.md
Developer 07df41f3c1 chore: 发布 v6.6.29 正式版,完成多平台适配优化
本次更新包含:
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
2026-06-30 04:31:05 +08:00

2836 lines
193 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 36release 模式运行正常,无 `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 版本打包进 APKGoogle 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 运行时加载,不打包进 APKGoogle 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` 后缀的 cacheKeyv6.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`(真实数据)
- 端到端验证:登录 → 查询 detaillike_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` 及相关 importdart: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 → 200 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` importcupertino.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 个 infoasync gap、`bytes` 废弃、参数冗余)按需后续处理
---
## [v6.138.0] - 2026-06-26
### 🔧 全量迁移应用数据至 Application Supportv6.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 MBGitea 服务端统计),本地 `.git` 277 MB。历史中累积了大量第三方二进制、服务端运行产物与调试截图导致单次 `git push` 触发 Nginx HTTP 413请求体过大
#### 操作(`git-filter-repo` 改写全部历史)
从所有历史提交中移除以下路径:
| 路径 | 类型 | 说明 |
|---|---|---|
| `windows/local_packages/sqlite3/nuget_test/` | 第三方二进制 | NuGet 测试包(多平台 .so/.dll/.dylibFlutter 项目不该提交 |
| `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` | 相机:扫码/拍照生成卡片/OCRAVCaptureDevice 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 targetPBXBuildFile + 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: 586261192App 计划在 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()` 自动遍历所有 sectiontoMap 补全 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 默认开启 ImpellerIntel Mac 默认关闭Metal 驱动有渲染资源累积 bug
- **举一反三**:
- `FLUTTER_ENGINE_SWITCH_<N>` 环境变量机制仅适用于 mobile embedderiOS/Android桌面 embeddermacOS/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.0pins `.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/fromMap14 个语言文件新增 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 提交用 pkgx86_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` — 生成 xcarchiveAutomatic 签名Apple Development 证书)
4. `xcodebuild -exportArchive` — 导出 App Store pkg使用 Apple Distribution 证书重新签名
- **产物**:
- `build/Runner.xcarchive` — 归档文件
- `build/Runner/闲言.pkg` — 110MBApp 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=5V9NVUU6K5Format=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 改为 IconDataCupertinoIcons.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 类
- 删除不再需要的 importswindow_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 方法对小数指数处理有 bug0.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 + ScaleTransition0.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.dartPresetCard 翻译)
- lib/features/home/presentation/widgets/quick_card_state_logic.dartliquidGlass 预设补全)
- lib/features/home/presentation/widgets/quick_card_layouts.dartt 参数传递)
## [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.dartMac 端逻辑)
- lib/features/desktop/desktop_tray_controller.dartonThemeChanged 透传 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('登录已过期,请重新登录')` 友好提示
- 使用 Toastbot_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+ 学习计划重构预告 + 闲情逸致"免费"改"平价" + 纠错页全面多语言