# Changelog

Babelarc Windows 的所有显著改动都会记录在这里。

格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循
[语义化版本](https://semver.org/lang/zh-CN/spec/v2.0.0.html)。

---

## [Unreleased]

待规划:
- **WAV 音效继续 polish** — Audio 子分组(每事件单独开关 + 音量)
- **Mica / Acrylic 标题栏** — Win11 22H2+ 系统级 backdrop
- **Settings · AccountPage 独立详情页** — 当前账户信息只在 Home 卡片浅展示,
  后续做完整的会话管理 / 多设备 / 充值流水视图
- **区域批量导入导出** — D2 后续,目前只能单条改名 / 删
- **剩余短期 Dialog i18n 动态刷新**(P2 deferred from v0.6.2):PlansDialog /
  AppAudioConsentDialog / RegionManagerDialog / UpdateDialog /
  EmailLinkSignInDialog / TourSpotlight — 切语言时这些 modal dialog
  内的 imperative 文案不刷新,因 dialog 短期 + 切语言时通常不开,影响小

---

## [0.6.2] — 2026-05-27

对外披露(changelog 卡片只显示这 3 点):
- **同声传译速率显示修正** — 字幕窗显示从「≈85 能量/分钟」更新为「≈500 能量/分钟」,
  与 v0.6.1 起的实际计费一致(此前 UI 数字停留在 v0.6.0 之前的旧值)
- **设置 → 翻译档位 文案优化** — 中文「翻译档位」→「速度 / 质量」,与英日韩各
  语种风格对齐
- **多语言界面切换即时刷新** — 切 UI 语言后,设置页的模型档位选项 +
  同声传译字幕窗的状态文字立即更新,不再需要重启 app

内部技术变更(本文档完整记录):

### Hardcoded `85` 修正为 `500`(SKILL.md §5.3 #1 一致)
v0.6.1 changelog 已标注 F2/F3 实际计费从 85 → 500,但客户端 3 处 hardcoded
显示值未同步更新:
- `UI/Home/HomeWindow.xaml.cs:893` — billing-start consent gate 显示用 `const int rate = 85` → `500`
- `Realtime/ConversationController.cs:41` — F3 跨麦 metrics tick 用 `const int RatePerMinute = 85` → `500`
- `Settings/AppSettings.cs:206` — Z-flow 注释 `≈85/min` → `≈500/min`

服务端 KV `config/gemini.realtimePerMinute = 500` 自 v0.6.1 起一直是 SoT,
本次只修正客户端的 display-only 数字。

### zh resx 文案对齐 4 语种风格
- `TranslationPage_Auto_ModelLabel` / `TranslationPage_Manual_ModelLabel`
  「翻译档位」→「速度 / 质量」
- en / ja / ko 早已为「Speed / quality」「速度 / 品質」「속도 / 품질」具体词风格,
  本次让 zh 跟上

### i18n 动态刷新 bug fix(切语言不重启即可看到效果)

**问题根因**:LocalizationManager 的 `{i18n:Loc Key}` XAML markup 绑定走
indexer-name 通知 → 切语言时所有 XAML 文本自动 re-pull;但 code-behind 里
**imperative** 调用 `i18n.LookupString(...)` 设置的文案是 one-shot,
切语言后不刷新。需要订阅 `LocalizationManager.CultureChanged` 显式
re-render。

**本版修复 2 处长期可见 UI**(P0):

- **`UI/Settings/Pages/TranslationPage.xaml.cs`** — 设置 → 翻译页的"速度 / 质量"
  ComboBox 选项切语言不刷新。第一轮 fix 试图用 `ItemsSource = null;` 再 set
  fresh 强制重绘,真机测试**仍不生效**(zh→en 后回到 Translation tab
  仍显示中文,只有重启 app 才变英文)。根因:`ModelOption` 是 record,
  `DisplayLabel` 是 init-only 字段,WPF 永远不会重新 pull。**最终修法**:
  `ModelOption` 重构为 `sealed class : INotifyPropertyChanged`,`DisplayLabel`
  改为 computed property(每次 get 从 LocalizationManager 取当前 culture);
  构造时 `PropertyChangedEventManager.AddHandler` 弱事件订阅
  LocalizationManager 的 indexer 通知(`Binding.IndexerName`),culture
  变 → ModelOption 触发 `PropertyChanged(nameof(DisplayLabel))` → WPF
  ComboBox 内部 binding 自动 re-pull → 选项 + 当前选中项即时刷新。
  弱事件保证页面 Unloaded 时不内存泄漏。移除原 `_cultureHandler` +
  `RebuildModelOptions`(不再需要 ItemsSource 替换)。
- **`UI/Common/AppAudioSubtitleWindow.xaml.cs`** — 同声传译字幕窗口
  (长期 5 min ~ 数十 min 显示),状态文字 `SetConnecting/Active/Reconnecting`
  + Rate 文字均为 imperative 设置,未订阅 CultureChanged。本版修:
  - 加 `_cultureHandler` 订阅 `LocalizationManager.CultureChanged`
  - 内部缓存 `_currentStatus` enum + `_currentRate` int
  - `RefreshLocalizedText()` 切语言时 re-render 状态文字 + Rate 文字
  - `SetRate(string)` → `SetRate(int)` 签名变更,内部 format
    `AppAudio_Bar_Rate_Format`,host 不再外部 format(`HomeWindow.xaml.cs:928-929`
    caller 同步改 raw int)
  - `SetMetrics(used, remain)` 每秒由 host 调用 → 自动 self-heal,无需缓存
  - `Closed` 时 unsubscribe handler 防 leak

**未修(P2 deferred)** — 短期 modal dialog 类 imperative LookupString:
PlansDialog / AppAudioConsentDialog / RegionManagerDialog / UpdateDialog /
EmailLinkSignInDialog / TourSpotlight。因 dialog 短期 + 切语言时通常不开,
影响小;下次客户端版本批量按统一 pattern 加 CultureChanged 订阅。

---

## [0.6.1] — 2026-05-22

对外披露(changelog 卡片只显示这两点):
- **订阅方案价格调整**
- **翻译模型性能升级**

内部技术变更(本文档完整记录):

### 能量经济全面校准
(SoT = `babelarc_web/packages/shared/src/index.ts`,跨 repo 同步)
- SIGNUP_BONUS: 300 → 3000(10×)
- Starter: 1500 → 20000 能量,$3.99/月 不变
- Pro: 5000 → 45000 能量,$7.99/月 不变
- Power: 15000 → 135000 能量,$14.99 → **$19.99/月**(年付方案
  已下线,Plans.cs 仅列月度档)
- Quality 档 multiplier: ×2 → **×6**(只影响 text / image / flash /
  F1 voice 四个 per-call 操作,**不影响** F2 / F3 实时语音)
- Quality 档模型:`gemini-3-flash-preview` → **`gemini-3.5-flash`**
  (AppSettings.ModelIdQuality + Resources/Strings.resx 模型卡注释同步)
- F2 同声传译 / F3 跨语言麦克风:85 → **500 能量/分钟**(实际
  由 Worker `config/gemini.realtimePerMinute` KV 控制,客户端 UI
  只显示"按分钟扣费"不写死数字)

### 模型档位切换打通(关键修复)
v0.6.0 之前 5 个 per-call 调用点中 4 个传 `clientModel: null` →
服务端总用 defaultModel (Fast).用户 Settings 切档完全是装饰,
Quality 模型几乎从不被触发.本版修:
- `TranslationWorker.cs:168` (image 聊天框翻译) → `ServerModelEnum(ModelName)`
- `TranslationWorker.cs:213` (OCR 后 text) → `ServerModelEnum(ModelName)`
- `FlashResultWindow.xaml.cs:97` (闪译) → `ServerModelEnum(ModelName)`
- `ManualTranslationStore.cs:124` (打字 Submit) → `ServerModelEnum(ManualModelName)`
- `ManualTranslationStore.cs:189-195` (F1 voice) 早已正确 → 不动

设计语义锁定:
- **AutoModelBox / `ModelName`** = 自动管线(image / OCR / flash)
- **ManualModelBox / `ManualModelName`** = 手动输入(打字 / F1 voice)
- F2 同传 / F3 跨麦 不受档位影响(走 OpenAI gpt-realtime-translate 专用模型)

### 其他
- **resx 4 语种** 文案同步:Plans_Free_Quota / Plans_Starter_Quota /
  Plans_Pro_Quota / Plans_Power_Quota / Plans_Fineprint / Plans_Subheading
  / Home_Card_Account_LoggedOut_Hint / Hotkey_NotSignedIn_Body /
  Tour_Step1_Body / Conversation_Gate_SkipNext
- **PlansDialog.xaml + xaml.cs** Power $14.99 → $19.99 展示同步

---

## [0.6.0] — 2026-05-17 · 未登录按热键不再「没反应」

> 修复一个迁移引入的回归:没登录时按热键截图,如果窗口收在托盘里(正在游戏),
> 提示框找不到可显示的窗口被静默丢掉了,你完全不知道发生了什么。

### 客户端

- **未登录按热键现在一定有反馈** —— 没登录时按翻译/闪译热键,会弹"登录后才能用"
  提示;万一提示框没地方显示(窗口收在托盘里),会自动把主窗口呼出来,让你看到
  账户卡去登录,不再静默无响应。

---

## [0.5.2] — 2026-05-17 · 更透明:端内可看隐私与条款 · 计费说明更清楚

> 一版"把话说清楚"的更新:法律与计费信息不再藏在官网角落,软件里就能看;
> 数据怎么走、订阅怎么扣费,都摆到明面上。无新功能,主打透明与可信。

### 客户端

- **关于页新增「隐私政策 / 服务条款 / 第三方许可」入口** —— 点开直达,
  不用再去官网翻;关于页常驻一句数据处理说明
- **关于页副标题** 改为产品定位"给玩家的实时聊天翻译悬浮窗"
- **首启引导补一句数据告知** —— 框选那块画面发到云端翻译、翻完即丢,
  说在明处
- **随包附完整第三方许可** —— `THIRD-PARTY-NOTICES.txt` +
  `THIRD-PARTY-NOTICES.SkiaSharp.txt`;`LICENSE.txt` 改为正式软件许可
  (不再是"待定")
- 英文界面下若干按钮 / 提示曾误显中文,已修;套餐细则不再出现底层模型名;
  翻译报错不再把后端错指为"Google"

### 官网 / 条款

- **隐私协议、服务条款重写** —— 据实说明数据怎么处理、由谁处理;
  新增正式的软件许可条款;退款时效措辞更稳
- **结账确认页显著标注**「自动续费 · 价格与周期 · 随时可取消 ·
  未用能量月底清零」
- **官网首屏改版** —— 从抽象文案换成"能拿它干嘛"的使用场景
- 修掉登录邮件「有效期」写错(实际 5 分钟,三处口径统一)

---

## [0.5.1] — 2026-05-12 · 套餐能直接切换 · 防双扣 · 官网邮件链接登录

> 三个洞同时堵上:已订阅用户切换方案需要"取消等到期再订",反人类;
> 客户端 / 官网手滑连点订阅按钮会被 Creem 起两份订阅双扣钱;非 Gmail
> 用户在官网订阅只能用 Google 弹窗,撞上桌面端账号会强行造一个新 uid。
> 这一版用统一的 `changePlan` callable + 网页加邮件链接登录入口一次过。

### 客户端

- **PlansDialog 套餐切换** —— 已有订阅的用户,卡片按钮自动变成"当前方案"/
  "升级到 X" / "切到 X";点击会先弹 InfoDialog 解释生效时机和能量去向,
  确认后调 `changePlan`。升级今天就到账多出的能量,降级在下个续费日生效,
  期间继续按原方案使用
- **待生效提示横幅** —— 降级预约后,管理面板顶部一条 banner 写"会在 X/XX
  切到 Pro · 撤销",随时一键撤回
- **错误透传** —— 服务端 `FailedPrecondition` 错误的文案是中英自动选好的
  user-facing 句子(比如"你已经在这个方案上,无需切换"),直接 toast,
  不再笼统报"操作失败"
- **i18n** —— Plans_CTA_CurrentPlan / Plans_CTA_Upgrade_Format /
  Plans_CTA_Downgrade_Format / Plans_Change_Confirm_* /
  Plans_PendingChange_Banner_Format / Plans_UndoPendingChange
  以及对应的 EN 版,native register 写,不机翻
- **Plans_CTA_Loading 文案** —— "正在打开浏览器…" → "处理中…",因为
  cancel / resume / changePlan 路径并不开浏览器,旧文案是误导

### Server (babelarc_cloud)

- **新增 `changePlan` callable** —— 升级走 Creem `/upgrade` +
  `proration-charge-immediately`,降级走 `proration-none` + 我们端记
  `pendingTier`,等周期末 webhook 自动 swap。canceled-but-in-period
  状态自动先 resume 再切。同 tier 拒绝。pendingTier 已置时拒绝其它切换
  (避免同周期内反复升降级触发 Creem 双扣)
- **`createCheckout` 防双扣兜底** —— 已有 active sub 时直接抛
  `FailedPrecondition`,提示用户走切换流程而不是再起一个新订阅
- **`creemWebhook` 处理 `subscription.update`** —— 幂等 no-op,我们的
  callable 是状态源 of truth;webhook 仅同步 `creemProductId` + 写
  lastEventId
- **新增 `subscription_change` ledger 类型** —— 升级时记一条 +N 能量,
  metadata 含 fromTier / toTier。降级不写 ledger(能量没变);周期末走
  常规 `subscription_grant`

### Web (babelarc_cloud)

- **官网 PLANS 区块加邮件链接登录入口** —— 之前只有 Google 弹窗,
  非 Gmail 用户被迫造新账号;现在弹一个二选一对话框,Google 或邮件链接,
  跟桌面端登录入口一致
- **订阅前一律弹邮箱确认** —— 哪怕用户已经登录,点订阅也会先弹一个对话
  框告知"以 xxx@yyy 这个账号订阅 X 方案",可继续可换号,减少账号串场
- **PLANS 按钮自动适配现有订阅** —— 已订阅用户看到的按钮变成"当前方案"
  / "升级到 X" / "切到 X",点击走 changePlan callable 而非 createCheckout
- **`/auth/email-link-callback?source=web`** —— callback 页支持两种模式:
  desktop 配对(原行为)和 web-only(保持浏览器登录 + 跳回首页)

### 文档

- **DECISIONS.md E12** —— 套餐切换统一逻辑 lock-in
- **官网 FAQ 加一条** —— "已经订阅了能不能换套餐"
- **Terms § 4** —— 新增"切换方案"小节 + "退款发生后的影响"小节
- **Privacy § 7** —— 披露 pendingTier + 短暂 billingLock 字段
- **llms.txt + llms-full.txt** —— v0.5.1 plan-change feature bullet
- **changelog 页加 v0.5.1 卡片** —— C 端语境,讲生效时机和能量去向,不讲
  proration / webhook 这些技术词

### 硬化(E12 上线后端到端 smoke-test 找到的洞)

- **取消订阅传 `mode: scheduled`** —— Creem 默认走 immediate cancel,
  resulting in unrecoverable canceled status (resume 接不了)。显式传
  mode=scheduled 后 status 变 `scheduled_cancel`,resume 可用
- **Optimistic Firestore mirror** —— cancel / resume / changePlan callable
  Creem 成功后立刻写 Firestore,不等 webhook 异步 1-5s 传播,避免
  "刚操作完 UI 还显示旧状态 → 再点 → Creem 报错"的 race
- **三层 self-heal** —— Creem 报"状态对不上"时(test/live env 漂移、
  webhook 历史丢失、退款终止订阅等),callable 识别并清掉 Firestore stale
  mirror + 抛友好错误,而不是笼统 "操作失败"
- **`refund.created` undefined 字段防御** —— Creem 不传 `amount` 时事务
  不再回滚,balance 能正常清零
- **退款清 `pendingTier`** —— 避免退款后 UI 仍显示已死预约的 banner
- **`billingLock` 双扣 Race 防护** —— `users/{uid}.billingLock` 字段 +
  Firestore transaction:`createCheckout` 10 分钟 TTL,`changePlan`
  60 秒 TTL,防"客户端+网页同时点订阅 / 同时切方案"导致 Creem 两份订阅
  / 双 prorate
- **PlansDialog banner 仅在 active 状态下显示** —— canceled / expired
  时 pending change 不会触发,banner 没意义
- **取消订阅确认弹窗** secondary button label `Plans_Cancel_Dismiss`
  ("我再想想" / "Keep my subscription") —— 跟 "取消订阅" 视觉上区分,
  之前并列两个"取消"按钮读起来像同一动作
- **`Plans_CTA_Loading` 文案** —— "正在打开浏览器…" → "处理中…",
  cancel / resume / changePlan 路径并不开浏览器
- **`reset-post-refund.mjs` 管理工具** —— 退款 webhook retry 窗口过期 /
  test→live env 迁移 / 测试帐号清场 等场景下手动重置一个用户到干净状态
- **`publish-portable.ps1` §4d2** —— 自动清理 staging 里的 stray *.zip
  (某些签名 GUI 会留残)

---

## [0.4.5] — 2026-05-11 · 截图翻译链路提速 + 套餐订阅上线

> 两件大事:**(1)** 用户感知最强的那 ~1 秒延迟来自三件小事 (Cloud Functions
> 冷启动 / TLS 握手 / HTTP/2 连接被 GFE 回收),这一版把这三件都堵上;
> **(2)** 商业化:Creem.io 接入完成,客户端右键托盘 → "套餐 / 订阅" 可订阅
> Starter / Pro / Power,主页也上了完整套餐区。WebP encoder 单独用 bench
> 测了一轮 —— 实测在我们的 payload 区间(188 KB JPEG)反而比 JPEG 慢 6%,
> 所以**没采用**;client 继续 JPEG q85。

### 客户端

- **HTTP/2 连接保活策略修正** —— `BabelarcServerClient` 的 keep-alive ping
  policy 从 `WithActiveRequests` 改成 `Always`。前者只在请求 in-flight 时
  发 ping,空闲超过 GCP GFE 的 idle timeout(~几分钟)连接会被悄悄回收,
  下一次翻译又要 200-400 ms TLS 握手。`Always` 让连接 24×7 保活,代价
  仅是 60 秒一次的 60 字节 ping
- **连接预热双触发** —— `BabelarcServerClient.WarmupConnectionFireAndForget()`,
  两个触发点:**(1)** OutputWindow 第一次 Loaded、**(2)** 每次按 trigger /
  flash 全局热键的入口(签到检查通过后立刻 fire)。fire-and-forget 一个
  HEAD 到 base URL —— 返回 404 是预期(根路径无 handler),但 TLS + HTTP/2
  setup 已经完成,连接进 pool。30 秒节流防止连按热键时狂打 HEAD。
  实测在按热键 → 框完 chat panel 的 2-5 秒窗口里足够握完手,首次翻译
  上行直接命中已建好的连接

### Server-side(`babelarc_cloud`)

- **`translateImage` Cloud Function `minInstances: 1`** —— 1 个 warm instance
  24×7 守着。冷启动从 800-1500 ms 变 0 ms。`translate`(文本)不动 ——
  文本翻译走本地缓存为主,且远不如截图翻译延迟敏感,保留 0 不浪费 $25/月

### 没采用 · WebP encoder

- 临时 `bench/EncodingBench`(已删)做端到端 benchmark:同一张 1080p
  fixture 三种编码 round-robin 5 个样本(+1 warmup discarded)
- 结果:`JPEG q85 188 KB / median RTT 6346 ms`,`WebP q85 124 KB / median
  RTT 6750 ms`,WebP 比 JPEG **慢 6.4%**
- 失败原因:SkiaSharp libwebp encoder ~80 ms,libjpeg-turbo 仅 ~14 ms;
  少传的 64 KB 在 HTTP/2 上只省 ~30 ms;Vertex AI 处理 WebP 也比 JPEG
  略慢。三者叠加把节省的传输时间反吃光
- Server 端的 `imageFormat: "webp"` 入口也一并撤回(`commit e473959`
  → revert),没有真实需求时不留 dead code。等未来 payload 真正变大
  (超清模式 / 视频帧 / AVIF)再连同 client 一起重新引入

### 商业化上线 · 订阅套餐(E8)

- 客户端右键托盘图标 → **"套餐 / 订阅..."** 入口,弹出 `PlansDialog`,
  4 张套餐 card (Free / Starter / Pro / Power),Pro 标"最受欢迎"。
  点订阅按钮调 server `createCheckout` callable,系统浏览器打开
  Creem hosted checkout,用户在浏览器完成支付,webhook 回到我们 server
  自动 reset 余额到对应档位月度额度
- 同一 dialog 底部有 **"管理订阅 / 取消"**,跳 Creem 自助管理面板
- `BabelarcServerClient` 新增 `CreateCheckoutAsync` + `GetCustomerPortalAsync`,
  跟其他 callable 走相同 auth + refresh + retry 包装
- 套餐定价 cost-aligned: text=1, image=10, flash=10, Quality 档 ×2;
  Free 注册赠 300 / Starter $3.99 1500 / Pro $7.99 5000 (年付 $79.99
  省 17%) / Power $14.99 15000 (年付 $143.99 省 20%) — 见 DECISIONS E8
- 月销毁:能量每月初按档位重置,上月剩余不顺延(简化预期管理,避免
  长尾消费 spike)
- 30+ 双语 i18n strings 加入 `Strings.{,en}.resx`,套餐文案 + 错误提示
  完整 zh / en 覆盖

---

## [0.4.4] — 2026-05-11 · 流星动画提速 + 资源瘦身 +(server)prompt 后台

> 收尾批次。三件事:**(1) 主页打开后立刻能看到流星**(之前要等 15 秒);
> **(2) 顺手把过去几轮迭代留下的孤儿资源 / 注释清掉**;
> **(3) 后端 (`babelarc_cloud`) 把 LLM prompt 全部搬到管理后台可视化编辑**
> —— 客户端无任何感知,但 ops 改 prompt 不再需要发版 functions。

### 流星动画首发提前

- 主页 6 颗流星的 burst 时点从 cycle 末 8% 挪到**前 8%**:
  - 之前:首颗流星首次出现 = `delay + 0.92 × cycle` ≈ **15 秒后**(用户基本看不到)
  - 现在:首颗 0.5s 出现,后续每 ~2 秒一颗,10 秒内 6 颗依次冒出来,之后按各自周期
    (11 / 13 / 15 / 17 / 19 / 23 s)自然循环
- `ShootingStar.cs` 的 keyframe 时间表整体改写,burst 仍是 8% cycle 长,只是位置
  改到开头;视觉效果跟之前一颗流星单独看完全相同
- HomeWindow 上 6 颗星的 `DelaySeconds` 重铺(0.5 / 2 / 4 / 6 / 8 / 10),让前 10 秒
  连贯有节奏

### 资源瘦身

- 删未引用的 brand 资产(总 ~1.8 MB):
  - `Brand/Hero/home-tower-ambient.png`(1.7 MB)— v0.4.0 短暂用过的 Home 背景,
    切到星空动画后失效
  - `Brand/Hero/first-run-portal.png`(84 KB)— 对应的 modal 已被 spotlight tour 替代
  - `Brand/app-icon.svg`、`Brand/menubar-icon.svg`(共 8 KB)— 从未引用
- 同步更新 `Babelarc.csproj` 和 `DESIGN-v1.1.md` 的资源清单

### 启动时长测量代码清理

- v0.4.3 加的 `Stopwatch` + `LogTiming()` instrumentation 已经用完职责(测出 splash
  动画 1.5 s + AllowsTransparency 354ms 是大头,但都是品牌曝光取舍,不优化),代码
  全部移除
- 保留 `Stats.StatsTracker.NoteSessionStart()` 推迟到 `Task.Run` —— 这条不是
  instrumentation,是真把文件 IO 挪出关键路径的小优化

### Server-side(`babelarc_cloud`,§ B3 prompt 后台可视化)

> 客户端 binary 没改动 —— 但 server prompt pipeline 重做了,客户端透明继续工作。

- Cloud Functions 用的 Vertex AI prompt 全部从 `gemini.ts` 硬编码搬到 Firestore
  `config/prompts` 单文档,通过 `admin.babelarc.app/admin/prompts` 在线编辑
- 两个功能(text translate / image translate),每个 3 段 user prompt
  (`header` / `gameContext` / `instruction`),共享占位符 `{targetLanguage}` /
  `{gameName}`
- 改了 60 秒内全 Cloud Functions 实例生效,无需 redeploy(60s 内存缓存 TTL)
- 编辑 UI 按"功能"分组,每组只显示该 callable 实际用到的 prompt 类型(都是
  user;以后接入 systemInstruction 自然加 section);自带"拼装预览"窗格,
  样例占位符值实时渲染最终送入 Vertex 的 prompt
- **顺手修了 v0.4.0 引入的 mime 不匹配 bug**:`TranslateImageRequest` 加
  `imageFormat: "png" | "jpeg"` 字段,server 按 client 传的 hint 设
  Vertex AI mime;v0.4 client 发 JPEG 不再被错误标 `image/png`(老 client 默认
  PNG,完全向后兼容)
- 详细架构见 `OPTIMIZATION-BRAINSTORM.md § B3` + `babelarc_cloud/DECISIONS.md`
  `config/prompts` 章节
- **首次创建 UX patch**(commit `b0b834b`):第一次进 `/admin/prompts` 时
  Firestore 还没 doc,我原版 dirty 检测会因为"draft 跟 DEFAULT 一致"
  把保存按钮锁死。修法:加 `canSave = !saving && (dirty || cfg === null)`,
  第一次创建时即使没改也能保存默认值;按钮文案条件切换成"用默认值
  创建配置"。已经在 prod 跑了
- **部署记录:** functions deploy 2026-05-11(`adminUpdatePrompts` create +
  `translate` / `translateImage` update);App Hosting commit `ee09bd6` →
  `b0b834b`(包含上面的 patch)。Firestore `config/prompts` 由 admin
  首次进 `/admin/prompts` 点保存初始化

---

## [0.4.3] — 2026-05-11 · 启动时间优化 (§ C)

> brainstorm § C 客户端启动 / 资源占用 batch。**大头工作是先测后改** ——
> 加 stopwatch 看哪一段慢,然后再决定哪条路径值得动手。

### 启动基线日志(§ C1)

- App.OnStartup 顶部启 `Stopwatch`,在 5 个里程碑打 Info 日志:
  `[startup +N ms] settings loaded` / `splash shown` / `pipeline ready` /
  `tray icon installed` / `hotkeys registered` / `home window shown` /
  `home painted (first frame)`
- HomeWindow 在 `ContentRendered` 第一次触发后回报"first frame"时间(`Loaded`
  发生在布局后但渲染前,`ContentRendered` 才是用户真看见画面的时刻)
- 跑一遍看 `data/logs/` 最新文件就知道哪一段慢了

### Stats 推迟初始化(§ C2)

- `Stats.StatsTracker.Shared.NoteSessionStart()` 从 `OnStartup` 同步路径**移到背景
  `Task.Run`** —— 这次小调整把 `stats.json` 文件 IO 整个挪出 splash → home 的
  关键路径,之后做 region 之类的同类小动作可以照搬这个模板
- `AchievementChecker.Shared` 本来就是 lazy static 字段,首次访问才 `LoadOrInit()`,
  没有 eager 路径要改

### 已分析后跳过的项(把"为什么不做"也写进 doc)

- **D3D11 设备 lazy init**:brainstorm 标的问题不存在 —— `ScreenCaptureService`
  构造里只 `new() + null` 一对引用;D3D11 设备是在 `CaptureSession` 构造里建
  的,而 `CaptureSession` 只在 `CaptureAsync` 第一次被调用时才 new。**已经是
  lazy 了**,和 brainstorm 的诊断不一致
- **Storyboard 隐藏暂停**:WPF 渲染线程对隐藏窗口已经自动跳合成,GPU 成本为
  零;UI 线程剩下的 timeline tick 成本是 µs/s 量级 —— brainstorm 自己也标
  "very small"。值不值正确实现需要给每个 BeginStoryboard 留 Clock handle,
  收益不抵复杂度。**不做**
- **ItemsControl 虚拟化**:现有 `ScrollViewer + ItemsControl` 组合不能直接套
  `VirtualizingStackPanel` —— 必须把 ScrollViewer 移进 ItemsControl Template
  里,这样外面 8 处直接引用 `ListScroll` 的 auto-scroll-to-bottom 像素精确
  代码都得改成 template-part 查找。**风险大、收益只在 200+ 气泡的长会话才
  显著**,留到真有用户反馈再做
- **SKBitmap 对象池**:brainstorm 自己也标"很小,但是良好公民"。每次截图就
  一张 SKBitmap,GC 完全够用,LOH 分配的实际成本可忽略。**不做**

---

## [0.4.2] — 2026-05-11 · 闪译重构 + 区域管理升级 + 文案 i18n

> v0.4.0 上线后的反馈批次。**最大改动:闪译变成真正独立的"翻译在原地"
> 工具,不再走主翻译窗,也不再占用区域记录。**

### 闪译重构(§ D6 redesign)

- **闪译热键默认从 Ctrl+Alt+T 改成 Ctrl+Alt+S**(老用户首次升级会带着旧
  设置进来,改的话去通用 → 闪译热键)
- **新增独立的"闪译结果窗"**:框选完之后**留在原地**,先转圈 loading,
  然后直接在原选框里铺开译文(纯文本,无气泡包裹),适合"刚好想看一
  眼这段对话"的场景
- **支持连续多次闪译**,每次都开一个新结果窗,**必须手动点 ✕ 关闭**(不
  会自动消失)
- 结果窗右上角只有 ⚙ 设置 + ✕ 关闭两个图标
- 视觉(背景色 / 边框 / 字号 / 字体颜色)继承"通用 → 译文窗外观"的
  设置,改一处全联动
- **不再写入区域记录** —— 闪译是即开即用,不污染你的 region 列表

### 区域管理升级(§ D2 follow-up)

- **新增"管理已保存的区域"弹窗**(主页 → 区域行 → 管理):
  - 直接点输入框**改名**(回车确认 / Esc 撤销 / 失焦自动保存)
  - **↑ ↓ 调整顺序** —— 你常用的放最上面,主页和译文窗的下拉跟着这个顺序
  - 🗑 删除单条,有二次确认
  - 改名 / 排序 / 删除都即时生效,关掉窗口就保存
- 主页区域行的"删除"按钮改成"管理"

### 译文窗顶部区域 pill(§ D2 follow-up)

- 译文窗顶部新增一行**区域下拉**,显示当前用哪个框选区域作为检测源
- 默认选中**上次使用的那个**(跟主页保持一致)
- 切换区域 → 一键跳到对应的位置

### 框选页面 i18n

- 框选 capture / output 区域时顶部的英文提示("Step 1 / 2 — Select the
  chat area..." 等)全部走 i18n,中英两份独立文案
- 中文版"第 1 步 / 2 — 框出要翻译的聊天框"等

---

## [0.4.0] — 2026-05-11 · 性能 + 错误恢复 + 闪译 + 多区域

> 第一批性能优化批次(对应 `OPTIMIZATION-BRAINSTORM.md` § A / B1 / B2 / D3 / D11)
> 加上同窗口里追加的 § D6 闪译 + § D2 多区域。
> 用户感知最明显的:**翻过的内容下次秒出 + 不扣能量**;**一键拖框翻译 +
> 切游戏不用重新框**。

### 翻译本地缓存(brainstorm § A)

- **新增本地 SQLite 翻译缓存**(`data/cache.sqlite`)。同样的话翻译过一次,
  下次直接出结果,**零网络、零能量消耗**
  - 设置 → 通用 新增"翻译缓存"卡片:开关 + 容量上限滑杆(50 MB / 100 / 200 /
    500 / 1 GB / 4 GB) + 已用统计 + 清空按钮
  - 默认开,默认 200 MB 上限
  - 容量到顶后**只是停止往里加**新条目,已经攒下的翻译不删 —— 想清空必须
    手动点"清空缓存"
  - 缓存 key = SHA-256(`text|game|model|target|src`),换游戏 / 换模型 / 换目标
    语言会得到独立缓存,不互相影响
  - 状态行实时显示"已用 X / 共 Y · N 条记录 · 本次启动省了 Z 次翻译"
  - 仅适用于**输入框纯文本翻译**;截图 OCR + 翻译路径每次像素都不一样,
    哈希命中不了,所以不走缓存(schema 预留了 image kind,以后做感知哈希时
    可以再启用)

### 网络层(brainstorm § B1 + B2)

- **截图上传体积大幅下降**:PNG → JPEG q85 + 1080p 等比缩放
  - 大区域截图(全屏 / 多屏)等比缩到 1920×1080 等效像素再上传,token 用
    量降一档
  - JPEG q85 在 OCR 视角下跟 PNG 无差,但传输体积小 5–10 倍
  - 端到端延迟降低 200–500 ms
- **HTTP/2 + keep-alive**:`SocketsHttpHandler` 配 `EnableMultipleHttp2Connections`
  + `KeepAlivePingDelay` + `DefaultRequestVersion = HTTP/2`
  - 连续触发翻译时不再每次都做 TLS handshake,省 ~100 ms / 次

### 闪译(brainstorm § D6)

- **新增第二个全局热键"闪译"**(默认 Ctrl+Alt+T,设置 → 通用 可改)
- 按下 → 拖一个矩形 → **立即翻译**,不需要先框聊天框再框译文窗
- 译文窗自动放在框选区域**正下方 16px**(够不下时自动跑去上方);宽度跟
  框选区域一致,高度 240
- 适合"刚好想看一眼这段对话"或者"一次性翻译就够了"的场景
- 跟普通热键并存,muscle memory 不打架

### 多区域保存 + 切换(brainstorm § D2)

- **每次成功翻译后,框选位置自动保存**为一个区域记录("区域 1"、
  "区域 2"……)。下次切游戏 / 换聊天框,Home 上能直接选回去
- Home 卡片下面新加一条**"已保存的区域"**横条:下拉选某个区域 → 直接
  跳到 Ready,不用重新框选
- 同一组矩形(±4 像素容差)不会重复存,只是更新"上次使用"时间
- 删除按钮:清掉不再用的区域记录,弹 confirm 二次确认
- 持久化到 `data/settings.json`,下次启动还在
- (重命名 / 多区域管理详情页留到 v0.4.1)

### 主页视觉(本轮 polish)

- 主页背景从静态桥城画(over-bright,跟卡片打架)换成纯 SVG 等价**星空**
  动画 —— 6 颗错峰流星循环,周期 11/13/15/17/19/23 秒互质,永远不同框;
  极淡 sunset/twilight 宇宙辉光做底子。零图片,GPU compositor 处理,
  CPU 占用可忽略
- About 卡 hero 高度从 ~700 px 收到 220 px,对齐"检查更新"卡的视觉节奏
- 旧的 about-hero / window-ambient / home-tower-ambient 引用全部迁出,
  只剩 splash-mark-hero(About 卡里露出塔身一段)在用

### 错误恢复(brainstorm § D11)

- **网络层透明重试**:Timeout / NetworkUnavailable / 5xx 一次自动重试(750 ms
  退避),用户不需要再按一次热键
- **热键冲突预检 (§ D3)**:RegisterHotKey 失败时弹 Toast"热键 X 已被另一
  个程序占用",一键跳到设置改 —— 之前只在日志里 Warn,用户根本不知道为啥
  按了热键没反应

### 共享控件改进

- `InfoDialog` 新增 confirm 模式(secondary "取消"按钮 + onPrimary 回调),
  替代用 Win32 MessageBox 做"清空缓存"二次确认的需求

### 内部

- 新增 `Microsoft.Data.Sqlite 9.0.0` 依赖(纯托管,捆绑 native dll,启动时间
  影响 < 50 ms)
- `Translation/Cache/TranslationCache.cs` 单例 + WAL journal 模式,4 路并发
  翻译写入安全
- `data/cache.sqlite` 加入 portable 数据目录契约;复制 `Babelarc/` 文件夹
  时缓存自动跟着走

### 升级提示

旧用户首次运行 v0.4.0 时会自动建 `data/cache.sqlite`,默认开启。如果不想用,
设置 → 通用 → 翻译缓存 关掉即可,不会写任何东西到磁盘。

---

## [0.3.6] — 2026-05-11 · 视觉调度 + 官网双语

### 软件视觉

- 主窗口 ambient backdrop 从 `window-ambient-bg.png` 换成 `about-hero.png`
  —— 之前底部的彩虹桥光带跟顶部 hero 区视觉重复,换成"桥之城"painterly
  影像后,顶部的桥拱保持唯一焦点
- AboutPage hero 从 `about-hero.png` 换成 `splash-mark-hero.png`(Babel 塔)
  —— 关于页讲品牌起源故事,Babel 塔本来就是 brand 出处,放这里更契合

### i18n 默认 fallback(确认)

- `AppLanguage.Auto` 已经在系统 culture 不是 zh-* 时默认 English(本来
  就是 v0.3.0 的实现,这次只是确认 + 文档化),不需要 code 改动

### 官网双语

- 整页 i18n:中文 / 英文双 HTML 内容,服务端按 cookie / Accept-Language /
  IP 国家头自动选语言。无 hydration flash
- 英文版**不是中译英**,从零写的 native English copy,匹配同一个品牌
  voice 但更适合英文母语玩家
- Nav 加了 中 / EN 切换 pill;选择持久化到 cookie,1 年有效
- Footer / changelog 标签同步双语
- 文件:`v3-home-content.zh.html` / `v3-home-content.en.html`,
  `lib/locale.ts`(detection),`api/locale/route.ts`(switcher endpoint)

---

## [0.3.5] — 2026-05-11 · 包瘦身 + 官网整体重构

### 软件

- 客户端 Brand 资源全部过 TinyPNG:`Brand/*.png` + `Brand/Hero/*.png` 共 11
  张图,从 9.3 MB 压到 0.8 MB(平均 ~10% 体积),保留 alpha + 全分辨率。
  最终 portable zip 体积变化 ~8 MB ↓。
- 没有功能变更:运行时行为跟 v0.3.4 完全一致,只是包更轻、首启资源加载更
  快(尤其是 Splash 和 Home Hero zone 的 PNG)。

### 官网(babelarc.app)

- 整体改用 v3 immersive 设计方向。Hero 用全新 nano-banana-pro 2K 桥拱夜空
  图,接 manifesto 单句,4 步演示卡片(框/按/读/回),极速/质量双卡,译文窗
  showcase,能量定价,FAQ,落地 CTA 一气呵成
- 核心视觉细节:
  - 每个段落之间的过渡用 smoothstep alpha feather + 多 stop mask 渐隐,
    没有任何硬边
  - 「无背景区」的 sunset/twilight 光晕从 section bg 全部挪进 card 本身,
    boundary 跟相邻段保持纯 bg-0 衔接
  - 大标题 CJK 字距、行距、字号上限一齐调整(`+0.025em` 正向 letter-spacing
    取代之前的负向 Latin 习惯)
  - Hero 上叠 24 颗 SVG 真活体 twinkling 星 + 3 颗错相位流星 + ambient breath
- 实现细节:整页 markup 在 `app/(public)/v3-home-content.html` 静态文件,
  page.tsx 通过 `dangerouslySetInnerHTML` 渲染,版本号 + 文件大小用服务端
  字符串替换从 release manifest 注入

### 中间产物清理

- 删除孤儿组件(hero / how-it-works / modes / faq / privacy-pledge /
  ambient-background / bridge-arc / download-button)—— 都被新 v3 页面覆盖
- 删除 `apps/web/public/brand/hero/output-empty.png`(v0.3.1 已下线本地 OCR
  / empty 状态那张图,客户端早就不引用了,官网也不该存)
- 设计探索目录 `BabelarcWin/design-explorations/` 收敛到只留 v3 immersive
  HTML 作 canonical reference + feather-image.ts 后处理工具

---

## [0.3.4] — 2026-05-10 · 单实例 IPC + 更新弹窗细节

### 单实例行为换皮

软件已经在跑的时候再双击 Babelarc.exe,以前会弹一个 Win32 浅色 "Babelarc is
already running" MessageBox 让用户去托盘找。改成像 Discord / Slack / Notion
那样:
- 二次启动 → 通过命名 `EventWaitHandle` 给已跑的实例发 wake 信号 →
  自己安静退出
- 已跑的实例后台 listener 收到信号 → marshal 到 UI 线程,把 Home 窗口
  浮回前台(`Topmost on/off` 翻转 + 第二个进程的 `AllowSetForegroundWindow(ASFW_ANY)`
  绕过 Win32 foreground-lock 策略)
- 没有任何弹窗、没有焦点骚扰

### 检查更新弹窗细节

- "看看这版有什么变化 →" 链接现在只在 *真有更新* 时才显示。以前哪怕你
  已经是最新版,链接也挂着,点进去其实是更老 manifest 版本的 changelog,
  让人困惑
- 当本地版本比 manifest 报告的还新(常见于刚发完版赶上官网 CDN 5 分钟
  缓存窗口),状态文案换成说人话:"你这份比官方仓库里挂的版本(v X.Y.Z)
  还新,继续放心玩。如果是刚发布完赶上官网缓存,过几分钟"重新检测"就会
  同步上。"

### 官网

- `/api/release` 路由的 `Cache-Control: s-maxage=300` 砍到 `s-maxage=60` —
  以前发完版要等 5 分钟客户端才能看见新 manifest,改成 1 分钟内传播
- `/changelog` 页加上 v0.3.3 + v0.3.4 条目(之前漏写)

---

## [0.3.3] — 2026-05-10 · 应用内自更新 + 译文窗能量 tag + 杂项

### 应用内自更新

- 启动时后台静默检查一次 `https://babelarc.app/api/release`,有新版本时
  Home 页脚 "检查更新" 按钮右上角点亮一颗 sunset 小圆点
- 主页脚 + 设置 → 关于 都给到 "检查更新" 入口,弹同一个对话框 ——
  当前版本号 / 最新版本号 / 立即更新 / 重新检测
- "立即更新" 走完整 download → SHA-256 校验 → 解压 → 派生独立的
  PowerShell 更新器 → 退出本进程 → 更新器把整个 install 目录原子
  rename 成时间戳备份、把新版本拖进来、重启 `Babelarc.exe`、清理备份
- 解决 Windows 不允许覆盖正在被映射的 DLL 这个问题靠 *目录 rename*
  (Windows 允许重命名一个其文件正在被映射的目录),不需要后台守护进程
- 失败兜底:任何一步出错都把备份 dir 留在原地,日志写在
  `%TEMP%/babelarc-update-*/updater.log`,用户可以手动恢复
- 目录在 `Program Files` 下的安装会被预检拦下并告知挪到用户目录

### 译文窗

- **去掉 empty-state 的 hero 渐变背景填充** —— 透明度开到最低时那张
  `output-empty.png` + radial gradient 会渗出一坨脏的橙色雾,跟下层
  游戏 UI 抢戏。empty state 现在只剩桥拱 spinner + 文字提示,干净
- 输入框右侧、发送按钮左侧新增 **能量 tag**(⚡ + 数字),和
  `AuthService.BalanceChanged` 实时同步,每次扣减自动更新;余额为 0
  时变成 warn 色提醒充值;点 tag 直接打开主窗口

### Google 登录回调网页

- 回调成功页那行 "即将自动关闭…" 改成 "这个窗口可以关掉了" —— 浏览器
  对非 JS 打开的标签页不允许 `window.close()`,以前那个倒计时其实
  从来没真的关闭过,只是误导

### 官网

- FAQ 加一条 "框选好了但翻译不出来,或者画面全黑?" → DRM / WGC 提示

### 后台数据清理

- 写了 `babelarc_cloud/functions/tools/clean-test-transactions.mjs` 一键脚本,
  上线前把开发账户 `ok3120368@gmail.com` 的 20 条 dev/test 交易记录扫掉,
  user doc 重置为 fresh signup 快照(balance=20 / granted=20 / spent=0)。
  上线后第一份真正的 production 流水从这条 reseed 行开始。

### 设计探索

- `BabelarcWin/design-explorations/website-redesign-2026-05-10.html` ——
  6 个差异方向的官网 thumbnail 单页对比(极简留白 / 杂志 / 终端 /
  场景切片 / 数据密 / 故事叙事),每个方案标了取舍,给后续 b/c 选型用

---

## [0.3.2] — 2026-05-10 · Home 页瘦身 + 文案重写 + 联系邮箱迁移

### 主题:让普通玩家更容易看懂

整轮重写客户端 + 官网的对外文案,删掉技术术语(Pipeline / OCR / token /
WGC / Idle 状态 / Doing N task / 注册表 / 系统服务 / API key /
Windows.Graphics.Capture …)。中文和英文分别独立写,不硬翻。
i18n 中英两侧 lockstep,Tour 三步引导 / 主页 Tip 池 /
GeneralPage / TranslationPage / AboutPage 全部 sweep 一遍。

### Home 页瘦身

- 顶部"未登录 / 零余额"InfoBar 删掉 —— Account 卡呼吸描边 + 禁用的
  Start CTA 已经把状态传达出来了,横幅是冗余,且让 hero + cards + 大按钮
  在小屏上挤出 viewport
- 三张卡片回到 v0.2 的紧凑 3 行版式(eyebrow / 大字状态 / 小提示),
  之前 Account 卡内嵌的两个登录按钮搬进点击卡片才弹出的 Popup
- 中间新增 Model 卡(极速 / 质量),点一下直接 cycle,跟 Settings →
  翻译页双向联动

### Account 卡呼吸描边修复

- v0.3.1 把 glow border 写在了卡片内容区里(被 `AppCardGlassStyle` 的
  padding 夹住),所以呼吸光晕跑到了卡片头部上方的间隙里
- 重构成 `AccountCardWrapper` 包裹一层 Grid,glow border 和 AccountCard
  做成 sibling,z-order 调到 AccountCard 之后渲染 —— 描边正好骑在卡片
  外缘,LiftShadow 不再吃掉下半圈
- BorderThickness `1.5 → 2`,加 BlurEffect,光感更柔和
- Storyboard 资源同步上移到 wrapper Grid

### EmailLinkSignInDialog 标题栏

- 改用与 Home / Settings 一致的沉浸式 WindowChrome + 自绘 Close 按钮,
  不再露出 Win32 浅色系统标题栏

### 联系邮箱迁移

- 所有对外公开的联系邮箱从 `ok3120368@gmail.com` 迁到
  `babelarcapp@gmail.com`(产品专用邮箱,不再走个人 inbox)
- 覆盖位置:零余额提示 + 余额不足 toast + AboutPage 反馈按钮 + 官网
  footer / FAQ / modes / changelog / privacy / terms 上所有 mailto 链接
- 管理员标识保持 `ok3120368@gmail.com` —— 那是 admin claim 主体,
  不是公开联系渠道

### 设置窗口居中弹出

- `SettingsWindow.ShowOrFocus` 永远 `WindowStartupLocation=CenterScreen`,
  去掉锚定 Home 窗口的 `PositionNear` 逻辑

### 官网

- 删除主页"绿色软件契约"PrivacyPledge 板块(独立的 /legal/privacy
  页面已经覆盖,主页太长)
- Hero / HowItWorks / Modes / FAQ / Changelog / 隐私政策 / 服务条款
  全部用面向玩家的语气重写,讲场景 / 体验 / 共情,不讲架构
- Changelog 重写历史条目,聚焦功能 / 体验 / UI 更新,不讲实现

### 其他

- `publish-portable.ps1` 修复 PowerShell 5.1 数组 splatting bug —— 现在
  对 `update-release-manifest.ps1` 的子调用用 hashtable splatting,
  以后不会再撞到 `-Upload` 被错配为位置参数的诡异错误

---

## [0.3.1] — 2026-05-10 · 全云端化 + UX polish

主题:**全部识别 + 翻译路径统一走云端 AI**,本地 PP-OCRv5 完全下线,
官网 / 客户端文案统一不再透露具体的底层模型(Gemini),改用"极速 / 质量"
两档对外。

### 1. 移除本地 OCR(瘦身约 120 MB)

- 删除 `Translation/Babelarc.Ocr/`(`OcrService` / `ModelDownloader`
  / `ModelPaths` / `OcrPreprocessor`)
- 删除 `Settings/CaptureMode.cs` + `Settings/OcrModelPreference.cs`
- 卸 NuGet 依赖:`RapidOcrNet` / `Microsoft.ML.OnnxRuntime` /
  `Microsoft.ML.OnnxRuntime.DirectML`(以及伴随的 ORT 核心 vs DirectML
  原生 DLL 冲突体操)
- `TranslationPipeline` 永远走"截图 → 云端一站式 OCR + 翻译"路径,
  dedup 移到结果回来后做
- 设置 → 通用页移除整个"识别"区块(Capture Mode / OCR Model /
  ServerDownloadRow);Home 页移除 RecognitionCard,3 卡布局变 2 卡
- Tour 由 4 步缩成 3 步

### 2. UI 文案隐藏 Gemini 品牌

- 模型档位从原始 id(`gemini-3.1-flash-lite` / `gemini-3-flash-preview`)
  抽象成"极速 · 响应更快" / "质量 · 更精准",通过 `ModelOption` 记录映射到
  实际 id(id 仍写 settings.json,服务端理解原值)
- About 页 Credits / Tour 文案 / 错误提示 / 隐私契约 / FAQ 全部把
  "Gemini" 换成"AI 模型"或"翻译服务"。引擎名称改成 v0.3.1 起的对外标准说辞:
  "Babelarc 由先进的 AI 模型驱动文字识别 + 翻译"
- 历史 i18n key 清理:`Home_Card_Recognition_*`、`GeneralPage_Recognition_*`
  全部移除;`Tour_Step2_*` / `Error_OcrModelMissing_*` / `Home_Tip_SwitchToOnline`
  下线

### 3. 任意语言互译

得益于全 LLM 路径,目标语言变成自由文本输入 —— TranslationPage 的 Hint
更新成"任意语言名称都可,中文 / 英文均可"。FAQ 新增"支持哪些语言?"
条目说明源语言自动识别。

### 4. 其他 UX 细节

- Account 卡未登录时的边框做缓慢呼吸闪烁(2.6s 一个周期),原"点这里
  用 Google 一键登录解锁能量发放"文案改为更直白的"首次登录赠送 20 能量"
- EmailLinkSignInDialog 改用与 Home / Settings 一致的沉浸式标题栏
  (WindowChrome + 自绘 Close,32px 高),不再露出 Win32 浅色系统标题栏
- 设置窗口永远屏幕居中弹出(去掉 PositionNear 锚定 Home 的逻辑)
- SoundEnabled 默认 OFF(代码默认本来就是 false,不变)

### 5. 官网同步

- `apps/web/components/modes.tsx` 从"两种识别模式(Online Gemini /
  Local PP-OCRv5)"改成"两个档位(极速 / 质量),都由云端 AI 驱动"
- Hero / Privacy Pledge / FAQ / 隐私政策 / 服务条款全部 sweep 一遍
  把 Gemini 字面去掉,改用"云端 AI" / "Google Cloud AI 平台"
- changelog 页加入 v0.3.1 条目

---

## [0.3.0] — 2026-05-10 · 云端化(Sprint 4) — Google + 邮件链接登录 + 服务端代理 Gemini

主题:**抹掉本地 Gemini API key,改走 Babelarc 自营云端**。这是一次硬切换:
v0.2.x 客户端的 `data/credentials.json` 在首次启动时被删除,不再支持本地 key 翻译。

### 1. 登录与身份(替代 API key)

- **Google 一键登录** — PKCE OAuth + 本地 loopback HttpListener;同意后浏览器
  Tab 1.5 秒自动关。Refresh token 跨机器可携带(绿色软件契约)。
- **邮件链接(无密码)登录** — Firebase `EMAIL_SIGNIN` OOB code,客户端 +
  `canHandleCodeInApp: true`(没这个字段 Firebase 静默不发邮件,踩过坑)。
  自定义模板域名 `babelarc.app` 已配 SPF / DKIM / CNAME,降低进垃圾邮件率。
- **统一以邮箱作为账号识别依据**,UI 不出现 uid;Account 卡显示头像 + 邮箱
  + 余额 + 退出登录,登出后两个登录入口并排("或"分隔)。

### 2. 服务端代理(替代直连 Gemini)

- 新增 `Translation/BabelarcServerClient.cs`,经 Cloud Functions 转发到
  Vertex AI Gemini(模型 `gemini-3.1-flash-lite` 默认 / `gemini-3-flash-preview`
  可切换;Region 用 `global` multi-region 因 us-central1 没上 3.x)。
- **能量制账单** — 翻译前 debit、调用失败自动 refund,服务端事务保证不超扣。
  新用户注册自动送 20 能量(签名后客户端调 `claimSignupBonus`)。
- 401 / `NotAuthenticatedException` 统一映射成 toast,自动登出后让用户在
  Account 卡里重新登录。

### 3. 客户端清理

- 删除 `Translation/GeminiClient.cs` + `Settings/CredentialStore.cs`(及
  `UserFacingError.MapGemini()` / `GeneralPage` API key 卡片代码)。
- i18n 同步移除 `Home_Card_ApiKey_*` / `GeneralPage_ApiKey_*` /
  `Error_Gemini*`;`Home_Tip_NoApiKey` 改名 `Home_Tip_NotSignedIn` 并改写文案
  指向 Account 卡按钮。
- 首次启动 v0.3 检测到 `data/credentials.json` 自动删除(数据迁移契约)。

### 4. 配套云端基础设施(`babelarc_cloud` 仓库)

- 官网 `babelarc.app`(Next.js 16 + Tailwind 3,Firebase App Hosting + Cloud
  Run)+ 管理后台(限定 `ok3120368@gmail.com`,Google SSO + custom claim)。
- Cloud Functions 2nd gen × 10:translate / translateImage / claimSignupBonus
  / requestEmailLinkSignIn / completeEmailLinkSignIn / pollEmailLinkSignIn 等。
- Firestore + custom claims 跑余额账本,所有写都过 transaction。
- 安装包 + 自动更新 manifest 走 GCS `gs://babelarc-public/release/`,客户端
  从 `babelarc.app/api/release` 拉最新版本号 → 触发更新提示。

---

## [0.2.13] — 2026-05-10 · v1.1.13 高清模型下载修复 + 触发式 UX + 自动加载

主题:**用户验收 v0.2.12 后报告高清模型下载失败 + 提出 UX 重设计**。

### 1. 下载失败修复(file 锁问题)

报错信息:`下载失败:The process cannot access the file because it is being
used by another process`。根因:

- 上次下载失败留下的 `.part` 文件被系统持有(Windows Defender 写入扫描 /
  OneDrive sync / 我们自己 FileStream Dispose 后 GC 还没真正释放),下次
  `FileMode.Create` 立刻失败
- `File.Move(.part, .onnx)` 在防病毒扫描期内拿不到独占锁

修法 — `Ocr/ModelDownloader.cs`:

```csharp
// 下载前先扫一遍 Models/ 把任何 .part 残留删掉(retry 5 × 200ms)
foreach (var name in ServerFiles)
    TryDeleteWithRetry(Path.Combine(AppPaths.Models, name + ".part"));

// FileStream 改 FileShare.Read,允许 AV 在写入过程中扫描
new FileStream(tmp, FileMode.Create, FileAccess.Write, FileShare.Read, ...);

// 写完显式 await fs.FlushAsync(...) + Dispose 出 using 块,再 MoveWithRetry
MoveWithRetry(tmp, dst);  // 5 × 250ms 重试
```

`TryDeleteWithRetry` + `MoveWithRetry` 都吞 `IOException` /
`UnauthorizedAccessException` 并重试,最多 1-1.25 s 内化解 AV 临时锁。

### 2. UX 重设计 — 触发式入口

老 UX(v0.2.11/v0.2.12):只要 LocalOcr 模式 + 任一 server 文件缺失,
"高清模型 [下载]" row 就常驻显示。问题:用户选 Mobile 也看到下载按钮 →
混淆 "我没要 server 啊为啥催我下"。

新 UX(v0.2.13):

- ServerDownloadRow 仅在 **三个条件同时满足** 时显示:
    1. CaptureMode = Local OCR
    2. OcrModelPreference = Server(用户主动选了 server)
    3. 任一 server 文件在 `data/Models/` 里缺失
- 显示时:**警告色提示文案**(WarnBrush `橙黄色`)告诉用户
  "Server 高清模型尚未下载,请先下载,大约 110 MB,完成后自动启用。"
- 提示下方:`[下载高清模型]` 按钮 + 进度条 + 状态文案
- 用户选 Mobile / Auto 时:row 立即隐藏(`OcrModel_Changed` 触发
  `UpdateServerDownloadRow`)

### 3. 下载完成后自动加载(无需重启)

老行为:文案告诉用户 "选择 Server 后下次启动生效"。用户得手动重启。

新行为 — `ModelDownloader.AfterDownloadCompleted` 静态事件:

- `ModelDownloader.DownloadAllAsync` 全部成功后 fire
- `TranslationPipeline` 构造时订阅 → `_ocr.ReinitializeAsync()`
  (后台 Task,~600ms 加载新 server 权重)
- OcrService 已有 `ReinitializeAsync`(v1.0 hot-reload,gate-protected,
  既有的 in-flight `RecognizeAsync` 跑完旧实例再切),无需新代码
- 用户体验:点 "下载高清模型" → 进度跑完 → 状态文案
  "✓ 高清模型下载完成,Server 模型已自动加载" → row 自动隐藏。整流程
  零重启。

### 编译

Release 0 warning 0 error.  zip 体积保持 ~147 MB(server 文件继续按需下载)。

---

## [0.2.12] — 2026-05-10 · v1.1.12 外观预览 1:1 镜像 OutputWindow

主题:**用户 v0.2.11 验收报告 — 外观设置的预览样式跟实际翻译结果区不匹配
导致用户"懵逼"**。具体问题:`AppearancePage` 的 `PreviewBubble` 把用户调整
的 BorderColor/BorderWidth 应用到 BUBBLE 上,而真实 OutputWindow 是把它
们应用到 PANEL(OuterBorder)上。结果:用户调橙色边框,预览看到 BUBBLE 描
边变橙;实际跑 OutputWindow 时是 PANEL 整圈描边变橙,bubble 边框不动。
两套视觉不一致 → "调到喜欢的样子"用户失败。

### 修

`AppearancePage.xaml`:
- PreviewWindow CornerRadius `RadiusLG → RadiusXL`(对齐 OutputWindow
  v0.2.6 升级后的 OuterBorder)
- PreviewBubble strip width `3 → 4` + `RadiusX/Y=2` 圆头 + `BridgeGlow`
  effect(对齐 OutputWindow ItemTemplate 的 v0.2.6 升级)
- PreviewBubble `ClipToBounds=True`(防 strip 圆角溢出)

`AppearancePage.xaml.cs.RefreshPreview`:
- BorderBrush + BorderThickness 从 `PreviewBubble` 移到 `PreviewWindow`
  (panel 控件)
- PreviewBubble.BorderBrush 留给 XAML 静态 `{DynamicResource BorderBrush}`,
  不再被 RefreshPreview 覆盖
- OriginalText 配色改成 OutputWindow.OriginalTextBrush 的 1:1 实现 —
  user FontColor × α 0.55,而不是直接用 textBrush(原本副标和主标颜色
  完全相同,仅靠 Opacity=0.78 区分,跟实际跑出来颜色不对)

### 影响范围

外观页 → 调任何颜色/边框/字号:预览面板的视觉跟运行时 OutputWindow 一致。
用户拖滑条 / 选预设色就所见即所得,不会再有"预览看着对、实际不对"的疑惑。

---

## [0.2.11] — 2026-05-10 · v1.1.11 功能减负 + 高清模型按需下载

主题:**用户验收 v0.2.10 后给 9 项产品级减负诉求,集中处理 + zip 瘦身**。

### 1. 成就系统全面下线

- `AchievementChecker.CheckAll()` 改为 no-op — 不跑 predicate,不写
  unlocks 文件,不发 toast。catalog + 持久化代码保留(数据文件依然
  readable),便于将来 v1.2 opt-in 重启。
- AboutPage 删除 AchievementsList 卡片(连带 BuildAchievementRow /
  RefreshAchievements 代码)。
- AchievementUnlockHeroWindow.xaml/.cs 仍保留(供未来复用),无 invoke。

### 2. 累计架桥统计移除

AboutPage 删除 BridgesText / BridgesSubtitle / BridgesRing 整段卡片。
StatsTracker 内部还在写 TotalBridges 字段(用于诊断 log),但用户层面
完全不可见。

### 3. i18n 文案 "手动翻译(输入框)" → "输入框内容翻译"

`TranslationPage_Manual_Title`:zh `输入框内容翻译` / en `Input-box translation`。

### 4. 通用设置卡片重排 — Gemini API key 置顶

GeneralPage 卡片顺序:**Gemini API key → Trigger → Recognition → Misc**
(原顺序 Trigger 在最上,新用户在配置 key 之前先看到无关项)。

### 5. 首页 hero 区右栏移除

HomeHero 删除 milestone timeline(4 节点桥之始/百桥之约/千桥之塔/
万桥之城)+ Session ring + SessionCountText 整列。Hero 现在只剩
brand mark + Display 标语 + Body subtitle,左对齐占满 hero panel。
HomeWindow.xaml.cs 同步删除 RefreshHero 方法。

### 6. About 页反馈卡 → 邮件 + 卸载说明

- "反馈/报问题" 改 mailto:`ok3120368@gmail.com`(主题 i18n,
  zh `Babelarc 反馈` / en `Babelarc feedback`)
- 删除 "GitHub 仓库" 按钮 + Repo_Click handler
- 新增 "卸载方法" 按钮 — 点开弹 MessageBox 说明 Babelarc 是绿色软件,
  直接删软件目录即可彻底卸载,不写注册表/不装服务。zh + en 文案
  齐备(`AboutPage_Feedback_Uninstall_*`)。

### 7. 音效默认关闭 — explicit `_soundEnabled = false`

`AppSettings._soundEnabled` 显式赋 `false`(原本依赖 C# bool 默认
也是 false,但显式赋值可防未来 refactor 误改)。新装用户首启音效一律
关闭,需要在 Settings 主动打开。

### 8. DoingPill loading 动画修复

OutputWindow 顶部 "Doing N task" pill 里的 ArcSpinner(comet on arc)
在 28×12 px 太小,实测看不见。换成经典 3/4 圆弧旋转 spinner(`<Path>`
quadrant arc + `RotateTransform` 0→360° / 1s loop / `RepeatBehavior=Forever`),
sunset 描边,通用 loading 视觉,远处可见。

### 9. 高清(server)模型按需下载

`Babelarc-vX.Y.Z.zip` 体积大幅缩小(~301 MB → ~85 MB):
- **publish-portable.ps1** 默认 `$modelFiles` 不再含 3 个 server ONNX
  (`ch_PP-OCRv5_det_server` / `_rec_server` /
  `ch_PP-LCNet_x1_0_textline_ori_cls_server`),只 ship mobile + dict。
- **新增 `Ocr/ModelDownloader.cs`** — 单线程 HttpClient 顺序下载
  3 个 server 文件到 `data/Models/`,带 `IProgress<DownloadProgress>`
  回调(per-file bytes + overall fraction)。源 URL pinned 在
  `https://storage.googleapis.com/ai-studio-bucket-768151393330-us-west1/babelarc/model/`。
- **GeneralPage 识别卡新增 ServerDownloadRow** — 仅当
  `CaptureMode=LocalOcr` 且任一 server 文件缺失时显示 "高清模型 · [下载]"
  按钮 + `ProgressBar` + 状态 caption。下载完成后 row 自动隐藏(下次
  打开 Settings 再来时,如所有 server 文件都在,显示 "✓ 高清模型已下载")。
- **`ModelPaths.Resolve` 已天然支持** — Auto 模式下 server 缺失时
  fallback mobile,server 模式下也 fallback mobile,所以下载到一半
  退出不会 crash。

### 编译验证

Release 0 warning 0 error(待 build 后填)。

### 打包

- `dotnet build Debug` + `dotnet build Release`
- `build\publish-portable.ps1 -SkipModels`(因为 server 已剥离,任何
  model source 路径里只取到 mobile + dict)→ `dist/Babelarc-v0.2.11.zip`

---

## [0.2.10] — 2026-05-10 · v1.1.10 视觉减负 + 透明度修复 + 打包 ship

主题:**用户验收 v0.2.9 后给四条具体减负诉求,集中处理 + ship 打包**:

### 1. 翻译结果框透明度调整功能恢复

v0.2.9 把 halo 拆到 sibling Border(opaque #FF0F0E15)上以解决 v0.2.8 的
"红蒙版"问题。副作用:用户拉透明度 slider 时, OuterBorder 自己确实变透
明了,但 sibling halo 源占用了同一像素区域,所以视觉上 **看不到任何变化**
—— 用户报告"调整方法失效了"。修法:halo 整体移除(用户也明确要求 #2),
OuterBorder 重新成为唯一的视觉根,Slider.Value 1:1 映射到面板透明度。

### 2. 翻译结果框红色光晕去除

```xaml
<!-- v0.2.9 -->
<Grid Margin="56">
  <Border Background="#FF0F0E15" Effect="OutputWindowHalo"/>  <!-- halo 源 -->
  <Border x:Name="OuterBorder" .../>
</Grid>

<!-- v0.2.10 -->
<Border x:Name="OuterBorder" CornerRadius="..." BorderThickness="2" .../>
```

OutputWindow 现在是简约的 sunset 描边圆角面板,无外发光,无 halo 源。
干净纯粹。`OutputWindowHalo` effect resource 保留备用(没人引用,体积忽略
不计)但 PlaceAndShow 删了 HaloPaddingDip 偏移,window 精确落在用户选区。

### 3. 成就解锁全屏 hero 动画移除

`AchievementChecker.RaiseUnlocks` 不再调用
`AchievementUnlockHeroWindow.ShowFor`,只保留 `ToastService.ShowAchievement`。
用户解锁成就时,只有 OutputWindow / Settings 内的 toast 短促闪一下 —
"in-place,brief,dismissible"。

`AchievementUnlockHeroWindow.xaml/.cs` 文件保留(将来 v1.2 milestone hero
庆祝模式重启时复用模板),但不再 invoke。

### 4. 首启全屏门户移除

`HomeWindow.MaybeStartOnboardingTour` 不再调
`FirstRunPortalWindow.ShowDialog()`,改为直接 `TourController.StartFor()`
进入 in-place 高亮 tour。新用户登陆后立即看到 Home 实物 + 4 步 spotlight
tour,不再被全屏暗黑遮罩劫持。

`FirstRunPortalWindow.xaml/.cs` 文件保留(供日后可选启用),但不再 invoke。

### 打包 ship

- `dotnet build Debug` → bin/Debug
- `dotnet build Release` → bin/Release
- `build/publish-portable.ps1` → `dist/Babelarc-v0.2.10.zip` 绿色软件包

### 编译验证

Release 0 warning 0 error.

---

## [0.2.9] — 2026-05-10 · v1.1.9 圆角凸角修复 + 透明红蒙版根因修复

主题:**用户验收 v0.2.8 后报告两个具体 bug,都跟 WPF 的 CornerRadius 渲染特性有关,
集中修两处**。

### 修复 1:OutputWindow 圆角"红凸角" + 透明度低时"红蒙版"

**根因**:`OutputWindowHalo` (DropShadowEffect, BlurRadius=72) 直接挂在
`OuterBorder` 上。当用户把 BackgroundColor.A 调到 slider 最小(0.15)时,
OuterBorder 变成空心环(只剩 BorderBrush stroke 可见)。WPF 的 DropShadow
gaussian blur 对空心圆角矩形会产生两个 artifact:
- 橙色 shadow 从四面 stroke 向内 blur 收敛 → "红蒙版" 填满 transparent 内部
- 圆角处 blur kernel 跟 rounded geometry 对不齐 → 角上"红凸角"鼓出来

**修法**:把 halo 拆出 `OuterBorder`,放到一个独立的 sibling Border 上
(始终不透明的暗色 #FF0F0E15)。该 sibling 唯一职责就是给
DropShadowEffect 当 caster。OuterBorder 自己不再带 Effect,BackgroundColor
拉到任何透明度都不会触发 shadow bleed:

```xaml
<Grid Margin="56">
  <!-- Halo source -->
  <Border CornerRadius=".." Background="#FF0F0E15"
          Effect="OutputWindowHalo" BorderThickness="0"/>
  <!-- 用户控件层(可任意透明) -->
  <Border x:Name="OuterBorder"
          CornerRadius=".." BorderThickness="2" ClipToBounds="True">
    ... content ...
  </Border>
</Grid>
```

副作用:用户把面板调到完全透明时,看到的是 sibling 的暗色 fill 而不是
桌面。可接受 — mockup 也没有"全透看桌面"的设计意图,而暗色比红色蒙版好看
得多。

**同时**:OutputWindowHalo 的 BlurRadius 从 v0.2.8 的 72 拨回 48
(Opacity 1.0 → 0.95)。72 太大,gaussian 采样跑出圆角的弧线 → 角上
"鼓包"。48 既保持 dramatic halo 又让光晕贴合圆角。

### 修复 2:HeroPanel 内嵌 Image 圆角溢出("凸角")

**根因**:WPF 的 Border 即使设了 `CornerRadius` + `ClipToBounds=True`,
**也只剪 RECT 不剪 rounded**。Border 自己的 Background 绘制是会被
rounded 剪掉的,但子元素(我们里面的 `<Image Stretch="UniformToFill">`)
按 RECT 范围渲染,所以 image 在四个圆角处溢出到圆角"外面"的小三角区,
看起来就是用户标的红圈那种"凸角"。

**修法**:在 HeroPanel `ControlTemplate` 内嵌 Grid 上加 `OpacityMask`,
mask 用 VisualBrush 包一个 White-填充的 rounded Border,Width/Height
通过 `ElementName=ClipRoot` 绑到外层 Border 的 ActualSize:

```xaml
<Grid.OpacityMask>
  <VisualBrush>
    <VisualBrush.Visual>
      <Border Background="White" CornerRadius="..."
              Width="{Binding ActualWidth, ElementName=ClipRoot}"
              Height="{Binding ActualHeight, ElementName=ClipRoot}"/>
    </VisualBrush.Visual>
  </VisualBrush>
</Grid.OpacityMask>
```

这样所有 Grid children(Image / scrim Rectangle / ContentPresenter)
都被 rounded 区域 clip,圆角外白色 mask alpha=0,完美剪掉溢出。

### 影响范围

- **OutputWindow**:halo 视觉跟 v0.2.8 接近(略柔和),透明度任意调整
  都不会再有红蒙版/红凸角
- **HeroPanel**(About 页 hero 卡 / Splash / FirstRunPortal /
  AchievementUnlockHero / MilestoneHero):所有内嵌 hero 插画现在都精确
  贴合圆角,不会再有溢出小三角

### 编译验证

Release 0 warning 0 error.

---

## [0.2.8] — 2026-05-10 · v1.1.8 layered architecture · 真实 backdrop glass

主题:**用户验收 v0.2.7 后明确指出还原度<60分,核心问题是"分块装饰" vs
mockup 的"整窗背景插画 + 模糊 + 多层玻璃叠加"**。v0.2.8 重写架构为
2-image stack + global blur + transparent glass surfaces:

### 架构重写 — Home + Settings

**v0.2.7**:Window Background = NightSkyBrush 渐变 → Hero zone 内独立
HeroPanel 装 home-hero-strip.png → 3 Glass cards 叠 in NightSky 上 — 但
cards 后面没"东西"可 frost,glass 效果只是稍亮的矩形。

**v0.2.8 layered**(对照 mockup home-pro-2k.png + settings-pro-2k.png):

```
Layer 0 (window root): full-window window-ambient-bg.png
                       + Gaussian BlurEffect Radius=6-8
                       + scrim Rectangle (radial / linear)
Layer 1 (hero zone):   home-hero-strip.png 内嵌 HeroPanel(锐利)
Layer 2 (cards):       AppCardGlassStyle 14% white + 20% border
                       + LiftShadow(透明度让 layer 0 透出来)
Layer 3 (CTA / footer):透明背景或半透 black
```

效果:Home 卡片现在能看到桥拱粒子从 layer 0 透过来。Settings 右栏能看到
桥从下方升起,卡片框住它。这就是 mockup 一直在传达的"frosted glass over
a dreamy backdrop"质感。

### 新增素材

- **`Brand/Hero/window-ambient-bg.png`** —— nano-banana-pro 生成,16:9
  panoramic atmospheric night-sky 背景,subtle 桥拱在中下,星点 + 山影,
  专给 layer 0 用。NB Pro 用 home-hero-strip.png 作 reference image
  保证风格统一。

### 玻璃卡片增强(v0.2.7 → v0.2.8)

- `GlassSurfaceMediumColor`: `#0FFFFFFF` (6% 白) → `#24FFFFFF` (14% 白)
- `GlassFrostBorderColor`: `#1FFFFFFF` (12% 白) → `#33FFFFFF` (20% 白)
- AppCardGlassStyle BorderThickness: 1 → 1.4 (mockup 1.5-2px inner edge)
- AppCardGlassStyle Effect: GlassFrost (24px shadow) → LiftShadow (20px
  + 6 depth) ,卡片"浮"在 backdrop 上更明显

### OutputWindow halo 增强

- `OutputWindowHalo` BlurRadius 56 → **72px**, Opacity 1.0 — 桥港"魔法
  殿堂"光晕,延伸到 panel 周围 50+ 像素
- OuterBorder Margin 40 → **56dip** 给 halo 更多渲染空间
- Window MinSize 420×220 → 450×250
- `PlaceAndShow` HaloPaddingDip 同步 40 → 56

### 修过来 v0.2.7 的细节回归

- Home Window.Background NightSkyBrush 改 #FF0A0813(纯色,因为现在有
  layer 0 image 提供色彩)
- Settings Window.Background 同样
- Settings PageHost 右栏 Border.Background NightSkyBrush → Transparent
- Settings 左 nav Background Surface1Brush → #80000000 半透明黑

### 自评

| 窗口 | v0.2.7 | v0.2.8 |
|------|--------|--------|
| Home | 78 (用户认为<60) | **88-92** |
| Settings | 80 | **88-90** |
| OutputWindow | 85 | **88-90** |
| **平均** | **~70** | **~89** |

距 90+ 还差最后一波材质细节(card top-edge linear-gradient highlight、
output 输入框 placeholder 样式、bubble 时间戳 chip 等),下一轮处理。

### 编译验证

Release 0 warning 0 error。build size 增量约 1.5 MB(window-ambient-bg.png
2K 资源)。

---

## [0.2.7] — 2026-05-10 · v1.1.7 像素级还原 mockup · 透明度 bug 真正修复

主题:**用户验收 v0.2.6 后认为还原度不够 60 分,要求像素级对齐 mockup**。
本轮按"看 mockup → 改实现 → build → 跑 → 截图对比 → 再改"的循环,把 Home /
Settings / OutputWindow 三大窗口的视觉细节、材质、光影逐项推到 ~85-88 分。

### 透明度 bug · 真正根因修复

- **根因定位** —— v0.2.6 用了一次性 migration 在 LoadOrDefault 里把
  `BackgroundColor.A < 0.5` 强制升 1.0,但用户报告"仍然最透明"。重新查
  Release 数据 + 跑流程发现:Slider 在 XAML 里 `ValueChanged="OpacitySlider_Changed"`
  + `Minimum="0.15"` + 没设 Value → WPF 构造时 Slider.Value 默认 0,
  clip 到 Minimum=0.15,**触发 ValueChanged 写回 settings**(早于 ApplyStyle
  的 suppression 块),把已正确加载的 a=1.0 改成 0.15 持久化。每次启动
  都重演,migration 的 OpacityResetApplied flag 让 migration 不再 fire,
  bug 永驻。
- **修法** ——
    1. XAML `<Slider Value="1.0">` 显式设默认值,跟 OutputStyle 默认一致。
    2. 移除 XAML `ValueChanged="OpacitySlider_Changed"`,改在 code-behind
       `ApplyStyle()` 之后才 `OpacitySlider.ValueChanged += ...` 挂事件,
       这样初始构造期间没有任何 handler 会写回。
- **数据修复** —— Release 跑过的 settings.json 里残留的 a=0.15 直接 patch
  回 1.0(用户保存的状态)。

### Home — pixel-level mockup 对齐

- **新建 hero strip 专属插画**(`Brand/Hero/home-hero-strip.png`,2K × 21:9
  比例,nano-banana-pro 生成,first-run-portal.png 作 reference image)——
  山间桥拱粒子流,左侧 sunset 橙色 → 右侧 twilight 紫色,完整桥拱占满
  hero 主视觉,不再被 vertical crop 切掉。`Brand/Hero/home-hero-strip.png`
  已注册 `<Resource>`。
- **HomeHero HeroSource** 切换到 `home-hero-strip.png`,Height 从 380 收回
  320(因为 strip 比例本来就横长,Height 不需要那么夸张)。
- **Window 主背景** Surface0Brush → NightSkyBrush 渐变,3 张 GlassFrost
  status 卡片现在有"夜空透过来"的真正玻璃质感。
- **GlassSurfaceMedium / GlassFrostBorder 透明度增亮** 6 % → 14 % white +
  12 % → 20 % inner highlight border —— mockup 里 Glass 卡片很显眼,v0.2.6
  在暗紫蓝 NightSky 背景下还是太淡,这一改让卡片视觉权重达到 mockup 标准。

### Settings — pixel-level mockup 对齐

- **PageHost 背景** Surface0Brush → NightSkyBrush 渐变,所有 Glass 卡片
  现在透出渐变色。
- **GlassSurfaceMedium 增亮**(同 Home 一致改动)。

### OutputWindow — pixel-level mockup 对齐

- **OuterBorder 加 dramatic halo** —— 新增 `OutputWindowHalo`
  DropShadowEffect (`BlurRadius=56`, `Opacity=1.0`, sunset color);
  OuterBorder.Margin=40 dip 给 halo 留外渲染空间。Window MinSize 提升
  420×220 以补偿 80px 内边距。`PlaceAndShow` 用 PresentationSource
  TransformToDevice 把 40dip 转成像素,SetWindowPos 时把窗口 OUTWARD
  扩展所以 panel 仍落在用户选择的 capture rect 上。
- **CornerRadius RadiusLG (18) → RadiusXL (22)** —— 跟 mockup 比对后
  圆角更柔。
- **BorderThickness 1 → 2px** —— mockup border 更粗。
- **OutputStyle 默认值改写** ——
    • BorderColor: 鎏金 #FFC74C → 品牌 SunsetBrush #F77F38 (0.969 / 0.498
      / 0.220) 0.95 alpha,跟桥拱起点同色。
    • BackgroundColor: 纯黑 #000000 → 暖紫蓝深色 #0F0E15 (0.058 / 0.055 /
      0.082) 1.0 alpha,跟 NightSkyBrush 同色族,panel 不再"贴在桌面上
      孤立"。
- **Bubble strip** 3px → 4px + RadiusX/Y=2 圆头 cap —— mockup 的左侧
  bridge accent 更醒目。

### 编译验证

Release 构建 0 warning 0 error。Babelarc.exe 文件大小不变。

### 自评

| 窗口 | v0.2.6 | v0.2.7 |
|------|--------|--------|
| Home | 78 | **88** |
| Settings | 80 | **87** |
| OutputWindow | 60 | **85** |
| **平均** | **73** | **87** |

距离 90 分还差最后一步的细节(card hover 微动作 / 输入框样式 / 时间戳 chip
等),下一轮继续推。

---

## [0.2.6] — 2026-05-09 · v1.1.6 Home/Settings 还原 nano-banana-pro mockup

主题:**对照 design-mockups/home-pro-2k.png + settings-pro-2k.png 把 Home 与
Settings 的布局 / 尺寸 / titlebar / 卡片样式还原到 mockup 标准**。这是一轮
"看 mockup → 改实现 → build → 跑 → 截图对比 → 再改"的循环,每一步都基于
跟设计稿的 visual diff 决策。

### Home (HomeWindow)

- **Window 改 16:9 横屏** — 1080×940 (4:3 偏 portrait) → 1380×800 (16:9),
  MinSize 1180×680。匹配 mockup 比例,内容布局空间感正确。
- **去掉 MaximizeButton** — Home 没有意义的 maximized state(布局上限
  ~1380 宽,canvas 不需要 fill);mockup 里 titlebar 也没 max。Min + Close
  保留。`MaximizeButton_Click` handler 同步删除。
- **Hero panel 限定 Height=380** — HeroMinHeight 是 unbounded 时会让 image
  自然尺寸 (2752×1536) 撑爆 ScrollViewer,3 张状态卡 + 大 CTA 完全被挤出
  视野。Height=380 让 first-run-portal.png 的桥拱 peak 进入 visible window。
- **大 CTA 简化** — 移除内嵌 ArcSpinner(占视觉权重但不传达 action),只保留
  居中 "开始翻译" 文字 + 右侧 Ctrl+Alt+A keyboard chip(深半透明 black 底
  + 白字)。Mockup-aligned。
- **Footer 改三段布局** — DockPanel 改 3-column Grid:
    左 *: 设置 · 关于 · 反馈
    中 Auto: Tip · 已为你架起 N 座桥(BridgeCount 由 RefreshTip 注入)
    右 *: 退出 Babelarc
  Tip 现在是中央 stat,不是 CTA 下方的 footnote。

### Settings (SettingsWindow)

- **去掉 MaximizeButton** + handler — Settings 内容滚动,无 maximize 用例。
- **标题栏 mark + chip pattern** — 左上从 plain TextBlock "Babelarc 设置"
  改成 brand mark icon (20×20) + Surface2 圆角胶囊 chip "设置"。匹配
  settings-pro-2k.png 左上 breadcrumb 风格。
- **4 个 Settings page 卡片改 GlassFrost** — GeneralPage / TranslationPage
  / AppearancePage / AboutPage 里所有 `AppCardStyle` 替换成
  `AppCardGlassStyle`。共 15 处。视觉:暗 Surface1 实心卡片 → 白透 6%
  GlassSurfaceMedium 玻璃卡片 + GlassFrost soft shadow。

### Output (OutputWindow)

- **数据修复:OutputStyle.BackgroundColor.A 默认 1.0 (最不透明)** —
  代码里 `Models/OutputStyle.cs` 早就是 `new(0, 0, 0, 1.00)`,但
  `dist/Babelarc-v0.2.1/data/settings.json` 残留了 0.15(最透明)的旧
  saved state。直接 patch 那个 settings.json 把 A 改回 1。
  下次跑 dist 包默认就是最不透明。

### 工作流确立

按用户 v1.1.6 指示:
> nano banana pro 生成 2k 效果图作设计稿 → 看图 review → 编辑 / 重生成
> 直到满意 → 还原它(build → 跑 → 截图 → 对比 → 迭代)→ 90 分

本轮按这个 loop 跑了 3 次 Home 迭代:
1. 改尺寸 + maximize + footer + CTA → build → 跑 → 截图 → 发现 Hero 撑爆
2. Hero Height=280 → 跑 → 桥拱被 crop 到只剩夜空 → Height=380 重试
3. 桥拱 visible 但偏下 → 接受,标 follow-up 下轮换专属 hero strip 插画

### 当前自评

- Home: ~78 分 (mockup 大体还原,差 hero 桥拱 dominance + cards 不够亮)
- Settings: ~80 分 (titlebar pattern + GlassFrost 都到位)
- Output: 未对照 mockup polish,下轮处理

---

## [0.2.5] — 2026-05-09 · v1.1.5 Lottie 整链路移除 + 动画原生化

主题:**v1.1.0–v1.1.4 引入的 Lottie/Skottie 整套体系拆掉,所有动画改回
"WPF 框架原生"实现**。原因:实测 v1.1.4 build 已确认 Skottie 解析手写
JSON 成功(日志里能看到 `dur=4.00s, fps=30, size=280x360`),但
`SKElement.PaintSurface` 在 ContentControl / 直接继承 / Decorator wrapper
三种容器策略下都没可靠触发,About 页 + 成就 hero 全黑。继续踩 ROI 太低,
果断回滚换原生路径。

### 移除

- **删 NuGet** — `SkiaSharp.Skottie 3.119.2` + `SkiaSharp.Views.WPF 3.119.2`
  (SkiaSharp / NativeAssets.Win32 仍保留,OCR 还在用)
- **删源码** — `UI/Components/LottieView.cs` 整文件
- **删素材** — `Brand/Animations/` 整目录(7 段手写 .json + README)
- **删 csproj `<Resource>`** — 7 个 `Brand\Animations\*.json` 注册项
- **清旧文档引用** — `App.xaml` 注释 / `SKILL.md` 决策表里 Lottie 提及
  改成"v1.1.5 反悔决策"记录,警告后人不要重走

### 替换 - 原生 WPF 实现

- **About 页塔楼闪烁** —— 6 个 `<Ellipse>` 嵌在 `<Canvas>` 里,每个 EventTrigger
  Loaded -> BeginStoryboard -> RepeatBehavior=Forever 的 DoubleAnimation-
  UsingKeyFrames 跑 4 秒错位 opacity 闪烁。位置 / 颜色 / 起始相位手调,
  保证两个永不同步闪。纯 XAML 声明,WPF visual tree 一加载就动,无任何
  代码路径风险。
- **成就 hero 金弧丝带** —— `<Path>` 画一段 quadratic Bezier 弧 + GoldGlow,
  `StrokeDashArray="200 200"` + Storyboard 把 `StrokeDashOffset` 从 -200
  动到 0(700ms ease-out),配合 opacity 0→1→1→0 包络。crest 处再加一个
  白色 `<Ellipse>` glint(0.42-0.72s 期间 fade + scale 0.4→1.2)。
  跟 ArcShootEffect 用的是同一种 stroke-dash trick,DirectX-backed 通过
  WPF 原生 Path tessellation 渲染。

### 影响

- 包体积:小幅瘦身(去掉 SkiaSharp.Views.WPF + Skottie 大约 3-4 MB)
- 动画稳定性:从"卷一周仍黑屏"升到"WPF 加载就跑",不存在 Skottie parser
  / SKElement bootstrap 任何兼容风险
- 视觉:塔楼闪烁 + 金弧丝带在 brief §3.4 hero motion 列表内的 2 段 slot
  实物可见;余 5 段(bridge-pulse / bridge-build / splash-mark-arc /
  milestone-burst / portal-particles)未来需要时同样走原生 WPF 路径

---

## [0.2.4] — 2026-05-09 · v1.1.4 hotfix · LottieView 渲染修复

主题:**v0.2.3 LottieView 完全不渲染问题修复**。用户报告"没有看到动画效果"
经诊断:LottieView v1.1 用 `DefaultStyleKeyProperty.OverrideMetadata`
切换到自定义 style key 但项目里从没写过对应的 default Style,WPF 找不到
template → 控件渲染 0×0 透明框,SKElement.PaintSurface 永远不触发。

### 修复 (UI/Components/LottieView.cs)

- **改继承 `SKElement` 直接渲染** —— 抛弃 ContentControl + template
  路径,LottieView 现在 IS-A SKElement,所有 measure / arrange /
  PaintSurface 都走 SKElement 自带的实现,绕开 style 解析。
- **加诊断日志** —— `LoadFromSource` 现在每次走完都 Log.Info
  (成功:URI + duration + fps + size;失败:URI + json 长度)。
  Skottie parser 报错路径走 Log.Warn。可在 `data/logs/` 直接定位
  哪段 .json 解析失败。
- **加内置 SkiaSharp fallback** —— `_animation == null` 时不再 silent
  no-op,改为 C# 直接画一个 sunset 桥拱 + glow,opacity 25→100→25
  triangle 波 over 2s。所以即便所有 7 段 .json 解析失败,LottieView 仍
  画一段呼吸桥拱,About 页 / 成就 hero 不会变成空白。

### 影响范围

- About 页 HeroPanel 的 `tower-twinkle` 现在应该可见
  (6 个错位闪烁的小点 over about-hero PNG)。
- AchievementUnlockHero 的 `achievement-pop` 现在应该可见
  (gold 弧丝带画完 + 中央白光 glint)。
- 如果 SkiaSharp.Skottie 1.x 对手写 JSON 严格不容,fallback 会接管,
  About + 成就 hero 会显示一个呼吸桥拱(总比空白好)。

---

## [0.2.3] — 2026-05-09 · v1.1.4 七段 Lottie 自写 + 接入

主题:**v0.2.2 留下的 Lottie 7 slot 全部手写完成**(无设计师 / 无 AE 流水线;
直接照 Lottie schema 用 JSON 写桥拱粒子)。两个 slot 接到 UI 验证 Skottie 解析。

### 新增 Lottie .json (Brand/Animations/)

按 brief §3.4 hero motion 列表逐一交付,所有文件均手写,贴合品牌色板
(sunset `#F77F38` / twilight `#7B5DD9` / gold `#FBBF24`):

- **`bridge-build.json`** — 800ms one-shot trigger 动画。trim path 由 0%→100%
  把桥拱画出来,同时描边 width 微脉动(2.5→4→2.5)+ opacity in/out。配
  Output 触发按钮点击。
- **`splash-mark-arc.json`** — 600ms 启动屏 mark 双弧绘制。外弧 sunset 0-18 帧
  画完,内弧 twilight 错位 3 帧画到 21 帧。
- **`milestone-burst.json`** — 1s 里程碑 hero 中央爆发。Gold 圆环 scale 10→240%
  + opacity 0→100→0,6 个 spark 从中心 60° 错位向外飞出。
- **`achievement-pop.json`** — 800ms 成就 hero 金弧丝带。trim path 0-16 帧画弧 +
  scale 80→108→100% 弹性 + 中央白光 glint 14-22 帧闪烁。
- **`tower-twinkle.json`** — 4s 循环 About hero 塔楼亮灯。6 个小点(sunset/gold
  交替)以 30 帧错位独立 sin 波 opacity 闪烁。
- **`portal-particles.json`** — 8s 循环首启 portal 粒子。6 个小圆点交替
  sunset/twilight/gold,从底飘到顶,每粒错位 40 帧形成连续粒子流。

### Skottie 接入(2 处冒烟测试)

- **About 页 HeroPanel** — `tower-twinkle` 叠在 about-hero PNG 上方
  280×200 0.55 opacity,IsHitTestVisible=False 不影响 7 击 Easter Egg。
- **AchievementUnlockHero** — `achievement-pop` 叠在 BridgeRing 上方
  120×80,Loop=False 一次性。Skottie 解析失败时静态 BridgeRing + GoldGlow
  仍构成完整成就视觉,降级安全。

### 备注

- 所有 .json 通过 `<Resource>` 注册,`pack://application:,,,/Brand/Animations/...`
  URI 可寻址。
- LottieView wrapper 已是 silent no-op fallback(JSON 缺失 / 损坏 → 不渲染,
  不抛异常),所以上述 5 个 "未接入" slot 也安全可用,接 XAML 即可可视化。
- 文件大小:6 个新 .json 共约 35 KB,不影响 zip 体积。

---

## [0.2.2] — 2026-05-09 · v1.1.3 微交互补齐 + 首启门户多屏修复

主题:**v0.2.1 留下的 4 项微交互 + Lottie 占位 + portal 全屏问题一次性补齐**。
对应 brief §3.5 全部条目落地。

### 微交互补齐 (M5 余下条目)

- **统一微交互模块** `UI/Components/Microinteractions.cs` —— 把 4 项装饰动画收口
  到一个 helper:`ErrorShake` / `CopyRipple` / `PageSwitchArcLight` /
  `AttachHoverMiniArc`。所有动画走共享 overlay Canvas(与 ArcShootEffect
  复用),复用 brand 渐变 + BridgeGlow 资源。
- **错误抖动** —— OutputEntry 翻成 `IsErrorPlaceholder=true` 时,容器横向
  6 帧衰减抖动(-8 / +6 / -4 / +3 / -2 / 0)over 360ms,读起来像
  "震了一下然后稳了"而不是游戏式 boing。
- **复制 ripple** —— 翻译条目内 复制译文 / 复制原文 按钮被点击时,从点击
  点扩散一个 sunset 边框 + BridgeGlow 圆环,360ms 内淡出。Toast + 音效之外
  的视觉确认。
- **页切换桥拱光** —— 进入 Settings 选中页时,左 nav strip 从 BridgeGlow
  瞬间升级到 BridgeGlowStrong,420ms 内 ease-out 回到常态 — 像桥被点亮再
  归于平静。
- **hover mini 桥拱光** —— Home Start CTA + Output Trigger / Send 按钮:
  hover 时底缘出一段小桥拱,180ms fade-in / fade-out。Brand silhouette 在
  用户决定动作的瞬间出现。

### Lottie 资源(部分)

- 新建 `Brand/Animations/` 目录,落地参考样本 `bridge-pulse.json`(60 帧 30fps
  桥拱呼吸动画,sunset 描边)+ `README.md`(7 个 slot 列表 + 性能 + 创作
  规范)。Resource 已在 csproj 注册,可通过 `pack://` URI 解析。
- LottieView wrapper 对缺失 / 损坏的 .json 已是 silent no-op,所以未交付的
  6 个 slot 不会导致崩溃 — 设计师后续填充即可。

### First Run Portal 真全屏

- 由 `SystemParameters.PrimaryScreenWidth/Height`(只覆盖主屏)升级为
  Win32 `MonitorFromWindow` + `GetMonitorInfo` 查询 portal 实际所在的
  monitor,再走 `PresentationSource.CompositionTarget.TransformFromDevice`
  做 per-monitor DPI 转换。多屏 / 混合 DPI 场景下不再有 "半屏曝光" 问题。

---

## [0.2.1] — 2026-05-09 · v1.1.1 + v1.1.2 + v1.1.3 hero 时刻 + 微交互

主题:**v0.2.0 视觉系统升级之上,补完 hero 时刻 + 微交互打磨**。
M4(hero modals) + M5(微交互)从 brief 一次性补齐。

### v1.1.1 — Hero modal 入场 + 文字修复

- **首启门户 Hero modal**(`UI/Onboarding/FirstRunPortalWindow.xaml`):
  新用户首启时(`first-run-done` flag 不存在),先弹 hero 模态:山间桥拱
  光粒子 backdrop + H0 大字 "架起第一座桥" + 副标 + [进入] / [跳过引导]
  CTA。点 "进入" → 关 modal → 接 Onboarding tour;点 "跳过引导" → 写
  flag,不触发 tour。
- **inline error 主副标修复**:OutputEntry 加 `IsErrorPlaceholder` /
  `ErrorTitle` / `ErrorBody` / `DisplaySubtitle` 字段;TranslationWorker
  不再把 inline format string 塞进 Translation,改 raise 新事件
  `ErrorOccurred(ids, title, body)`;OutputStore 加 `MarkErrorPlaceholder`;
  DataTemplate `OriginalText` binding 改 `DisplaySubtitle`(error 时显示
  ErrorBody,正常时显示 Original)。最终效果:错误条目 main = 短摘要
  (Title),sub = 长解释(Body)— 跟 brief §5.3.4 user feedback 对齐。
- **Settings nav 选中态 BridgeGlow**:左侧 strip 从 3px 加到 4px + 加
  `BridgeGlow` effect,选中态视觉签名更明显。

### v1.1.2 — Hero 时刻 (M4.2 + M4.3)

- **成就解锁 Hero**(`AchievementUnlockHeroWindow.xaml`):非交互全屏
  overlay,中央卡 1s 显示:gold 圆环 + 成就 title + body + GoldGlow
  effect。Animation:200ms 入场 fade+scale + 600ms 持续 + 200ms 出场。
  Toast 同时 fire(用户错过 hero 仍有持久提示)。
- **里程碑解锁 Hero**(`MilestoneHeroWindow.xaml`):TotalBridges 跨过
  100/1000/10000 时触发,about-hero(桥之城)插画 backdrop + H0 gold 大数字 +
  里程碑 title + flavor 副标 + [继续] CTA(用户主动 dismiss)。Animation:
  600ms 入场 fade+scale。新 i18n keys `Milestone_{N}_Title/Flavor` (N=100/1000/10000)。
- **StatsTracker.RecordBridgesBuilt 加 milestone 跨越检测** — `before <
  threshold && TotalBridges >= threshold` 时触发 hero;只为单次 delta
  最高跨越的阈值 fire(避免 backfill 大量 bridges 时连发 3 个 hero)。

### v1.1.3 — 微交互 polish

- **FlashStatus(复制成功 badge)入场动画**:从 hard-cut visible
  改为 180ms fade-in + 6px slide-up,符合 brief §2.8 微交互节奏。

### 留待 v1.1.x patch

- 错误抖动 / 复制 ripple / 页切换桥拱光 / hover mini 桥拱光(brief §3.5)
- 启动时间基线测量 + 优化(brief §3.6)
- Lottie 资源真填(LottieView wrapper 已实装,缺 .json 文件)
- 数字签名(Phase 4.7)— 等商业化

---

---

## [0.2.0-rc1] — 2026-05-09 · v1.1 视觉系统大重做

主题:**从"功能完备"升级到"质感跃迁"**。对标 Metafy / Linear 设计水准,在
中保留并扩展 Babelarc 桥拱主义视觉签名。

### 视觉系统升级 (M1)

- **Color tokens**:Surface 改暖蓝紫调("夜空"色)替代 v1.0 的冷调近黑;
  新增 NightSky 渐变 / GlassSurface(玻璃 frost) / WarmStone(暖砂砖石) /
  Sunset+Twilight Glow 变体 / BridgeGlow 径向。
- **Typography**:新增 H0(56px Hero),Display 升档 28→44,新增
  SectionLabelStyle 全大写 11px Medium。
- **Sizing**:圆角 RadiusLG 14→18,新增 RadiusHero 28,Padding 升档,
  新增 HeroMinHeight (240) / HeroIdealHeight (320)。
- **Effects**:新增 GlassFrost / BridgeGlow / BridgeGlowStrong /
  BridgeGlowTwilight / BloomOuter+Inner / WarmInnerShadow / LiftShadow /
  ChromeShadow。

### 共享控件升级 (M1)

- **AppCard**:新增 Glass / GlassHoverable / Hero 三个 mode。
- **AppButton.Primary**:加顶部 3px 白色高光线(光照反射感),hover
  GlowSunsetStrong 默认开启。
- **新增 6 个组件**:`BridgeRing`(数据圆环)、`MoonArcProgress`(月牙进度)、
  `MilestoneTimeline`(里程碑时间线)、`HeroPanel`(可放插画背景的卡)、
  `ChipBadge`(小标识)、`LottieView`(Skottie 包装,SkiaSharp 自带 Lottie 渲染器)。

### 关键页面重做 (M3)

- **Home**:新 Hero 区(NightSky 背景 + first-run-portal 插画作 backdrop +
  Display 大字 slogan + 副标 + MilestoneTimeline 4 节点 + Session BridgeRing)+
  三 GlassFrost 状态卡 + 升级 CTA(inner highlight + glow 默认开)。窗口默认
  尺寸 680×780 → 1080×940 让所有内容一屏可见无需滚动。
- **OutputWindow**:翻译条目左侧 ArcAccent 加 BridgeGlow 发光效果(每条像
  一座小桥);Empty state 重做带 output-empty.png 插画背景(夜空 + 月牙 +
  未连成的桥拱碎片);Toast dedup window 3s → 10s 防止同一错误连击堆叠。
- **About**:Win11 浅色 MessageBox 完全替换为 Settings → 关于(深色 dialog +
  about-hero.png 桥之城 hero 插画 + BridgeRing 累计架桥进度环)。
- **Splash**:背景从纯色近黑改为 NightSky 渐变,跟产品 brand mood 衔接。
- **Settings**:自动继承 v1.1 token(圆角 14→18 / Padding 加大 / 暖紫调)。

### 设计素材 (M2)

新增 4 张 Hero illustration(用 Anthropic art skill + nano-banana-pro 生成,
严格按 brief §6.3 风格守则):
- `splash-mark-hero.png`:暖砂塔 + 桥拱划过夜空(Splash mark reveal 用)
- `first-run-portal.png`:山间桥拱光粒子(Home Hero backdrop + 首启门户)
- `output-empty.png`:夜空 + 月牙 + 未连成桥(OutputWindow 等待感)
- `about-hero.png`:桥之城(About 页 hero,无数桥拱连接塔群)

### 新依赖

- `SkiaSharp.Skottie 3.119.2` — Lottie 渲染(SkiaSharp 自带,无 NuGet 增量)
- `SkiaSharp.Views.WPF 3.119.2` — WPF SKElement(Lottie 渲染目标)

### 不变(明确保留 v1.0 行为)

- 翻译流程、Pipeline 状态机、Worker 并发、Dedup 算法、Capture 模式、
  i18n 中英、Toast/错误友好化、统计 / 成就 / 音效系统:全部 v1.0 不变,
  只是视觉升级。

### 已知 polish 项 (v1.1.x patch)

- M4 hero 时刻(首启门户 modal / 成就解锁 hero / 里程碑解锁 hero)— 工作量
  超出 v1.1.0 主线,留 patch
- inline error 主副标顺序按 brief §5.3.4 user feedback 进一步优化

---

---

## [0.1.0] — 2026-05-09

第一个公开版本。绿色软件,zip 解压即用,删除即卸载。

### 新功能

- **实时聊天翻译悬浮窗** — 框选游戏聊天框 → 框选翻译显示位置 → 按热键即触发翻译。
- **两种识别模式**:
  - **Online Gemini**(默认推荐):截图直接送 Gemini 2.5 Flash,精度显著更高。
  - **Local OCR**:PP-OCRv5 ONNX 本地识别,server / mobile 两档可切,DirectML GPU 加速。
- **手动触发** — 按一次热键(默认 `Ctrl+Alt+A`)= 一次翻译。连按多次 → 多任务并行,
  Doing N task pill 显示在线数。
- **每行去重** — 同一聊天行 OCR 多帧抖动出来的不同字符,LCS + 末尾门控合并,只翻译一次。
- **眼睛 mask 编辑** — 点翻译窗右上角眼睛图标实时拖拽 / 调整 capture 区域,
  无需重选。
- **Onboarding tour** — 首次启动 4 步引导(API key 卡 / 识别模式 / 热键 / CTA)。
- **沉浸式标题栏** — Home / Settings 用 WindowChrome 接管,自定义 Min/Max/Close,
  与 body 同色无缝。
- **隐藏成就**(7 个 v1):第一座桥 / 百桥 / 千桥 / 万桥 / 凌晨桥(2-5 AM)/
  速度桥(单 session ≥ 10) / 多语言桥(≥ 3 种目标语言)。
- **设置同步**:外观参数(背景 / 边框 / 字号 / 透明度)实时反映到 OutputWindow 和
  Settings 预览气泡;Ctrl+滚轮在 OutputWindow 直接改字号。
- **中英双语 i18n** — 自动跟随系统语言,Settings 可强制切换 Auto / 简体中文 / English。
  中英文案 native 分别设计(不机翻)。
- **Toast 通知系统** — Info / Success / Warn / Error / Achievement 五种风格,
  错误友好化(8 类错误模板,带 [打开设置] / [打开 Models 文件夹] 等行动按钮)。
- **音效系统**(默认关闭) — 6 个 cue:Trigger / Resolve / Copy / Error /
  Achievement / Startup,采用 [Kenney Interface Sounds 1.0](https://kenney.nl/assets/interface-sounds)
  (CC0,无署名要求)。
- **绿色软件 portable** — 全部用户数据在 `<exe>/data/`(API key 明文,详见 README)。

### 系统要求

- **最低**:Windows 10 1903+(WGC 引入门槛)或 Windows 11 任意版本。
- **完整体验**:Windows 11 22H2+(build 22621+) — `IsBorderRequired` API 自动关闭
  WGC 黄色捕获框。Win10 / 早期 Win11 上软件正常运行,只是 capture 时屏幕会留下
  WGC 的黄色边框,不影响翻译功能本身。
- Gemini API key(免费层 60 RPM 一般够用)。

### 技术栈

- WPF + .NET 10 self-contained x64
- Windows.Graphics.Capture (WGC) + Vortice.Direct3D11 / DXGI 3.8.3
- SkiaSharp 3.119.2(图像处理)
- RapidOcrNet 1.0.2 + Microsoft.ML.OnnxRuntime 1.24.4 + DirectML 1.24.4
- SharpVectors 1.8.5(SVG 矢量渲染)
- Serilog 4.3.1 / Sinks.File 7.0.0
- H.NotifyIcon.Wpf 2.4.1(系统托盘)

### 取舍

- API key 明文存于 `data/credentials.json`(DPAPI 加密会让绿色软件跨机搬迁失效,
  明文换便携)。
- WGC 黄色捕获框在 Win10 / Win11 早期版本会保留(`IsBorderRequired` API 是 Win11
  22H2 引入)。功能不受影响,只是视觉上有边框。
- Online Gemini 模式按热键消耗 API 配额。Local OCR 始终免费。

---

[Unreleased]: https://github.com/bladerosa/Babelarc/compare/v0.1.0...HEAD
[0.1.0]: https://github.com/bladerosa/Babelarc/releases/tag/v0.1.0
