# Deep Link Platform Guide

## Overview

The `openhuman://` custom URL scheme is used to hand off authentication from a web browser to the Tauri desktop app. This document covers platform-specific behavior, gotchas, and build requirements discovered during development.

## Scheme Registration

Configured in `src-tauri/tauri.conf.json`:

```json
{ "plugins": { "deep-link": { "desktop": { "schemes": ["openhuman"] } } } }
```

## macOS

### How It Works

- URL schemes are registered via `Info.plist` inside the `.app` bundle
- The `CFBundleURLSchemes` entry is automatically generated by Tauri from the config
- macOS LaunchServices maps the scheme to the installed app

### Critical: `tauri dev` Does NOT Support Deep Links

- `npm run tauri dev` runs the binary directly without a `.app` bundle
- No `Info.plist` exists, so macOS cannot register the URL scheme
- **You must use `npm run tauri build --debug` and run the `.app` bundle**

### `register_all()` Is Unsupported on macOS

- The Tauri deep-link plugin's `register_all()` method panics on macOS with `unsupported platform`
- Only call it on Windows and Linux:
  ```rust
  #[cfg(any(windows, target_os = "linux"))]
  {
      app.deep_link().register_all()?;
  }
  ```

### Build & Install Workflow

```bash
# Build debug .app bundle
npm run tauri build -- --debug --bundles app

# Install to /Applications (or run from bundle path)
cp -R src-tauri/target/debug/bundle/macos/tauri-app.app /Applications/

# Launch
open /Applications/tauri-app.app

# Test deep link
open "openhuman://auth?token=YOUR_TOKEN"
```

### Cargo Caching Gotcha

- Cargo may not re-embed updated `dist/` frontend assets on incremental builds
- The `tauri-build` build script embeds frontend assets at compile time
- **If the app shows stale UI, run `cargo clean` before rebuilding:**
  ```bash
  cargo clean --manifest-path src-tauri/Cargo.toml
  npm run tauri build -- --debug --bundles app
  ```

### WebKit Cache

- The macOS WebView (WKWebView) caches aggressively
- Clear caches when debugging stale content:
  ```bash
  rm -rf ~/Library/WebKit/com.openhuman.app
  rm -rf ~/Library/Caches/com.openhuman.app
  rm -rf ~/Library/Application\ Support/com.openhuman.app
  ```

### Debug Builds: Secondary Instance Behavior

- In debug mode, clicking a deep link while the app is running may briefly spawn a secondary instance
- The `onOpenUrl` event may fire twice (one may be an empty string)
- The `deepLinkHandled` localStorage flag prevents double-processing

## Windows & Linux

### How It Works

- URL schemes are registered at runtime via `register_all()` in the `setup` hook
- This works in both dev mode (`tauri dev`) and production builds
- No special build or install steps needed for deep link testing

### Code

```rust
#[cfg(any(windows, target_os = "linux"))]
{
    app.deep_link().register_all()?;
}
```

## All Platforms

### `window.__TAURI__` Is Not Available Immediately

- The Tauri IPC bridge (`window.__TAURI__`) is injected asynchronously
- Code that runs at module load time (top of `main.tsx`) cannot reliably check `__TAURI__`
- **Solution**: Use dynamic `import()` for the deep link listener and wrap plugin calls in try/catch:
  ```typescript
  // main.tsx
  import('./utils/desktopDeepLinkListener').then(m => {
    m.setupDesktopDeepLinkListener().catch(console.error);
  });
  ```

### `alert()` Is Suppressed in Tauri WebView

- `window.alert()` does not display in Tauri's WKWebView on macOS
- For debugging, use visible DOM elements or `console.log()` (viewable via Safari Web Inspector)

### `document.title` Is Overridden by Tauri

- Tauri sets the window title from `tauri.conf.json` `app.windows[].title`
- Setting `document.title` in JS has no visible effect on the window title bar

### Infinite Reload Loop Prevention

- `getCurrent()` returns the deep link URL that launched/activated the app
- After `window.location.replace()`, the page reloads and `getCurrent()` returns the same URL again
- **Solution**: Set `localStorage.deepLinkHandled = 'true'` before navigating, check and clear it on next load

### CORS: Use Rust for Backend Calls

- Browser `fetch()` from the Tauri WebView is subject to CORS
- External APIs (especially ngrok tunnels) typically don't set `Access-Control-Allow-Origin`
- **Solution**: Use a Rust Tauri command with `reqwest` to make HTTP requests (no CORS):
  ```typescript
  // Instead of fetch():
  const data = await invoke('exchange_token', { backendUrl, token });
  ```

## Tauri Plugin Dependencies

### Cargo.toml

```toml
tauri-plugin-deep-link = "2.0.0"
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }
```

### package.json

```json
"@tauri-apps/plugin-deep-link": "^2"
```

### Capabilities (`src-tauri/capabilities/default.json`)

```json
{ "permissions": ["core:default", "opener:default", "deep-link:default"] }
```

---

_Last updated: 2026-01-28_
