Commit Graph
124 Commits
Author SHA1 Message Date
H-Chris233 67bb14ec40 Restore foreground Windows hotkey edges
Windows can deliver modifier-only key events to the focused WebView without the low-level hook seeing the same edge, so the main window now forwards only hotkey-relevant DOM key events to the coordinator. Both native hook and window-forwarded events share the same coordinator edge gate so a working hook does not double-trigger toggle sessions.

Constraint: Issue #85 requires OpenLess foreground Right Control to reuse existing pressed/released/cancel semantics without changing global hook behavior

Rejected: Start dictation directly in the frontend | would duplicate coordinator state-machine semantics

Rejected: Keep a separate window-held flag | review found it can double-trigger toggle mode when both DOM and low-level hook report the same physical press

Confidence: medium

Scope-risk: moderate

Tested: cargo check --manifest-path openless-all/app/src-tauri/Cargo.toml

Tested: cargo test --manifest-path openless-all/app/src-tauri/Cargo.toml window_key_matcher_mirrors_windows_trigger_aliases

Tested: npm run build

Tested: git diff --check

Not-tested: Physical Windows foreground Right Control on this Linux host
2026-04-30 18:16:59 +08:00
baiqing 1efc20b2b2 chore(release): bump 1.2.2 -> 1.2.3 + 自有 updater pubkey + 重启前 strip xattr
修复 Codex audit 对 main HEAD a9c81e6 的 2 条 HIGH + 把 macOS auto-update 的
"重启后 Gatekeeper 拦截"问题封死。

## HIGH #2: 版本号不一致
PR #84 没顺手 bump 版本,main 仍自报 1.2.2,下次直接打 v1.2.3-tauri 会让 updater
manifest 自报 1.2.2,已安装的 1.2.2 客户端永远拿不到 "有新版本"。

修:package.json + tauri.conf.json + Cargo.toml 全部改 1.2.3。

## HIGH #1: updater pubkey 所有权
PR #84 使用的 pubkey 是外部贡献者本地生成的,appergb 不掌握对应 private key。
任何持有那把私钥的人都能签发更新 -> 客户端 OTA 装上恶意版本。

修:
- 用 npx @tauri-apps/cli signer generate --ci 生成 appergb 自己的 keypair
- 新 pubkey: F0FCDE68E08E6D4E (写入 tauri.conf.json plugins.updater.pubkey)
- 私钥已通过 gh secret set TAURI_SIGNING_PRIVATE_KEY 配到 GitHub repo secret
- 私钥本地副本只在 /tmp,不进 git,commit 后会清除

## macOS auto-update 后 strip xattr
restart_app 在 app.restart() 前对 .app bundle 跑 /usr/bin/xattr -cr。这是
Tauri auto-updater + 未公证应用的组合下唯一让"自动更新对用户零摩擦"的解法 —
否则 Gatekeeper 在重启时会拦说"OpenLess 已损坏",用户必须开终端 xattr 才能
继续用,违反 auto-update 的本意。

未来发版逻辑必须保留这一步。release-tauri.yml 上一次 PR (#83) 已经在 CI 侧
strip 过一次,本次在 client 侧重启时再 strip 一次,双保险覆盖
"下载 -> 解压 -> 安装 -> 重启"全链路。
2026-04-30 17:41:50 +08:00
baiqing a9c81e6e0e feat(updater): 增加应用自动更新入口 (#84)
H-Chris233: closes #33。

实现 Tauri 内置 updater 集成:
- Cargo: tauri-plugin-updater v2
- tauri.conf.json: 加 updater 配置(pubkey + endpoint 指向 GitHub Releases latest)
- capabilities/default.json: 授予 updater:default 能力
- lib.rs: 注册 updater 插件 + restart_app 命令
- src/pages/Settings.tsx + i18n: 关于区域加'检查'按钮、检查/下载/安装/重启对话框流
- 新脚本 scripts/write-updater-manifest.mjs: release 侧从 build artifact 拉签名生成 manifest
- workflow: TAURI_SIGNING_PRIVATE_KEY env + 各平台 build 时 createUpdaterArtifacts true

注意:下次 v*-tauri tag 必须配 TAURI_SIGNING_PRIVATE_KEY secret,否则 CI 硬失败。
2026-04-30 17:20:32 +08:00
baiqing 786bc574a7 chore(release): bump 1.2.1 → 1.2.2 — 含 PR #82 凭据加载竞态修复 2026-04-30 15:00:00 +08:00
Chris233 df8846913a fix(settings): 避免凭据加载前被空值覆盖 (#82)
* Prevent untouched credential fields from clearing secrets

Settings credential inputs can mount empty while readCredential is still pending. The field now waits for a successful read before enabling saves, tracks whether the user actually edited the value, and surfaces read failures without writing an empty credential back to storage.

Constraint: Issue #34 requires preserving existing credentials when the settings page is opened or blurred without edits.

Constraint: Keep secrets out of logs and UI.

Rejected: Add full E2E coverage now | larger test harness work than needed for the minimal bug fix.

Confidence: high

Scope-risk: narrow

Tested: npm install

Tested: npm run build

Tested: cargo check

Tested: git diff --check

* Keep credential fields recoverable after read errors

Review feedback identified that read failures left fields disabled while the temporary status disappeared. Read errors now leave the field interactive with a persistent readError status, and the credential-field status union is named explicitly for safer helper signatures.

Constraint: Preserve the issue #34 guard that only dirty loaded fields save credentials.

Rejected: Broader state-machine rewrite | unnecessary for this targeted review fix.

Confidence: high

Scope-risk: narrow

Tested: npm run build

Tested: cargo check

Tested: git diff --check
2026-04-30 14:58:53 +08:00
TRIPandbaiqing e3f91e849c ci(release): 显式 strip macOS bundle xattr 作为 defense-in-depth (#83)
GitHub Actions 输出本来就不带 com.apple.quarantine (actions/upload-artifact
不跨机器持久化 xattr, softprops/action-gh-release 上传 binary stream 也不带),
但加这一步让 "云端 artifact 一定干净" 变成可验证的承诺, 未来 release 不会因
runner 环境变化退化.

用户下载到本地后浏览器/LaunchServices 仍会加 quarantine, 依赖 release notes
里的 xattr -cr 指引或一行安装命令处理.

Co-authored-by: baiqing <lbx12309@icloud.com>
2026-04-30 14:56:22 +08:00
TRIP 90141e2731 Merge pull request #81 from appergb/develop
release(v1.2.1): 累积外部 PR + 终审 HIGH 修复
2026-04-30 14:31:30 +08:00
baiqing ca31264b44 chore(release): bump v1.2.0 → v1.2.1
包含本轮所有合并:
- Codex 终审两条 HIGH (cancel race) 修复 (PR #79)
- 6 个 Cooper-X-Oak/Codex bot PRs 自动合并 (#44 #49 #53 #68 #72 #73)
- 2 个有冲突 PR 本地 rebase 后合并 (#66 cancel + 空转写并存 / #67 Windows docs)
- README 破图修复 (PR #80)
- workflow-scope 受限的 #48 + #75 由用户在 GitHub UI 直接合并

3 处版本字段同步:package.json + tauri.conf.json + Cargo.toml
2026-04-30 14:31:05 +08:00
TRIPandbaiqing 7a3a51a1ed fix(readme): 修复 README 中破损的图片引用 (#80)
Resources/AppIcon.png 已在 commit 170283e(清理 Swift 残留)中删除,但 README.md
和 README.zh.md 顶部的 hero 图片仍然指向它,GitHub 上显示破图。

改为指向 Tauri 自身的图标:openless-all/app/src-tauri/icons/128x128@2x.png

Co-authored-by: baiqing <lbx12309@icloud.com>
2026-04-30 14:27:46 +08:00
Cooper OakandCooper-X-Oak 7797d1479a docs(windows): 补齐上游交付与测试工作流 (#67)
Co-authored-by: Cooper-X-Oak <Cooper-X-Oak@users.noreply.github.com>
2026-04-30 14:25:51 +08:00
Cooper OakandCooper-X-Oak 848cfc35c8 fix(windows): 收口录音会话失败态 (#66)
Co-authored-by: Cooper-X-Oak <Cooper-X-Oak@users.noreply.github.com>
2026-04-30 14:25:43 +08:00
Cooper OakandCooper-X-Oak 61177eafc3 ci(release): 补齐 macOS Tauri 产物发布 (#75)
Co-authored-by: Cooper-X-Oak <Cooper-X-Oak@users.noreply.github.com>
2026-04-30 14:21:54 +08:00
Cooper OakandCooper-X-Oak ff694c07c1 fix(windows): 收敛启动和开发产物路径 (#48)
* fix(windows): 收敛启动和开发产物路径

* ci(windows): 校验运行时 smoke 脚本

---------

Co-authored-by: Cooper-X-Oak <Cooper-X-Oak@users.noreply.github.com>
2026-04-30 14:21:35 +08:00
Cooper OakandCooper-X-Oak 96ff66eb80 test(windows): 增加真机回归 smoke 矩阵 (#73)
Co-authored-by: Cooper-X-Oak <Cooper-X-Oak@users.noreply.github.com>
2026-04-30 14:19:29 +08:00
Cooper OakandCooper-X-Oak b8988c3330 fix(windows): 缺失托盘图标时继续启动 (#72)
Co-authored-by: Cooper-X-Oak <Cooper-X-Oak@users.noreply.github.com>
2026-04-30 14:18:54 +08:00
Cooper OakandCooper-X-Oak 8de575d8de fix(ui): 本会话内延后提供商配置提示 (#68)
Co-authored-by: Cooper-X-Oak <Cooper-X-Oak@users.noreply.github.com>
2026-04-30 14:18:18 +08:00
Cooper OakandCooper-X-Oak ce0f00fd54 fix(windows): 校准麦克风权限探测 (#53)
Co-authored-by: Cooper-X-Oak <Cooper-X-Oak@users.noreply.github.com>
2026-04-30 14:17:42 +08:00
Cooper OakandCooper-X-Oak bb7300ee85 fix(settings): 防止凭据字段清空已有密钥 (#49)
Co-authored-by: Cooper-X-Oak <Cooper-X-Oak@users.noreply.github.com>
2026-04-30 14:16:58 +08:00
Cooper OakandCooper-X-Oak 3d6870822f test(windows): 增加低层热键钩子回归 (#44)
Co-authored-by: Cooper-X-Oak <Cooper-X-Oak@users.noreply.github.com>
2026-04-30 14:16:03 +08:00
TRIP 94c8507b5e Merge pull request #78 from appergb/develop
release(v1.2.0): i18n + ASR 数据不丢失 + 状态机修复 + 单实例 + UI 动画补齐
2026-04-30 14:05:24 +08:00
TRIPandbaiqing bcd4d815c8 fix(coordinator): 关闭 cancel race 两处窗口期 — Codex audit HIGH #1 + HIGH #2 (#79)
closes Codex audit blockers on PR #78

## HIGH #1 — begin_session 内 await 期间 cancel 被覆盖
背景:volcengine open_session().await + Recorder::start 都是异步,期间用户按
Esc 调用 cancel_session 把 phase 改回 Idle 并 cancelled=true。但原代码后续无
条件 `state.phase = SessionPhase::Listening`,把 Idle 又翻回 Listening →
用户的 cancel 被吞掉,session 仍然继续。

修:
- 新增 `cancel_raced_during_starting(inner)` helper:持锁查 cancelled or phase
  != Starting
- ASR open_session().await 后调用:如已 race,asr.cancel() + 回 Idle
- Recorder::start Ok 分支用 BeginOutcome { Started / PendingStop / CancelRaced }
  原子在同一 lock 内决定,CancelRaced 时清理 recorder + asr 资源不进 Listening

## HIGH #2 — cancelled check 与 inserter.insert 之间的窗口
背景:end_session 检查 cancelled 后到调用 inserter.insert 之间释放了 lock,
此时 Esc 触发 cancel_session 设 cancelled=true 已经晚了 — Cmd+V 即将发出,
撤销不掉。但 cancel_session 仍然 emit "已取消" → UI 与实际行为相反(已插入但
显示已取消)。

修:
- 新增 SessionPhase::Inserting:表示「已过最后一次 cancel 检查、即将/正在
  调用 inserter.insert」的窗口
- end_session:把 polish 后的 cancel check + phase 转换 atomic 在同一 lock
  内:cancelled → Idle + return;否则 → phase=Inserting 后 release lock 走 insert
- cancel_session:phase==Inserting → 直接 return(不设 cancelled、不 emit 取
  消)。理由:物理上无法撤销 Cmd+V,硬装"已取消"只会让 UI 撒谎

cargo check 通过,13 个 warnings 全部是 pre-existing。

Co-authored-by: baiqing <lbx12309@icloud.com>
2026-04-30 14:05:04 +08:00
baiqing d0d2da27a5 chore: merge origin/main into develop — 拉回 PR #45 的 polish 推理清理 2026-04-30 13:49:14 +08:00
TRIPandbaiqing e99fe6cfbd fix(capsule): 终止态后 2 秒自动隐藏 — 覆盖 Done / Cancelled / Error 全部路径 (#77)
之前只有 Done 路径有 700ms 自动隐藏,cancel / 各种 error 都让胶囊一直挂着。
用户反馈:不论点 ✕ / ✓ / 中途出错 / 按 Esc,2 秒后底部胶囊都应自动消失。

修改:
- 新增 CAPSULE_AUTO_HIDE_DELAY_MS = 2000 + schedule_capsule_idle(inner, ms) helper
  延迟后再次检查 phase==Idle 才 emit Idle,避免覆盖期间用户重新触发的 Recording
- Done 路径:原 700ms inline schedule 替换为 helper 调用 (2s)
- Cancelled 路径:cancel_session 末尾追加 schedule_capsule_idle
- Error 路径 5 处:mic 权限失败 / ASR 连接失败 / 录音启动失败 /
  await_final 失败 / Whisper transcribe 失败,全部追加 schedule_capsule_idle

Co-authored-by: baiqing <lbx12309@icloud.com>
2026-04-30 13:45:34 +08:00
TRIPandbaiqing 1e101a3147 fix(coordinator): 会话状态机修复 — Starting 阶段补偿热键边沿 + Esc 取消支持 Processing (#76)
closes #51, #52

## #51 — Starting 阶段忽略热键边沿
握手 ASR 期间(phase=Starting)的"停止"边沿(toggle 第二次按 / hold 松开)
之前被直接 _ => {} 掉,会话锁死直到用户再按一次或重启 app。

修:
- SessionState 加 pending_stop: bool
- handle_pressed Toggle/Starting → pending_stop = true
- handle_released Hold/Starting → pending_stop = true
- begin_session 转 phase=Listening 时 mem::replace 取出 pending_stop,
  若为 true 立即 await end_session

## #52 — Esc 取消在 Processing 阶段无效
用户按 Esc 时若 end_session 已进入 Processing(润色 / 插入进行中),cancel
信号被忽略,文本仍插入到光标位置 → 数据无意写入用户当前 app。

修:
- SessionState 加 cancelled: bool
- cancel_session 设 cancelled=true(不再仅 Idle 之外 = 转 Idle)
  Processing 阶段保持 phase=Processing 让 end_session 自己走完检查 + 收尾
- end_session 在 ASR 完成后、polish 之前 + polish 完成后、insert 之前
  各检查一次 cancelled,命中即跳过 insert/history.append

新会话 begin_session 入口清两个 flag,避免遗留触发奇怪行为。

Co-authored-by: baiqing <lbx12309@icloud.com>
2026-04-30 13:36:39 +08:00
TRIPandbaiqing fea402dd15 fix(asr): 保证用户的话不丢 — partial 缓存 + 末帧排队 + Whisper 失败保留 buffer (#74)
closes #54, #55, #56

三件事,同一目标:用户的口述内容不能因为网络抖动 / 服务端关连接 / 凭证错误
而消失。

## #54 partial cache (volcengine.rs)
- SyncState 加 last_partial_text;每次非 final result 缓存当前累积文本
- 服务端 close(无 final 帧)和 receive loop Err(网络中断)路径不再直接 NoFinalResult
- 新增 fallback_to_partial_or_error:有 partial 就 signal_success(RawTranscript),
  没有才 signal_error;外层 await_final_result 拿到的是已识别那段文字而非空
- 实测命中:网络抖动断线,用户至少拿到断线前那段已识别的话

## #55 audio finalize race (volcengine.rs)
- 新增 pending_sends: AtomicUsize + send_done: Notify
- consume_pcm_chunk 每个 spawn fetch_add(1),spawn 内发送完毕 fetch_sub(1) + notify
- send_last_frame 进函数先 await pending_sends == 0(800ms 上限避网络极端场景)
  再发 leftover + NegativeSequence 末帧
- 防止:fire-and-forget 的 chunk-send 与末帧并发,末帧先到服务端 → 后续 chunk
  被当成"流已结束"后多余帧丢弃 → 尾句吞掉

## #56 Whisper buffer 保留 (whisper.rs)
- 之前 transcribe 一进函数就 mem::take 把 buffer 清空,凭证错 / 网络挂 / 解析失败
  都让 PCM 直接消失,无法重试也无法 fallback
- 改为先 clone (~960 KB / 30s 音频) → transcribe_inner → 成功才 clear buffer
- 失败时 buffer 保持,给上层"再试一次"或"留失败历史记录"留余地

Co-authored-by: baiqing <lbx12309@icloud.com>
2026-04-30 13:33:54 +08:00
TRIPandbaiqing 0d5bfa9b11 fix(infra): 接入 tauri-plugin-single-instance — 防止两份 OpenLess 共存导致重复粘贴 (#71)
closes #50

现象:/Applications/OpenLess.app 与 dev build 同时存在时,两个进程各自注册
macOS CGEventTap 监听全局热键。用户按 Right Option 一次:
- A 实例捕获 → ASR session 1 → polish → Cmd+V
- B 实例同时捕获 → ASR session 2 → polish → Cmd+V
结果:用户口述被插入两份。

修复:
- Cargo.toml: 加入 tauri-plugin-single-instance v2
- lib.rs: builder 第一个 plugin 即注册它;第二个进程启动时回调聚焦已有主窗口

激活信号通过 plugin 内部 socket 传递,无需额外 IPC 设计。

Co-authored-by: baiqing <lbx12309@icloud.com>
2026-04-30 13:28:50 +08:00
TRIPandbaiqing 707eaa639b fix(settings): 移除 SiliconFlow ASR 选项 — 后端未实现,选了必然失败 (#70)
closes #58

后端 coordinator.rs 只把 active_asr=='whisper' 路由到 WhisperBatchASR,其他全部
走 VolcengineStreamingASR + 火山凭证。设置页之前暴露 SiliconFlow 选项,用户选后
ASR 必然失败。

修复:从 ASR_PRESETS 删除 siliconflow 条目 + 删除 asrProvider==='siliconflow' 的
凭证字段分支。重新启用方案:在后端实现真正的 SiliconFlow ASR provider 后再加回。

Co-authored-by: baiqing <lbx12309@icloud.com>
2026-04-30 13:27:12 +08:00
baiqing 3640924c23 feat(animations): 全局补齐过渡反馈 — 22 处 transition + 页面切换淡入 + ProviderPrompt 弹窗动画
按 Codex agent A 的前端审计 fix-now 列表批量补齐:

**hover/active/focus 反馈(一行 transition 即可)**
- WindowChrome winBtnStyle: bg + color
- _atoms Btn: bg + color + border + shadow + transform
- FloatingShell sidebar nav / FooterIcon / 检查更新 / ProviderPrompt 两按钮
- History filter chip / 列表行
- Vocab input / chip
- Style 卡片 / 单卡 toggle / 整体 toggle
- Settings 分区 nav / 录音方式 segmented / Toggle / inputStyle / iconBtnStyle
- Capsule cancel/confirm(opacity + bg + transform)
- Onboarding 授权按钮
- Overview WeekChart bars(height + opacity)

**新增入场动画**
- 页面 tab 切换 ol-page-fade 0.18s(每次 setCurrentTab 重 mount key 触发淡入 + 上滑 4px)
- ProviderSetupPrompt ol-prompt-fade(背景)+ ol-prompt-pop(卡片)
- Vocab chip ol-chip-in(添加新词条时缩放淡入)

**保持不变**
- Capsule 自身的 audio bars 动画(上一轮已优化)
- SettingsModal 的 ol-modal-fade / ol-modal-pop(已有)

零功能变化、零 bug 风险。仅 CSS / inline style 添加。
2026-04-30 13:22:54 +08:00
Chris233 af9b2b62c0 Merge pull request #45 from H-Chris233/main
fix(polish): 过滤模型深度思考输出
2026-04-30 13:17:23 +08:00
baiqing c3e434faa7 fix(modal): 设置弹窗去掉所有不能点的空壳,每个可见控件都接到真实后端
之前设置弹窗里有一组占位组件(SegSimple/SwitchLite/SelectLite),看起来可点
但只切换内部 useState,不影响任何实际行为。这次按 issue #69 的清单全清。

移除:
- AccountSection 整个 tab —— 没有账号系统,"登录/同步" 按钮无 onClick
- PersonalizeSection 的「外观(跟随系统/浅色/深色)」—— 没有 dark mode CSS
- PersonalizeSection 的「启动时打开(概览/上次位置)」—— 无对应 preference
- PersonalizeSection 的「开机自启」—— 无 autostart 集成
- 不再 import SegSimple / SwitchLite / SelectLite

接通:
- modal sub-nav 的「帮助中心」「版本说明」点击实际打开 GitHub README / Releases
- AboutMini 的「检查」「文档 ↗」「GitHub Issues ↗」三个按钮接到 openExternal
- modal nav 按钮加入 transition 动画(background/color 0.12s ease-out)
- 关闭按钮加 hover 反馈

保留可用:
- PersonalizeSection 的「界面语言」+「毛玻璃强度」—— 都已正常工作,保留

后续 issue:#69 跟踪未来要补回来的功能(账号 / 主题 / 启动项 / autostart)。
2026-04-30 13:13:36 +08:00
H-Chris233 32eb406d0b Keep reasoning cleanup linear and conservative
Review feedback pointed out that repeated replace_range passes could become quadratic and that provider tags may vary by casing or attributes. The parser now scans the response once, allocates only when a complete think block is removed, and recognizes explicit think tags with optional attributes and ASCII casing variants.

Constraint: Keep cleanup conservative and avoid localized heading stripping.

Rejected: Regex dependency | unnecessary for one small tag parser.

Rejected: Strip broad reasoning headings | can remove valid user-facing prose.

Confidence: medium

Scope-risk: narrow

Tested: cargo test polish::tests -- --nocapture

Tested: cargo check

Tested: npm run build
2026-04-30 13:10:21 +08:00
baiqing eb9fbabce0 fix: 胶囊波形/玻璃随电平实时跳动 + 词汇本命中后实时刷新
修三件事:

1. **波形不动** — 后端电平回调 ~185 Hz emit,CSS 0.12s transition 导致 22 个并行
   transition 互相覆盖、视觉上被平均成静止。
   - coordinator.rs: 节流 emit 到 30 Hz(33ms 最少间隔)
   - Capsule.tsx: bars transition 改 0.06s linear,每次 emit 完整跳到目标高度

2. **玻璃静止** — 加电平驱动的微缩放 + 投影增强(≤2%),录音态整个胶囊在呼吸。
   transition: transform 0.06s linear, box-shadow 0.06s linear。

3. **词汇本不实时** — record_hits 改完 dictionary.json 但前端不知道,需切 tab 才更新。
   - coordinator.rs: total_hits > 0 时 emit('vocab:updated', total_hits)
   - Vocab.tsx: useEffect 订阅 vocab:updated 后立即 refresh()

不改产品语义;不动 ASR / polish 路径。
2026-04-30 13:03:23 +08:00
baiqing 648df0579d chore: 清理已废弃的 Swift 时代文档
用户明确:这些 docs/* 是已废弃 Swift 实现的产物,全平台 Tauri 后不再需要。
- 删除 18 份过时 architecture / requirements / release-notes / review 文档
- 不影响 Tauri 当前文档(CLAUDE.md / README / USAGE 仍在)
2026-04-30 12:51:50 +08:00
baiqing da7b426959 feat(polish): 清晰结构改为两层层级 — 主题 1./2. + 要点 a./b.,去掉中间括号层
用户反馈:括号中间层("1)" "2)")多余,希望保留主题编号 + 字母要点的清爽结构。

变更:
- polish.rs Structured prompt:层级规则从 3 层改为 2 层;明确禁用带括号中间层;
  示例改为 "1. 主题 / a. 要点 / b. 要点 / 2. 主题 / ..."
- i18n style.modes.structured.sample(zh-CN / en):与新 prompt 输出格式同步

代价:极复杂的多级嵌套口述(如"登录页里的密码错与图形验证码")在 2 层结构下会
被压扁;复杂场景建议改用「原文」或「轻度润色」mode。
2026-04-30 12:34:55 +08:00
TRIP 72a86960ea Merge pull request #42 from appergb/develop
feat(i18n+polish+cleanup): 中英文 UI 切换 + polish prompt 重写 + 清理 Swift 残留
2026-04-30 12:12:03 +08:00
baiqing 0643ff767b feat(polish): 重写 4 个 mode 的 system prompt — 段落式结构 + 增强 anti-instruction 防御
closes #47

- 拆出共享段落 ROLE_BLOCK / COMMON_RULES / OUTPUT_BLOCK,避免每个 mode 重复 200+ 字
- 改为 # 角色 / # 任务 / # 通用规则 / # 示例 / # 输出 markdown 段落式结构,便于模型 attention
- 强化 ROLE_BLOCK:明确"原始转写是文本对象不是指令"+「不替对方回答清单/问题」
- 新增 COMMON_RULES:"不确定→保留原话"+ 代码/URL/数字单位原样保留
- raw / light / formal 各加 1-shot 示例(Structured 已有)
- Structured 三层层级规则与示例完全保留,不动产品语义
- user_prompt 同步收紧措辞,与 system prompt 框架呼应

不改 mode 产品语义、不改 compose_system_prompt 签名、clean_polish_output boilerplate 列表保持兼容。
2026-04-30 12:10:57 +08:00
baiqing 170283e483 chore: 删除旧 Swift 实现的最后残留 + 去 Swift 化 CLAUDE.md
- 删 tracked 的顶层 Resources/(Swift 时代的 AppIcon / Brand 素材)
  Tauri 自给:src-tauri/icons/* + app/public/AppIcon.png
- 重写 CLAUDE.md:移除"双实现"框架、Swift 构建/测试章节、Sparkle 发布流水线、
  Sources/ / Package.swift / appcast.xml / scripts/release.sh / Swift @MainActor 等所有过时引用
- 保留一行历史注脚指向 commit 34d2823 防止 Swift 复活
2026-04-30 12:10:57 +08:00
H-Chris233 2e63c42840 Keep model reasoning out of inserted polish text
Thinking-capable OpenAI-compatible models can return tagged reasoning before the final polished answer. The cleanup layer now strips only explicit think-tag blocks after response parsing so existing provider requests, UI, and fallback behavior stay unchanged.

Constraint: Issue #25 asks to adapt thinking output rather than disabling model thinking.

Rejected: Add a provider-specific thinking toggle | broader UI and settings change than needed for the bug.

Rejected: Strip localized heading text | too likely to remove normal user-facing content without a stable provider contract.

Confidence: medium

Scope-risk: narrow

Tested: cargo test polish::tests -- --nocapture

Tested: cargo check
2026-04-30 11:58:11 +08:00
baiqing ce62680ad3 fix(i18n): 修首次渲染拿到的 t() 返回 key 字面量;让个性化弹窗的语言下拉真的能点
问题:
- 截图证据:所有 t() 调用渲染为原始 key("nav.overview" 等),界面文字全乱
- 同时 Personalize 弹窗的语言下拉是 SelectLite —— 一个纯展示 div,点击无任何反应

根因:
- LanguageDetector 异步 init + react-i18next useSuspense=false 默认下首次渲染拿到 key
- SelectLite 不接受任何点击/选择事件

修复:
- 干掉 i18next-browser-languagedetector,手写 4 行 detectSystemLocale + getStoredLocale
- 显式 react: { useSuspense: false } + partialBundledLanguages: true
- main.tsx 改为 i18n 就绪后再 ReactDOM.render(双保险,避免任何竞态)
- Personalize 弹窗的语言行替换为可用的原生 <select> LanguagePicker,与 Settings → Language 共享同一 localStorage 偏好
2026-04-30 11:49:49 +08:00
baiqing e1c0957631 chore: merge origin/main into develop
将 main 的 PR #41(coordinator hotkey 测试基础设施)合入 develop。
保留 develop 上的 i18n 变更与 main 的 hotkey 测试改动,无冲突。
2026-04-30 11:38:41 +08:00
Chris233 ac611d5151 Merge pull request #41 from H-Chris233/main
test(hotkey): 补齐热键自动注入门禁
2026-04-30 11:37:15 +08:00
baiqing 99d2e9d831 feat(i18n): 引入语言包系统,支持 zh-CN / en 切换 + 跟随系统
- 接入 react-i18next + i18next-browser-languagedetector,资源以 TS 模块静态打包
- 资源 src/i18n/{zh-CN,en}.ts;en.ts 通过 typeof 绑定 zh-CN 形状,缺 key 编译报错
- 全部 UI 表面(胶囊、Onboarding、主窗口侧栏/页脚、Overview/History/Vocab/Style 页、设置弹窗、设置页各分区、WindowChrome)改走 t() 查找
- SettingsSectionId 从 zh-CN 字面量切换为稳定 EN id(recording/providers/...);ModalSectionId 同改
- 设置 → 语言 分区新增「跟随系统 / 简体中文 / English」切换器,localStorage 'ol.locale' 持久化
- 首启检测顺序 localStorage → navigator.language;fallback 永远 zh-CN
- lib/hotkey.ts 改用 i18n.t 直接渲染触发键 / 适配器名称

Closes #40
2026-04-30 11:36:16 +08:00
H-Chris233 8df0fb1f6c Prove coordinator hotkey handling without physical keys
Windows regression currently depends on a tester pressing a real modifier key, which cannot distinguish OS-hook delivery failures from coordinator state-machine failures. Add a debug/test-only injection path that drives handle_pressed and handle_released, logs the coordinator hotkey edge, and cancels the dry-run session automatically.

Constraint: Must not rely on SendInput or a physical keyboard for the coordinator gate.

Constraint: Keep physical hotkey testing as the OS hook coverage layer.

Rejected: Fake log-only gate | would not exercise the coordinator hotkey path.

Rejected: Production IPC command | unnecessary surface area for a test/dev-only gate.

Confidence: high

Scope-risk: narrow

Tested: npm run check:hotkey-injection

Tested: npm run build

Tested: cargo check

Tested: cargo check --release

Tested: git diff --check

Related: https://github.com/appergb/openless/issues/35
2026-04-30 11:26:26 +08:00
Chris233 4245ddf56f Merge pull request #1 from appergb/main
feat(settings): 硅基流动 ASR/LLM 配置 + 关于页完善 + onAsrProviderChange bug 修复
2026-04-30 11:24:07 +08:00
baiqing 4a0d8efead feat(settings): 硅基流动 ASR/LLM 配置 + 关于页完善 + onAsrProviderChange bug 修复
- LLM 供应商新增「硅基流动」(https://api.siliconflow.cn/v1,默认模型 Qwen2.5-7B)
- ASR 供应商新增「硅基流动 SenseVoice」(Whisper 兼容,默认模型 FunAudioLLM/SenseVoiceSmall)
- 修复 onAsrProviderChange 调用未定义 setPrefsState/setSettings 的 bug,改用 updatePrefs
- 关于页:新增源码入口(GitHub 链接)、QQ 群号 1078960553(可一键复制)
2026-04-30 10:54:05 +08:00
Chris233 85045c3ff4 chore(repo): 统一项目目录命名 (#39)
* Remove fragile space from active app path

The Tauri app lived under a path containing a literal space, which made CI paths and local commands easy to quote incorrectly. Rename the active tree to openless-all and update repository references so scripts, docs, and release workflows use the same space-free path.

Constraint: Preserve the existing app layout and move only the containing directory name.

Rejected: Add compatibility symlinks | would keep two app paths and leave quoting hazards in place.

Confidence: high

Scope-risk: moderate

Tested: npm run build

Tested: cargo clean && cargo check

Tested: git diff --check

Tested: grep -R for old openless -all and openless%20-all references

* Keep Windows build docs aligned with space-free path

The app directory was renamed to openless-all, but the GNU/MinGW route still described the new name as if it contained a space. Update the wording so the rationale matches the current path and keeps the helper-script guidance focused on consistent GNU setup.

Constraint: Documentation-only code review follow-up.

Confidence: high

Scope-risk: narrow

Tested: git diff --check
2026-04-30 10:44:31 +08:00
baiqing 7f83bca5b6 docs: 更新 README / USAGE / CLAUDE.md 至 Tauri v1.1 现状
- 两个 README 改为描述 Tauri 2 + Rust + React/TS,macOS 12+ 和 Windows
- Badges:去掉 Swift/macOS15+,加 Tauri/Rust/Windows/macOS12+
- 状态章节:v1.0 Swift → v1.1 Tauri
- 构建说明:Tauri 优先,Swift 降为 legacy
- 架构章节:更新为 Tauri 模块表
- Roadmap:删除已完成项(Developer ID + Sparkle)
- 下载说明:更新为 DMG/MSI 安装流程
- 发布检查:更新为 tag CI 流程
- 新增 USAGE.md(快速上手指南,中文)
- QQ 群号 1078960553 加入显眼位置(README 标题区 + USAGE)
- CLAUDE.md:补充 whisper ASR、HotkeySettingsContext、cargo check
2026-04-30 09:12:31 +08:00
baiqing cecbb31c22 feat: ASR 供应商切换 + 设置页自动保存
- 新增 Whisper 批量 ASR:录音完毕后将 PCM 编码为 WAV,POST 到任意 OpenAI 兼容端点
- persistence: 增加 AsrApiKey / AsrEndpoint / AsrModel 凭据账户,支持活跃 ASR 供应商读写
- coordinator: 引入 ActiveAsr 枚举,begin/end/cancel_session 按 active.asr 路由到 Volcengine(流式)或 Whisper(批量)
- 设置页"提供商"新增 ASR 供应商下拉(火山引擎 / Whisper),切换后自动更新凭据与偏好
- CredentialField 改为防抖 300ms 自动保存,失焦立即保存;设置关闭前不再丢数据
2026-04-30 09:12:31 +08:00
Chris233 82a24faa83 fix(hotkey): 收口桌面热键适配边界 (#38)
统一热键适配边界:macOS 保留 CGEventTap,Windows 改用原生 WH_KEYBOARD_LL hook,前端热键展示改为读取后端 capability/status。
2026-04-30 08:58:55 +08:00
baiqing eec4c37c38 feat: LLM 供应商选择、胶囊焦点修复、默认配置填入
- 设置 > 提供商:新增供应商下拉(ARK / DeepSeek / OpenAI / 自定义)
  切换供应商自动填入 Base URL,凭据字段随供应商 key 重挂载
- ASR Resource ID:空值时显示填入默认按钮(默认值 volc.bigasr.sauc.duration)
- 焦点 bug:胶囊 show() 后若 OpenLess 已是前台 app 则用 makeKeyWindow
  还原主窗口焦点,不调 NSApp.activate,不抢其他 app 光标
- 后端:新增 set_active_llm_provider 命令,更新 credentials.json active.llm
2026-04-29 22:50:44 +08:00