Repository navigation
feat(desktop-cards): pin cards to the Windows desktop - #3527
Conversation
Attach a card window to the Explorer desktop layer that hosts SHELLDLL_DefView, so a pinned card sits above the icons and behind other applications. Attachment keeps a DPI-context guard, saves and restores the original parent/style, rolls back on failure, and persists screen coordinates so detaching and multi-monitor restore do not jump. Desktop pinning and global always-on-top stay mutually exclusive: the stacking request applies before persistence, reports a host refusal instead of a fake success, and the card menu negotiates the advertised capability before extending the legacy always-on-top payload.
Hidden cards have no window, so expose desktop pinning everywhere cards are listed: the Home card list shows the applied mode and toggles it, and Settings adds one pin switch per card. The host advertises desktopPinningSupported in the card list, so those surfaces render the action without reading every card first, and an older host simply keeps it hidden. A refused change is reported and never flips the switch. Document desktop pinning in the interactive capability catalog and regenerate the docs, search, and product-control artifacts.
The capability contract check requires an unstructuredInteraction reason to quote its own item title, so a reviewer can match the reason to the capability without cross-referencing. Quote the title in both languages and regenerate the docs, search, and product-control artifacts.
# Conflicts: # docs/interactive-capabilities/README.md # docs/interactive-capabilities/capabilities.json # docs/interactive-capabilities/technical/product-control-open-audit.json # docs/interactive-capabilities/technical/tauri-command-map.json # src/apps/desktop/Cargo.toml # src/crates/contracts/product-domains/src/generated/product-control-catalog.json # src/web-ui/src/app/global-search/generated/interactive-capabilities.json # src/web-ui/src/infrastructure/api/generated/productControl.ts
|
Merged current
Checks run on macOS after the merge: Before this merges, a few things need a real Windows check:
|
- Recreate a pinned card when Explorer destroys its desktop parent and reconcile pinned cards on the TaskbarCreated broadcast. - Retry pinning with bounded backoff (about 31 s) when the desktop is not available yet, then unpin the card, show it as a normal window, and record desktopPinFailure so the card, Home and Settings report it. - Restore the always-on-top mode a card had before it was pinned. - Refuse to attach to a hung desktop and set WS_EX_NOPARENTNOTIFY so destroying a pinned card never sends WM_PARENTNOTIFY into Explorer.
|
Pushed 529869a to address the Windows review points:
Checks on macOS: Not verified: none of this has run on Windows. Windows CI will be the compile evidence. A manual pass is still needed for |
…op hosts The explicit desktop capability list introduced for CLI peer hosts dropped SshProxyCommandV1, which left the checked-in generated registry stale on main.
# Conflicts: # src/crates/contracts/product-domains/src/remote_surface/capabilities.rs
What
Desktop cards can now be pinned to the Windows desktop: the card window is attached to the Explorer desktop layer that hosts
SHELLDLL_DefView, so a pinned card sits above the desktop icons and behind other applications. This is a second display mode next to global always-on-top (they are mutually exclusive).Pinning is available from three places, and hidden cards can be pinned too — their preference is stored and applied the next time the card is shown:
Pin to desktop/Always on top (all apps))How it works
Native layer (
src/apps/desktop/src/desktop_cards/desktop_layer.rs, Windows only)SHELLDLL_DefView, then reparents the card withSetParent+WS_CHILD, keeping the original style and parent in a window property so detach is lossless.SetParentwith mismatched DPI can forcibly reset the DPI awareness of every OpenBitFun window.Explorermissing, a rejectedSetParent, or a failed style change produce an explicit error and a rollback instead of a half-attached window.Movedevents persistGetWindowRectscreen coordinates, because an attached window reports parent-client coordinates; otherwise detach and multi-monitor restore would jump.Product layer
set_stackingapplies the native change before persisting, rolls back on failure, and reports the host error (the card menu shows it via the newdesktopCard.stackingFailedcopy).set_visible(true)re-attaches a pinned card;bring_to_frontis skipped for pinned cards so they are not promoted into the global topmost band.Capability negotiation / compatibility
desktop_card_set_always_on_topkeeps its name;desktopPinnedis a new optional field, so an older client still sends the original payload and keeps working.desktop_card_getanddesktop_card_listadvertisedesktopPinningSupportedas a runtime fact (never persisted). Older hosts omit it, and the UI then hides the pin action instead of failing.desktopPinnedlives in the record's forward-compatible extension map, so a rewrite by an older build keeps it (covered by a store test).Capability catalog
desktop-pinningitem insrc/shared/interactive-capabilities/catalog.json(classified asopen/unstructuredInteraction, since theDesktopCardtool deliberately does not carry this display preference), plus regenerated docs, search, and product-control artifacts.Verification (local)
cargo test --locked -p openbitfun-desktop --no-default-features --lib desktop_cards→ 17 passed, 1 ignored... live_explorer_attachment_round_trip -- --ignored→ passed against a real Explorer session (hidden disposable windows; Explorer itself is never modified)pnpm --dir src/web-ui run test:run src/app/components/DesktopCard/DesktopCardWindow.test.tsx src/infrastructure/api/service-api/DesktopCardAPI.test.ts src/app/scenes/personal-overview/OverviewDesktopCardsSection.test.tsx src/app/scenes/settings/pages/application/DesktopCardsSettingsSection.test.tsx→ 34 passedpnpm --dir src/web-ui run type-check,pnpm run i18n:audit,pnpm run capabilities:check,pnpm run fmt:rs→ cleanDeferred to CI and to a manual Windows pass (not reproducible headlessly): dragging/resizing a pinned card, Win+D, Explorer restart, mixed-DPI monitor moves, and hide/show switching between both modes.
Remote scenarios
Desktop cards remain
LocalOnly/ControllerLocalin the Product Operation Registry; no command was added and the registry table is unchanged. Remote control, peer device, remote workspace, and detached dispatch are unaffected, and the management surfaces keep the pin action hidden when the host cannot advertise it.Upgrade compatibility
Additive request field, additive runtime capability in
get/listpayloads, and an additive persisted extra field. No migration, no data removal, and older clients/hosts stay on their working path.