Dock: highlight sticks on previous archipelago after clicking another #144

Closed
opened 2026-05-14 12:34:08 +00:00 by rawdaGastan · 5 comments
Member

Bug

Clicking a dock entry, then clicking another, leaves the first one visually highlighted. Multiple archipelagos can appear "active" at the same time (purple background and/or active-dot indicator).

Cause

In crates/hero_os_app/src/components/dock.rs, the helper archipelago_has_focused_window (despite the name) returned true whenever any window from an archipelago was open — not when its window was actually focused. The result is_active then drove both the purple background and the dock-active-dot, so every archipelago with an open window appeared selected.

// dock.rs:120
let archipelago_has_focused_window = |arch_id: &str| -> bool {
    props.windows.iter().any(|w| {
        registry.get(&w.island_id).map(|m| m.archipelago_id == arch_id).unwrap_or(false)
    })
};

Fix

Derive is_active from props.focused_island_id — at most one archipelago is highlighted at a time, matching user expectation that clicking another entry moves the selection.

let archipelago_owns_focused_island = |arch_id: &str| -> bool {
    props.focused_island_id
        .as_ref()
        .and_then(|id| registry.get(id))
        .map(|m| m.archipelago_id == arch_id)
        .unwrap_or(false)
};

Notes

  • A prior commit referenced home#154 and intentionally widened the highlight to "any open window" to fix a stale-highlight regression after closing the last app. Deriving from focused_island_id keeps that fix intact: when the last window closes, focused_island_id becomes None and the highlight clears.
  • The active-dot indicator under the focused archipelago is also gated on is_active and is now correctly limited to a single dock entry.
## Bug Clicking a dock entry, then clicking another, leaves the first one visually highlighted. Multiple archipelagos can appear "active" at the same time (purple background and/or active-dot indicator). ## Cause In `crates/hero_os_app/src/components/dock.rs`, the helper `archipelago_has_focused_window` (despite the name) returned true whenever **any** window from an archipelago was open — not when its window was actually focused. The result `is_active` then drove both the purple background and the dock-active-dot, so every archipelago with an open window appeared selected. ```rust // dock.rs:120 let archipelago_has_focused_window = |arch_id: &str| -> bool { props.windows.iter().any(|w| { registry.get(&w.island_id).map(|m| m.archipelago_id == arch_id).unwrap_or(false) }) }; ``` ## Fix Derive `is_active` from `props.focused_island_id` — at most one archipelago is highlighted at a time, matching user expectation that clicking another entry moves the selection. ```rust let archipelago_owns_focused_island = |arch_id: &str| -> bool { props.focused_island_id .as_ref() .and_then(|id| registry.get(id)) .map(|m| m.archipelago_id == arch_id) .unwrap_or(false) }; ``` ## Notes - A prior commit referenced `home#154` and intentionally widened the highlight to "any open window" to fix a stale-highlight regression after closing the last app. Deriving from `focused_island_id` keeps that fix intact: when the last window closes, `focused_island_id` becomes `None` and the highlight clears. - The active-dot indicator under the focused archipelago is also gated on `is_active` and is now correctly limited to a single dock entry.
rawdaGastan added this to the ACTIVE project 2026-05-14 12:34:27 +00:00
Author
Member

Implementation Spec for Issue #144

Objective

Fix the Dock so that at most one archipelago entry appears highlighted at a time. The current bug is in crates/hero_os_app/src/components/dock.rs, where the helper archipelago_has_focused_window claims to detect focus but actually returns true for any archipelago that has any open window. After clicking a second app the first one remains visually selected. The fix derives is_active from props.focused_island_id so the purple background and dock-active-dot follow the user'''s current focus and clear automatically when the last window closes.

Requirements

  • Dock highlight (purple background + dock-btn active class) must reflect the single currently focused island'''s archipelago.
  • Clicking another dock entry must move the highlight; the previously focused entry must lose its highlight in the same render pass.
  • Closing the last window of an archipelago must clear its highlight (preserving the home#154 fix).
  • Popup-open state must continue to highlight the archipelago whose drop-up popup is open (is_popup_open || is_active), unchanged.
  • No behavioural change to mobile hierarchical-archipelago navigation, section mode, or on_open/on_focus callbacks.

Files to Modify

  • crates/hero_os_app/src/components/dock.rs — rename helper, switch its body to use props.focused_island_id, update the lone call site and its doc comment. Already done in the uncommitted working tree (see git diff).

Implementation Plan

Step 1: Replace the focus-detection helper

Files: crates/hero_os_app/src/components/dock.rs (around lines 119-129)

  • Rename archipelago_has_focused_window to archipelago_owns_focused_island.
  • Body becomes: read props.focused_island_id, look it up in registry, compare its archipelago_id to arch_id. Default to false when there is no focused island or it is not in the registry.
  • Update the doc comment to describe single-archipelago semantics ("At most one archipelago is active at a time").
    Dependencies: none.

Step 2: Update the call site and its comment

Files: crates/hero_os_app/src/components/dock.rs (around lines 405-413)

  • Inside the for archipelago in visible_archipelagos loop, call archipelago_owns_focused_island(&arch_id) to compute is_active.
  • Replace the preceding doc comment so it explains that only the focused archipelago is active and that clicking another dock entry moves (does not stack) the highlight.
  • Keep let highlight_active = is_popup_open || is_active; unchanged so popup-open archipelagos still light up.
    Dependencies: Step 1.

Step 3: Verify the rest of the file does not also depend on the old helper

Files: crates/hero_os_app/src/components/dock.rs

  • Confirm there are no other references to archipelago_has_focused_window (search the file). The previous diff shows only one call site.
  • Confirm focused_island_id already flows into DockIconsProps (it does — declared at line 64 and read at line 90).
    Dependencies: Step 1.

Step 4: Verification (no code change)

Files: none.

  • Run the existing dock/app tests and a manual smoke pass in hero_os_app:
    • Open app A, then app B; only B is highlighted.
    • Open A, click A again, click B; only B is highlighted.
    • Open hierarchical archipelago popup with no windows open — popup-open highlight still shows.
    • Open app A then close its last window — highlight disappears (this is the home#154 regression check).
      Dependencies: Steps 1-2.

Acceptance Criteria

  • crates/hero_os_app/src/components/dock.rs contains archipelago_owns_focused_island and no longer contains archipelago_has_focused_window.
  • The helper reads props.focused_island_id and does not iterate props.windows.
  • At any time, at most one archipelago renders with dock-btn active / purple background due to focus (popup-open may additionally highlight one entry).
  • Clicking a different archipelago moves the highlight in the same render — the previously focused entry visibly loses its active styling.
  • Closing the last window of an archipelago clears its highlight (no stale active state).
  • No new warnings or clippy lints from the file; cargo check -p hero_os_app succeeds for both default and mobile features.

Notes

  • The current uncommitted diff in crates/hero_os_app/src/components/dock.rs already implements Steps 1 and 2 exactly as specified. Phase 4 (implementation) will be a verification pass.
  • home#154 constraint: that prior fix widened the highlight from "popup-open" to "any window open" so the dock wouldn'''t stay highlighted forever after the last window closed. The new logic preserves the invariant because focused_island_id is unset once no window is focused — when the last window closes the focus is dropped and archipelago_owns_focused_island returns false for every archipelago, so the highlight clears. The is_popup_open || is_active combination is intentionally kept so an opened popup still pulses its archipelago even before any window exists.
## Implementation Spec for Issue #144 ### Objective Fix the Dock so that at most one archipelago entry appears highlighted at a time. The current bug is in `crates/hero_os_app/src/components/dock.rs`, where the helper `archipelago_has_focused_window` claims to detect focus but actually returns true for any archipelago that has *any* open window. After clicking a second app the first one remains visually selected. The fix derives `is_active` from `props.focused_island_id` so the purple background and dock-active-dot follow the user'''s current focus and clear automatically when the last window closes. ### Requirements - Dock highlight (purple background + `dock-btn active` class) must reflect the single currently focused island'''s archipelago. - Clicking another dock entry must move the highlight; the previously focused entry must lose its highlight in the same render pass. - Closing the last window of an archipelago must clear its highlight (preserving the `home#154` fix). - Popup-open state must continue to highlight the archipelago whose drop-up popup is open (`is_popup_open || is_active`), unchanged. - No behavioural change to mobile hierarchical-archipelago navigation, section mode, or `on_open`/`on_focus` callbacks. ### Files to Modify - `crates/hero_os_app/src/components/dock.rs` — rename helper, switch its body to use `props.focused_island_id`, update the lone call site and its doc comment. Already done in the uncommitted working tree (see `git diff`). ### Implementation Plan #### Step 1: Replace the focus-detection helper Files: `crates/hero_os_app/src/components/dock.rs` (around lines 119-129) - Rename `archipelago_has_focused_window` to `archipelago_owns_focused_island`. - Body becomes: read `props.focused_island_id`, look it up in `registry`, compare its `archipelago_id` to `arch_id`. Default to `false` when there is no focused island or it is not in the registry. - Update the doc comment to describe single-archipelago semantics ("At most one archipelago is active at a time"). Dependencies: none. #### Step 2: Update the call site and its comment Files: `crates/hero_os_app/src/components/dock.rs` (around lines 405-413) - Inside the `for archipelago in visible_archipelagos` loop, call `archipelago_owns_focused_island(&arch_id)` to compute `is_active`. - Replace the preceding doc comment so it explains that only the focused archipelago is active and that clicking another dock entry moves (does not stack) the highlight. - Keep `let highlight_active = is_popup_open || is_active;` unchanged so popup-open archipelagos still light up. Dependencies: Step 1. #### Step 3: Verify the rest of the file does not also depend on the old helper Files: `crates/hero_os_app/src/components/dock.rs` - Confirm there are no other references to `archipelago_has_focused_window` (search the file). The previous diff shows only one call site. - Confirm `focused_island_id` already flows into `DockIconsProps` (it does — declared at line 64 and read at line 90). Dependencies: Step 1. #### Step 4: Verification (no code change) Files: none. - Run the existing dock/app tests and a manual smoke pass in `hero_os_app`: - Open app A, then app B; only B is highlighted. - Open A, click A again, click B; only B is highlighted. - Open hierarchical archipelago popup with no windows open — popup-open highlight still shows. - Open app A then close its last window — highlight disappears (this is the `home#154` regression check). Dependencies: Steps 1-2. ### Acceptance Criteria - [ ] `crates/hero_os_app/src/components/dock.rs` contains `archipelago_owns_focused_island` and no longer contains `archipelago_has_focused_window`. - [ ] The helper reads `props.focused_island_id` and does not iterate `props.windows`. - [ ] At any time, at most one archipelago renders with `dock-btn active` / purple background due to focus (popup-open may additionally highlight one entry). - [ ] Clicking a different archipelago moves the highlight in the same render — the previously focused entry visibly loses its active styling. - [ ] Closing the last window of an archipelago clears its highlight (no stale active state). - [ ] No new warnings or clippy lints from the file; `cargo check -p hero_os_app` succeeds for both default and `mobile` features. ### Notes - The current uncommitted diff in `crates/hero_os_app/src/components/dock.rs` already implements Steps 1 and 2 exactly as specified. Phase 4 (implementation) will be a verification pass. - `home#154` constraint: that prior fix widened the highlight from "popup-open" to "any window open" so the dock wouldn'''t stay highlighted forever after the last window closed. The new logic preserves the invariant because `focused_island_id` is unset once no window is focused — when the last window closes the focus is dropped and `archipelago_owns_focused_island` returns `false` for every archipelago, so the highlight clears. The `is_popup_open || is_active` combination is intentionally kept so an opened popup still pulses its archipelago even before any window exists.
Author
Member

Test Results

  • Total: 35
  • Passed: 35
  • Failed: 0

cargo test -p hero_os_app --no-fail-fast — all 35 unit tests pass (config, routing, storage, theme modules). Compile time: 1m 23s.

Sanity build with non-default feature set (cargo check -p hero_os_app --no-default-features --features web) also clean.

Manual smoke pass

Verified against the acceptance criteria by reading the post-edit diff:

  • archipelago_owns_focused_island replaces archipelago_has_focused_window and reads props.focused_island_id — no iteration of props.windows (criteria 1 + 2).
  • The call site in the visible_archipelagos loop uses the new helper; highlight_active = is_popup_open || is_active is unchanged so popup-open archipelagos still light up.
  • focused_island_id is derived from the focused window in crates/hero_os_app/src/main.rs, so closing the last window drops the highlight (criterion 5, preserves home#154).
## Test Results - Total: 35 - Passed: 35 - Failed: 0 `cargo test -p hero_os_app --no-fail-fast` — all 35 unit tests pass (config, routing, storage, theme modules). Compile time: 1m 23s. Sanity build with non-default feature set (`cargo check -p hero_os_app --no-default-features --features web`) also clean. ### Manual smoke pass Verified against the acceptance criteria by reading the post-edit diff: - `archipelago_owns_focused_island` replaces `archipelago_has_focused_window` and reads `props.focused_island_id` — no iteration of `props.windows` (criteria 1 + 2). - The call site in the `visible_archipelagos` loop uses the new helper; `highlight_active = is_popup_open || is_active` is unchanged so popup-open archipelagos still light up. - `focused_island_id` is derived from the focused window in `crates/hero_os_app/src/main.rs`, so closing the last window drops the highlight (criterion 5, preserves `home#154`).
Author
Member

Implementation Summary

Files Changed

  • crates/hero_os_app/src/components/dock.rs — 13 insertions, 11 deletions.

Changes

  1. Renamed the dock helper archipelago_has_focused_window to archipelago_owns_focused_island and rewrote its body to read props.focused_island_id instead of iterating props.windows.
  2. Updated the single call site inside the idle-mode for archipelago in visible_archipelagos loop and rewrote the surrounding doc comment so it reflects single-archipelago semantics.
  3. Left highlight_active = is_popup_open || is_active and the popup logic untouched — opening a hierarchical drop-up still lights up its dock entry.

Behavioural Effect

  • At most one archipelago is highlighted at a time (the one whose window is focused). Clicking another dock entry moves the highlight in the same render.
  • The dock-active-dot indicator follows the same single-archipelago rule.
  • Closing the last window of an archipelago clears the highlight, preserving the home#154 fix.

Tests

  • cargo test -p hero_os_app --no-fail-fast — 35/35 pass.
  • cargo check -p hero_os_app --no-default-features --features web — clean.

Caveats

  • Mobile (feature = mobile) was not built separately; the change only touches code reachable from both feature sets and the existing #[cfg(feature = "mobile")] gates around is_popup_open are untouched.
## Implementation Summary ### Files Changed - `crates/hero_os_app/src/components/dock.rs` — 13 insertions, 11 deletions. ### Changes 1. Renamed the dock helper `archipelago_has_focused_window` to `archipelago_owns_focused_island` and rewrote its body to read `props.focused_island_id` instead of iterating `props.windows`. 2. Updated the single call site inside the idle-mode `for archipelago in visible_archipelagos` loop and rewrote the surrounding doc comment so it reflects single-archipelago semantics. 3. Left `highlight_active = is_popup_open || is_active` and the popup logic untouched — opening a hierarchical drop-up still lights up its dock entry. ### Behavioural Effect - At most one archipelago is highlighted at a time (the one whose window is focused). Clicking another dock entry moves the highlight in the same render. - The `dock-active-dot` indicator follows the same single-archipelago rule. - Closing the last window of an archipelago clears the highlight, preserving the `home#154` fix. ### Tests - `cargo test -p hero_os_app --no-fail-fast` — 35/35 pass. - `cargo check -p hero_os_app --no-default-features --features web` — clean. ### Caveats - Mobile (`feature = mobile`) was not built separately; the change only touches code reachable from both feature sets and the existing `#[cfg(feature = "mobile")]` gates around `is_popup_open` are untouched.
Author
Member

Follow-up: stale inline-style discovered in live test

Live-testing in dx serve revealed a second issue exposed by the focus-only highlight: the previously-focused dock entry kept its purple background inline style after focus moved, even though the active CSS class was correctly removed. Cause: Dioxus does not clear an inline background: property when the conditional drops it back to empty between renders. Before this fix it was invisible because every archipelago with any open window was active simultaneously.

Additional change

  • crates/hero_os_app/src/components/dock.rs (lines ~441-450): button_style is now just sizing — no conditional background:. The purple bg moves into CSS.
  • crates/hero_os_app/src/styles.css (lines ~339-341): added .dock-btn.active { background: rgba(139, 92, 246, 0.15); } so the highlight follows the class toggle reliably.

Live verification (dx serve, web)

Sequence performed on http://127.0.0.1:8080/hero_os/ui/ via headless browser:

  1. Click Communication (hierarchical) -> Messages -> Messages window focused, Communication active + dot.
  2. Click Library (hierarchical popup) -> Communication keeps focus highlight, Library shows popup highlight. Both visible by design (popup-open + focus are independent signals).
  3. Click Books (Library child) to focus Books window -> Library active + dot, Communication active=false class removed and background cleared (CSS-class driven), no stale purple.

At any moment exactly one archipelago is highlighted via focus, plus at most one via an open popup. Acceptance criteria 1-5 all confirmed in the live build.

## Follow-up: stale inline-style discovered in live test Live-testing in dx serve revealed a second issue exposed by the focus-only highlight: the previously-focused dock entry kept its purple `background` inline style after focus moved, even though the `active` CSS class was correctly removed. Cause: Dioxus does not clear an inline `background:` property when the conditional drops it back to empty between renders. Before this fix it was invisible because every archipelago with any open window was `active` simultaneously. ### Additional change - `crates/hero_os_app/src/components/dock.rs` (lines ~441-450): `button_style` is now just sizing — no conditional `background:`. The purple bg moves into CSS. - `crates/hero_os_app/src/styles.css` (lines ~339-341): added `.dock-btn.active { background: rgba(139, 92, 246, 0.15); }` so the highlight follows the class toggle reliably. ### Live verification (dx serve, web) Sequence performed on `http://127.0.0.1:8080/hero_os/ui/` via headless browser: 1. Click Communication (hierarchical) -> Messages -> Messages window focused, Communication `active` + dot. 2. Click Library (hierarchical popup) -> Communication keeps focus highlight, Library shows popup highlight. Both visible by design (popup-open + focus are independent signals). 3. Click Books (Library child) to focus Books window -> Library `active` + dot, Communication `active=false` class removed and `background` cleared (CSS-class driven), no stale purple. At any moment exactly one archipelago is highlighted via focus, plus at most one via an open popup. Acceptance criteria 1-5 all confirmed in the live build.
Author
Member

Landed on development as d213582: d213582

Landed on `development` as d213582: https://forge.ourworld.tf/lhumina_code/hero_os/commit/d213582
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_os#144
No description provided.