Skip to content
Open
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
15 changes: 9 additions & 6 deletions apps/desktop-ui/src/components/shell/top-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,14 @@ export function TopBar() {
const user = useAppUser()

// macOS traffic lights are inset into this bar (titleBarStyle: Overlay), so
// pad the left only inside the Tauri window. Mounted-guarded to avoid an SSR
// hydration mismatch (isDesktop() is false on the server).
const [isTauri, setIsTauri] = useState(false)
// reserve room for them on the left — but ONLY on macOS. titleBarStyle and
// trafficLightPosition are macOS-only options; Linux and Windows keep their
// native title bar, so the same inset there is just an empty 92px gap.
// Mounted-guarded to avoid an SSR hydration mismatch (isDesktop() is false on
// the server).
const [isMacDesktop, setIsMacDesktop] = useState(false)
useEffect(() => {
setIsTauri(isDesktop())
setIsMacDesktop(isDesktop() && /Mac/i.test(navigator.userAgent))
}, [])

// macOS hides the traffic lights in fullscreen, so the left inset that
Expand Down Expand Up @@ -72,9 +75,9 @@ export function TopBar() {
data-tauri-drag-region
className={cn(
'flex h-14 w-full shrink-0 items-center gap-3 border-b border-border bg-[hsl(var(--surface-2))]',
!isTauri ? 'px-4' : 'pr-6',
!isMacDesktop ? 'px-4' : 'pr-6',
)}
style={isTauri ? { paddingLeft: isFullscreen ? 16 : 92 } : undefined}
style={isMacDesktop ? { paddingLeft: isFullscreen ? 16 : 92 } : undefined}
>
{/* Brand → dashboard. Text wordmark in Courier Prime, no logo mark. */}
<button
Expand Down
61 changes: 61 additions & 0 deletions apps/desktop/RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,64 @@ Actions minutes, so promote deliberately.
would reject every future update and need a manual reinstall.
- **Always bump the version** before releasing.
- **Keep the updater key backed up** — it's critical infrastructure.

---

## Building the Linux app

Linux **cannot be cross-compiled from macOS** — the build links against
webkit2gtk/GTK system libraries, so it has to run on Linux (a machine, a VM, WSL2,
or a container). Unlike macOS there is **no signing or notarization**: no Apple
account, no certificate, no notarytool. You just build and ship the artifacts.

### Build dependencies (Debian/Ubuntu)

```bash
sudo apt update && sudo apt install -y \
libwebkit2gtk-4.1-dev build-essential curl wget file \
libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev patchelf \
libdbus-1-dev
```

`libdbus-1-dev` is required by the `dbus-secret-service` crate, which backs the
Linux keyring (see the platform-split `keyring` dependency in `Cargo.toml`).

### Build

```bash
pnpm install --frozen-lockfile
cd apps/desktop

# updater artifacts are signed, so the key must be present
export TAURI_SIGNING_PRIVATE_KEY="$(cat ~/.tauri/mydevtools-updater.key)"
export TAURI_SIGNING_PRIVATE_KEY_PASSWORD="$(cat ~/.tauri/mydevtools-updater.password)"

pnpm tauri build --bundles deb,appimage
# → src-tauri/target/release/bundle/{deb,appimage}/
```

### Verify before shipping

The keyring is the one thing that can compile fine and still fail at runtime: the
app fetches the SQLCipher key from the Secret Service at startup, and a headless
shell has no keyring daemon.

```bash
sudo apt install -y gnome-keyring dbus-x11
dbus-run-session -- ./src-tauri/target/release/mydevtools-desktop
```

Then: create a note → quit → **reboot** → reopen. If the note is still there, the
key persisted correctly.

### Publishing

Only **AppImage** can self-update — Tauri's updater cannot replace a `.deb`/`.rpm`
(the package manager owns those files), so AppImage is the auto-updating format and
`.deb` is a manual-install convenience.

> ⚠️ `latest.json` is shared across platforms. The macOS release writes a manifest
> containing only the `darwin-*` keys, so uploading a Linux manifest with
> `--clobber` **erases the macOS entries** (and vice versa) and silently breaks
> auto-update for the other OS. Merge the platform keys into the existing
> `latest.json` instead of overwriting it.
25 changes: 25 additions & 0 deletions apps/desktop/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 14 additions & 1 deletion apps/desktop/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ tauri = { version = "2", features = [] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
rusqlite = { version = "0.32", features = ["bundled-sqlcipher-vendored-openssl"] }
keyring = { version = "3", features = ["apple-native"] }
uuid = { version = "1", features = ["v4"] }
chrono = "0.4"
rand = "0.8"
Expand All @@ -41,6 +40,20 @@ redis = { version = "0.29", features = ["tokio-comp", "tokio-native-tls-comp"] }
futures-util = "0.3"
dashmap = "6"

# The keyring backend is the one genuinely platform-specific dependency: the
# macOS Keychain vs. the Linux Secret Service (GNOME Keyring / KWallet).
# db/device_key.rs uses only the portable `Entry` API, so the code is identical
# on both — only the backend feature differs.
#
# Linux uses sync-secret-service, NOT linux-native: the latter stores in kernel
# keyutils, which is session-scoped and can be lost on reboot. The SQLCipher key
# must persist or the user loses access to their local database.
[target.'cfg(target_os = "macos")'.dependencies]
keyring = { version = "3", features = ["apple-native"] }

[target.'cfg(target_os = "linux")'.dependencies]
keyring = { version = "3", features = ["sync-secret-service"] }

[dev-dependencies]
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }

Expand Down
14 changes: 12 additions & 2 deletions apps/desktop/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,21 @@
},
"bundle": {
"active": true,
"targets": ["app", "dmg"],
"targets": ["app", "dmg", "deb", "appimage"],
"createUpdaterArtifacts": true,
"icon": ["icons/icon.icns"],
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns"
],
"macOS": {
"minimumSystemVersion": "12.0"
},
"linux": {
"deb": {
"depends": []
}
}
}
}
Loading