Introduce hub shell with dashboard, storage, and health sections - #7
Merged
Merged
Conversation
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
HubShellthat 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:
HubShellwith persistent sidebar navigation and section-based routing (dashboard, search, storage, health, security)Design System (Hermes Tokens):
HermesTokensclass porting design tokens from the Hermes WebUI stylesheetNew Hermes Widgets:
HermesCard: Base surface with border-based elevationHermesNavItem: Sidebar navigation items with active/hover statesHermesStatTile: Dashboard tiles with icon, value, unit, and optional visualHermesSparkline: Minimal line chart for rolling data seriesHermesBadge: Semantic status indicators with tone-based coloringHermesCopyCommand: Monospace command display with copy buttonRefactored System Monitoring:
SystemStatsServiceto centralize polling of system stats (memory, CPU, uptime, disk)LinuxSystem,LinuxProcess, andLinuxFilesystemfor testabilitySystemStatuswidget now reads from shared service instead of polling independentlySecurity & Health Views:
SecurityCheckOverviewto separate page chrome from read-only contentSecurityCheckContentnow displays findings without offering to execute fixesSecurityFindingmodel with severity levels and command suggestionsLinuxHealthOverviewsimilarly, extractingLinuxHealthContentfor hub embeddingDashboard Section:
DashboardSectionshowing quick-action tiles and system statsSystemStatsServicefor live statsStorage Section:
StorageSectiondisplaying mounted filesystems with fill levelsLocalization:
Theme Controller:
ThemeControllerfor app-wide appearance state managementTesting:
hermes_tokens_test.dartvalidating WCAG AA contrast ratios for text pairshermes_widgets_test.dartcovering HermesCard, HermesNavItem, HermesStatTile, etc.system_parsers_test.darttesting parsers for df, free, ps, and uptime outputNotable Implementation Details
SystemStatsServiceusesValueListenablefor reactive updates across multiple widgetshttps://claude.ai/code/session_01HioAo2j6ufA9GsyKBWF69u