Skip to content

experiment: Tauri v2 migration prototype#280

Merged
seonglae merged 440 commits into
mainfrom
experiment/tauri
May 14, 2026
Merged

experiment: Tauri v2 migration prototype#280
seonglae merged 440 commits into
mainfrom
experiment/tauri

Conversation

@seonglae
Copy link
Copy Markdown
Owner

@seonglae seonglae commented Feb 4, 2026

Summary

  • Prototype migration from electron-nuxt to Tauri v2
  • Rust backend with system WebView
  • 10-15x smaller bundle (~10-15MB vs ~150MB)

Why Tauri?

Aspect Electron (current) Tauri v2
Bundle size ~150MB ~10-15MB
Memory usage High Low
Runtime Chromium System WebView
Backend Node.js Rust
Auto-updater Yes Yes (built-in)

Trade-offs

Pros:

  • Dramatically smaller bundle (10-15x reduction)
  • Lower memory footprint
  • Faster startup
  • Better security (Rust, restrictive IPC)
  • Production-ready, active development

Cons:

  • Requires learning basic Rust for backend
  • WebView2 dependency on Windows (usually pre-installed)
  • Longer first build (Rust compilation)
  • Different mental model than Electron

AHK Integration

Full process control via Rust's Command API:

use std::process::Command;

#[tauri::command]
async fn spawn_ahk(app_handle: tauri::AppHandle) -> Result<String, String> {
    let ahk_path = get_ahk_path(&app_handle);
    match Command::new(&ahk_path).spawn() {
        Ok(child) => Ok(format!("PID: {}", child.id())),
        Err(e) => Err(e.to_string()),
    }
}

Verdict: FULLY COMPATIBLE - Can spawn, track PID, kill process cleanly.

Files Changed

  • src-tauri/Cargo.toml - Rust dependencies
  • src-tauri/tauri.conf.json - Tauri configuration
  • src-tauri/src/main.rs - Rust backend with AHK commands
  • src-vue/ - Vue 3 frontend
  • vite.config.ts - Vite configuration

Migration Path

  1. Set up Tauri project structure
  2. Learn basic Rust for backend commands (~50 lines)
  3. Port Vue components to pure Vue 3 (from Nuxt)
  4. Implement IPC for AHK control
  5. Test on Windows thoroughly

Recommendation

PRIMARY RECOMMENDATION - Best overall choice for:

  • Users who care about download size
  • Background apps that need low memory
  • Long-term maintainability

🤖 Generated with Claude Code

seonglae and others added 30 commits November 16, 2022 05:44
deps: Update all non-major dependencies
Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2.
- [Release notes](https://github.com/SamVerschueren/decode-uri-component/releases)
- [Commits](SamVerschueren/decode-uri-component@v0.2.0...v0.2.2)

---
updated-dependencies:
- dependency-name: decode-uri-component
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
…t-0.2.2

chore(deps): bump decode-uri-component from 0.2.0 to 0.2.2
deps: Update all non-major dependencies
deps: Update dependency @types/node to v18.11.18
Bumps [json5](https://github.com/json5/json5) from 1.0.1 to 1.0.2.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](json5/json5@v1.0.1...v1.0.2)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
deps: Update all non-major dependencies
deps: Update dependency electron to v20.3.10
deps: Update all non-major dependencies
Bumps [jszip](https://github.com/Stuk/jszip) from 3.7.1 to 3.10.1.
- [Release notes](https://github.com/Stuk/jszip/releases)
- [Changelog](https://github.com/Stuk/jszip/blob/main/CHANGES.md)
- [Commits](Stuk/jszip@v3.7.1...v3.10.1)

---
updated-dependencies:
- dependency-name: jszip
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
…3.10.1

chore(deps): bump jszip from 3.7.1 to 3.10.1
Bumps [http-cache-semantics](https://github.com/kornelski/http-cache-semantics) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/kornelski/http-cache-semantics/releases)
- [Commits](kornelski/http-cache-semantics@v4.1.0...v4.1.1)

---
updated-dependencies:
- dependency-name: http-cache-semantics
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
chore(deps): bump http-cache-semantics from 4.1.0 to 4.1.1
seonglae and others added 17 commits February 4, 2026 21:32
- Move resources/ahk to packages/ahk
- Add packages/ahk-builder for AHK compilation
- Add workspaces to package.json
- Update builder.config.js path
- Keep src/renderer and electron-nuxt intact

This is a minimal restructure that organizes AHK into a package
while maintaining full compatibility with the existing build system.

Co-authored-by: seonglae <[email protected]>
- Add electron-vite configuration
- Create TypeScript main process with AHK spawning
- Add preload script with IPC APIs
- Create Vue 3 + Vuetify renderer
- Document migration notes

Co-authored-by: seonglae <[email protected]>
Co-authored-by: Claude Opus 4.5 <[email protected]>
…278)

Implements macOS equivalent to Windows AHK input control:

- Mouse movement via Cmd+IJKL (smooth, accelerated)
- Mouse clicks via Cmd+U/O/M (left/right/middle)
- Scrolling via Alt+U/O and Cmd+H/P
- Text navigation via Alt+IJKL (word/line movement)
- Text selection shortcuts via Alt+W/A

Platform detection in Run.vue automatically chooses:
- Windows: AutoHotkey (existing)
- macOS: Hammerspoon (new)

Configuration mirrors AHK settings in const.ahk for consistency.
Installation scripts handle Hammerspoon setup via Homebrew.

Co-authored-by: seonglae <[email protected]>
Co-authored-by: Claude Opus 4.5 <[email protected]>
- Fix Hammerspoon check to not fail when not installed
- Add process plugin for exit functionality
- Fix dancing button size to 200x200 with centered play icon
- Fix transparent window and border-radius for rounded corners
- Add uppercase tabs and proper title styling
- Update capabilities with process permissions

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Restore title to original: "Intuit Manager" with text-uppercase
- Fix Exit button color (remove bg class, use color prop)
- Add Custom tab with Cmd+1-0 text fields like original
- Keep other tabs as "Developing" placeholder

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Fix dancing button to original 200x60 shape (not 200x200)
- Make "INTUIT" bold in title
- Option tab: startup/menubar/notifications switches
- Shortcut tab: keyboard shortcuts table
- Extension tab: mode list (text/mouse/window)
- Custom tab: Cmd+1-0 path fields

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Replace Vue with React
- Use Tailwind CSS utility classes (no custom CSS)
- Implement all tabs with center alignment
- Original dancing button (200x60)
- Title: bold INTUIT, light MANAGER

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add teal/cyan colors to dancing button
- Ensure html/body/root have transparent backgrounds
- Fix rounded corners

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add text-white to root container
- Fix header background color
- Remove custom button colors (use defaults)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add space between Intuit and Manager
- Remove bold from Intuit (keep light on Manager)
- Match original: "INTUIT MANAGER"

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Comment thread .github/workflows/ci.yaml
Comment on lines +15 to +43
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Set Github Token
shell: bash
run: echo "GH_TOKEN=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'

- name: Get Seonglae Key
shell: bash
run: |
echo "${{ secrets.PFX }}" | base64 --decode > Seonglae.pfx
echo "${{ secrets.ENV }}" > .env
- name: Install frontend dependencies
run: npm ci

- name: Install Node.js
uses: actions/setup-node@v2
with:
node-version: 14.x
- name: Build frontend
run: npm run build

- name: Cache node modules
uses: actions/cache@v2
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable

- name: Cache Rust
uses: Swatinem/rust-cache@v2
with:
path: |
**/node_modules
key: ${{ runner.OS }}-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.OS }}-build-
${{ runner.OS }}-
workspaces: src-tauri

- name: Install npm dependencies
run: yarn
- name: Check Rust
working-directory: src-tauri
run: cargo check

- name: Run build task
shell: bash
run: |
yarn build
ln build/Intuiter*.exe Intuiter.exe
build:
Comment thread .github/workflows/ci.yaml
Comment on lines +44 to +99
needs: test
strategy:
fail-fast: false
matrix:
include:
- platform: macos-latest
target: aarch64-apple-darwin
- platform: macos-latest
target: x86_64-apple-darwin
- platform: ubuntu-22.04
target: x86_64-unknown-linux-gnu
- platform: windows-latest
target: x86_64-pc-windows-msvc

validate:
runs-on: windows-latest
runs-on: ${{ matrix.platform }}
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
repository: microsoft/winget-pkgs
node-version: 20
cache: 'npm'

- name: Make Manifest
shell: bash
run: |
cd manifests/s/Seonglae/Intuiter
mkdir latest
cd latest
HASH=$(curl -sL https://github.com/seonglae/intuiter/releases/latest/download/Intuiter.exe | sha256sum | cut -c-64)

echo "PackageIdentifier: Seonglae.Intuiter" >> Seonglae.Intuiter.yaml
echo "Publisher: Seonglae" >> Seonglae.Intuiter.yaml
echo "PackageName: Intuiter" >> Seonglae.Intuiter.yaml
echo "PackageVersion: latest" >> Seonglae.Intuiter.yaml
echo "License: MIT" >> Seonglae.Intuiter.yaml
echo "InstallerType: exe" >> Seonglae.Intuiter.yaml
echo "InstallerSwitches:" >> Seonglae.Intuiter.yaml
echo " Silent: /S" >> Seonglae.Intuiter.yaml
echo " SilentWithProgress: /S" >> Seonglae.Intuiter.yaml
echo "Installers:" >> Seonglae.Intuiter.yaml
echo "- Architecture: x86" >> Seonglae.Intuiter.yaml
echo " InstallerUrl: https://github.com/seonglae/intuiter/releases/latest/download/Intuiter.exe" >> Seonglae.Intuiter.yaml
echo " InstallerSha256: ${HASH}" >> Seonglae.Intuiter.yaml
echo "ShortDescription: Seonglae.Intuiter" >> Seonglae.Intuiter.yaml
echo "PackageLocale: en-US" >> Seonglae.Intuiter.yaml
echo "ManifestType: singleton" >> Seonglae.Intuiter.yaml
echo "ManifestVersion: 1.0.0" >> Seonglae.Intuiter.yaml

- name: Install winget
shell: powershell
run: |
# Install NtObjectManager module
Install-Module NtObjectManager -Force
# Install winget
$vclibs = Invoke-WebRequest -Uri "https://store.rg-adguard.net/api/GetFiles" -Method "POST" -ContentType "application/x-www-form-urlencoded" -Body "type=PackageFamilyName&url=Microsoft.VCLibs.140.00_8wekyb3d8bbwe&ring=RP&lang=en-US" -UseBasicParsing | Foreach-Object Links | Where-Object outerHTML -match "Microsoft.VCLibs.140.00_.+_x64__8wekyb3d8bbwe.appx" | Foreach-Object href
$vclibsuwp = Invoke-WebRequest -Uri "https://store.rg-adguard.net/api/GetFiles" -Method "POST" -ContentType "application/x-www-form-urlencoded" -Body "type=PackageFamilyName&url=Microsoft.VCLibs.140.00.UWPDesktop_8wekyb3d8bbwe&ring=RP&lang=en-US" -UseBasicParsing | Foreach-Object Links | Where-Object outerHTML -match "Microsoft.VCLibs.140.00.UWPDesktop_.+_x64__8wekyb3d8bbwe.appx" | Foreach-Object href
Invoke-WebRequest $vclibsuwp -OutFile Microsoft.VCLibs.140.00.UWPDesktop_8wekyb3d8bbwe.appx
Invoke-WebRequest $vclibs -OutFile Microsoft.VCLibs.140.00_8wekyb3d8bbwe.appx
Add-AppxPackage -Path .\Microsoft.VCLibs.140.00.UWPDesktop_8wekyb3d8bbwe.appx
Add-AppxPackage -Path .\Microsoft.VCLibs.140.00_8wekyb3d8bbwe.appx
Invoke-WebRequest https://github.com/microsoft/winget-cli/releases/download/v1.0.11451/Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.appxbundle -OutFile Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.appxbundle
Add-AppxPackage -Path .\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.appxbundle
# Create reparse point
$installationPath = (Get-AppxPackage Microsoft.DesktopAppInstaller).InstallLocation
Set-ExecutionAlias -Path "C:\Windows\System32\winget.exe" -PackageName "Microsoft.DesktopAppInstaller_8wekyb3d8bbwe" -EntryPoint "Microsoft.DesktopAppInstaller_8wekyb3d8bbwe!winget" -Target "$installationPath\AppInstallerCLI.exe" -AppType Desktop -Version 3
explorer.exe "shell:appsFolder\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe!winget"

- name: Validate Manifest
shell: powershell
- name: Install frontend dependencies
run: npm ci

- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}

- name: Install Linux dependencies
if: matrix.platform == 'ubuntu-22.04'
run: |
$ErrorActionPreference = 'Continue'
cat manifests/s/Seonglae/Intuiter/latest/Seonglae.Intuiter.yaml
winget validate --manifest manifests/s/Seonglae/Intuiter/latest
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf

- name: Cache Rust
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri

- name: Install Tauri CLI
run: npm install -g @tauri-apps/cli

- name: Build Tauri app
run: cargo tauri build --target ${{ matrix.target }}

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: intuiter-${{ matrix.target }}
path: |
src-tauri/target/${{ matrix.target }}/release/bundle/
if-no-files-found: error
seonglae-holistic and others added 2 commits May 12, 2026 11:19
The Nuxt 2 leftover devDependency @nuxtjs/composition-api requires
peer nuxt@^2.15 while the project upgraded to [email protected], causing
ERESOLVE during npm ci on CI.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@seonglae
Copy link
Copy Markdown
Owner Author

CI npm ci was failing with ERESOLVE because the legacy @nuxtjs/composition-api devDependency requires Nuxt 2 while the project is on Nuxt 3. Added .npmrc with legacy-peer-deps=true to make npm ci resolve the existing lockfile. Verified locally: npm ci, npm run build, and cargo check all pass.

seonglae-holistic and others added 5 commits May 13, 2026 14:27
The "os": ["darwin"] restriction caused `npm ci` to fail with
EBADPLATFORM on Linux/Windows CI runners. The package only contains
manually-invoked Hammerspoon install scripts (no postinstall), so
removing the restriction lets it install harmlessly on all platforms
without changing actual behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The lockfile preserved the old "os": ["darwin"] constraint, which
npm ci treats as authoritative. Removing it from package-lock.json so
the EBADPLATFORM error on Linux CI is actually resolved.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The previous lockfile was generated on darwin and was missing
platform-specific optional dependencies (e.g. @rollup/rollup-linux-x64-gnu)
due to npm bug #4828. This caused vite build to fail on Linux runners
with "Cannot find module @rollup/rollup-linux-x64-gnu".

Regenerated the lockfile from scratch so it includes all platform
variants, allowing npm ci to install the correct rollup binary on each
runner OS.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The test job runs on ubuntu-latest and runs `cargo check` against the
Tauri Rust crates, which depend on glib-sys / gobject-sys / webkit2gtk.
Without libwebkit2gtk-4.1-dev, libappindicator3-dev, librsvg2-dev, and
patchelf installed, pkg-config can't find glib-2.0 / gobject-2.0 and
the build script fails.

The build job already has this step; mirroring it into the test job.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
The workflow installs Tauri CLI via `npm install -g @tauri-apps/cli`,
which exposes a `tauri` binary, not the `cargo tauri` subcommand.
Calling `cargo tauri build` therefore failed with "no such command:
tauri" on all four build matrix platforms.

Switched to `tauri build --target ...` to use the globally-installed
npm binary directly.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@seonglae
Copy link
Copy Markdown
Owner Author

Automated CI fix applied. All 5 checks are now green (test + 4-platform build matrix).

Root causes

  • packages/mac-input declared os: ["darwin"], which made npm ci fail with EBADPLATFORM on Linux runners.
  • package-lock.json was generated on darwin and was missing platform-specific optional dependencies (e.g. @rollup/rollup-linux-x64-gnu), so vite build crashed on Linux (npm bug #4828).
  • The test job ran cargo check against the Tauri Rust crates on ubuntu-latest but never installed libwebkit2gtk-4.1-dev / libappindicator3-dev / librsvg2-dev / patchelf, so glib-sys and gobject-sys couldn't find their system libraries via pkg-config.
  • The build matrix called cargo tauri build, but only @tauri-apps/cli is installed (via npm). That provides a tauri binary, not a cargo tauri subcommand.

Fixes

  • Dropped the darwin-only os field from packages/mac-input/package.json and the matching entry in package-lock.json (mac-input has no postinstall and its scripts are manually invoked, so it installs harmlessly on every OS).
  • Regenerated package-lock.json so all platform-specific rollup binaries are tracked.
  • Added the Tauri Linux system-deps install step to the test job (mirroring the build job).
  • Changed cargo tauri build to tauri build in the build matrix.

@seonglae seonglae merged commit 207e7d5 into main May 14, 2026
9 checks passed
@seonglae seonglae deleted the experiment/tauri branch May 14, 2026 11:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants