Skip to content

Improve minimized window UX with restore support and count badges - #312

Merged
AllTerrainDeveloper merged 10 commits into
WordPress:trunkfrom
KarunyaChavan:fix/dock-window-ux-and-state-indicators
Jul 17, 2026
Merged

Improve minimized window UX with restore support and count badges#312
AllTerrainDeveloper merged 10 commits into
WordPress:trunkfrom
KarunyaChavan:fix/dock-window-ux-and-state-indicators

Conversation

@KarunyaChavan

@KarunyaChavan KarunyaChavan commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

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 compute data-minimized-count and inject a dynamic aria-label into the tile for screen reader accessibility.
  • assets/css/dock.css: Decoupled the edge state indicator from the window count. Introduced a new ::after pseudo-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 dynamic filemtime versioning.
  • tests/vitest/*: Added comprehensive test coverage for all new state transitions, hover/click behaviors, and accessibility labels.

How it fixes

  • Dock Peek Interactivity:

    • Previously, clicking a minimized window thumbnail in the Dock Peek menu failed to restore the window to the screen.
    • By invoking win.restore() inside the spawnFocusViewTransition click handler, minimized instances now correctly unpack to the viewport before receiving focus.
  • Hover Ghost-Focusing:

    • Hovering over a minimized instance card was automatically triggering the window manager's focus logic, improperly shifting focus to an invisible/tucked window.
    • Adding an early return (if (win.state === 'minimized')) inside the pointerenter listener cleanly prevents this unintended hijack.
  • Dedicated Minimized Badges:

    • The dock edge indicator (e.g., the focus bar) used to completely mask the count of tucked-away windows if another instance was actively focused.
    • This PR completely decouples them. The edge indicator now strictly reflects the highest priority app state, while a brand new, macOS-style hollow ring badge dynamically displays the exact count of minimized instances.
    • We anchored it to the bottom-right corner (bottom: 0, inset-inline-end: -4px) so it gracefully avoids colliding with the red unread-notification pills at the top-right.
  • Screen Reader Accessibility:

    • The dock tile now dynamically updates its aria-label (using _n() and sprintf()) 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:

    • Using the static plugin version in includes/assets.php caused aggressive browser caching for dock.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.
Open WordPress Playground Preview

…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.
@KarunyaChavan
KarunyaChavan marked this pull request as ready for review June 19, 2026 12:26
@AllTerrainDeveloper

Copy link
Copy Markdown
Collaborator

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.

image

@KarunyaChavan

Copy link
Copy Markdown
Contributor Author

Hey @AllTerrainDeveloper, thanks for taking the time to review the changes and for the thoughtful feedback!

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! 😆)

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.

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.

Sounds good.

@AllTerrainDeveloper

Copy link
Copy Markdown
Collaborator

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.

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
@KarunyaChavan

KarunyaChavan commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

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

Changes Overview

1. Persist tile highlight while the Peek popover is open

On trunk, moving the cursor from the dock icon into the Peek popover causes the dock tile to lose its highlight, making the interaction feel disconnected. This change introduces the data-peek-active state on the dock tile, ensuring the icon remains highlighted for the entire time the Peek popover is visible. The behavior now aligns with the Windows taskbar experience.

Trunk This Branch
Trunk behavior Updated behavior

2. Dock stacked card visual

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

Dock stacked card visual

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?

@AllTerrainDeveloper

Copy link
Copy Markdown
Collaborator

Nice! I'll review and test it tomorrow morning, I had many duties today :)

@AllTerrainDeveloper AllTerrainDeveloper left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Ran a high effort review over this branch's diff against trunk. Two correctness issues and one small cleanup, pinned to the lines below.

Comment thread src/dock-peek/index.ts
Comment on lines 364 to 370
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 );
} );

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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:

Suggested change
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 );
}
} );

Comment thread assets/css/dock.css Outdated
content: "";
position: absolute;
top: -4px;
left: -4px;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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:

Suggested change
left: -4px;
inset-inline-start: -4px;

Comment thread src/dock-peek/index.ts Outdated
Comment on lines +406 to +408
if ( win.state === 'minimized' ) {
win.restore();
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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.
@AllTerrainDeveloper

Copy link
Copy Markdown
Collaborator

Pushed 2e005d6 addressing the review above:

  • Cleared the peek card's collapsed data-state="minimized" when the underlying window is restored (hover and click paths), via a shared restoreIfMinimized() helper so the two call sites cannot drift apart again.
  • Switched the stacked card hint in dock.css from a hardcoded left offset to inset-inline-start so it mirrors correctly in RTL.

Verified locally: npm run build, npm run lint, npm run typecheck, and npm run test:js (full suite, 1843 tests) all pass.

@AllTerrainDeveloper
AllTerrainDeveloper merged commit 650e4c3 into WordPress:trunk Jul 17, 2026
5 checks passed
@AllTerrainDeveloper

Copy link
Copy Markdown
Collaborator

Just a note, that you are already appearing here:
https://next-admin.blog/contribute/

If you have any inconvenience, please tell it to me :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dock Peek Interactivity and Minimized Window UX

2 participants