Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
6260f95
docs: prune superseded artifacts and fix doc links
Sep 26, 2026
0ef216e
style: standardize code comments to English
Sep 26, 2026
ea8e247
feat(settings): expose pipeline mode and preserve inactive channel ac…
Sep 29, 2026
34b9c6e
fix(asr): preserve saved channel priority on startup (#1123)
Sep 29, 2026
07fcbf3
feat(providers): add optional API Route polish channel (#1124)
Sep 29, 2026
68e2654
fix(omni): correct DashScope audio payload and validation hints (#1121)
Sep 29, 2026
552ade5
feat(selection-voice): compose drafts and settle capsule lifecycle (#…
Sep 29, 2026
a1e59fb
fix(voice): use selected multimodal pipeline for auxiliary capture (#…
Sep 29, 2026
a6d5b2e
feat(android): integrate IME keyboard and recoverable runtime (#1067)
Sep 29, 2026
1abb734
chore(release): prepare Beta 4 version and current feature documentation
Sep 29, 2026
dd95ed7
style: format integrated Core and Tauri sources
Sep 29, 2026
1c86a1b
docs: link public contribution rules from the readmes
Sep 29, 2026
9b10cb8
docs(release): align Beta guidance and fix Clippy doc formatting
Sep 29, 2026
4fe6159
fix(startup): show progress while native UI preferences initialize
Sep 29, 2026
f1d8a5f
fix(settings): disable model pages outside the selected pipeline
Sep 29, 2026
dc856d4
fix(ui): make transitions interruptible and add cloud motion regressi…
Sep 29, 2026
6dc45ce
fix(sync-ui): keep automatic prompt revocation immediate
Sep 29, 2026
5a8b8d9
test(ui): await animation layer cleanup in WebKit
Sep 29, 2026
ca2a797
fix(settings): fit multimodal controls and consolidate macOS authoriz…
Sep 30, 2026
e48a5cc
fix(ui): complete service settings motion and loading feedback
Sep 30, 2026
948f9ba
fix(ui): make settings entrance visible from its first frame
Sep 30, 2026
98299b7
fix(ui): start settings motion after the initial paint
Sep 30, 2026
497fcae
test(ui): sample entrance progress independently of frame rate
Sep 30, 2026
13838a1
test(ui): observe the complete settings entrance timeline
Sep 30, 2026
de36cd2
feat(vocab): integrate confirmation-based edit learning (#1128)
Sep 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
31 changes: 28 additions & 3 deletions .github/workflows/android-apk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ name: Android APK (debug)
# - push v*-tauri tag → signed release APK + minisign + updater manifest → GitHub Release
# - workflow_dispatch → signed release APK when ANDROID_KEYSTORE_* secrets exist
# (overlay install + user data preserved); otherwise unsigned debug APK (annotated, non-blocking)
# - workflow_dispatch + signed_debug=true — debug APK signed with the release
# keystore (when secrets exist); artifact remains openless-android-debug-*.
#
# #1103 build-time changes:
# - Do not wipe Cargo/Gradle/target before cache post-save (removed Free disk step).
Expand All @@ -26,8 +28,12 @@ on:
description: 'Dispatch-only fast release profile (no LTO, codegen-units=16). Ignored on tags.'
type: boolean
default: false
signed_debug:
description: 'Build debug mode but sign it with the release keystore (requires ANDROID_KEYSTORE_* secrets).'
type: boolean
default: false

# 同一 tag 重复推送只跑最新一次;workflow_dispatch 用 run_id 隔离避免互相取消。
# Repeated pushes of the same tag run only the newest; workflow_dispatch isolates by run_id to avoid mutual cancellation.
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.ref }}
cancel-in-progress: ${{ github.event_name == 'push' }}
Expand Down Expand Up @@ -62,6 +68,7 @@ jobs:
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
INPUT_SIGNED_DEBUG: ${{ github.event.inputs.signed_debug }}
run: |
set -euo pipefail
is_tag=false
Expand Down Expand Up @@ -89,6 +96,11 @@ jobs:
echo "label=${{ github.ref_name }}" >> "$GITHUB_OUTPUT"
echo "signing=tag-release" >> "$GITHUB_OUTPUT"
echo "signing_label=Tag release with release keystore" >> "$GITHUB_OUTPUT"
elif [[ "${INPUT_SIGNED_DEBUG:-false}" == "true" ]] && $has_keystore; then
echo "mode=debug" >> "$GITHUB_OUTPUT"
echo "label=signed-debug-run-${{ github.run_number }}" >> "$GITHUB_OUTPUT"
echo "signing=signed-debug" >> "$GITHUB_OUTPUT"
echo "signing_label=Manual dispatch debug APK signed with release keystore" >> "$GITHUB_OUTPUT"
elif $has_keystore; then
echo "mode=release" >> "$GITHUB_OUTPUT"
echo "label=dispatch-run-${{ github.run_number }}" >> "$GITHUB_OUTPUT"
Expand All @@ -100,6 +112,9 @@ jobs:
echo "signing=debug-fallback" >> "$GITHUB_OUTPUT"
echo "signing_label=Unsigned debug fallback (no keystore secrets)" >> "$GITHUB_OUTPUT"
echo "::notice title=Unsigned debug APK::ANDROID_KEYSTORE_* secrets not configured. Building debug APK without release signing. Overlay install and user data preservation require the release keystore; uninstall before installing if replacing a signed build."
if [[ "${INPUT_SIGNED_DEBUG:-false}" == "true" ]]; then
echo "::warning::signed_debug was requested but Android keystore secrets are incomplete; falling back to an unsigned debug APK."
fi
fi
- name: Resolve ABI matrix
Expand Down Expand Up @@ -146,7 +161,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
# Android 不使用本地 Qwen3/Whisper C 子模块。
# Android does not use the local Qwen3/Whisper C submodules.
submodules: false

- name: Disable macOS-only Qwen3 MLX dependency
Expand Down Expand Up @@ -286,13 +301,15 @@ jobs:
run: node scripts/merge-android-updater-manifest.mjs

- name: Configure Android release signing
if: needs.plan.outputs.mode == 'release'
if: needs.plan.outputs.mode == 'release' || needs.plan.outputs.signing == 'signed-debug'
working-directory: openless-all/app
env:
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
# signed_debug: also wire debug buildType to the release keystore
OPENLESS_SIGN_DEBUG: ${{ needs.plan.outputs.signing == 'signed-debug' && 'true' || 'false' }}
run: node scripts/configure-android-release-signing.mjs

- name: Prime Gradle wrapper
Expand All @@ -313,6 +330,10 @@ jobs:
- name: Build Android debug APK
if: needs.plan.outputs.mode == 'debug'
working-directory: openless-all/app
env:
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
run: npm run tauri:android:build:debug

- name: Build Android release APK
Expand Down Expand Up @@ -378,6 +399,10 @@ jobs:
cat >> "$GITHUB_STEP_SUMMARY" << 'EOF'
> **Signed release APK** (manual dispatch). Same keystore as tag releases — supports overlay install and preserves user data when upgrading from an existing signed install.
EOF
elif [ "${{ needs.plan.outputs.signing }}" = "signed-debug" ]; then
cat >> "$GITHUB_STEP_SUMMARY" << 'EOF'
> **Signed debug APK** (manual dispatch). Debug mode is retained for diagnostics; the release keystore is used so `adb install -r` can upgrade the matching signed app without changing the debug artifact name.
EOF
fi
publish-android-release:
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/check-linux-egui.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
name: Linux egui build and backend checks

# Only the CI and tag-release workflows call this deb/rpm build and verification path.
# CI and independent manual Linux builds share this deb/rpm verification path.
# No standalone push trigger: a Linux PR must show one combined Linux check.
on:
workflow_call:
inputs:
# 'none' skips a change set that is only prose (see
# scripts/ci-changed-areas.sh): the callers that must always build the
# packages, including the tag release, leave the default in place.
# packages, including accepted tag builds, leave the default in place.
scope:
required: false
type: string
Expand All @@ -27,7 +27,7 @@ jobs:
# A cold dependency tree costs ~18 minutes; anything past this is a hang.
timeout-minutes: 90
env:
RELEASE_TAG: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') && endsWith(github.ref, '-tauri') && github.ref_name || '' }}
RELEASE_TAG: ${{ github.ref_type == 'tag' && startsWith(github.ref, 'refs/tags/v') && endsWith(github.ref, '-tauri') && github.ref_name || '' }}
# Debug info dominates both compile time and cache size for this host, and
# the gate below never reads it. The Linux job keeps its own cache so a
# red run still starts warm.
Expand Down
81 changes: 57 additions & 24 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ on:
options: [all, macos]
default: all

# 同一 PR 快速重复推送时取消旧运行;workflow_dispatch 用 run_id 隔离。
# Cancel old runs when the same PR is pushed repeatedly; workflow_dispatch is isolated by run_id.
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
Expand Down Expand Up @@ -56,6 +56,34 @@ jobs:
echo "$area=$( [ "$verdict" = 'false' ] && echo false || echo true )" >> "$GITHUB_OUTPUT"
done
ui-motion:
name: UI motion (Chromium + WebKit)
needs: [changes]
if: (github.event_name != 'workflow_dispatch' || inputs.platform != 'macos') && needs.changes.outputs.tauri != 'false'
runs-on: ubuntu-24.04
timeout-minutes: 20
defaults:
run:
working-directory: openless-all/app
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
cache-dependency-path: openless-all/app/package-lock.json
- run: npm ci
- run: npm run build
- run: npx playwright install --with-deps chromium webkit
- run: npm run test:ui-motion
- uses: actions/upload-artifact@v4
if: failure()
with:
name: ui-motion-failure
path: |
openless-all/app/test-results/
openless-all/app/playwright-report/
android-check:
needs: [changes]
if: (github.event_name != 'workflow_dispatch' || inputs.platform != 'macos') && needs.changes.outputs.tauri != 'false'
Expand All @@ -68,14 +96,15 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
# Android 不使用任何桌面本地 ASR 子模块;macOS MLX 依赖不进入此任务。
# Android uses no desktop local-ASR submodules; the macOS MLX dependency stays out of
# this job.
submodules: false

# Android 不编译任何本地 ASR C 代码:build.rs 按 TARGET 解析排除
# Android triple(不调 build_qwen_asr)。qwen3-asr-rs 是 path 依赖,
# Cargo 解析器跨所有 target 都要读它的 manifest——所以由下方的
# ci-disable-macos-qwen3.mjs 删掉该依赖行,否则 cargo check 硬失败。
# 去掉递归拉取省一次网络 fetch + 失败点。
# Android compiles no local-ASR C code: build.rs excludes the Android triple by TARGET
# (does not call build_qwen_asr). qwen3-asr-rs is a path dependency, and the Cargo
# resolver must read its manifest for all targets — so the ci-disable-macos-qwen3.mjs
# step below removes that dependency line, otherwise cargo check hard-fails.
# Dropping recursive fetch saves one network fetch and one failure point.
- name: Setup Android SDK + NDK
uses: android-actions/setup-android@v3
with:
Expand Down Expand Up @@ -229,15 +258,16 @@ jobs:
if: needs.changes.outputs.tauri != 'false'
name: ${{ matrix.label }} checks
strategy:
# 一个平台挂掉不阻塞其他平台拿到验证结果。
# One platform failing must not block other platforms from getting verification results.
fail-fast: false
matrix:
include: ${{ fromJSON(github.event_name == 'workflow_dispatch' && inputs.platform == 'macos' && '[{"os":"macos-latest","label":"macOS","preflight":false}]' || '[{"os":"macos-latest","label":"macOS","preflight":false},{"os":"windows-latest","label":"Windows","preflight":true}]') }}
runs-on: ${{ matrix.os }}
timeout-minutes: 60
env:
# 新增 shared Core 后,macOS 首次编译同时构建 MLX C++ 依赖和完整
# Tauri test binary;限制并发避免 arm64 runner 在峰值内存处被系统终止。
# With the shared Core in place, macOS's first build compiles the MLX C++ dependencies and
# the full Tauri test binary together; limit concurrency so the arm64 runner is not OOM-killed
# at peak memory.
CARGO_BUILD_JOBS: 2
CARGO_PROFILE_TEST_DEBUG: 0
CMAKE_BUILD_PARALLEL_LEVEL: 2
Expand All @@ -247,7 +277,7 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
# 只在 macOS 初始化 MLX 子模块;Windows 不解析该依赖。
# Initialize the MLX submodule on macOS only; Windows does not resolve that dependency.
submodules: ${{ matrix.os == 'macos-latest' && 'recursive' || 'false' }}
- name: Disable macOS-only Qwen3 MLX dependency
if: runner.os != 'macOS'
Expand All @@ -259,7 +289,7 @@ jobs:
cache: npm
cache-dependency-path: openless-all/app/package-lock.json

# macOS MSRV 在独立 job 并行检查;Windows 保留原有检查顺序。
# macOS MSRV is checked in a separate parallel job; Windows keeps the original check order.
- uses: dtolnay/rust-toolchain@1.88.0
if: runner.os == 'Windows'

Expand Down Expand Up @@ -343,22 +373,25 @@ jobs:
if: runner.os == 'Windows'
run: cargo check --locked --manifest-path src-tauri/Cargo.toml

# test 编译并运行 lib/bin,覆盖原 cargo check 的生产 binary 路径。
# 避免先 metadata-only check、再为同一依赖图做一次 codegen。
# test compiles and runs lib/bin, covering the production binary paths that plain cargo
# check misses. Avoids a metadata-only check followed by another codegen pass over the same
# dependency graph.
- name: Run Rust backend unit tests
if: runner.os != 'Windows'
run: cargo test --locked --manifest-path src-tauri/Cargo.toml --lib --bins --timings

- name: Compile Rust backend unit tests (Windows)
# Windows runner 能链接 lib test binary,但干净镜像缺少可选 native runtime
# DLL entrypoint 时,进程会在 test harness 启动前退出。这里保留 cfg/link
# 覆盖;共享 Core 的公开 compatibility contract 由下一步实际执行。
# A Windows runner can link the lib test binary, but on a clean image lacking an optional
# native runtime DLL entrypoint, the process exits before the test harness starts. Keep
# the cfg/link coverage here; the shared Core's public compatibility contract is actually
# executed in the next step.
if: runner.os == 'Windows'
run: cargo test --locked --manifest-path src-tauri/Cargo.toml --lib --no-run

- name: Run Rust-only backend unit tests (Windows)
# 独立 test crate 不链接完整 Tauri app lib,只验证公开 Core contract。
# Windows 专属 Tauri 测试在此 runner 只做上一步的 cfg/link 编译覆盖。
# The standalone test crate does not link the full Tauri app lib; it verifies the public
# Core contract only. Windows-specific Tauri tests on this runner only get the cfg/link
# compile coverage from the step above.
if: runner.os == 'Windows'
run: cargo test --locked --manifest-path src-tauri/backend-tests/Cargo.toml

Expand All @@ -371,18 +404,18 @@ jobs:
run: cargo +1.88.0 test --locked --manifest-path src-tauri/backend-tests/Cargo.toml --no-run

- name: Verify version sync across all 5 files
# 两个平台都跑这个校验:Windows runner 自带 git-bash,跨 shell 表现一致。
# 一旦版本号 drift 立刻 fail,避免发版时再发现漏改。
# 校验 5 处:package.json / package-lock.json (root + nested) /
# tauri.conf.json / Cargo.toml / Cargo.lock 的 [openless] 包。
# Runs on both platforms: the Windows runner ships git-bash, giving consistent behavior
# across shells. Fail immediately on version drift instead of discovering it at release.
# Checks 5 places: package.json / package-lock.json (root + nested) /
# tauri.conf.json / Cargo.toml / the [openless] package in Cargo.lock.
shell: bash
run: |
PKG=$(node -p "require('./package.json').version")
LOCK_ROOT=$(node -p "require('./package-lock.json').version")
LOCK_NESTED=$(node -p "require('./package-lock.json').packages[''].version")
TAU=$(node -p "require('./src-tauri/tauri.conf.json').version")
CRG=$(grep -E '^version = ' src-tauri/Cargo.toml | head -1 | sed -E 's/^version = "(.+)"$/\1/')
# Cargo.lock:找 [openless] 包紧跟的 version 行
# Cargo.lock: find the version line right after the [openless] package name
CARGO_LOCK_VER=$(awk 'BEGIN{found=0} /^name = "openless"$/{found=1; next} found && /^version = /{gsub(/"/,""); print $3; exit}' src-tauri/Cargo.lock)
echo "package.json = $PKG"
echo "package-lock root = $LOCK_ROOT"
Expand Down
40 changes: 3 additions & 37 deletions .github/workflows/release-linux-egui.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
name: Release Linux egui

# An admin pushes v*-tauri only after CI and Linux device acceptance. This
# workflow calls the exact same deb/rpm build used by CI, then attaches its
# verified assets to the shared Tauri/Android Release (Beta remains a draft).
# Independent Linux builds retain the same tests and deb/rpm packaging as CI.
# After product acceptance, an administrator builds the accepted release tag and
# explicitly attaches the verified Actions artifacts to that existing Release.
on:
push:
tags:
- 'v*-tauri'
workflow_dispatch:

permissions:
Expand All @@ -21,34 +18,3 @@ jobs:
permissions:
contents: read
uses: ./.github/workflows/check-linux-egui.yml

publish-linux-egui:
# Manual builds only upload Actions artifacts; they never change a Release.
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') && endsWith(github.ref, '-tauri')
needs: build-linux-egui
runs-on: ubuntu-24.04
timeout-minutes: 30
permissions:
contents: write
steps:
- uses: actions/download-artifact@v8
with:
name: openless-linux-egui-x86_64
path: linux-egui-packages

- name: Verify downloaded release assets
run: |
set -euo pipefail
test "$(find linux-egui-packages -maxdepth 1 -name '*.deb' | wc -l)" -eq 1
test "$(find linux-egui-packages -maxdepth 1 -name '*.rpm' | wc -l)" -eq 1
test "$(find linux-egui-packages -maxdepth 1 -name '*.AppImage' | wc -l)" -eq 0
( cd linux-egui-packages && sha256sum --check --strict SHA256SUMS )
- name: Attach Linux assets to the shared release draft
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
draft: ${{ endsWith(github.ref_name, '-beta-tauri') || contains(github.ref_name, '-Beta.') }}
prerelease: ${{ endsWith(github.ref_name, '-beta-tauri') || contains(github.ref_name, '-Beta.') }}
files: linux-egui-packages/*
fail_on_unmatched_files: true
Loading
Loading