Skip to content

Introduce hub shell with dashboard, storage, and health sections - #7

Merged
Toqsick merged 6 commits into
mainfrom
claude/linux-master-assistant-ui-1uvcse
Jul 25, 2026
Merged

Toqsick merged 6 commits into
mainfrom
claude/linux-master-assistant-ui-1uvcse

Conversation

@Toqsick

@Toqsick Toqsick commented Jul 25, 2026

Copy link
Copy Markdown
Owner

This PR introduces a new hub interface as the primary entry point for the application, replacing the previous search-first navigation model. The hub provides a persistent sidebar-based navigation with multiple sections for system monitoring and management.

Summary

The application now features a new HubShell that serves as the main frame, with collapsible sidebar navigation and swappable content sections. This includes a redesigned dashboard with quick actions, a storage section showing disk usage, and refactored health/security views that can be embedded as sections or accessed standalone.

Key Changes

New Hub Architecture:

  • Added HubShell with persistent sidebar navigation and section-based routing (dashboard, search, storage, health, security)
  • Sidebar collapses to an icon rail below 1000px width
  • Sections are created on first visit and kept alive to avoid unnecessary polling

Design System (Hermes Tokens):

  • Introduced HermesTokens class porting design tokens from the Hermes WebUI stylesheet
  • Supports light and dark themes with semantic colors (accent, success, warning, error, info)
  • Elevation expressed through 1px borders and tints rather than shadows

New Hermes Widgets:

  • HermesCard: Base surface with border-based elevation
  • HermesNavItem: Sidebar navigation items with active/hover states
  • HermesStatTile: Dashboard tiles with icon, value, unit, and optional visual
  • HermesSparkline: Minimal line chart for rolling data series
  • HermesBadge: Semantic status indicators with tone-based coloring
  • HermesCopyCommand: Monospace command display with copy button

Refactored System Monitoring:

  • Created SystemStatsService to centralize polling of system stats (memory, CPU, uptime, disk)
  • Extracted pure parsers from LinuxSystem, LinuxProcess, and LinuxFilesystem for testability
  • SystemStatus widget now reads from shared service instead of polling independently

Security & Health Views:

  • Refactored SecurityCheckOverview to separate page chrome from read-only content
  • SecurityCheckContent now displays findings without offering to execute fixes
  • Added SecurityFinding model with severity levels and command suggestions
  • Refactored LinuxHealthOverview similarly, extracting LinuxHealthContent for hub embedding

Dashboard Section:

  • New DashboardSection showing quick-action tiles and system stats
  • Loads action entries from catalog and surfaces most-used ones
  • Integrates with SystemStatsService for live stats

Storage Section:

  • New StorageSection displaying mounted filesystems with fill levels
  • Links to disk cleaner for remediation

Localization:

  • Added 30+ new localization strings for hub UI (dashboard, storage, search, quick actions, etc.)
  • Updated German, Italian, Finnish, and English translations
  • Fixed JSON formatting in localization files

Theme Controller:

  • New ThemeController for app-wide appearance state management
  • Enables light/dark theme switching without app restart

Testing:

  • Added hermes_tokens_test.dart validating WCAG AA contrast ratios for text pairs
  • Added hermes_widgets_test.dart covering HermesCard, HermesNavItem, HermesStatTile, etc.
  • Added system_parsers_test.dart testing parsers for df, free, ps, and uptime output

Notable Implementation Details

  • Section changes stay out of the Navigator to avoid unbounded history growth
  • SystemStatsService uses ValueListenable for reactive updates across multiple widgets
  • Hermes tokens follow a three-rule visual language: elevation via border+tint, tinted chips from accent colors, metadata de-emphasized with opacity
  • Security/health views are now read-only, showing commands for user execution rather than auto-fixing
  • MintY color defaults updated to use Hermes gold accent, with distribution-specific overrides still supported

https://claude.ai/code/session_01HioAo2j6ufA9GsyKBWF69u

claude added 6 commits July 25, 2026 12:59
The app had no ColorScheme in either theme -- only primaryColor was set --
so every Material 3 component fell back to Flutter's defaults instead of
the app's colors. Text styles hardcoded black or white, which is why a
second *White variant existed for every heading.

Introduce HermesTokens, a ThemeExtension carrying the palette ported from
hermes-webui's stylesheet (warm gold on cream, gold on navy-black), plus
the radius and spacing scale. Both themes are now built from one function,
so light and dark cannot drift apart.

Five of the shipped light values fail WCAG AA as text on cream -- accent
text lands at 4.31:1 on the strong tint, warning at 2.56:1, and white on
gold at 3.25:1. Gold is a light hue and needs two or three shade steps
down before it clears AA. The text-bearing colors are darkened accordingly
and a dedicated onAccent ink replaces white-on-accent; the hues are
unchanged. accessibleOn() walks the lightness axis to keep that guarantee
when a distribution color is substituted.

Also:
- MyApp becomes stateful and listens to a ThemeController, so switching
  light/dark no longer requires restarting the app.
- Per-distribution colors become opt-in (use_distro_colors, default off)
  now that the app ships a palette of its own.
- colorfulBackground changes from a static field to a getter; as a field
  it captured currentColor once at class-load time and kept painting the
  old gradient after a color change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HioAo2j6ufA9GsyKBWF69u
Every live tile owned a Timer.periodic that called setState with an empty
body. Because each tile also built its Future inside build(), that
re-ran the underlying command on every tick: SystemStatus forked `free`
and `/proc/loadavg` every 2s, and the Linux Health page forked seven
commands every 5s. Health also leaked its timer unless the user happened
to leave through the back button.

SystemStatsService collects one snapshot per interval and publishes it
through a ValueNotifier. Polling is reference counted, so it stops when
no tile is on screen, and an in-flight poll is not allowed to overlap --
on a busy machine `ps` can outlast the interval, and stacking runs would
only make it worse. A failed poll keeps the last good snapshot and
records the error rather than freezing silently.

getCpuThreadCount() now caches: the count cannot change while the app
runs, and it was forking `nproc` on every CPU reading.

The df, ps and uptime parsers are split into pure functions so they can
be tested against captured output instead of the live system.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HioAo2j6ufA9GsyKBWF69u
Every admin feature already existed as a working screen, but the only way
to reach one was to guess its name in the search box, and nothing showed
the state of the machine at a glance.

Add a persistent shell -- sidebar, top bar, content area -- with sections
for Dashboard, Search, Storage, Linux health and Security. Sections are
swapped inside the frame rather than pushed: the app's convention is to
"go back" by pushing the destination again, which grows the navigator
history without bound, and section changes should not add to that.
Sections are built on first visit so the search field cannot steal focus
while the dashboard is on screen.

The dashboard reads from SystemStatsService and shows system identity,
CPU, memory, processes and disk usage. Quick actions are looked up in the
existing ActionEntry catalog by action code, so their labels, icons and
translations stay in one place instead of being redeclared.

New widgets follow the Hermes visual language: elevation from a 1px
border and a tint rather than a shadow, the 8%/15%/solid tint triad for
every badge and active row, a 2px accent spine to mark selection, and
opacity as the hierarchy lever for metadata. The sparkline is a
CustomPainter -- fl_chart brings axes, legends and hit testing that 60
points on a tile never use.

The search keeps working unchanged, including its keyboard navigation.
What changes is dismissal: it used to minimize the window and, on
Wayland, quit the app five seconds later, because wmctrl cannot raise it
again there. That is the right behaviour for a launcher overlay and the
wrong one inside a hub, so the hub installs a callback that returns to
the dashboard. Super+Q still lands directly in the search box.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HioAo2j6ufA9GsyKBWF69u
The security screen offered "Fix" buttons that installed and configured a
firewall and rewrote home directory permissions. Inspection and
modification are different levels of trust, and this area should only
inspect.

Each finding now carries a P0-P3 severity, a short line on why it
matters, and the command to look closer or to make the change -- which
the user runs themselves. Nothing on this screen executes anything; the
only interactive control is "copy to clipboard". Findings are sorted most
severe first so the important ones are never below the fold, and the
section is labelled read-only so the guarantee is visible rather than
implied.

Linux.installAndConfigureFirewall() and fixHomeFolderPermissions() are
left in place; only this screen's calls to them are removed.

The findings are also extracted into a widget that the hub embeds, and
the checker is no longer re-run by rebuilding the whole page.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HioAo2j6ufA9GsyKBWF69u
Replaces test/widget_test.dart, which held the unmodified Flutter counter
template: it asserted on a counter and an add button this app has never
had, so it could not pass, and CI never ran it.

The 50 tests cover what the redesign relies on:
- Every text-bearing color pair clears WCAG AA in both palettes, so a
  future palette edit cannot quietly undo the contrast fixes, including
  when a distribution accent is substituted.
- The df, ps, uptime and free parsers, against captured output.
- Reference counting in the stats poller.
- The new widgets, including that a security finding exposes no control
  other than copy-to-clipboard.

Writing them caught a real bug: HermesCard drew the accent spine as a
left border of a different color while also setting a border radius,
which Flutter rejects at paint time. Since most dashboard and security
cards use a spine, that would have crashed the surfaces it was meant to
decorate. The spine is now painted as an overlay.

New strings are translated in English, German and Italian; five keys that
duplicated existing ones were dropped in favour of the originals. The
generated localizations are committed because the project keeps them in
lib/l10n rather than generating them at build time.

pubspec.lock picks up SDK-pinned transitive test packages from running
pub get with Flutter 3.44.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HioAo2j6ufA9GsyKBWF69u
The search painted MintY.colorfulBackground across its whole body. As a
full-window launcher overlay that is the point; as a panel inside the hub
it filled the content area with a gold gradient that fought with the
sidebar and top bar.

The gradient is now tied to standalone use. The colorfulBackground
setting still applies when the search is the whole window.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HioAo2j6ufA9GsyKBWF69u
@Toqsick
Toqsick merged commit f2f7ac3 into main Jul 25, 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