Skip to content

Collapse the popover's settings into expandable cards - #48

Merged
SorcRR merged 5 commits into
masterfrom
claude/ui-ux-improvements-dmkkol
Oct 14, 2026
Merged

SorcRR merged 5 commits into
masterfrom
claude/ui-ux-improvements-dmkkol

Conversation

@SorcRR

@SorcRR SorcRR commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

Proposal B from the popover design review, the follow-up to #46: the settings below DPI collapse into compact expandable cards, the way Control Center lists its modules. The main page gets much shorter.

What changes

  • Polling rate, Lighting, Power Management, Buttons and Profiles are each a compact card of one row. The cards sit 10 pt apart, like the cards above them. Each row shows its current value: "1000 Hz", "Static · 80%" (or "Off"), "5 min", "2 remapped", or the active profile name.
  • Rows expand in place, independently. Clicking a settings row opens its controls inside its own card, using the existing controls unchanged. Any number of rows can be open at once, and which ones are open is remembered between showings (popoverExpandedSections in UserDefaults). Buttons still opens its own page.
  • Row styling: the hover highlight is an inset rounded shape, like a menu item or a Control Center row. There's a 6 pt gap between a row and the controls it opens. The cards share cardSurface() with the other cards, so their style can't drift apart.
  • Problems show on the collapsed row. A failed profile apply or sleep-timer write turns that row's value red, while the row keeps showing the real value. The error text is in the expanded section.
  • Remaps that can't work are flagged. Buttons says "Needs access" when saved remaps can't fire without Accessibility permission.
  • Battery and DPI stay open at the top, since they're what you check most.
  • Tips only show inside their section. The battery and lighting tips now appear only when their section is open, not on the main page all the time.
  • Rows a mouse can't use are left out, as the cards were before: no Lighting on the Atheris, Power Management only on the Basilisk V3 X over Bluetooth, and no Polling or Profiles over Bluetooth.
  • Offline: the sections dim along with DPI, but their rows still open, so the last-known settings stay readable. The controls inside them and the Buttons page are disabled.
  • Sub-pages that scroll are at least 560 pt tall, so a short, collapsed main page doesn't squeeze the usage charts or the button list. The colour page keeps the main page's height.
  • A half-typed profile name is dropped when the Profiles row closes or disappears.

CHANGELOG.md [Unreleased] and docs/DOCUMENTATION.md §6.2 are updated, and Tests/Scripts/changelog-test.sh passes.

Deliberately not in this PR

  • Battery in the header. The mockup also moved the battery percentage into the header and removed the battery card. That would also remove the usage-chart and refresh buttons, and the mockup didn't decide where they go. It can be its own PR once there's a design for it.
  • Darker text on the selected green chips. White on Razer green is about 1.9:1 contrast. This will be part of the upcoming appearance/theme PR.

Testing

  • Not built locally: there was no Swift toolchain where this was written, so CI is the first build.
  • Check on a Mac:
    • Rows expand and collapse with a short animation, and the popover resizes cleanly while they do.
    • Several rows can be open at once, and they're restored the next time the popover opens.
    • The hover highlight sits inside its card, and an open row's controls don't touch its header.
    • With the mouse asleep, rows still open and their controls are greyed out.
    • The Usage, Buttons and Profiles pages aren't cramped when every row is collapsed.
    • On an Atheris the Lighting card is absent.

Code review

Two review passes found 15 issues in total, and 12 are fixed:

  • Remap count: "None (default)" mappings were counted as remaps.
  • Lighting at 0%: brightness 0 showed "Static · 0%" instead of "Off".
  • Row identity: rows were identified by position, so the remaining rows misanimated when one row was removed.
  • Unfinished profile name: a half-typed profile name came back after its row was closed, or after the row disappeared with a mouse that has no profiles.
  • Colour page sizing: the 560 pt minimum padded the colour page with empty space.
  • Offline rows: rows couldn't be opened while the mouse was offline, so last-known settings were unreadable.
  • Sleep timer error: a failed sleep-timer write replaced the known value with "Not verified".
  • Profile apply error: a failed profile apply replaced the active profile's name with "Couldn't apply" for the rest of the session, and the same error appeared twice when the section was open.
  • Remaps without access: saved remaps were counted as working while Accessibility permission was missing.
  • Card styling: the row cards duplicated the card styling instead of sharing it.

Left as they are:

  • Popover grows on sub-pages: the popover grows when you open a scrolling sub-page from a short main page. This is the deliberate 560 pt minimum.
  • Basilisk DPI button: the Buttons count doesn't include a Basilisk DPI button bound to a software action.
  • Section setting re-reads: the open-sections setting is re-read on each redraw. It's a few cheap string splits.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SJ8Eskh7E8TDXYvX2yS9yZ

Polling rate, Lighting, Power Management, Buttons and Profiles become
rows in a single grouped card, Control-Center style. Each row shows its
current value; the settings rows expand in place one at a time, and the
open one is remembered. Battery and DPI stay open at the top. The
battery/lighting tips now only appear inside their open section.

Scrolling sub-pages get a 560 pt minimum so a short collapsed main page
doesn't squeeze them; a half-typed profile name is dropped when its row
closes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SJ8Eskh7E8TDXYvX2yS9yZ
- Rows no longer auto-collapse each other; the set of open sections is
  remembered (popoverExpandedSections).
- The hover highlight is an inset rounded shape instead of a full-width
  band that ran past the inset separators; separators are inset on both
  sides.
- 6 pt between a row and its opened controls, so a highlighted header
  doesn't sit on top of them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SJ8Eskh7E8TDXYvX2yS9yZ
Polling rate, Lighting, Power Management, Buttons and Profiles each sit
in their own card, 10 pt apart like the cards above, instead of one card
split by separators, so an open section's controls are clearly bounded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SJ8Eskh7E8TDXYvX2yS9yZ
@SorcRR SorcRR changed the title Collapse the popover's settings into one expandable list Collapse the popover's settings into expandable cards Oct 14, 2026
- Section rows open offline so last-known settings stay readable; only
  their controls and the Buttons page are disabled.
- The sleep timer and Profiles rows keep showing the real value after a
  failed write, in red, instead of replacing it with an error that could
  stick for the session.
- Buttons says "Needs access" when saved remaps can't fire without
  Accessibility permission.
- Drop a half-typed profile name when the Profiles row disappears.
- Share the card surface (cardSurface) between card() and the rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SJ8Eskh7E8TDXYvX2yS9yZ
As a View extension it was main-actor isolated, and card() (a
nonisolated free function) couldn't call it: "non-Sendable 'some
View'-typed result can not be returned from main actor-isolated
instance method". A free function, like card() itself, has no
isolation, so both call sites compile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SJ8Eskh7E8TDXYvX2yS9yZ
@SorcRR
SorcRR merged commit 8509e92 into master Oct 14, 2026
1 check passed
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.

2 participants