Skip to content

feat(mini-player): remember window position and size#125

Merged
InstaZDLL merged 2 commits into
mainfrom
feat/mini-player-remember-position
May 23, 2026
Merged

feat(mini-player): remember window position and size#125
InstaZDLL merged 2 commits into
mainfrom
feat/mini-player-remember-position

Conversation

@InstaZDLL
Copy link
Copy Markdown
Owner

@InstaZDLL InstaZDLL commented May 23, 2026

Summary

  • 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 always 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 feat: mini player position overlap window taskbar info - can mack it remember last possition ? #110 (the overlap with the Windows taskbar info was the only way to escape the default anchor on small displays).

How it works

CaptureMiniPlayer.tsx subscribes to onMoved + onResized on mount and writes via setMiniPlayerBounds 300 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 against app.db independently of the source window.

RestoreminiPlayer.ts::openMiniPlayer reads the saved bounds before constructing the WebviewWindow. The rectangle is sanity-checked against availableMonitors(): 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 scopeapp_setting, not profile_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 existing ui.zoom_level key.

Test plan

  • Open the mini-player → defaults to bottom-right of the primary monitor (existing behaviour).
  • Drag it to the top-left, resize it slightly, close → reopen. The widget reappears at the same place & size.
  • Close WaveFlow entirely (tray quit), relaunch, open the mini-player → still remembered.
  • (Multi-monitor) drag to the second monitor, close, unplug the second monitor, reopen → falls back to the bottom-right anchor on the primary monitor instead of opening offscreen.
  • Drag rapidly for a few seconds → SQLite write count stays low (300 ms debounce, watch the dev log).
  • cargo check --manifest-path src-tauri/Cargo.toml --all-targets clean.
  • bun run typecheck + bun run lint clean.

Summary by CodeRabbit

  • New Features

    • Le mini-player conserve et restaure position + taille entre sessions; sauvegarde débouncée lors des déplacements/redimensions.
    • Restauration conditionnelle : appliquée uniquement si la zone restaurée recouvre encore un moniteur (seuil visible), sinon ancrage bas‑droite avec marge de 24 px.
    • Défauts de taille gérés (tailles par défaut/minima) et validation des valeurs restaurées.
  • Documentation

    • Mise à jour décrivant la persistance, la restauration conditionnelle et le comportement de fallback.

Review Change Stack

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
@InstaZDLL InstaZDLL added scope: frontend React/Vite frontend (src/) scope: backend Rust/Tauri backend (src-tauri/) scope: docs Docs, README, assets type: feat New feature size: l 200-500 lines labels May 23, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 23, 2026

📝 Walkthrough

Walkthrough

Le 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.

Changes

Persistance du mini-player

Layer / File(s) Summary
Backend persistence contracts and commands
src-tauri/src/commands/preferences.rs, src-tauri/src/lib.rs
Struct sérialisable MiniPlayerBounds et deux commandes Tauri get_mini_player_bounds / set_mini_player_bounds avec validation (finitude, positivité stricte) et stockage JSON. Enregistrement des commandes dans le handler Tauri.
Frontend Tauri preferences interface
src/lib/tauri/preferences.ts
Interface TypeScript MiniPlayerBounds et accesseurs getMiniPlayerBounds() / setMiniPlayerBounds() qui invoquent les commandes backend.
Mini-player window initialization with conditional restoration
src/lib/miniPlayer.ts
Helper boundsAreVisible() valide que les bounds sauvegardées recoupent au moins un moniteur (seuil 80 px par axe), conversion physical→logical via scaleFactor. Refactor de openMiniPlayer() : restauration conditionnelle avec fallback bottom-right (24 px margin). Nouvelles constantes DEFAULT_WIDTH/HEIGHT et MIN_WIDTH/HEIGHT.
Event listeners and debounced persistence
src/components/views/MiniPlayer.tsx
useEffect s'abonne à onMoved / onResized, débounce 300 ms, conversion via scaleFactor, appel setMiniPlayerBounds. Nettoyage des listeners et timer.
Documentation updates
CLAUDE.md, docs/features/ui.md
Description de la restauration persistée, comportement de fallback en cas de changement moniteur, persistance via stockage JSON avec debouncing.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🪟 La fenêtre apprend sa place,
Un débounce calme les traces,
Bounds sérialisés au repos,
Si l'écran change, bas-droit en dos —
Le mini-player garde sa grâce.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Le titre décrit précisément la fonctionnalité principale : la persistance de la position et taille de la fenêtre mini-player.
Linked Issues check ✅ Passed Les changements implémentent intégralement les objectifs de l'issue #110 : persistance de la position du mini-player, restauration avec fallback intelligent et prévention des chevauchements de barre des tâches.
Out of Scope Changes check ✅ Passed Les modifications sont strictement focalisées sur la persistance des bounds du mini-player sans changements hors-scope : API Tauri, logique de stockage/restauration, UI et documentation alignées.
Description check ✅ Passed La description suit les conventions Conventional Commits, décrIt clairement les modifications apportées, explique la raison d'être, et documente le plan de test avec cases cochées.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/mini-player-remember-position

Comment @coderabbitai help to get the list of available commands and usage tips.

@InstaZDLL InstaZDLL self-assigned this May 23, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 09de8bc and d5c4bc4.

📒 Files selected for processing (7)
  • CLAUDE.md
  • docs/features/ui.md
  • src-tauri/src/commands/preferences.rs
  • src-tauri/src/lib.rs
  • src/components/views/MiniPlayer.tsx
  • src/lib/miniPlayer.ts
  • src/lib/tauri/preferences.ts

Comment thread src/lib/miniPlayer.ts Outdated
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.
@InstaZDLL InstaZDLL merged commit 3012cd5 into main May 23, 2026
14 checks passed
@InstaZDLL InstaZDLL deleted the feat/mini-player-remember-position branch May 23, 2026 22:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: backend Rust/Tauri backend (src-tauri/) scope: docs Docs, README, assets scope: frontend React/Vite frontend (src/) size: l 200-500 lines type: feat New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: mini player position overlap window taskbar info - can mack it remember last possition ?

1 participant