feat(mini-player): remember window position and size#125
Conversation
Persists the mini-player bounds (logical x/y/width/height) in `app_setting['mini_player.bounds']` as a single JSON blob so the four fields move as one row. The widget no longer always reopens at the default bottom-right corner — it lands where the user last parked it (closes #110, the overlap with the Windows taskbar info was the only way to dismiss the default anchor on small screens). Capture: - `MiniPlayer.tsx` subscribes to `onMoved` + `onResized` and saves 300 ms after the last gesture so SQLite isn't hammered at 60 Hz during a drag. The debounce window is short enough that closing with Alt-F4 still flushes the latest position before the webview dies (the invoke completes against `app.db` independently). Restore: - `miniPlayer.ts` reads the saved bounds before constructing the WebviewWindow. The rectangle is validated against `availableMonitors()` — needs at least 80 px of overlap on both axes with some monitor before we trust it, otherwise we fall through to the Spotify-style bottom-right anchor (covers monitor disconnects, resolution drops, and first launches). Storage is machine-level (in `app_setting`, not `profile_setting`) because window position is a screen-geometry preference: a docked 4K and a 1080p laptop session would never share a sensible corner. Closes #110
📝 WalkthroughWalkthroughLe PR ajoute une persistance complète de la position et taille du mini-player : les coordonnées sont sauvegardées en JSON à chaque déplacement/redimensionnement, puis restaurées au lancement si le moniteur correspondant est toujours disponible, sinon reposage en bas à droite. ChangesPersistance du mini-player
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/lib/miniPlayer.ts`:
- Around line 103-109: The fallback bottom-right calculation ignores
monitor.position and clamps with Math.max(0,...), which breaks multi-monitor
setups and monitors with negative coordinates; update the block in
currentMonitor() handling so you compute logical origin using monitor.position
divided by scaleFactor (e.g., logicalX = monitor.position.x / scale, logicalY =
monitor.position.y / scale), compute logicalW/H as now, then set x =
Math.round(logicalX + logicalW - width - EDGE_MARGIN) and y =
Math.round(logicalY + logicalH - height - EDGE_MARGIN) (remove the
Math.max(0,...) clamp) so the mini window appears on the correct monitor.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 2b4a752f-17e1-4f1c-9ab8-42c0c35abc86
📒 Files selected for processing (7)
CLAUDE.mddocs/features/ui.mdsrc-tauri/src/commands/preferences.rssrc-tauri/src/lib.rssrc/components/views/MiniPlayer.tsxsrc/lib/miniPlayer.tssrc/lib/tauri/preferences.ts
The bottom-right fallback computed `logicalW - width - EDGE_MARGIN` relative to (0, 0) and clamped negatives away. That snaps the mini-window to the primary monitor's bottom-right even when `currentMonitor()` is reporting a secondary monitor — and breaks entirely on layouts where a monitor sits to the left of (or above) the primary and therefore has a negative `position.x` / `position.y`. Add `monitor.position` (in logical pixels) as the origin and drop the `Math.max(0, ...)` clamp so the anchor stays on the intended monitor.
Summary
app_setting['mini_player.bounds']as a single JSON blob so the four fields always move as one row.How it works
Capture —
MiniPlayer.tsxsubscribes toonMoved+onResizedon mount and writes viasetMiniPlayerBounds300 ms after the last gesture (debounced so SQLite isn't hammered at 60 Hz while dragging). The debounce window is short enough that closing with Alt-F4 still flushes the latest position before the webview dies — the invoke completes againstapp.dbindependently of the source window.Restore —
miniPlayer.ts::openMiniPlayerreads the saved bounds before constructing theWebviewWindow. The rectangle is sanity-checked againstavailableMonitors(): needs at least 80 px of overlap on both axes with some monitor before we trust it. Otherwise it falls through to the existing Spotify-style bottom-right anchor — handles monitor disconnects, resolution drops, and first launches cleanly.Storage scope —
app_setting, notprofile_setting. Window position is a screen-geometry preference: a docked 4K and a 1080p laptop session on the same install would never share a sensible corner. Same reasoning as the existingui.zoom_levelkey.Test plan
cargo check --manifest-path src-tauri/Cargo.toml --all-targetsclean.bun run typecheck+bun run lintclean.Summary by CodeRabbit
New Features
Documentation