Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
6a58890
refactor(linux): move shared runtime assets out of Tauri
aeoform Sep 4, 2026
759d188
feat(linux-egui): adopt redesigned native shell
aeoform Sep 4, 2026
94ff585
feat(linux-egui): add typed popup process bridge
aeoform Sep 5, 2026
33128ec
feat(linux-egui): add native desktop services
aeoform Sep 5, 2026
deb18d4
feat(linux-egui): add verified AppImage replacement
aeoform Sep 5, 2026
84bd210
feat(linux-egui): render native popup processes
aeoform Sep 5, 2026
c61188c
feat(linux-egui): bind vocabulary corrections and styles
aeoform Sep 5, 2026
d631bfd
feat(linux-egui): add real history actions
aeoform Sep 5, 2026
1d029a6
feat(linux-egui): wire native popup supervisors
aeoform Sep 5, 2026
239c63a
feat(linux-egui): bind marketplace and device login
aeoform Sep 5, 2026
685ca63
fix(linux-egui): do not overreport update capability
aeoform Sep 5, 2026
09fb8ff
feat(linux-egui): honor minimized startup
aeoform Sep 5, 2026
91cfbc2
test(linux-egui): lock Tauri-free parity contracts
aeoform Sep 5, 2026
baf0234
fix(core): keep provider URL validation clippy-clean
aeoform Sep 5, 2026
fedfabd
refactor(linux-egui): keep view data clippy-clean
aeoform Sep 5, 2026
43ca9c2
feat(settings): transact launch at login effects
aeoform Sep 5, 2026
5f5dc83
feat(linux-egui): complete verified AppImage updater
aeoform Sep 5, 2026
f635084
feat(linux-egui): restore native desktop lifecycle
aeoform Sep 5, 2026
a940d19
feat(linux-egui): restore history audio workflows
aeoform Sep 5, 2026
4139c7c
feat(linux-egui): complete local model controls
aeoform Sep 5, 2026
07dbf6e
feat(linux-egui): add native style pack editor
aeoform Sep 5, 2026
83d090b
feat(linux-egui): complete marketplace workflows
aeoform Sep 5, 2026
234cc06
feat(linux-egui): merge dirty settings on revision conflict
aeoform Sep 5, 2026
8e95ce5
feat(linux-egui): complete fcitx5 desktop hotkeys
aeoform Sep 5, 2026
2f12d88
feat(linux-egui): harden and restore native popups
aeoform Sep 5, 2026
9af7c3b
feat(linux-egui): bind shared vocabulary presets
aeoform Sep 5, 2026
f932b0e
feat(linux-egui): edit style hotkeys without blocking frames
aeoform Sep 5, 2026
23b04f6
feat(linux-egui): persist and export diagnostic logs
aeoform Sep 5, 2026
6f8672f
feat(linux-egui): edit all fcitx5 hotkeys
aeoform Sep 5, 2026
a779417
fix(linux-egui): follow terminal capture cancellation
aeoform Sep 5, 2026
eb1121f
docs(linux-egui): record parity and device limits
aeoform Sep 5, 2026
e95e365
docs(linux-egui): track latest 1019 head
aeoform Sep 6, 2026
b594a60
fix(linux-egui): reload fcitx5 after addon update
aeoform Sep 6, 2026
aedd7de
feat(linux-egui): restore recording and appearance settings
aeoform Sep 6, 2026
a30f7ed
feat(linux-egui): restore Core-backed overview
aeoform Sep 6, 2026
051553b
feat(linux-egui): restore native recording feedback
aeoform Sep 6, 2026
d241b1e
ci(linux-egui): lock release artifacts and checksums
aeoform Sep 6, 2026
d14a304
feat(linux-egui): localize native interface
aeoform Sep 6, 2026
649e60f
fix(linux-egui): tolerate legacy fcitx addon methods
aeoform Sep 6, 2026
3c2ced4
[pi] Work in progress
aeoform Sep 7, 2026
2f2eb63
[pi] Work in progress
aeoform Sep 7, 2026
213e547
[pi] All tasks are complete. Here's a summary of what w...
aeoform Sep 7, 2026
1b58a39
fix(linux-egui): preserve original frontend content geometry
aeoform Sep 7, 2026
40980c1
Merge branch 'beta' into feat/linux-egui-tauri-parity
aeoform Sep 11, 2026
4ad4b41
fix(linux-egui): align beta contracts and slim release
aeoform Sep 11, 2026
b4d29a6
ci: make Linux PR and release gates reliable
aeoform Sep 11, 2026
7e93d47
fix(linux): remove dead frontend artifacts and restore autostart
aeoform Sep 11, 2026
b2a306f
fix(settings): handle launch-at-login in Tauri restore
aeoform Sep 11, 2026
2724e96
fix(ci): follow relocated Linux remote input assets
aeoform Sep 11, 2026
72c5927
fix(ci): normalize Windows contract paths
aeoform Sep 11, 2026
4987d7a
refactor(linux): rely on packaged fcitx addon
aeoform Sep 11, 2026
92ab5b4
feat(linux): add audio backend fallback and fcitx clipboard
aeoform Sep 11, 2026
73a76c2
ci(linux): use native deb and rpm packaging
aeoform Sep 11, 2026
fcebd1d
fix(linux): bound addon reload and use app version
aeoform Sep 11, 2026
04c97af
fix(linux-egui): restore input and CI build
aeoform Sep 11, 2026
fbde862
fix(linux-egui): align window input regions
aeoform Sep 11, 2026
0303488
fix(linux-egui): restore frontend input routing
aeoform Sep 11, 2026
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
45 changes: 41 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ name: CI
# 多平台跨语言质量门禁。release-tauri.yml 是发版流水线(仅打包构建),这里负责
# 在合并前快速验证两件事:
# 1. 全部前端/契约测试与 vite bundle 通过(捕获行为、合同及跨 locale 类型 drift)
# 2. Tauri 后端在 macOS / Windows、移动端在 Android 编译;Linux 只测试共享 core。
# 2. Tauri 后端在 macOS / Windows、移动端在 Android 编译;Linux 同时验证共享 Core
# 与独立 egui host,且不拉取本地 Qwen ASR 或 Tauri/WebKit 依赖。
# 跑 build-mac.sh / windows-package-msvc.ps1 / Tauri bundle 太重;只跑轻量 cargo check + vite build。

on:
Expand Down Expand Up @@ -166,8 +167,10 @@ jobs:
--no-daemon

linux-core-contract:
name: Linux core tests
runs-on: ubuntu-22.04
name: Linux core and egui host tests
# cpal's PipeWire backend needs the PipeWire/libspa ABI shipped by 24.04.
# 22.04 headers fail to compile libspa 0.10.x (missing spa_meta_first etc.).
runs-on: ubuntu-24.04
defaults:
run:
working-directory: openless-all/app
Expand All @@ -182,8 +185,42 @@ jobs:
with:
workspaces: 'openless-all/app -> target'

- name: Install Linux egui build dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
build-essential \
libasound2-dev \
libdbus-1-dev \
libpipewire-0.3-dev \
libssl-dev \
libwayland-dev \
libx11-dev \
libxkbcommon-dev \
pkg-config \
ripgrep

- name: Test Core
run: cargo test --locked -p openless-core
run: |
cargo test --locked -p openless-core
cargo clippy --locked -p openless-core --all-targets

- name: Test Linux egui host without local Qwen ASR
run: |
cargo test --locked -p openless-linux-egui --all-targets
cargo check --locked -p openless-linux-egui --all-targets
node scripts/linux-egui-tauri-free-contract.test.mjs
node scripts/linux-egui-release-contract.test.mjs

- name: Enforce Linux dependency boundaries
shell: pwsh
run: |
./scripts/check-core-deps.ps1
./scripts/check-core-deps.ps1 openless-linux-egui
./scripts/check-core-secret-surface.ps1
./scripts/check-core-test-isolation.ps1
./scripts/check-core-runtime-seam.ps1
./scripts/check-linux-public-surface.ps1

- name: Test remote input TLS identity and Unix key permissions
run: cargo test --locked --manifest-path src-tauri/backend-tests/Cargo.toml --test remote_tls
Expand Down
115 changes: 21 additions & 94 deletions .github/workflows/release-linux-egui.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ on:
release_tag:
required: false
type: string
secrets:
LINUX_EGUI_MINISIGN_SECRET_KEY:
required: false

permissions:
contents: write
Expand All @@ -41,38 +38,21 @@ jobs:
build-essential \
cmake \
desktop-file-utils \
extra-cmake-modules \
fcitx5-modules-dev \
file \
fuse \
libasound2-dev \
libdbus-1-dev \
libpipewire-0.3-dev \
libpulse-dev \
libfcitx5config-dev \
libfcitx5core-dev \
libfcitx5utils-dev \
libopenblas-dev \
libssl-dev \
libwayland-dev \
libx11-dev \
libxkbcommon-dev \
patchelf \
pkg-config \
ripgrep \
rpm \
ruby-dev \
wget
sudo gem install --no-document fpm -v 1.16.0

- name: Install appimagetool
env:
APPIMAGETOOL_SHA256: b90f4a8b18967545fda78a445b27680a1642f1ef9488ced28b65398f2be7add2
run: |
wget -q https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage \
-O /tmp/appimagetool
echo "$APPIMAGETOOL_SHA256 /tmp/appimagetool" | sha256sum --check --strict
chmod +x /tmp/appimagetool
sudo install -m 0755 /tmp/appimagetool /usr/local/bin/appimagetool
file /usr/local/bin/appimagetool | grep -q ELF

- uses: dtolnay/rust-toolchain@stable
with:
Expand All @@ -92,24 +72,15 @@ jobs:
test -s build/libopenless.so
test -s build/openless.conf

- name: Checkout pinned Qwen ASR runtime source
run: git submodule update --init --depth 1 -- openless-all/app/src-tauri/vendor/qwen-asr

- name: Build portable Qwen ASR runtime
working-directory: openless-all/app/src-tauri/vendor/qwen-asr
run: |
make blas CFLAGS_BASE="-Wall -Wextra -O3 -ffast-math -mtune=generic"
test -x qwen_asr
./qwen_asr --help >/dev/null 2>&1

- name: Verify framework-independent Linux contract
working-directory: openless-all/app
shell: pwsh
run: |
cargo test --locked -p openless-core
cargo clippy --locked -p openless-core --all-targets -- -D warnings
cargo clippy --locked -p openless-core --all-targets
cargo test --locked -p openless-linux-egui --all-targets
cargo check --locked -p openless-linux-egui --all-targets
node scripts/linux-egui-tauri-free-contract.test.mjs
node scripts/linux-egui-release-contract.test.mjs
./scripts/check-core-deps.ps1
./scripts/check-core-deps.ps1 openless-linux-egui
./scripts/check-core-secret-surface.ps1
Expand All @@ -127,9 +98,9 @@ jobs:
exit 0
fi
case "$RELEASE_TAG" in
v*-tauri) ;;
v*) ;;
*)
echo "::error::release_tag must name an existing v*-tauri release"
echo "::error::release_tag must name an existing v* release"
exit 1
;;
esac
Expand Down Expand Up @@ -157,17 +128,16 @@ jobs:
VERSION=${RELEASE_TAG#v}
VERSION=${VERSION%-tauri}
else
VERSION=$(sed -n 's/^version = "\([^"]*\)"/\1/p' src-tauri/Cargo.toml | head -1)
VERSION=$(node -p "require('./package.json').version")
fi
test -n "$VERSION"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"

- name: Package deb, rpm, and AppImage without Tauri
- name: Package deb and rpm without Tauri
working-directory: openless-all/app
env:
OPENLESS_LINUX_VERSION: ${{ steps.version.outputs.version }}
OPENLESS_LINUX_ARCH: x86_64
APPIMAGE_EXTRACT_AND_RUN: '1'
run: bash scripts/package-linux-egui.sh

- name: Verify package contents and ELF dependencies
Expand All @@ -176,74 +146,31 @@ jobs:
OUTPUT=target/linux-egui-packages
test "$(find "$OUTPUT" -maxdepth 1 -name '*.deb' | wc -l)" -eq 1
test "$(find "$OUTPUT" -maxdepth 1 -name '*.rpm' | wc -l)" -eq 1
test "$(find "$OUTPUT" -maxdepth 1 -name '*.AppImage' | wc -l)" -eq 1
test "$(find "$OUTPUT" -maxdepth 1 -name '*.AppImage' | wc -l)" -eq 0
! ldd target/release/openless-linux-egui | grep -q 'not found'
! ldd target/release/openless-linux-egui | grep -Eqi 'webkit|wry|tauri'
! ldd src-tauri/vendor/qwen-asr/qwen_asr | grep -q 'not found'
dpkg-deb -c "$OUTPUT"/*.deb | grep -q 'usr/bin/openless'
dpkg-deb -c "$OUTPUT"/*.deb | grep -q 'fcitx5/libopenless.so'
dpkg-deb -c "$OUTPUT"/*.deb | grep -q 'usr/lib/openless/resources/qwen-asr/qwen_asr'
rpm -qlp "$OUTPUT"/*.rpm | grep -q '/usr/bin/openless'
rpm -qlp "$OUTPUT"/*.rpm | grep -q '/usr/lib64/fcitx5/libopenless.so'
rpm -qlp "$OUTPUT"/*.rpm | grep -q '/usr/lib/openless/resources/qwen-asr/qwen_asr'
"$OUTPUT"/*.AppImage --appimage-extract >/dev/null
test -x squashfs-root/usr/bin/openless
test -s squashfs-root/usr/lib/openless/resources/linux-fcitx5-plugin/libopenless.so
test -x squashfs-root/usr/lib/openless/resources/qwen-asr/qwen_asr
! ldd squashfs-root/usr/lib/openless/resources/qwen-asr/qwen_asr | grep -q 'not found'
squashfs-root/usr/lib/openless/resources/qwen-asr/qwen_asr --help >/dev/null 2>&1
rm -rf squashfs-root

- name: Sign AppImage and write independent updater manifest
- name: Generate release checksums
working-directory: openless-all/app
env:
MINISIGN_SECRET: ${{ secrets.LINUX_EGUI_MINISIGN_SECRET_KEY }}
VERSION: ${{ steps.version.outputs.version }}
run: |
set -euo pipefail
OUTPUT=target/linux-egui-packages
APPIMAGE=$(find "$OUTPUT" -maxdepth 1 -name '*.AppImage' -print -quit)
if [ -n "${RELEASE_TAG:-}" ] && [ -z "${MINISIGN_SECRET:-}" ]; then
echo "::error::LINUX_EGUI_MINISIGN_SECRET_KEY is required for release upload"
exit 1
fi
if [ -n "${MINISIGN_SECRET:-}" ]; then
if ! command -v minisign >/dev/null 2>&1; then
cargo install --locked --version 0.9.1 minisign
fi
printf '%s' "$MINISIGN_SECRET" > "$RUNNER_TEMP/linux-egui.minisign.key"
minisign -S -s "$RUNNER_TEMP/linux-egui.minisign.key" -m "$APPIMAGE" -x "$APPIMAGE.minisig"
fi
SHA256=$(sha256sum "$APPIMAGE" | cut -d' ' -f1)
ASSET=$(basename "$APPIMAGE")
RELEASE_REPOSITORY="${GITHUB_REPOSITORY:-Open-Less/openless}"
SIGNATURE=null
if [ -f "$APPIMAGE.minisig" ]; then
SIGNATURE=$(base64 -w0 "$APPIMAGE.minisig" | jq -R .)
fi
jq -n \
--arg version "$VERSION" \
--arg url "https://github.com/$RELEASE_REPOSITORY/releases/download/${RELEASE_TAG:-manual}/$ASSET" \
--arg sha256 "$SHA256" \
--argjson signature "$SIGNATURE" \
'{schemaVersion:1, host:"linux-egui", arch:"x86_64", version:$version, url:$url, sha256:$sha256, minisign:$signature}' \
> "$OUTPUT/latest-linux-egui-x86_64.json"
test "$(find "$OUTPUT" -maxdepth 1 -name '*.deb' | wc -l)" -eq 1
test "$(find "$OUTPUT" -maxdepth 1 -name '*.rpm' | wc -l)" -eq 1
test "$(find "$OUTPUT" -maxdepth 1 -name '*.AppImage' | wc -l)" -eq 0
( cd "$OUTPUT" && sha256sum ./*.deb ./*.rpm > SHA256SUMS )
test "$(wc -l < "$OUTPUT/SHA256SUMS")" -eq 2
cat "$OUTPUT/SHA256SUMS"

- name: Verify Linux updater manifest matches the artifact
- name: Verify Linux release checksums
working-directory: openless-all/app
run: |
set -euo pipefail
OUTPUT=target/linux-egui-packages
APPIMAGE=$(find "$OUTPUT" -maxdepth 1 -name '*.AppImage' -print -quit)
MANIFEST="$OUTPUT/latest-linux-egui-x86_64.json"
test -s "$APPIMAGE"
test -s "$MANIFEST"
SHA256=$(sha256sum "$APPIMAGE" | cut -d' ' -f1)
RELEASE_REPOSITORY="${GITHUB_REPOSITORY:-Open-Less/openless}"
jq -e \
--arg sha256 "$SHA256" \
--arg repository "$RELEASE_REPOSITORY" \
'.schemaVersion == 1 and .host == "linux-egui" and .arch == "x86_64" and .sha256 == $sha256 and (.url | startswith("https://github.com/" + $repository + "/releases/download/"))' \
"$MANIFEST" >/dev/null
test -s target/linux-egui-packages/SHA256SUMS
( cd target/linux-egui-packages && sha256sum -c SHA256SUMS >/dev/null )

- name: Upload Linux egui workflow artifacts
uses: actions/upload-artifact@v4
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ video-materials/
node_modules/
dist/
target/
# Explicitly ignore local cache symlinks as well as generated target directories.
/openless-all/app/target
/openless-all/egui-frontend/target
# 本地从 CI / Actions 下载的 APK 及解压目录(非源码)
ci-artifacts/
*.apk
Expand Down
4 changes: 2 additions & 2 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[submodule "openless-all/app/src-tauri/vendor/qwen-asr"]
path = openless-all/app/src-tauri/vendor/qwen-asr
[submodule "openless-all/app/vendor/qwen-asr"]
path = openless-all/app/vendor/qwen-asr
url = https://github.com/Open-Less/qwen-asr.git
[submodule "openless-all/app/src-tauri/vendor/qwen3-asr-rs"]
path = openless-all/app/src-tauri/vendor/qwen3-asr-rs
Expand Down
82 changes: 82 additions & 0 deletions docs/linux-egui-tauri2-parity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Linux egui / Tauri 2 parity tracker

This local stack is based on PR #1019 head `5f668b1d`. Linux ships one
`openless-linux-egui` executable and does not link Tauri, Wry, or WebKitGTK.
Windows, macOS, and Android remain on Tauri.

## Implemented on the native Core 2.0 path

- eframe/egui 0.33.3 shell, system CJK fallback, single instance, minimized
startup, close-to-tray, safe shutdown, XDG autostart, notifications, external
URLs, file dialogs, file logging, and diagnostic-log export
- fcitx5 dictation, QA, selection polish, translation, style switching, main
window, style-pack direct, and applicable Coding Agent hotkeys; settings use
strict collision checks and survive fcitx5 restart
- CPAL recording with canonical WAV archives, Core retention policy, history
playback/export and failed-session retranscription
- Native `mute_during_recording` (PipeWire `wpctl`/PulseAudio `pactl`) with
guaranteed sink restore by RAII across stop/cancel/error/drop/shutdown
- Native recording start/stop audio cues synthesized to the default sink on a
worker thread (never blocking the egui frame), gated by `audio_cue_on_record`
and muted-aware (`audio_mute`/`audio_cue` modules)
- Core-backed history search/delete/clear/copy/re-polish/retranscribe, vocabulary,
pending corrections, shared vocabulary presets, correction rules, and complete
style-pack CRUD/reset/prompt diagnostics/ZIP import-export/direct hotkeys
- provider channel CRUD/order/enable/activation, credential metadata, endpoint,
model listing, validation, and revision-aware settings conflict merging
- Qwen catalog/download/cancel/delete/activate/prepare/preload/release/test and
progress; Remote Input TLS service, URLs, PIN, locale, connection count, and
error events
- Marketplace list/detail/install/download/upload/update/delete, likes, authored
packs, GitHub device flow, polling, cancel, and logout
- dictation, QA text/voice, selection polish preview/confirm/cancel/revert, and
Less Computer text/voice/stream/tool approval/cancel through high-level Core APIs
- #997 native QA/selection/capsule popup design: same-executable re-entry,
versioned serde JSONL, Markdown, drag/Esc, QA microphone and Enter submit,
session/sequence/kind guards, nonblocking pipes, crash restart, and snapshot replay
- stable/beta AppImage checks, delayed/hourly/manual scheduling, byte progress,
SHA-256 and pinned minisign verification, same-directory fsync and atomic replace;
deb/rpm use the release page
- Remote Input assets, Qwen vendor files, shared icons, version parsing, packaging,
and release workflow are independent of `src-tauri`

## Automated evidence

- `cargo test -p openless-core --locked`
- `cargo test -p openless-linux-egui --locked`
- `cargo clippy --locked -p openless-linux-egui --all-targets -- -D warnings`
- PR #1019 Core/public-surface/dependency contract scripts
- fcitx5 C++ build plus `input_target_contract`
- `cargo tree` and release ELF `ldd` checks for Tauri/Wry/WebKitGTK
- Linux Tauri-free source, packaging, workflow, production-mock, capability, popup,
settings-conflict, updater-validation, lifecycle, and staged-file gates

## Deliberate limits and device evidence still required

- Linux supports fcitx5 only; there is no IBus or global-hotkey fallback.
- Selection Voice remains hidden because the Linux production target/intent adapter
is not implemented. It must not be advertised through capabilities. Selection
polish and QA remain available.
- Generic Qwen is the Linux local runtime. Windows Foundry and Apple MLX are not
Linux parity requirements.
- Foreground-application identity and native post-insertion edit observation
are explicit `Unsupported` on Linux: fcitx5 exposes surrounding text but no
reliable app/control identity across X11/Wayland, PRIMARY cannot prove an
original selection, and there is deliberately no IBus/global-hotkey fallback.
The factory keeps Core's Noop HostContext/EditObservation adapters (reporting
`source_app = None`) instead of faking edits. These are L02/L03 follow-ups,
not simulated UI data or false capabilities.
- Mute/restore and start/stop cues are implemented (`audio_mute`/`audio_cue`),
but the cpal cue-playback and real sink mute/restore paths still require
X11/Wayland device runs before being recorded as verified; a Linux output
stream is a PipeWire/KDE sink-input, so device evidence must confirm the cue
does not disturb the session or trigger unexpected volume OSD.
- X11 and Wayland device runs are still required for focus, Unicode insertion,
popup positioning, tray, fcitx5 reload/rebind, microphone unplug/recovery,
Secret Service, real phone Remote Input, real Qwen inference, and signed
AppImage install/rollback. Ignored hardware tests or a green build are not
recorded as device proof.

The automated scope is complete only when every command above passes at the
current PR #1019 head. The device-only rows remain “implemented, awaiting device
evidence” or “explicit unsupported” and must not be described as verified.
Loading
Loading