docs(desktop): rewrite Auto-Launch section to match new architecture

Replaces the stale Auto-Launch section with an accurate description of the
Tasks 3–7 implementation: OS-authoritative read model, platform-specific
applyLoginItemSettings() contract, Linux/AppImage-only conditional re-apply,
defence-in-depth hidden-launch detection, and the pure helpers in autoLaunch.ts.
Also updates Startup Sequence step 9 to reflect that unconditional re-apply is gone.
This commit is contained in:
Jannis Braun
2026-04-27 13:01:35 +02:00
parent 113efcf583
commit e7413bb504
+39 -14
View File
@@ -102,28 +102,53 @@ Settings stored in `{userData}/auto-launch.json`:
```typescript
interface AutoLaunchSettings {
openAtLogin: boolean; // default: false
startMinimized: boolean; // default: true
openAtLogin: boolean; // default: false; disk cache, NOT source of truth
startMinimized: boolean; // default: true; disk cache; OS-authoritative on Windows
}
```
### Source of Truth
The OS is the source of truth for `openAtLogin` on all platforms. Disk is used only to recover values Electron does not expose:
- **Windows:** OS-authoritative for both `openAtLogin` (via `executableWillLaunchAtLogin`, which honours Task Manager's `StartupApproved\Run` disable) and `startMinimized` (derived from `launchItems[].args`).
- **macOS:** OS-authoritative for `openAtLogin`. Disk-cached for `startMinimized` (no introspection available).
- **Linux:** OS-authoritative for `openAtLogin`. Disk-cached for `startMinimized` (we deliberately do not parse `Exec=` lines from `.desktop` files; out-of-band edits are rare and parsing shell-quoted strings is fragile).
### Platform-Specific Implementation (`applyLoginItemSettings()`)
| Platform | Method | Minimized Launch |
|----------|--------|-----------------|
| macOS | `app.setLoginItemSettings({ openAtLogin, openAsHidden })` | `openAsHidden` flag |
| Windows | `app.setLoginItemSettings({ openAtLogin, args, name })` | `--hidden` CLI arg |
| Linux | `app.setLoginItemSettings({ openAtLogin, path, args })` | `--hidden` CLI arg; `path` set to `$APPIMAGE` for AppImage portability |
| Platform | Method | Key parameters | Rationale |
|----------|--------|----------------|-----------|
| macOS | `app.setLoginItemSettings({ openAtLogin, openAsHidden, args })` | `openAsHidden: startMinimized`; `args: ['--hidden']` when `startMinimized` | Both detection paths covered (legacy `wasOpenedAsHidden` for macOS < 13 and `--hidden` argv for macOS 13+) |
| Windows | `app.setLoginItemSettings({ openAtLogin, enabled, path, args, name })` | `enabled: openAtLogin`; `path: process.execPath`; `args: ['--hidden']` when `startMinimized`; `name: 'Backspace'` | `enabled` is required to clear Task Manager's `StartupApproved\Run` disable when re-enabling. `path`/`args` enable correct matching in subsequent `getLoginItemSettings`. |
| Linux | `app.setLoginItemSettings({ openAtLogin, name, path?, args? })` | `name: 'backspace'` (deterministic `.desktop` filename); `path: $APPIMAGE` when running as AppImage; `args: ['--hidden']` when `startMinimized` | `name` ensures stable `~/.config/autostart/backspace.desktop` path. AppImage path tracks updates. |
On startup, settings are re-applied to the OS (`applyLoginItemSettings`) to refresh the login item path (important for AppImage updates where the path changes).
### Startup Re-Apply
### Launch Detection
The unconditional startup re-apply was removed (it was overwriting user changes made via Task Manager / System Settings). Today the only re-apply happens on Linux/AppImage and only when needed:
Hidden launch is detected at `ready-to-show` via:
- `process.argv.includes('--hidden')` (Windows/Linux)
- `app.getLoginItemSettings().wasOpenedAsHidden` (macOS)
```
if linux AND $APPIMAGE is set:
read ~/.config/autostart/backspace.desktop → recordedExecPath
if saved.openAtLogin AND $APPIMAGE != recordedExecPath:
re-apply to refresh the autostart entry's Exec= path
```
If launched hidden, the window is created but never shown (stays in tray).
This keeps AppImage updates working (the AppImage moved to a new path → autostart entry needs the new path) without overriding any user-level OS state on Windows or macOS.
### Hidden-Launch Detection
At `ready-to-show`:
- `process.argv.includes('--hidden')` — primary signal on all platforms (we pass `args: ['--hidden']` everywhere when `startMinimized`).
- `app.getLoginItemSettings().wasOpenedAsHidden` — macOS-only fallback for the legacy `openAsHidden` path (macOS < 13).
If either is true, the window is created but not shown (stays in tray).
### Pure Helpers
Pure logic lives in `packages/desktop/src/autoLaunch.ts` with vitest coverage in `autoLaunch.test.ts`:
- `deriveStartMinimizedFromArgs(args)` — used by both IPC handlers on Windows.
- `parseExecPathFromDesktopFile(content)` — used by the Linux/AppImage path-refresh.
- `shouldReapplyAppImage(currentAppImagePath, recordedExecPath)` — used by the Linux/AppImage path-refresh.
---
@@ -765,7 +790,7 @@ GitHub releases are the update source. The `electron-updater` library handles ch
6. Create tray icon
7. Initialize auto-updater (10s delayed first check)
8. Start activity detection (immediate first poll, 15s interval, background remote sync)
9. Sync auto-launch settings with OS
9. Linux/AppImage path-refresh: re-apply autostart entry if `$APPIMAGE` path changed (conditional; no-op on Windows/macOS)
10. Check for deep link in launch args
### Shutdown Sequence (`before-quit`)