Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,27 @@ For maintainers:
- Tag `v<version>-tauri` **on `main`**, not on `beta`. The release workflow keys off the tag, but tagging on `main` keeps the release commit linear with the always-releasable line.
- Avoid direct pushes to `main` outside the `beta → main` merge — it bypasses the smoke-test gate.

Channel distribution (in progress): per-channel updater endpoints + a Settings toggle for "join Beta channel" are tracked as a separate change. Until that lands, every release reaches every user; treat all `v*-tauri` tags as Stable-grade for now and avoid tagging anything from `beta` directly.
Channel distribution (manual-download opt-in):

- **Tag convention.** `v<v>-tauri` → Stable release (GitHub `prerelease=false`, manifest `latest-{tgt}-{arch}.json`). `v<v>-beta-tauri` → Beta release (GitHub `prerelease=true`, manifest `latest-{tgt}-{arch}-beta.json`). The two manifest filenames never overlap, so the in-app updater endpoint (which is fixed at compile time to the no-suffix file) cannot pick up Beta releases. This is the **physical isolation** that guarantees Beta does not leak to Stable users.
- **Why not auto-update for Beta.** `tauri-plugin-updater` 2.10's `Builder` does not expose `endpoints()` — endpoints are only readable from `tauri.conf.json` at build time and cannot be swapped at runtime. Rather than fork the plugin or write a custom updater (~500 lines, high risk), Beta opt-in is implemented as a manual-download flow: Settings → About has a "Join Beta channel" toggle that, when on, calls `fetch_latest_beta_release` (GitHub Releases API), shows the latest pre-release tag, and routes the user to the GitHub release page to download manually. No installer signing/install path needs to be re-implemented.
- **Where the wiring lives.** Pref field: `UserPreferences::update_channel` (`types.rs`). IPC: `get_update_channel` / `set_update_channel` / `fetch_latest_beta_release` (`commands.rs`). UI: `BetaChannelControl` inside `AboutMini` (`SettingsModal.tsx`). i18n: `settings.about.betaChannel*` keys.

### Release verification checklist (run after every tag push)

Run after pushing **either** a `v*-tauri` or `v*-beta-tauri` tag, **before** announcing the release:

1. **GitHub Release page** matches expectation:
- Stable tag: not marked `Pre-release`, in the `releases/latest` redirect.
- Beta tag: marked `Pre-release`, **not** the target of `releases/latest`.
2. **Release assets** are channel-correct:
- Stable tag includes `latest-{darwin,windows,linux}-{aarch64,x86_64}.json` + their `-mirror.json` siblings, **without** `-beta` suffix.
- Beta tag includes `latest-{tgt}-{arch}-beta.json` + `-beta-mirror.json`, **without** the no-suffix variant.
3. **Stable user flow.** Install a Stable build, click `Settings → About → Check for updates`. After a Stable release: should offer the new version. After a Beta release only: should report "up to date" (Beta must not appear).
4. **Beta user flow.** In the same Stable build, toggle on `Join Beta channel`. The latest Beta tag should appear (or "no Beta released yet"). Clicking the download button should open the corresponding GitHub release page.
5. **Updater endpoint sanity.** `curl -fsSL https://github.com/appergb/openless/releases/latest/download/latest-darwin-aarch64.json` returns the Stable manifest (version field matches the latest Stable tag). It should never return a Beta version, regardless of which tag was pushed most recently.

If any step fails, do not announce the release; investigate `release-tauri.yml` channel detection (`endsWith(github.ref_name, '-beta-tauri')`) and the `OPENLESS_RELEASE_CHANNEL` env propagation in the run logs.

## Repo conventions

Expand Down
42 changes: 34 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,16 @@ OpenLess does one thing: **turn speech into usable written text (especially AI p

- Tauri 2 + Rust backend + React/TS frontend. macOS 12+, Windows 10+.
- **Toggle and push-to-talk** recording modes. `Esc` cancels at any phase, including polish/insert.
- Volcengine streaming ASR + OpenAI Whisper-compatible batch ASR; Ark / DeepSeek / OpenAI-compatible chat-completions for polish.
- 4 output modes: raw, light polish, structured (**AI prompt mode**), formal.
- **Cloud ASR**: Volcengine streaming ASR, OpenAI Whisper-compatible batch ASR, Apple Speech (macOS).
- **Local ASR**: bundled Qwen3-ASR (0.6B / 1.7B) via vendored `antirez/qwen-asr`; Windows Foundry Local Whisper variants.
- **Polish providers**: Ark / DeepSeek / OpenAI / Doubao / Anthropic-compatible chat-completions, plus any OpenAI-compatible endpoint you bring.
- 4 output modes: raw, light polish, structured (**AI prompt mode**), formal. Plus a **translation hotkey** that converts speech directly into the configured target language ([#43](../../issues/43)).
- **Selection-ask QA panel** — separate hotkey opens a floating panel that runs voice Q&A against the highlighted text in any app ([#118](../../issues/118)).
- Main window: Overview / History / Vocab / Style / Settings. Persistent tray icon. Mini status capsule floating on screen.
- **Bilingual UI** — Settings → Language switches between 简体中文 and English (auto-detects on first launch).
- **Multilingual UI** — Settings → Language switches between 简体中文 / 繁體中文 / English / 日本語 / 한국어 (auto-detects on first launch).
- **In-app auto-update** — Settings → About → Check button; signed updater artifacts via Tauri updater plugin.
- **Beta channel (opt-in)** — Settings → About → Join Beta channel exposes the latest pre-release build for manual download; Beta releases never reach Stable users automatically (see [Contributing workflow](#contributing-workflow)).
- **Distribution channels** — direct DMG/EXE from [Releases](../../releases), Homebrew Cask (`brew install --cask openless`), Windows installer.
- **Single-instance lock** — prevents two OpenLess processes from racing the same hotkey edge.
- Dictionary entries injected as Volcengine ASR `context.hotwords` and as semantic hints during polish; hits accumulate per session.
- Platform-native global hotkey: CGEventTap on macOS, low-level keyboard hook (`WH_KEYBOARD_LL`) on Windows.
Expand Down Expand Up @@ -237,7 +242,7 @@ Rules of thumb:
- **Beta work must not leak to Stable.** `main` only receives merges from `beta`, performed by maintainers after a successful two-platform smoke build. No direct pushes to `main`.
- **Stable releases are cut from `main`** by pushing a `v<version>-tauri` tag — see the maintainer release checklist below.

Beta release distribution (opt-in, not yet wired): Beta builds are intended for users who consciously join the Beta channel; the in-app updater currently treats every release as Stable, and a follow-up change will introduce per-channel updater endpoints + a Settings toggle.
Beta release distribution (manual-download opt-in): the in-app updater always reads the Stable manifest, so regular users never get Beta builds via auto-update. Users who want to try Beta open **Settings → About**, flip "Join Beta channel", and download the latest Beta installer manually from the link the app fetches from GitHub. Tag convention: `v<version>-beta-tauri` produces the Beta release (marked GitHub pre-release; manifest written as `latest-{tgt}-{arch}-beta.json`); `v<version>-tauri` produces the Stable release. The two manifest files never overlap, so Stable users' updater feed cannot pick up Beta releases.

## Credentials

Expand Down Expand Up @@ -287,7 +292,7 @@ The main window is organized as Home / History / Dictionary / Settings. The Dict

## Architecture

The active implementation is Tauri 2 (`openless-all/app/`). Auto-updates ride on the Tauri updater plugin; signed updater artifacts are produced by CI on every `v*-tauri` tag.
The active implementation is Tauri 2 (`openless-all/app/`). Releases are split into two channels: **Stable** (`v<v>-tauri` tag, auto-updated to all users) and **Beta** (`v<v>-beta-tauri` tag, GitHub pre-release, manually downloaded by opt-in users). Signed updater artifacts are produced by CI on every release tag.

**Tauri backend (Rust)** — each module depends only on `types.rs`:

Expand Down Expand Up @@ -323,10 +328,31 @@ Planned but not yet shipped:

## Maintainer release checklist

- Bump version in `openless-all/app/package.json`, `src-tauri/tauri.conf.json`, and `src-tauri/Cargo.toml`.
OpenLess ships two release channels. Branch name = channel name (see [Contributing workflow](#contributing-workflow)).

### Common prep (both channels)

- Bump version in **all five** files: `package.json`, `package-lock.json` (root + nested entry under `packages.""`), `src-tauri/tauri.conf.json`, `src-tauri/Cargo.toml`, `Cargo.lock` (look for the `name = "openless"` block). CI's `Verify version sync` step will fail the build otherwise.
- Run `INSTALL=0 ./scripts/build-mac.sh` and confirm the `.app` launches.
- Verify on a clean macOS box: permission flow, hotkey, recording, ASR, polish, insertion, clipboard fallback.
- Push a `v<version>-tauri` tag — CI builds + signs the updater artifacts and the macOS `.dmg` + Windows `.msi`. The updater needs `TAURI_SIGNING_PRIVATE_KEY` repo secret (matching the pubkey in `tauri.conf.json`).
- Smoke-test on a clean machine: permission flow, hotkey, recording, ASR, polish, insertion, clipboard fallback.
- Confirm `TAURI_SIGNING_PRIVATE_KEY` and (for macOS) the Apple signing/notarization secrets are set on the repo.

### Beta channel — `v<v>-beta-tauri`

1. Land changes onto the `beta` branch via PR review.
2. Push tag **on `beta`**: `git tag v<v>-beta-tauri && git push origin v<v>-beta-tauri`.
3. CI tags the GitHub Release as `Pre-release` and uploads only `latest-{tgt}-{arch}-beta.json` updater manifests. Stable users' `releases/latest` redirect is unaffected.
4. Announce in the appropriate channel (issue thread, QQ group) that opt-in Beta users can grab it from Settings → About → Join Beta channel.

### Stable channel — `v<v>-tauri`

1. Merge `beta → main` after the Beta release has soaked enough (or run a final two-platform smoke build directly).
2. Push tag **on `main`**: `git tag v<v>-tauri && git push origin v<v>-tauri`.
3. CI publishes a normal GitHub Release and uploads `latest-{tgt}-{arch}.json` (no `-beta` suffix). All Stable users get the update through the in-app updater.

### Post-release verification (always run)

Run the 5-step checklist in [`CLAUDE.md` → Branch & release-channel workflow → Channel distribution](CLAUDE.md): page status (pre-release flag), asset filename channel-correctness, Stable user flow, Beta opt-in flow, raw endpoint sanity.

## Acknowledgements

Expand Down
42 changes: 34 additions & 8 deletions README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,16 @@ OpenLess 只做一件事:**把语音变成可用的书面文字(尤其是 AI

- Tauri 2 + Rust 后端 + React/TS 前端;macOS 12+,Windows 10+。
- **切换式 + 按住说话** 双模式录音;任意阶段按 `Esc` 都能取消(包括润色 / 插入中)。
- 接入火山引擎流式 ASR + OpenAI Whisper 兼容批式 ASR;Ark / DeepSeek / OpenAI 兼容 Chat Completions 进行润色。
- 4 种输出模式:原文、轻度润色、清晰结构(**AI prompt 模式**)、正式表达。
- **云端 ASR**:火山引擎流式 ASR、OpenAI Whisper 兼容批式 ASR、Apple Speech(macOS)。
- **本地 ASR**:内置 Qwen3-ASR(0.6B / 1.7B),通过 vendored `antirez/qwen-asr` 链接;Windows 端支持 Foundry Local Whisper。
- **润色 Provider**:Ark / DeepSeek / OpenAI / Doubao / Anthropic 兼容的 Chat Completions,以及任意 OpenAI 兼容的自定义 endpoint。
- 4 种输出模式:原文、轻度润色、清晰结构(**AI prompt 模式**)、正式表达。另含**翻译热键**——按下后说一段话直接转成目标语言插入([#43](../../issues/43))。
- **划词语音问答(QA)面板** — 独立热键打开浮窗,对当前选中文本发起语音 Q&A([#118](../../issues/118))。
- 主窗口按「概览 / 历史 / 词典 / 风格 / 设置」组织;托盘图标常驻;浮动状态胶囊。
- **中英双语 UI** — 设置 → 语言 切换简体中文 / English(首启按系统语言自动)。
- **多语言 UI** — 设置 → 语言 切换简体中文 / 繁體中文 / English / 日本語 / 한국어(首启按系统语言自动)。
- **应用内自动更新** — 设置 → 关于 → 检查按钮;CI 用 Tauri updater 签名 manifest,客户端校验后下载安装。
- **Beta 渠道(opt-in)** — 设置 → 关于 → 加入 Beta 渠道,会显示最新 prerelease 的下载入口供手动安装;Beta 包永远不会被自动推送给正式版用户(详见 [贡献流程](#贡献流程))。
- **分发渠道** — [Releases](../../releases) 直接下载 DMG/EXE,Homebrew Cask(`brew install --cask openless`),Windows 安装程序。
- **单实例锁** — 防止两份 OpenLess 进程并存争抢同一热键边沿。
- 词典条目作为 Volcengine ASR `context.hotwords` 注入 + 润色语义提示,每次会话累计命中数。
- 平台原生全局快捷键:macOS 使用 CGEventTap,Windows 使用低层键盘钩子(`WH_KEYBOARD_LL`)。
Expand Down Expand Up @@ -240,7 +245,7 @@ OpenLess 采用 **Beta / 正式版** 双渠道分支模型。
- **Beta 不能溢出到正式版。** `main` 只接收来自 `beta` 的合并,由维护者在双端冒烟测试通过后执行;任何人不要直接 push `main`。
- **正式版 Release 从 `main` 切出**,通过推送 `v<版本>-tauri` tag 触发,详见下方"维护者:发布检查"。

Beta 包的分发(opt-in,尚未接入):Beta 包面向主动加入 Beta 渠道的用户;当前 App 内 updater 把所有 release 都当作正式版处理,后续会在设置页加入"加入 Beta 渠道"开关,并把 updater endpoint 按渠道拆开
Beta 包的分发(手动下载式 opt-in):App 内自动更新永远只读正式版 manifest,普通用户拿不到 Beta 包。想试 Beta 的用户去 **设置 → 关于**,打开「加入 Beta 渠道」开关,App 会从 GitHub 拉到最新 Beta release 信息并展示下载入口,由用户手动下载安装。Tag 约定:`v<版本>-beta-tauri` 出 Beta release(GitHub 标 pre-release,manifest 写到 `latest-{tgt}-{arch}-beta.json`);`v<版本>-tauri` 出正式版。两组 manifest 文件名物理隔离,正式版用户的 endpoint 永远拿不到 Beta release

## 凭据

Expand Down Expand Up @@ -290,7 +295,7 @@ OpenLess 的润色模型只做文本整理,不做问答、不做任务执行

## 架构概览

当前活跃实现是 Tauri 2(`openless-all/app/`)。自动更新走 Tauri updater 插件;CI 在每次 `v*-tauri` tag 自动签名 updater artifact + manifest。
当前活跃实现是 Tauri 2(`openless-all/app/`)。Release 分两条渠道:**正式版**(`v<v>-tauri` tag,自动推送给所有用户)和 **Beta**(`v<v>-beta-tauri` tag,GitHub 标 pre-release,由 opt-in 用户手动下载)。CI 在每次 release tag 都签名 updater artifact + manifest。

**Tauri 后端(Rust)** — 各模块只依赖 `types.rs`:

Expand Down Expand Up @@ -326,10 +331,31 @@ commands.rs Tauri IPC 接口

## 维护者:发布检查

- 同步更新 `openless-all/app/package.json`、`src-tauri/tauri.conf.json`、`src-tauri/Cargo.toml` 中的版本号。
OpenLess 走两条 release 渠道,分支名 = 渠道名(详见 [贡献流程](#贡献流程))。

### 通用准备(两条渠道都要做)

- 同步更新**全部 5 处**版本号:`package.json`、`package-lock.json`(root + `packages.""` 嵌套项)、`src-tauri/tauri.conf.json`、`src-tauri/Cargo.toml`、`Cargo.lock`(找 `name = "openless"` 的那段)。CI 的 `Verify version sync` 步骤会拦截不同步的版本号。
- 运行 `INSTALL=0 ./scripts/build-mac.sh`,确认 `.app` 可启动。
- 在干净 macOS 机器上验证:权限引导、快捷键、录音、ASR、润色、插入、剪贴板兜底。
- 推送 `v<version>-tauri` tag → CI 构建并签名 updater artifact + macOS `.dmg` + Windows `.msi`。需要 repo secret `TAURI_SIGNING_PRIVATE_KEY`(对应 `tauri.conf.json` 中的 pubkey)才能签名 updater 包。
- 在干净机器上跑冒烟:权限引导、快捷键、录音、ASR、润色、插入、剪贴板兜底。
- 确认 repo 已配置 `TAURI_SIGNING_PRIVATE_KEY`,macOS 还需 Apple 签名/公证 secrets。

### Beta 渠道 — `v<v>-beta-tauri`

1. 通过 PR review 把改动落到 `beta` 分支。
2. **在 `beta` 上**打 tag:`git tag v<v>-beta-tauri && git push origin v<v>-beta-tauri`。
3. CI 把 GitHub Release 标为 `Pre-release`,只上传 `latest-{tgt}-{arch}-beta.json` updater manifest;正式版用户的 `releases/latest` 重定向不受影响。
4. 在合适的频道(issue 帖子、QQ 群)通知 opt-in Beta 用户:可以从 设置 → 关于 → 加入 Beta 渠道 拿到最新版本下载入口。

### 正式版渠道 — `v<v>-tauri`

1. Beta 经过足够时间 soak(或直接做最终的双端冒烟)后把 `beta` 合到 `main`。
2. **在 `main` 上**打 tag:`git tag v<v>-tauri && git push origin v<v>-tauri`。
3. CI 发布常规 GitHub Release 并上传 `latest-{tgt}-{arch}.json`(不带 `-beta` 后缀)。所有正式版用户通过应用内 updater 收到此版本。

### 发版后验证(每次必跑)

走 [`CLAUDE.md` → Branch & release-channel workflow → Channel distribution](CLAUDE.md) 里的 5 步 checklist:页面状态(pre-release 标记)、资产文件名按渠道正确、正式版用户流、Beta opt-in 流、原始 endpoint 抽查。

## 致谢

Expand Down
Loading
Loading