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
134 changes: 134 additions & 0 deletions .github/workflows/build-desktop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
name: Build Desktop Apps

on:
# Build on version tags (e.g., v1.0.0, v1.2.3)
push:
tags:
- 'v*'

# Also allow manual trigger from Actions tab
workflow_dispatch:

# Minimal permissions for security
permissions:
contents: read
actions: write # Required for artifact cleanup

# Cancel in-progress runs for the same branch
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- platform: macos-latest
target: universal-apple-darwin
name: macOS
- platform: windows-latest
target: x86_64-pc-windows-msvc
name: Windows

runs-on: ${{ matrix.platform }}
name: Build (${{ matrix.name }})

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: 'yarn'

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

- name: Install dependencies (macOS)
if: matrix.platform == 'macos-latest'
run: |
rustup target add aarch64-apple-darwin x86_64-apple-darwin

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Build Tauri app
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: ${{ matrix.platform == 'macos-latest' && '--target universal-apple-darwin' || '' }}

- name: Upload macOS artifacts
if: matrix.platform == 'macos-latest'
uses: actions/upload-artifact@v4
with:
name: miden-wallet-macos-${{ github.ref_name }}
path: |
src-tauri/target/universal-apple-darwin/release/bundle/dmg/*.dmg
src-tauri/target/universal-apple-darwin/release/bundle/macos/*.app
retention-days: 30
if-no-files-found: error

- name: Upload Windows artifacts
if: matrix.platform == 'windows-latest'
uses: actions/upload-artifact@v4
with:
name: miden-wallet-windows-${{ github.ref_name }}
path: |
src-tauri/target/release/bundle/msi/*.msi
src-tauri/target/release/bundle/nsis/*.exe
retention-days: 30
if-no-files-found: error

# Cleanup old artifacts, keep only last 10 builds per platform
cleanup:
runs-on: ubuntu-latest
needs: build
steps:
- name: Delete old artifacts
uses: actions/github-script@v7
with:
script: |
const { data: artifacts } = await github.rest.actions.listArtifactsForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
per_page: 100
});

// Group by prefix (miden-wallet-macos or miden-wallet-windows)
const groups = {};
for (const artifact of artifacts.artifacts) {
const prefix = artifact.name.startsWith('miden-wallet-macos') ? 'macos'
: artifact.name.startsWith('miden-wallet-windows') ? 'windows'
: null;
if (prefix) {
if (!groups[prefix]) groups[prefix] = [];
groups[prefix].push(artifact);
}
}

// For each group, keep only the 10 most recent
for (const [prefix, items] of Object.entries(groups)) {
// Sort by created_at descending
items.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));

// Delete everything after the first 10
const toDelete = items.slice(10);
for (const artifact of toDelete) {
console.log(`Deleting old artifact: ${artifact.name} (${artifact.id})`);
await github.rest.actions.deleteArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: artifact.id
});
}
}

console.log(`Cleanup complete. Kept 10 most recent builds per platform.`);
3 changes: 3 additions & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ on:
SSH_DEPLOY_KEY:
required: false

permissions:
contents: read

env:
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}

Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ on:
SSH_DEPLOY_KEY:
required: false

permissions:
contents: read

env:
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}

Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/feature-branch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ on:
- 'dev'
- 'next'

permissions:
contents: read

jobs:
ci:
uses: ./.github/workflows/ci.yml
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ on:
- 'mw-**'
- 'feat/**'

permissions:
contents: read

jobs:
translations:
name: Update Translation Files
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/production-branch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ on:
branches:
- 'main'

permissions:
contents: read

jobs:
ci:
uses: ./.github/workflows/ci.yml
Expand Down
80 changes: 80 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,86 @@ sleep 2 && xcrun simctl io booted screenshot /tmp/ios-main.png
- **Content cut off:** Check if containers have `overflow: hidden` without proper height constraints.
- **Safe area gaps:** Ensure `public/mobile.html` has `padding: env(safe-area-inset-*)` on body, and body background color matches app background (white).

## Desktop Development (Tauri)

The desktop app uses Tauri to wrap the web app in a native macOS window.

### Commands

```bash
yarn build:desktop # Production build for desktop (outputs to dist/desktop/)
yarn build:desktop:dev # Development build for desktop
yarn tauri dev # Build and run desktop app in dev mode
yarn tauri build # Build release desktop app
```

**Node version:** Requires Node >= 22. Use nvm to switch:
```bash
source ~/.nvm/nvm.sh && nvm use 22 && yarn tauri dev
```

### Key Directories

```
src-tauri/
├── src/
│ ├── main.rs # Tauri app entry point
│ ├── dapp_browser.rs # dApp browser window and wallet API
│ └── lib.rs # Command registration
├── scripts/
│ └── dapp-injection.js # Wallet API injected into dApp pages
├── capabilities/ # Tauri permission capabilities
└── tauri.conf.json # Tauri configuration

src/lib/desktop/
├── dapp-browser.ts # TypeScript bindings for Tauri commands
├── DesktopDappHandler.tsx # Handles wallet requests from dApps
└── DesktopDappConfirmationModal.tsx # Manages confirmation overlay in dApp WebView
```

### Clearing App State (macOS)

To completely reset the desktop app state (useful for testing fresh installs or debugging):

```bash
# Clear all wallet data (IndexedDB, localStorage, WebKit caches)
rm -rf ~/Library/WebKit/com.miden.wallet
rm -rf ~/Library/WebKit/miden-wallet

# Optional: Also clear Application Support and Caches
rm -rf ~/Library/Application\ Support/com.miden.wallet
rm -rf ~/Library/Caches/com.miden.wallet
```

**Important:** The WebKit directories contain the actual IndexedDB/localStorage data. The Application Support directory may be empty or contain minimal data.

After clearing, restart the app with `yarn tauri dev` to see the onboarding screen.

### dApp Browser Architecture

The desktop app includes a separate browser window for dApps:

```
┌─────────────────────────────────────────────────────────────┐
│ Desktop App Architecture │
├─────────────────────────────────────────────────────────────┤
│ Main Window (wallet UI) │ dApp Browser Window │
│ - React app │ - External dApp webpage │
│ - DesktopDappHandler │ - Injected window.midenWallet│
│ - Confirmation modal logic │ - URL interception for msgs │
│ │ │
│ ◄──── Tauri Events (dapp-wallet-request) ────► │
└─────────────────────────────────────────────────────────────┘
```

**Communication flow:**
1. dApp calls `window.midenWallet.connect()` or other methods
2. Injection script encodes request as base64 and navigates to `https://miden-wallet-request/{payload}`
3. Tauri's `on_navigation` callback intercepts this URL
4. Request is emitted to main window via Tauri event
5. `DesktopDappHandler` processes and shows confirmation overlay in dApp window
6. Response flows back via similar URL interception pattern

## Code Style (Prettier)

This project uses Prettier for code formatting. Always write code that conforms to Prettier rules:
Expand Down
Loading
Loading