Improve minimized window UX with restore support and count badges - #312
Conversation
…s minimized windows. - Aborts the `pointerenter` handler if the target window is in a minimized state. This prevents the window manager from improperly shifting focus to an invisible/tucked window simply by hovering over its peek card. - Injects a `win.restore()` call into the `spawnFocusViewTransition` click handler so that minimized instances correctly unpack and restore to the viewport before receiving focus. - Adds Vitest coverage to verify both hover and click behaviors on minimized instance cards.
…port. - Add a dedicated badge to display minimized window counts on dock tiles. - Update `aria-label`s to announce minimized counts and improve accessibility. - Add cache-busting for dock CSS and expand Vitest coverage.
|
Hey @KarunyaChavan! Thanks for your contribution, we've been focused on other duties and we've just came back to Desktop Mode, and I love your focus on the thumbnails work. It definitely need some love and we are super grateful about you focusing on it. I would love to hear your thoughts about slightly changing the implementation of it. A second badge indicating the number of minimized tabs it might feel confusing for the user (eg, 2 badges at the same time might look crazy! 😆) So my question is, how do you feel about actually changing the preview state of the thumbnail? I mean, removing the "content" and just showing up the title bar for the minimized window.
|
|
Hey @AllTerrainDeveloper, thanks for taking the time to review the changes and for the thoughtful feedback!
Yes, I think it's better to change the current indication method for minimized windows instead of using a badge. We could take inspiration from the stacked/layered feeling in Windows OS. Let me know what you think about that.
Sounds good. |
I'm open to anything, we can aim for creativity or simplicity. |
…icons. - Remove `data-minimized-count` badge and redundant minimized guard in dock-peek; use hover-only stacked `::before` card when ≥2 windows minimized - Collapse minimized peek cards to titlebar-only via `data-state="minimized"` attribute (no `!important`) - Keep tile highlight and stacked card visual active while peek popover is open via `data-peek-active`
- Hover over a minimized peek card now calls `win.restore()` so the user can actually see the window - Click path (`spawnFocusViewTransition`) restores before focusing for a clean visual transition - Non-minimized windows keep existing scrub-to-focus behavior unchanged
…inimize state. - `--stacked` class now fires when a tile has ≥2 open instances in any state, not just ≥2 minimized - Drops the `minimizedCount` filter — `instances.length > 1` is the simpler, correct condition
…nd-state-indicators
|
Hey @AllTerrainDeveloper, Please refer to the video below for the updated behavior of the minimized windows UX: Screen.Recording.2026-07-15.at.12.20.21.PM.movChanges Overview1. Persist tile highlight while the Peek popover is openOn
2. Dock stacked card visualWhen multiple windows of the same application are open, the dock now displays a stacked card visual, similar to the Windows taskbar, providing a clearer indication that multiple windows are available. I'd love to get your thoughts on the preview state of the thumbnails. As you can see in the video above, we're currently showing only the title bars for minimized windows, whereas for non-minimized windows we display the window content. Do you think we should make the behavior consistent for both states, or do you prefer keeping the current distinction? |
|
Nice! I'll review and test it tomorrow morning, I had many duties today :) |
AllTerrainDeveloper
left a comment
There was a problem hiding this comment.
Ran a high effort review over this branch's diff against trunk. Two correctness issues and one small cleanup, pinned to the lines below.
| card.addEventListener( 'pointerenter', () => { | ||
| if ( deps.windowManager.getFocused() === win ) { | ||
| return; | ||
| if ( win.state === 'minimized' ) { | ||
| win.restore(); | ||
| } else if ( deps.windowManager.getFocused() !== win ) { | ||
| deps.windowManager.focus( win ); | ||
| } | ||
| deps.windowManager.focus( win ); | ||
| } ); |
There was a problem hiding this comment.
The pointerenter handler restores the window here, but never clears card.dataset.state, which was stamped to minimized at build time (lines 353 to 356 above). The CSS rule for [data-state="minimized"] keeps collapsing the card to just its titlebar after that, so the peek card stays visually collapsed even though the underlying window is no longer minimized, which works against the stated goal of this change (restore on hover so the peek card actually shows the window). Clearing the attribute alongside the restore call fixes it:
| card.addEventListener( 'pointerenter', () => { | |
| if ( deps.windowManager.getFocused() === win ) { | |
| return; | |
| if ( win.state === 'minimized' ) { | |
| win.restore(); | |
| } else if ( deps.windowManager.getFocused() !== win ) { | |
| deps.windowManager.focus( win ); | |
| } | |
| deps.windowManager.focus( win ); | |
| } ); | |
| card.addEventListener( 'pointerenter', () => { | |
| if ( win.state === 'minimized' ) { | |
| win.restore(); | |
| delete card.dataset.state; | |
| } else if ( deps.windowManager.getFocused() !== win ) { | |
| deps.windowManager.focus( win ); | |
| } | |
| } ); |
| content: ""; | ||
| position: absolute; | ||
| top: -4px; | ||
| left: -4px; |
There was a problem hiding this comment.
This hint uses a hardcoded physical left offset. Every other absolutely positioned decoration in this file (badges, dots, chips around lines 222, 465, 534, 761) uses inset-inline-start or inset-inline-end so it mirrors correctly on RTL locales. As written, this hint will sit on the wrong edge in RTL. Switching to the logical property matches the rest of the file:
| left: -4px; | |
| inset-inline-start: -4px; |
| if ( win.state === 'minimized' ) { | ||
| win.restore(); | ||
| } |
There was a problem hiding this comment.
Small cleanup, not blocking: this minimized check and restore is duplicated verbatim between here and the pointerenter handler above (lines 365 to 366). Pulling it into a small shared helper would keep both call sites in sync if the restore condition ever changes, something like:
function restoreIfMinimized( win: WPWindow, card?: HTMLElement ): void {
if ( win.state !== 'minimized' ) {
return;
}
win.restore();
if ( card ) {
delete card.dataset.state;
}
}Then both call sites become restoreIfMinimized( win, card ) and restoreIfMinimized( win ). Not a blocker, just flagging so the two paths do not drift apart.
…in RTL Addresses review feedback on this branch's hover-restore feature: - buildInstanceCard() stamped a peek card's data-state="minimized" once at build time but never cleared it when the pointerenter/click handlers restored the underlying window, so the CSS kept the card collapsed to its titlebar even after the window was restored. Extracted a shared restoreIfMinimized() helper used by both the hover and click paths so restoring a window always clears the card's collapsed state, and the two call sites can no longer drift apart. - The new stacked-card hint in dock.css used a hardcoded left offset instead of this file's inset-inline-start/end convention, so it didn't mirror on RTL locales. Switched to the logical property.
|
Pushed 2e005d6 addressing the review above:
Verified locally: |
|
Just a note, that you are already appearing here: If you have any inconvenience, please tell it to me :) |




This PR fixes unresponsive minimized window thumbnails in the Dock Peek popover, resolves ghost-focusing bugs when hovering over minimized cards, and significantly improves the dock UX by introducing a dedicated, accessible counter badge for tucked-away windows. It also patches an aggressive browser caching issue that prevented dock styling updates from loading.
Closes #311
Screen.Recording.2026-06-19.at.5.46.53.PM.mov
What Changed
src/dock-peek/index.ts: Added window restoration logic to the Dock Peek click handler and guarded the hover behavior against minimized windows.src/dock.ts: Added state logic to computedata-minimized-countand inject a dynamicaria-labelinto the tile for screen reader accessibility.assets/css/dock.css: Decoupled the edge state indicator from the window count. Introduced a new::afterpseudo-element to render a dedicated, non-colliding count badge at the bottom-right of the dock tile.includes/assets.php: Updated the CSS enqueue registration from a static version to dynamicfilemtimeversioning.tests/vitest/*: Added comprehensive test coverage for all new state transitions, hover/click behaviors, and accessibility labels.How it fixes
Dock Peek Interactivity:
win.restore()inside thespawnFocusViewTransitionclick handler, minimized instances now correctly unpack to the viewport before receiving focus.Hover Ghost-Focusing:
if (win.state === 'minimized')) inside thepointerenterlistener cleanly prevents this unintended hijack.Dedicated Minimized Badges:
bottom: 0,inset-inline-end: -4px) so it gracefully avoids colliding with the red unread-notification pills at the top-right.Screen Reader Accessibility:
aria-label(using_n()andsprintf()) so screen readers accurately announce"[App Name], X minimized windows"to visually impaired users, keeping them fully aware of background tasks.Reliable CSS Cache Busting:
includes/assets.phpcaused aggressive browser caching fordock.css. Switching to$built_version( 'assets/css/dock.css' )leverages file modification times, forcing the browser to reliably fetch the latest stylesheet changes after deployments.