From 883a3c135dce5dc66393e7bd919402ea27637e69 Mon Sep 17 00:00:00 2001 From: joyy_dev Date: Sat, 19 Sep 2026 20:20:15 +0100 Subject: [PATCH 1/4] implement dynamic light/dark/system theme switching --- .../lib/core/theme/hud_colors.dart | 85 +++++++++++ .../lib/core/theme/hud_text_theme.dart | 123 ++++++++++++++++ .../lib/core/theme/hud_theme.dart | 85 +++++++++++ .../theme/hudd_theme.dart} | 0 .../dashboard/screens/dashboard_screen.dart | 6 +- .../dashboard/widgets/active_process.dart | 19 +-- .../dashboard/widgets/cpu_bar_chart.dart | 2 +- .../dashboard/widgets/cpu_load_report.dart | 2 +- .../dashboard/widgets/cpu_memory.dart | 2 +- .../dashboard/widgets/custom_button.dart | 2 +- .../widgets/custom_progress_indicator.dart | 2 +- .../features/dashboard/widgets/net_rate.dart | 2 +- .../features/dashboard/widgets/status.dart | 2 +- .../dashboard/widgets/thermal_sensor.dart | 2 +- frontend/gs_analyzer_ui/lib/main.dart | 24 +-- .../lib/models/app_settings.dart | 1 + .../lib/providers/settings_provider.dart | 3 +- .../lib/providers/telemetry_provider.dart | 2 +- .../lib/screen/analyzer_dashboard.dart | 2 +- .../lib/screen/automation_screen.dart | 2 +- .../lib/screen/cpu_metrics_screen.dart | 2 +- .../lib/screen/disk_io_screen.dart | 2 +- .../lib/screen/master_layout.dart | 3 +- .../lib/screen/network_module_screen.dart | 2 +- .../lib/screen/process_explorer_screen.dart | 2 +- .../lib/screen/ram_scannner_screen.dart | 2 +- .../lib/screen/scan_diff_screen.dart | 2 +- .../lib/screen/settings_screen.dart | 29 +++- .../lib/screen/startup_manager_screen.dart | 2 +- .../lib/screen/storage_screen.dart | 2 +- .../lib/screen/telemetry_history_screen.dart | 2 +- .../lib/screen/thermal_module_screen.dart | 2 +- .../lib/utils/csv_exporter.dart | 2 +- .../gs_analyzer_ui/lib/utils/hud_label.dart | 2 +- .../lib/utils/nuke_protocol.dart | 2 +- .../lib/widgets/_directory_search_widget.dart | 2 +- .../lib/widgets/age_heatmap_overlay.dart | 2 +- .../lib/widgets/directory_node_widget.dart | 2 +- .../lib/widgets/directory_table_header.dart | 2 +- .../lib/widgets/disk_alert_banner.dart | 2 +- .../lib/widgets/disk_io_card.dart | 2 +- .../lib/widgets/drive_telemetry_widget.dart | 2 +- .../lib/widgets/duplicate_scanner_pannel.dart | 2 +- .../lib/widgets/export_scan_dialog.dart | 2 +- .../widgets/extension_breakdown_screen.dart | 2 +- .../lib/widgets/file_type_analyzer_panel.dart | 2 +- .../lib/widgets/global_sidebar_widget.dart | 38 ++--- .../lib/widgets/go_up_row_widget.dart | 2 +- .../lib/widgets/large_file_scanner_panel.dart | 2 +- .../lib/widgets/nuke_preview_dialog.dart | 2 +- .../lib/widgets/nuke_progress_dialog.dart | 2 +- .../lib/widgets/permission_audit_panel.dart | 2 +- .../lib/widgets/rule_editor_dialog.dart | 2 +- .../lib/widgets/scheduled_scans_panel.dart | 2 +- .../lib/widgets/side_bar_widget.dart | 2 +- .../lib/widgets/telemetry_history_chart.dart | 2 +- .../lib/widgets/telemetry_hud_widget.dart | 2 +- .../lib/widgets/temp_cleaner_panel.dart | 2 +- .../lib/widgets/undo_history_panel.dart | 2 +- .../lib/widgets/watcher_event_log_panel.dart | 2 +- .../test/widgets/disk_io_card_test.dart | 2 +- .../widgets/settings_appearance_test.dart | 139 ++++++++++++++++++ .../test/widgets/theme_mode_rebuild_test.dart | 100 +++++++++++++ 63 files changed, 661 insertions(+), 92 deletions(-) create mode 100644 frontend/gs_analyzer_ui/lib/core/theme/hud_colors.dart create mode 100644 frontend/gs_analyzer_ui/lib/core/theme/hud_text_theme.dart create mode 100644 frontend/gs_analyzer_ui/lib/core/theme/hud_theme.dart rename frontend/gs_analyzer_ui/lib/{utils/hud_theme.dart => core/theme/hudd_theme.dart} (100%) create mode 100644 frontend/gs_analyzer_ui/test/widgets/settings_appearance_test.dart create mode 100644 frontend/gs_analyzer_ui/test/widgets/theme_mode_rebuild_test.dart diff --git a/frontend/gs_analyzer_ui/lib/core/theme/hud_colors.dart b/frontend/gs_analyzer_ui/lib/core/theme/hud_colors.dart new file mode 100644 index 0000000..7359c10 --- /dev/null +++ b/frontend/gs_analyzer_ui/lib/core/theme/hud_colors.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; + +class HudColors { + // --------- HUD DARK THEME COLOR ------------------ + static const Color darkBgBase = Color(0xFF161616); + static const Color darkBgPanel = Color(0xFF1E1E1E); + static const darkPrimaryBorder = Colors.cyan; + + // Text Colors + static const Color darkTextMain = Colors.white; + static const Color darkTextMuted = Colors.white70; + static const Color darkTextDim = Colors.white54; + + // --------- HUD LIGHT THEME COLOR ----------------- + static const lightBgBase = Color(0xFFF4F7F8); + static const lightBgPanel = Color(0xFFF0F6FF); + static const lightPrimaryBorder = Color(0XFF00545D); + + // Text Colors + static const lightTextMain = Color(0xFF172026); + static const lightTextMuted = Color(0xFF5F6B72); + static const lightTextDim = Color(0xFF8A969D); + + // --------- ACCENT COLORS ---------------- + + static const Color lightAccentCyan = Color(0xFF00E5FF); + static const Color darkAccentCyan = Color(0xFF00545D); + + static const Color lightAccentGreen = Color(0xFF69F0AE); + static const Color darkAccentGreen = Color(0xFF006F3A); + + static const Color lightAccentRed = Color(0xFFFF5252); + static const Color darkAccentRed = Color(0xFF9D0E0E); + + static const Color lightAccentAmber = Color(0xFFFFC107); + static const Color darkAccentAmber = Color(0xFF896700); + + static const Color lightAccentPurple = Color(0xFFE040FB); + static const Color darkAccentPurple = Color(0xFF7E0095); + + static const Color lightAccentBlue = Color(0xFF448AFF); + static const Color darkAccentBlue = Color(0xFF003082); + + + static Color resolveAccent(String? accentKey, Brightness brightness) { + final isdark = brightness == Brightness.dark; + + switch (accentKey?.toLowerCase()) { + case 'cyan': + return isdark ? darkAccentCyan : lightAccentCyan; + case 'green': + return isdark ? darkAccentGreen : lightAccentGreen; + case 'amber': + return isdark ? darkAccentAmber : lightAccentAmber; + case 'red': + return isdark ? darkAccentRed : lightAccentRed; + case 'purple': + return isdark ? darkAccentPurple : lightAccentPurple; + case 'blue': + return isdark ? darkAccentBlue : lightAccentBlue; + default: + return isdark ? darkAccentCyan : lightAccentCyan; + } + } + + static Color fileTypeColor(String category) { + switch (category.toLowerCase()) { + case 'media': + return const Color(0xFF00FFFF); + case 'documents': + return const Color(0xFF4CAF50); + case 'executables': + return const Color(0xFFFF5252); + case 'archives': + return const Color(0xFFFFB300); + case 'code': + return const Color(0xFF9C27B0); + case 'system': + return Colors.white38; + default: + return Colors.white12; + } + } + +} \ No newline at end of file diff --git a/frontend/gs_analyzer_ui/lib/core/theme/hud_text_theme.dart b/frontend/gs_analyzer_ui/lib/core/theme/hud_text_theme.dart new file mode 100644 index 0000000..ead01f2 --- /dev/null +++ b/frontend/gs_analyzer_ui/lib/core/theme/hud_text_theme.dart @@ -0,0 +1,123 @@ +import 'package:flutter/material.dart'; +import 'package:gs_analyzer_ui/core/theme/hud_colors.dart'; + +class HudTextTheme { + + static const String fontCore = 'Courier'; + + static const light = TextTheme( + headlineLarge: TextStyle( + fontFamily: fontCore, + color: HudColors.lightPrimaryBorder, + fontWeight: FontWeight.bold, + letterSpacing: 2, + fontSize: 18 + ), + + headlineMedium: TextStyle( + fontFamily: fontCore, + color: HudColors.lightPrimaryBorder, + fontWeight: FontWeight.bold, + letterSpacing: 2, + fontSize: 18 + ), + + headlineSmall: TextStyle( + fontFamily: fontCore, + fontWeight: FontWeight.w600 + ), + + bodyLarge: TextStyle( + fontFamily: fontCore, + color: HudColors.lightAccentGreen, + fontSize: 14, + fontWeight: FontWeight.bold + ), + + bodyMedium: TextStyle( + fontFamily: fontCore, + color: HudColors.lightAccentCyan, + fontSize: 13, + fontWeight: FontWeight.bold + ), + + bodySmall: TextStyle( + fontFamily: fontCore, + color: HudColors.lightAccentGreen, + fontSize: 12, + letterSpacing: 1 + ), + + displayMedium: TextStyle( + fontFamily: fontCore, + color: HudColors.lightAccentRed, + fontSize: 14, + fontWeight: FontWeight.bold, + letterSpacing: 1.5 + ), + + titleMedium: TextStyle( + fontFamily: fontCore, + color: HudColors.lightTextMuted, + fontSize: 13 + ), + ); + + static const dark = TextTheme( + headlineLarge: TextStyle( + fontFamily: fontCore, + color: HudColors.darkPrimaryBorder, + fontWeight: FontWeight.bold, + letterSpacing: 2, + fontSize: 18 + ), + + headlineMedium: TextStyle( + fontFamily: fontCore, + color: HudColors.darkPrimaryBorder, + fontWeight: FontWeight.bold, + letterSpacing: 2, + fontSize: 18 + ), + + headlineSmall: TextStyle( + fontFamily: fontCore, + fontWeight: FontWeight.w600 + ), + + bodyLarge: TextStyle( + fontFamily: fontCore, + color: HudColors.darkAccentGreen, + fontSize: 14, + fontWeight: FontWeight.bold + ), + + bodyMedium: TextStyle( + fontFamily: fontCore, + color: HudColors.darkAccentCyan, + fontSize: 13, + fontWeight: FontWeight.bold + ), + + bodySmall: TextStyle( + fontFamily: fontCore, + color: HudColors.darkTextDim, + fontSize: 12, + letterSpacing: 1 + ), + + displayMedium: TextStyle( + fontFamily: fontCore, + color: HudColors.darkAccentRed, + fontSize: 14, + fontWeight: FontWeight.bold, + letterSpacing: 1.5 + ), + + titleMedium: TextStyle( + fontFamily: fontCore, + color: HudColors.darkTextMuted, + fontSize: 13 + ), + ); +} \ No newline at end of file diff --git a/frontend/gs_analyzer_ui/lib/core/theme/hud_theme.dart b/frontend/gs_analyzer_ui/lib/core/theme/hud_theme.dart new file mode 100644 index 0000000..7049197 --- /dev/null +++ b/frontend/gs_analyzer_ui/lib/core/theme/hud_theme.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; +import 'package:gs_analyzer_ui/core/theme/hud_colors.dart'; +import 'package:gs_analyzer_ui/core/theme/hud_text_theme.dart'; + +class HudTheme { + static ThemeData lightTheme(Color accentColor) { + return ThemeData( + brightness: Brightness.light, + + scaffoldBackgroundColor: HudColors.lightBgBase, + + textTheme: HudTextTheme.light, + + colorScheme: ColorScheme.light( + primary: accentColor, + surface: HudColors.lightBgPanel, + onSurface: HudColors.lightTextMain, + outline: HudColors.lightPrimaryBorder, + error: HudColors.lightAccentRed, + onPrimary: HudColors.lightAccentCyan, + secondary: HudColors.lightAccentGreen, + tertiary: HudColors.lightAccentAmber, + onSecondary: HudColors.lightAccentPurple, + onTertiary: HudColors.lightAccentBlue, + surfaceDim: HudColors.lightTextDim + ), + + cardTheme: CardThemeData( + color: HudColors.lightBgPanel + ), + + dividerTheme: DividerThemeData( + color: Colors.black12 + ) + ); + } + + static ThemeData darkTheme(Color accentColor) { + return ThemeData( + brightness: Brightness.dark, + + scaffoldBackgroundColor: HudColors.darkBgBase, + + textTheme: HudTextTheme.dark, + + colorScheme: ColorScheme.dark( + primary: accentColor, + surface: HudColors.darkBgPanel, + onSurface: HudColors.darkTextMain, + outline: HudColors.darkPrimaryBorder, + error: HudColors.darkAccentRed, + onPrimary: HudColors.darkAccentCyan, + secondary: HudColors.darkAccentGreen, + tertiary: HudColors.darkAccentAmber, + onSecondary: HudColors.darkAccentPurple, + onTertiary: HudColors.darkAccentBlue, + surfaceDim: HudColors.darkTextDim + ), + + cardTheme: CardThemeData( + color: HudColors.darkBgPanel + ), + + dividerTheme: DividerThemeData( + color: Colors.white10 + ) + ); + } + + static ThemeMode resolveThemeMode(String? theme) { + switch (theme?.toLowerCase()) { + case 'cyber_light': + case 'light': + return ThemeMode.light; + + case 'cyber_dark': + case 'dark': + return ThemeMode.dark; + + case 'system': + default: + return ThemeMode.system; + } + } +} \ No newline at end of file diff --git a/frontend/gs_analyzer_ui/lib/utils/hud_theme.dart b/frontend/gs_analyzer_ui/lib/core/theme/hudd_theme.dart similarity index 100% rename from frontend/gs_analyzer_ui/lib/utils/hud_theme.dart rename to frontend/gs_analyzer_ui/lib/core/theme/hudd_theme.dart diff --git a/frontend/gs_analyzer_ui/lib/features/dashboard/screens/dashboard_screen.dart b/frontend/gs_analyzer_ui/lib/features/dashboard/screens/dashboard_screen.dart index 331eae6..49fa8d5 100644 --- a/frontend/gs_analyzer_ui/lib/features/dashboard/screens/dashboard_screen.dart +++ b/frontend/gs_analyzer_ui/lib/features/dashboard/screens/dashboard_screen.dart @@ -7,7 +7,6 @@ import 'package:gs_analyzer_ui/features/dashboard/widgets/custom_button.dart'; import 'package:gs_analyzer_ui/features/dashboard/widgets/net_rate.dart'; import 'package:gs_analyzer_ui/features/dashboard/widgets/thermal_sensor.dart'; import 'package:gs_analyzer_ui/providers/hud_density_provider.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; class DashboardScreen extends ConsumerStatefulWidget { const DashboardScreen({super.key}); @@ -21,6 +20,7 @@ class _DashboardScreenState extends ConsumerState { @override Widget build(BuildContext context) { final d = ref.watch(hudDensityProvider); + final theme = Theme.of(context); return LayoutBuilder( builder: (context, constraints) { @@ -50,11 +50,11 @@ class _DashboardScreenState extends ConsumerState { children: [ Text( 'System_Overview'.toUpperCase(), - style:HudTheme.headerCyan + style:theme.textTheme.headlineLarge ), Text( 'Real-Time Telemetry Stream'.toUpperCase(), - style: HudTheme.labelMuted, + style: theme.textTheme.titleMedium, ) ], ), diff --git a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/active_process.dart b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/active_process.dart index 5847ea2..f67e556 100644 --- a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/active_process.dart +++ b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/active_process.dart @@ -5,7 +5,6 @@ import 'package:gs_analyzer_ui/features/dashboard/widgets/process_table.dart'; import 'package:gs_analyzer_ui/features/dashboard/widgets/status.dart'; import 'package:gs_analyzer_ui/providers/hud_density_provider.dart'; import 'package:gs_analyzer_ui/providers/process_explorer_provider.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; import 'package:gs_analyzer_ui/widgets/custom_container.dart'; class ActiveProcess extends ConsumerStatefulWidget { @@ -22,9 +21,10 @@ class _ActiveProcessState extends ConsumerState { final processes = ref.watch(filteredProcessesProvider).take(4).toList(); final d = ref.watch(hudDensityProvider); final selectedPid = ref.watch(selectedProcessPidProvider); + final theme = Theme.of(context); return CustomContainer( - color: HudTheme.bgPanel, + color: theme.cardColor, padding: EdgeInsets.all(20), child: Column( children: [ @@ -34,7 +34,7 @@ class _ActiveProcessState extends ConsumerState { Expanded( child: Text( 'ACTIVE PROCESS TREE', - style: HudTheme.labelMuted, + style: Theme.of(context).textTheme.headlineSmall, overflow: TextOverflow.ellipsis, ), ), @@ -49,7 +49,7 @@ class _ActiveProcessState extends ConsumerState { final isCpuHot = group.totalCpuPercent > 10.0; final isMemHot = group.totalPercentMem > 10.0; final isHot = isCpuHot || isMemHot; - final textColor = isHot ? HudTheme.accentAmber : HudTheme.textMain; + final textColor = isHot ? theme.colorScheme.tertiary : theme.colorScheme.onSurface; return SizedBox( height: d.rowHeight + 16, @@ -59,32 +59,33 @@ class _ActiveProcessState extends ConsumerState { Expanded( child: Text( group.primaryPid.toString(), - style: HudTheme.bodyText, + style: theme.textTheme.bodySmall, ), ), Expanded( child: Text( displayName, overflow: TextOverflow.ellipsis, - style: HudTheme.bodyText.copyWith(color: isSelected ? HudTheme.accentCyan : textColor), + style: theme.textTheme.bodyMedium?.copyWith(color: isSelected ? theme.colorScheme.onPrimary : textColor), ), ), Expanded( child: Text( group.primaryUser, - style: HudTheme.bodyText, + style: Theme.of(context).textTheme.bodySmall, + // style: HudTheme.bodyText, ), ), Expanded( child: Text( '${group.totalCpuPercent.toStringAsFixed(1)}%', - style: HudTheme.statGreen.copyWith(color: isCpuHot ? HudTheme.accentAmber : HudTheme.accentCyan), + style: theme.textTheme.bodySmall?.copyWith(color: isCpuHot ? theme.colorScheme.tertiary : theme.colorScheme.onPrimary), ), ), Expanded( child: Text( '${group.totalPercentMem.toStringAsFixed(1)}%', - style: HudTheme.statGreen.copyWith(color: textColor), + style: Theme.of(context).textTheme.bodySmall?.copyWith(color: textColor), ), ), Expanded( diff --git a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_bar_chart.dart b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_bar_chart.dart index 0bef9a9..02b0544 100644 --- a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_bar_chart.dart +++ b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_bar_chart.dart @@ -1,6 +1,6 @@ import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; class CpuBarChart extends StatelessWidget { final Map> coreGroups; diff --git a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_load_report.dart b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_load_report.dart index 9f68b78..082a450 100644 --- a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_load_report.dart +++ b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_load_report.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gs_analyzer_ui/features/dashboard/widgets/cpu_bar_chart.dart'; import 'package:gs_analyzer_ui/providers/cpu_provider.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/widgets/custom_container.dart'; class CpuLoadReport extends ConsumerStatefulWidget { diff --git a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_memory.dart b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_memory.dart index 4d61768..f9d886d 100644 --- a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_memory.dart +++ b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_memory.dart @@ -4,7 +4,7 @@ import 'package:gs_analyzer_ui/features/dashboard/widgets/custom_progress_indica import 'package:gs_analyzer_ui/providers/ram_provider.dart'; import 'package:gs_analyzer_ui/providers/ram_alert_provider.dart'; import 'package:gs_analyzer_ui/utils/hud_label.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/widgets/custom_container.dart'; class CpuMemory extends ConsumerStatefulWidget { diff --git a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/custom_button.dart b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/custom_button.dart index 5934bbb..14edeb7 100644 --- a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/custom_button.dart +++ b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/custom_button.dart @@ -4,7 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gs_analyzer_ui/providers/cpu_provider.dart'; import 'package:gs_analyzer_ui/providers/ram_provider.dart'; import 'package:gs_analyzer_ui/providers/thermal_provider.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; class CustomButton extends ConsumerWidget { final EdgeInsets? padding; diff --git a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/custom_progress_indicator.dart b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/custom_progress_indicator.dart index c1af93a..0828331 100644 --- a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/custom_progress_indicator.dart +++ b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/custom_progress_indicator.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; class CustomProgressIndicator extends StatelessWidget { final String label; diff --git a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/net_rate.dart b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/net_rate.dart index 1828f70..cd04669 100644 --- a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/net_rate.dart +++ b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/net_rate.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gs_analyzer_ui/providers/network_provider.dart'; import 'package:gs_analyzer_ui/utils/formatters.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/widgets/custom_container.dart'; class NetRate extends ConsumerStatefulWidget { diff --git a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/status.dart b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/status.dart index 0e3fbed..a903b2e 100644 --- a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/status.dart +++ b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/status.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; class Status extends StatelessWidget { final String status; diff --git a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/thermal_sensor.dart b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/thermal_sensor.dart index 397a64d..e4b8374 100644 --- a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/thermal_sensor.dart +++ b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/thermal_sensor.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gs_analyzer_ui/providers/thermal_provider.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/widgets/custom_container.dart'; class ThermalSensor extends ConsumerStatefulWidget { diff --git a/frontend/gs_analyzer_ui/lib/main.dart b/frontend/gs_analyzer_ui/lib/main.dart index 3684f5f..866eea1 100644 --- a/frontend/gs_analyzer_ui/lib/main.dart +++ b/frontend/gs_analyzer_ui/lib/main.dart @@ -1,9 +1,10 @@ // frontend/gs_analyzer_ui/lib/main.dart import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:gs_analyzer_ui/core/theme/hud_colors.dart'; +import 'package:gs_analyzer_ui/core/theme/hud_theme.dart'; import 'package:gs_analyzer_ui/providers/settings_provider.dart'; import 'package:gs_analyzer_ui/screen/master_layout.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; import 'package:gs_analyzer_ui/providers/window_provider.dart'; import 'package:gs_analyzer_ui/utils/globals.dart'; import 'package:gs_analyzer_ui/services/notification_service.dart'; @@ -94,20 +95,25 @@ class _GSAnalyzerAppState extends ConsumerState @override Widget build(BuildContext context) { - final appearance = ref.watch( - settingsProvider.select((s) => s.currentSettings?.appearance), + final theme = ref.watch( + settingsProvider.select((s) => s.currentSettings?.appearance.theme), + ); + final accentKey = ref.watch( + settingsProvider.select((s) => s.currentSettings?.appearance.accentColor), ); - final accentColor = HudTheme.resolveAccent(appearance?.accentColor); - final bgColor = HudTheme.resolveBgBase(appearance?.theme); + final lightAccent = HudColors.resolveAccent(accentKey, Brightness.light); + final darkAccent = HudColors.resolveAccent(accentKey, Brightness.dark); + final themeMode = HudTheme.resolveThemeMode(theme); return MaterialApp( scaffoldMessengerKey: snackbarKey, debugShowCheckedModeBanner: false, - theme: ThemeData.dark().copyWith( - scaffoldBackgroundColor: bgColor, - colorScheme: ColorScheme.dark(primary: accentColor), - ), + + theme: HudTheme.lightTheme(lightAccent), + darkTheme: HudTheme.darkTheme(darkAccent), + themeMode: themeMode, + home: const MasterLayout(), ); } diff --git a/frontend/gs_analyzer_ui/lib/models/app_settings.dart b/frontend/gs_analyzer_ui/lib/models/app_settings.dart index 8bb3493..66830c4 100644 --- a/frontend/gs_analyzer_ui/lib/models/app_settings.dart +++ b/frontend/gs_analyzer_ui/lib/models/app_settings.dart @@ -179,6 +179,7 @@ class CacheSettings { class AppearanceSettings { String theme, accentColor; bool compactMode, showAnimations; + AppearanceSettings({ this.theme = 'cyber_dark', this.accentColor = 'cyan', diff --git a/frontend/gs_analyzer_ui/lib/providers/settings_provider.dart b/frontend/gs_analyzer_ui/lib/providers/settings_provider.dart index 41df53d..c0af99a 100644 --- a/frontend/gs_analyzer_ui/lib/providers/settings_provider.dart +++ b/frontend/gs_analyzer_ui/lib/providers/settings_provider.dart @@ -88,7 +88,7 @@ class SettingsNotifier extends StateNotifier { void updateUI() { state = state.copyWith( - currentSettings: state.currentSettings, + currentSettings: state.currentSettings?.clone(), validationErrors: [], ); } @@ -103,6 +103,7 @@ class SettingsNotifier extends StateNotifier { final newSave = state.currentSettings!.clone(); state = state.copyWith( savedSettings: newSave, + currentSettings: newSave.clone(), validationErrors: [], isLoading: false, ); diff --git a/frontend/gs_analyzer_ui/lib/providers/telemetry_provider.dart b/frontend/gs_analyzer_ui/lib/providers/telemetry_provider.dart index 58d26b5..27e93ba 100644 --- a/frontend/gs_analyzer_ui/lib/providers/telemetry_provider.dart +++ b/frontend/gs_analyzer_ui/lib/providers/telemetry_provider.dart @@ -7,7 +7,7 @@ import 'package:gs_analyzer_ui/providers/schedule_provider.dart'; import 'package:gs_analyzer_ui/services/telemetry_service.dart'; import 'package:gs_analyzer_ui/utils/globals.dart'; import 'package:flutter/material.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/providers/network_provider.dart'; import 'package:gs_analyzer_ui/providers/disk_io_provider.dart'; import 'package:gs_analyzer_ui/providers/directory_provider.dart'; diff --git a/frontend/gs_analyzer_ui/lib/screen/analyzer_dashboard.dart b/frontend/gs_analyzer_ui/lib/screen/analyzer_dashboard.dart index 8920646..1750c8a 100644 --- a/frontend/gs_analyzer_ui/lib/screen/analyzer_dashboard.dart +++ b/frontend/gs_analyzer_ui/lib/screen/analyzer_dashboard.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:gs_analyzer_ui/services/api_service.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/widgets/_directory_search_widget.dart'; import 'package:gs_analyzer_ui/widgets/age_heatmap_overlay.dart'; import 'package:gs_analyzer_ui/widgets/directory_node_widget.dart'; diff --git a/frontend/gs_analyzer_ui/lib/screen/automation_screen.dart b/frontend/gs_analyzer_ui/lib/screen/automation_screen.dart index 41220e6..5e70742 100644 --- a/frontend/gs_analyzer_ui/lib/screen/automation_screen.dart +++ b/frontend/gs_analyzer_ui/lib/screen/automation_screen.dart @@ -5,7 +5,7 @@ import 'package:gs_analyzer_ui/models/automation_rule.dart'; import 'package:gs_analyzer_ui/providers/automation_provider.dart'; import 'package:gs_analyzer_ui/providers/hud_density_provider.dart'; import 'package:gs_analyzer_ui/utils/formatters.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/widgets/rule_editor_dialog.dart'; class AutomationScreen extends ConsumerStatefulWidget { diff --git a/frontend/gs_analyzer_ui/lib/screen/cpu_metrics_screen.dart b/frontend/gs_analyzer_ui/lib/screen/cpu_metrics_screen.dart index 03f97cf..61b1e38 100644 --- a/frontend/gs_analyzer_ui/lib/screen/cpu_metrics_screen.dart +++ b/frontend/gs_analyzer_ui/lib/screen/cpu_metrics_screen.dart @@ -3,7 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:fl_chart/fl_chart.dart'; import 'package:gs_analyzer_ui/providers/cpu_provider.dart'; import 'package:gs_analyzer_ui/models/cpu_snapshot.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/widgets/telemetry_history_chart.dart'; import 'package:gs_analyzer_ui/providers/hud_density_provider.dart'; diff --git a/frontend/gs_analyzer_ui/lib/screen/disk_io_screen.dart b/frontend/gs_analyzer_ui/lib/screen/disk_io_screen.dart index e812ef2..90ca944 100644 --- a/frontend/gs_analyzer_ui/lib/screen/disk_io_screen.dart +++ b/frontend/gs_analyzer_ui/lib/screen/disk_io_screen.dart @@ -3,7 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gs_analyzer_ui/providers/disk_io_provider.dart'; import 'package:gs_analyzer_ui/providers/hud_density_provider.dart'; import 'package:gs_analyzer_ui/utils/formatters.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/widgets/disk_io_card.dart'; import 'package:gs_analyzer_ui/widgets/telemetry_history_chart.dart'; diff --git a/frontend/gs_analyzer_ui/lib/screen/master_layout.dart b/frontend/gs_analyzer_ui/lib/screen/master_layout.dart index fc6840e..8f088bc 100644 --- a/frontend/gs_analyzer_ui/lib/screen/master_layout.dart +++ b/frontend/gs_analyzer_ui/lib/screen/master_layout.dart @@ -6,7 +6,6 @@ import 'package:gs_analyzer_ui/screen/analyzer_dashboard.dart'; import 'package:gs_analyzer_ui/screen/ram_scannner_screen.dart'; import 'package:gs_analyzer_ui/screen/settings_screen.dart'; import 'package:gs_analyzer_ui/screen/thermal_module_screen.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; import 'package:gs_analyzer_ui/widgets/coming_soon.dart'; import 'package:gs_analyzer_ui/widgets/global_sidebar_widget.dart'; import 'package:gs_analyzer_ui/screen/storage_screen.dart'; @@ -39,7 +38,7 @@ class MasterLayout extends ConsumerWidget { }); return Scaffold( - backgroundColor: HudTheme.bgBase, + // backgroundColor: Theme., body: Row( children: [ const GlobalSidebarWidget(), diff --git a/frontend/gs_analyzer_ui/lib/screen/network_module_screen.dart b/frontend/gs_analyzer_ui/lib/screen/network_module_screen.dart index edf4d32..213a47d 100644 --- a/frontend/gs_analyzer_ui/lib/screen/network_module_screen.dart +++ b/frontend/gs_analyzer_ui/lib/screen/network_module_screen.dart @@ -5,7 +5,7 @@ import 'package:gs_analyzer_ui/models/network_telemetry.dart'; import 'package:gs_analyzer_ui/providers/hud_density_provider.dart'; import 'package:gs_analyzer_ui/providers/network_provider.dart'; import 'package:gs_analyzer_ui/utils/formatters.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/widgets/telemetry_history_chart.dart'; class NetworkModuleScreen extends ConsumerStatefulWidget { diff --git a/frontend/gs_analyzer_ui/lib/screen/process_explorer_screen.dart b/frontend/gs_analyzer_ui/lib/screen/process_explorer_screen.dart index 6136fb2..8e24e87 100644 --- a/frontend/gs_analyzer_ui/lib/screen/process_explorer_screen.dart +++ b/frontend/gs_analyzer_ui/lib/screen/process_explorer_screen.dart @@ -6,7 +6,7 @@ import 'package:gs_analyzer_ui/models/process_telemetry.dart'; import 'package:gs_analyzer_ui/providers/cpu_provider.dart'; import 'package:gs_analyzer_ui/providers/process_explorer_provider.dart'; import 'package:gs_analyzer_ui/providers/ram_provider.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/utils/hud_label.dart'; import 'package:gs_analyzer_ui/providers/hud_density_provider.dart'; diff --git a/frontend/gs_analyzer_ui/lib/screen/ram_scannner_screen.dart b/frontend/gs_analyzer_ui/lib/screen/ram_scannner_screen.dart index f2d2648..462ad17 100644 --- a/frontend/gs_analyzer_ui/lib/screen/ram_scannner_screen.dart +++ b/frontend/gs_analyzer_ui/lib/screen/ram_scannner_screen.dart @@ -3,7 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gs_analyzer_ui/features/dashboard/widgets/status.dart'; import 'package:gs_analyzer_ui/providers/ram_provider.dart'; import 'package:gs_analyzer_ui/providers/ram_alert_provider.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/utils/hud_label.dart'; import 'package:gs_analyzer_ui/widgets/telemetry_history_chart.dart'; import 'package:gs_analyzer_ui/providers/hud_density_provider.dart'; diff --git a/frontend/gs_analyzer_ui/lib/screen/scan_diff_screen.dart b/frontend/gs_analyzer_ui/lib/screen/scan_diff_screen.dart index c16ee9c..e5064ae 100644 --- a/frontend/gs_analyzer_ui/lib/screen/scan_diff_screen.dart +++ b/frontend/gs_analyzer_ui/lib/screen/scan_diff_screen.dart @@ -8,7 +8,7 @@ import 'package:gs_analyzer_ui/providers/drive_stats_provider.dart'; import 'package:gs_analyzer_ui/providers/scan_diff_provider.dart'; import 'package:gs_analyzer_ui/providers/storage_view_provider.dart'; import 'package:gs_analyzer_ui/services/api_service.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; /// Signed byte formatter: "+4.8 GB", "-512 MB", "0 B". String _formatSignedBytes(int bytes) { diff --git a/frontend/gs_analyzer_ui/lib/screen/settings_screen.dart b/frontend/gs_analyzer_ui/lib/screen/settings_screen.dart index d09d093..7b466e1 100644 --- a/frontend/gs_analyzer_ui/lib/screen/settings_screen.dart +++ b/frontend/gs_analyzer_ui/lib/screen/settings_screen.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gs_analyzer_ui/providers/settings_provider.dart'; import 'package:gs_analyzer_ui/models/app_settings.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/utils/hud_label.dart'; import 'package:gs_analyzer_ui/utils/globals.dart'; import 'package:gs_analyzer_ui/widgets/scheduled_scans_panel.dart'; @@ -70,7 +70,7 @@ class _SettingsScreenState extends ConsumerState { final settings = state.currentSettings!; return Scaffold( - backgroundColor: HudTheme.bgBase, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, appBar: AppBar( backgroundColor: Colors.transparent, elevation: 0, @@ -503,6 +503,13 @@ class _SettingsScreenState extends ConsumerState { SettingsState state, SettingsNotifier notifier, ) { + final currentTheme = app.theme.toLowerCase(); + final isLight = currentTheme == 'cyber_light' || currentTheme == 'light'; + final isSystem = currentTheme == 'system'; + final isDark = + (currentTheme == 'cyber_dark' || currentTheme == 'dark') || + (!isLight && !isSystem); + return _SettingsSection( title: 'APPEARANCE', errorFilter: 'Appearance', @@ -517,6 +524,24 @@ class _SettingsScreenState extends ConsumerState { app.showAnimations = val; notifier.updateUI(); }), + _buildToggle('DARK MODE', isDark, (val) { + if (val) { + app.theme = 'cyber_dark'; + } + notifier.updateUI(); + }), + _buildToggle('LIGHT MODE', isLight, (val) { + if (val) { + app.theme = 'cyber_light'; + } + notifier.updateUI(); + }), + _buildToggle('SYSTEM MODE', isSystem, (val) { + if (val) { + app.theme = 'system'; + } + notifier.updateUI(); + }), const SizedBox(height: 16), Align( alignment: Alignment.centerLeft, diff --git a/frontend/gs_analyzer_ui/lib/screen/startup_manager_screen.dart b/frontend/gs_analyzer_ui/lib/screen/startup_manager_screen.dart index bcf7184..1a4b4be 100644 --- a/frontend/gs_analyzer_ui/lib/screen/startup_manager_screen.dart +++ b/frontend/gs_analyzer_ui/lib/screen/startup_manager_screen.dart @@ -3,7 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gs_analyzer_ui/models/startup_program.dart'; import 'package:gs_analyzer_ui/providers/startup_provider.dart'; import 'package:gs_analyzer_ui/services/api_service.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; class StartupManagerScreen extends ConsumerWidget { const StartupManagerScreen({super.key}); diff --git a/frontend/gs_analyzer_ui/lib/screen/storage_screen.dart b/frontend/gs_analyzer_ui/lib/screen/storage_screen.dart index c4e1561..e1330d4 100644 --- a/frontend/gs_analyzer_ui/lib/screen/storage_screen.dart +++ b/frontend/gs_analyzer_ui/lib/screen/storage_screen.dart @@ -3,7 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../models/drive_info.dart'; import '../providers/drive_stats_provider.dart'; import '../providers/settings_provider.dart'; -import '../utils/hud_theme.dart'; +import '../core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/providers/directory_provider.dart'; import 'package:gs_analyzer_ui/providers/storage_view_provider.dart'; import 'package:gs_analyzer_ui/providers/storage_mode_provider.dart'; diff --git a/frontend/gs_analyzer_ui/lib/screen/telemetry_history_screen.dart b/frontend/gs_analyzer_ui/lib/screen/telemetry_history_screen.dart index 290243b..369a73c 100644 --- a/frontend/gs_analyzer_ui/lib/screen/telemetry_history_screen.dart +++ b/frontend/gs_analyzer_ui/lib/screen/telemetry_history_screen.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gs_analyzer_ui/widgets/telemetry_history_chart.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/providers/hud_density_provider.dart'; class TelemetryHistoryScreen extends ConsumerWidget { diff --git a/frontend/gs_analyzer_ui/lib/screen/thermal_module_screen.dart b/frontend/gs_analyzer_ui/lib/screen/thermal_module_screen.dart index ad11eba..a22988e 100644 --- a/frontend/gs_analyzer_ui/lib/screen/thermal_module_screen.dart +++ b/frontend/gs_analyzer_ui/lib/screen/thermal_module_screen.dart @@ -5,7 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gs_analyzer_ui/providers/cpu_provider.dart'; import 'package:gs_analyzer_ui/providers/thermal_provider.dart'; import 'package:gs_analyzer_ui/models/thermal_telemetry.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/utils/hud_label.dart'; import 'package:gs_analyzer_ui/widgets/telemetry_history_chart.dart'; import 'package:gs_analyzer_ui/providers/hud_density_provider.dart'; diff --git a/frontend/gs_analyzer_ui/lib/utils/csv_exporter.dart b/frontend/gs_analyzer_ui/lib/utils/csv_exporter.dart index c539d68..c001f01 100644 --- a/frontend/gs_analyzer_ui/lib/utils/csv_exporter.dart +++ b/frontend/gs_analyzer_ui/lib/utils/csv_exporter.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:gs_analyzer_ui/models/extension_breakdown_model.dart'; import 'package:gs_analyzer_ui/models/watcher_event.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; class CsvExporter { static Future exportExtensionBreakdown( diff --git a/frontend/gs_analyzer_ui/lib/utils/hud_label.dart b/frontend/gs_analyzer_ui/lib/utils/hud_label.dart index 78370d0..bba6e0d 100644 --- a/frontend/gs_analyzer_ui/lib/utils/hud_label.dart +++ b/frontend/gs_analyzer_ui/lib/utils/hud_label.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; class HudLabel extends StatelessWidget { final String text; diff --git a/frontend/gs_analyzer_ui/lib/utils/nuke_protocol.dart b/frontend/gs_analyzer_ui/lib/utils/nuke_protocol.dart index efaa5c2..a0d61fc 100644 --- a/frontend/gs_analyzer_ui/lib/utils/nuke_protocol.dart +++ b/frontend/gs_analyzer_ui/lib/utils/nuke_protocol.dart @@ -8,7 +8,7 @@ import 'package:gs_analyzer_ui/providers/nuke_provider.dart'; import 'package:gs_analyzer_ui/services/api_service.dart'; import 'package:gs_analyzer_ui/providers/root_tree_provider.dart'; import 'package:gs_analyzer_ui/utils/globals.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import '../widgets/nuke_preview_dialog.dart'; import '../widgets/nuke_progress_dialog.dart'; import '../widgets/undo_history_panel.dart'; diff --git a/frontend/gs_analyzer_ui/lib/widgets/_directory_search_widget.dart b/frontend/gs_analyzer_ui/lib/widgets/_directory_search_widget.dart index 01edd51..6338155 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/_directory_search_widget.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/_directory_search_widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gs_analyzer_ui/providers/directory_provider.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; class DirectorySearchWidget extends ConsumerStatefulWidget { const DirectorySearchWidget({super.key}); diff --git a/frontend/gs_analyzer_ui/lib/widgets/age_heatmap_overlay.dart b/frontend/gs_analyzer_ui/lib/widgets/age_heatmap_overlay.dart index b9ee7e9..2266d25 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/age_heatmap_overlay.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/age_heatmap_overlay.dart @@ -3,7 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gs_analyzer_ui/models/age_heatmap_model.dart'; import 'package:gs_analyzer_ui/providers/age_heatmap_provider.dart'; import 'package:gs_analyzer_ui/providers/directory_provider.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/widgets/directory_node_widget.dart'; /// Resolves the HudTheme accent color for a given age bucket string. diff --git a/frontend/gs_analyzer_ui/lib/widgets/directory_node_widget.dart b/frontend/gs_analyzer_ui/lib/widgets/directory_node_widget.dart index badb32e..fd44dad 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/directory_node_widget.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/directory_node_widget.dart @@ -4,7 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gs_analyzer_ui/models/storage_node.dart'; import 'package:gs_analyzer_ui/services/api_service.dart'; import 'dart:math'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/utils/hud_label.dart'; import 'package:gs_analyzer_ui/widgets/age_heatmap_overlay.dart'; import '../providers/age_heatmap_provider.dart'; diff --git a/frontend/gs_analyzer_ui/lib/widgets/directory_table_header.dart b/frontend/gs_analyzer_ui/lib/widgets/directory_table_header.dart index 5ca8c4b..6caaccc 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/directory_table_header.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/directory_table_header.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:gs_analyzer_ui/utils/hud_label.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; class DirectoryTableHeader extends StatelessWidget { const DirectoryTableHeader({super.key}); diff --git a/frontend/gs_analyzer_ui/lib/widgets/disk_alert_banner.dart b/frontend/gs_analyzer_ui/lib/widgets/disk_alert_banner.dart index 538cc6c..0c6b557 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/disk_alert_banner.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/disk_alert_banner.dart @@ -6,7 +6,7 @@ import 'package:gs_analyzer_ui/providers/drive_stats_provider.dart'; import 'package:gs_analyzer_ui/providers/hud_density_provider.dart'; import 'package:gs_analyzer_ui/providers/storage_mode_provider.dart'; import 'package:gs_analyzer_ui/providers/storage_view_provider.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; /// Renders a list of stacked disk alert banners at the top of the Storage screen, /// ordered by usedPercent descending. diff --git a/frontend/gs_analyzer_ui/lib/widgets/disk_io_card.dart b/frontend/gs_analyzer_ui/lib/widgets/disk_io_card.dart index 0a4d6e0..272a273 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/disk_io_card.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/disk_io_card.dart @@ -5,7 +5,7 @@ import 'package:gs_analyzer_ui/models/disk_io_telemetry.dart'; import 'package:gs_analyzer_ui/providers/disk_io_provider.dart'; import 'package:gs_analyzer_ui/utils/formatters.dart'; import 'package:gs_analyzer_ui/utils/hud_label.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; class DiskIoCard extends ConsumerWidget { final DiskIoSnapshot? overrideDisk; diff --git a/frontend/gs_analyzer_ui/lib/widgets/drive_telemetry_widget.dart b/frontend/gs_analyzer_ui/lib/widgets/drive_telemetry_widget.dart index 5a8bebf..4bcb58c 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/drive_telemetry_widget.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/drive_telemetry_widget.dart @@ -5,7 +5,7 @@ import 'package:gs_analyzer_ui/providers/settings_provider.dart'; import 'package:gs_analyzer_ui/utils/hud_label.dart'; import 'dart:math'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; String formatBytes(int bytes) { if (bytes < 0) return "--"; diff --git a/frontend/gs_analyzer_ui/lib/widgets/duplicate_scanner_pannel.dart b/frontend/gs_analyzer_ui/lib/widgets/duplicate_scanner_pannel.dart index 7167f2b..2bdadfe 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/duplicate_scanner_pannel.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/duplicate_scanner_pannel.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gs_analyzer_ui/providers/duplicate_provider.dart'; import 'package:gs_analyzer_ui/providers/storage_mode_provider.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/utils/nuke_protocol.dart'; class DuplicateScannerPanel extends ConsumerStatefulWidget { diff --git a/frontend/gs_analyzer_ui/lib/widgets/export_scan_dialog.dart b/frontend/gs_analyzer_ui/lib/widgets/export_scan_dialog.dart index dabd1b8..e8f3b48 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/export_scan_dialog.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/export_scan_dialog.dart @@ -9,7 +9,7 @@ import 'package:gs_analyzer_ui/providers/storage_mode_provider.dart'; import 'package:gs_analyzer_ui/providers/storage_view_provider.dart'; import 'package:gs_analyzer_ui/services/api_service.dart'; import 'package:gs_analyzer_ui/utils/formatters.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; class ExportScanDialog extends ConsumerStatefulWidget { final String driveName; diff --git a/frontend/gs_analyzer_ui/lib/widgets/extension_breakdown_screen.dart b/frontend/gs_analyzer_ui/lib/widgets/extension_breakdown_screen.dart index 7ea213b..d9d673a 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/extension_breakdown_screen.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/extension_breakdown_screen.dart @@ -7,7 +7,7 @@ import 'package:gs_analyzer_ui/providers/directory_provider.dart'; import 'package:gs_analyzer_ui/providers/extension_breakdown_provider.dart'; import 'package:gs_analyzer_ui/providers/file_type_provider.dart'; import 'package:gs_analyzer_ui/utils/csv_exporter.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; class ExtensionBreakdownScreen extends ConsumerStatefulWidget { final String scanRoot; diff --git a/frontend/gs_analyzer_ui/lib/widgets/file_type_analyzer_panel.dart b/frontend/gs_analyzer_ui/lib/widgets/file_type_analyzer_panel.dart index 40f97e6..feb90ce 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/file_type_analyzer_panel.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/file_type_analyzer_panel.dart @@ -5,7 +5,7 @@ import 'package:gs_analyzer_ui/models/file_type_model.dart'; import 'package:gs_analyzer_ui/providers/file_type_provider.dart'; import 'package:gs_analyzer_ui/providers/extension_breakdown_provider.dart'; import 'package:gs_analyzer_ui/widgets/extension_breakdown_screen.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; class FileTypeAnalyzerPanel extends ConsumerWidget { final String driveName; diff --git a/frontend/gs_analyzer_ui/lib/widgets/global_sidebar_widget.dart b/frontend/gs_analyzer_ui/lib/widgets/global_sidebar_widget.dart index 5a1c594..cfdab97 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/global_sidebar_widget.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/global_sidebar_widget.dart @@ -1,8 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:gs_analyzer_ui/core/theme/hud_colors.dart'; +import 'package:gs_analyzer_ui/core/theme/hud_text_theme.dart'; import 'package:gs_analyzer_ui/providers/navigation_provider.dart'; import 'package:gs_analyzer_ui/providers/settings_provider.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; class GlobalSidebarWidget extends ConsumerStatefulWidget { const GlobalSidebarWidget({super.key}); @@ -19,14 +20,15 @@ class _GlobalSidebarWidgetState extends ConsumerState { Widget build(BuildContext context) { final currentRoute = ref.watch(navigationProvider); final double width = _isExpanded ? 240.0 : 54.0; + final theme = Theme.of(context); return AnimatedContainer( duration: const Duration(milliseconds: 200), curve: Curves.easeInOut, width: width, - decoration: const BoxDecoration( - color: Color(0xFF0F0F0F), - border: Border(right: BorderSide(color: Colors.white10)), + decoration: BoxDecoration( + color: theme.scaffoldBackgroundColor, + border: Border(right: BorderSide(color: theme.dividerColor)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -36,7 +38,7 @@ class _GlobalSidebarWidgetState extends ConsumerState { padding: const EdgeInsets.only(left: 7.0), alignment: Alignment.centerLeft, child: IconButton( - icon: const Icon(Icons.menu, color: HudTheme.textDim, size: 20), + icon: Icon(Icons.menu, color: theme.colorScheme.surfaceDim, size: 20), onPressed: () { setState(() { _isExpanded = !_isExpanded; @@ -55,23 +57,23 @@ class _GlobalSidebarWidgetState extends ConsumerState { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( 'NODE_01', style: TextStyle( - color: HudTheme.accentCyan, + color: theme.colorScheme.onPrimary, fontSize: 14, fontWeight: FontWeight.bold, - fontFamily: HudTheme.fontCore, + fontFamily: HudTextTheme.fontCore, ), ), const SizedBox(height: 4), - const Text( + Text( 'ONLINE', style: TextStyle( - color: HudTheme.accentGreen, + color: theme.colorScheme.secondary, fontSize: 10, fontWeight: FontWeight.bold, - fontFamily: HudTheme.fontCore, + fontFamily: HudTextTheme.fontCore, ), ), const SizedBox(height: 12), @@ -179,15 +181,16 @@ class _GlobalSidebarWidgetState extends ConsumerState { AppRoute currentRoute, { bool isAction = false, }) { + final theme = Theme.of(context); final isActive = route == currentRoute && !isAction; - final color = isActive ? HudTheme.accentCyan : HudTheme.textDim; + final color = isActive ? theme.colorScheme.onPrimary : theme.colorScheme.surfaceDim; // Windows 11 style accent line final accentLine = Container( width: 3, height: 16, decoration: BoxDecoration( - color: isActive ? HudTheme.accentCyan : Colors.transparent, + color: isActive ? theme.colorScheme.onPrimary : Colors.transparent, borderRadius: BorderRadius.circular(2), ), ); @@ -228,7 +231,7 @@ class _GlobalSidebarWidgetState extends ConsumerState { title, maxLines: 1, overflow: TextOverflow.ellipsis, - style: HudTheme.bodyText.copyWith( + style: theme.textTheme.bodySmall?.copyWith( color: color, fontSize: 13, fontWeight: isActive ? FontWeight.w500 : FontWeight.normal, @@ -257,13 +260,14 @@ class _GlobalSidebarWidgetState extends ConsumerState { final bool hasUnsavedChanges = ref .watch(settingsProvider) .hasUnsavedChanges; - final color = isSelected ? HudTheme.accentCyan : HudTheme.textDim; + final theme = Theme.of(context); + final color = isSelected ? theme.colorScheme.onPrimary : theme.colorScheme.surfaceDim; final accentLine = Container( width: 3, height: 16, decoration: BoxDecoration( - color: isSelected ? HudTheme.accentCyan : Colors.transparent, + color: isSelected ? theme.colorScheme.onPrimary : Colors.transparent, borderRadius: BorderRadius.circular(2), ), ); @@ -298,7 +302,7 @@ class _GlobalSidebarWidgetState extends ConsumerState { 'SETTINGS', maxLines: 1, overflow: TextOverflow.ellipsis, - style: HudTheme.bodyText.copyWith( + style: theme.textTheme.bodySmall?.copyWith( color: color, fontSize: 13, fontWeight: isSelected diff --git a/frontend/gs_analyzer_ui/lib/widgets/go_up_row_widget.dart b/frontend/gs_analyzer_ui/lib/widgets/go_up_row_widget.dart index 5e2aec5..a2b3f7c 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/go_up_row_widget.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/go_up_row_widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gs_analyzer_ui/providers/directory_provider.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; class GoUpRowWidget extends ConsumerWidget { const GoUpRowWidget({super.key}); diff --git a/frontend/gs_analyzer_ui/lib/widgets/large_file_scanner_panel.dart b/frontend/gs_analyzer_ui/lib/widgets/large_file_scanner_panel.dart index 2ddea32..d171a30 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/large_file_scanner_panel.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/large_file_scanner_panel.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gs_analyzer_ui/providers/storage_mode_provider.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/utils/nuke_protocol.dart'; import 'package:gs_analyzer_ui/providers/large_file_provider.dart'; diff --git a/frontend/gs_analyzer_ui/lib/widgets/nuke_preview_dialog.dart b/frontend/gs_analyzer_ui/lib/widgets/nuke_preview_dialog.dart index d44b32b..f085067 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/nuke_preview_dialog.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/nuke_preview_dialog.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:gs_analyzer_ui/models/nuke_preview.dart'; import 'package:gs_analyzer_ui/services/api_service.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; class NukePreviewResult { final bool confirmed; diff --git a/frontend/gs_analyzer_ui/lib/widgets/nuke_progress_dialog.dart b/frontend/gs_analyzer_ui/lib/widgets/nuke_progress_dialog.dart index a2668d5..b069743 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/nuke_progress_dialog.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/nuke_progress_dialog.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gs_analyzer_ui/providers/nuke_provider.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import '../services/api_service.dart'; class NukeProgressDialog extends ConsumerWidget { diff --git a/frontend/gs_analyzer_ui/lib/widgets/permission_audit_panel.dart b/frontend/gs_analyzer_ui/lib/widgets/permission_audit_panel.dart index 0df86c4..cca9855 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/permission_audit_panel.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/permission_audit_panel.dart @@ -4,7 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gs_analyzer_ui/models/permission_audit_models.dart'; import 'package:gs_analyzer_ui/providers/drive_stats_provider.dart'; import 'package:gs_analyzer_ui/providers/permission_audit_provider.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; class PermissionAuditPanel extends ConsumerWidget { const PermissionAuditPanel({super.key}); diff --git a/frontend/gs_analyzer_ui/lib/widgets/rule_editor_dialog.dart b/frontend/gs_analyzer_ui/lib/widgets/rule_editor_dialog.dart index 6e4395f..922e022 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/rule_editor_dialog.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/rule_editor_dialog.dart @@ -6,7 +6,7 @@ import 'package:gs_analyzer_ui/models/nuke_preview.dart'; import 'package:gs_analyzer_ui/providers/automation_provider.dart'; import 'package:gs_analyzer_ui/services/api_service.dart'; import 'package:gs_analyzer_ui/utils/formatters.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; class RuleEditorDialog extends ConsumerStatefulWidget { final AutomationRule? existingRule; diff --git a/frontend/gs_analyzer_ui/lib/widgets/scheduled_scans_panel.dart b/frontend/gs_analyzer_ui/lib/widgets/scheduled_scans_panel.dart index 4411eb7..a497ade 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/scheduled_scans_panel.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/scheduled_scans_panel.dart @@ -4,7 +4,7 @@ import 'package:gs_analyzer_ui/models/scheduled_scan_model.dart'; import 'package:gs_analyzer_ui/providers/schedule_provider.dart'; import 'package:gs_analyzer_ui/providers/settings_provider.dart'; import 'package:gs_analyzer_ui/providers/drive_stats_provider.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:intl/intl.dart'; class ScheduledScansPanel extends ConsumerWidget { diff --git a/frontend/gs_analyzer_ui/lib/widgets/side_bar_widget.dart b/frontend/gs_analyzer_ui/lib/widgets/side_bar_widget.dart index f6ef403..6e8051d 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/side_bar_widget.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/side_bar_widget.dart @@ -4,7 +4,7 @@ import 'package:gs_analyzer_ui/providers/root_tree_provider.dart'; import 'package:gs_analyzer_ui/providers/directory_provider.dart'; import 'package:gs_analyzer_ui/services/api_service.dart'; import 'package:gs_analyzer_ui/widgets/directory_node_widget.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/utils/hud_label.dart'; import 'package:gs_analyzer_ui/providers/settings_provider.dart'; diff --git a/frontend/gs_analyzer_ui/lib/widgets/telemetry_history_chart.dart b/frontend/gs_analyzer_ui/lib/widgets/telemetry_history_chart.dart index df09588..6d70896 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/telemetry_history_chart.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/telemetry_history_chart.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:fl_chart/fl_chart.dart'; import 'package:gs_analyzer_ui/providers/telemetry_history_provider.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:intl/intl.dart'; import 'package:gs_analyzer_ui/utils/formatters.dart'; diff --git a/frontend/gs_analyzer_ui/lib/widgets/telemetry_hud_widget.dart b/frontend/gs_analyzer_ui/lib/widgets/telemetry_hud_widget.dart index 756ba61..8295cff 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/telemetry_hud_widget.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/telemetry_hud_widget.dart @@ -4,7 +4,7 @@ import 'package:gs_analyzer_ui/providers/telemetry_provider.dart'; import 'package:gs_analyzer_ui/utils/globals.dart'; import '../providers/directory_provider.dart'; import '../services/api_service.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; class TelemetryHudWidget extends ConsumerWidget { const TelemetryHudWidget({super.key}); diff --git a/frontend/gs_analyzer_ui/lib/widgets/temp_cleaner_panel.dart b/frontend/gs_analyzer_ui/lib/widgets/temp_cleaner_panel.dart index 3518b1b..0bc8119 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/temp_cleaner_panel.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/temp_cleaner_panel.dart @@ -4,7 +4,7 @@ import 'package:gs_analyzer_ui/providers/storage_mode_provider.dart'; import 'package:gs_analyzer_ui/providers/temp_cleaner_provider.dart'; import 'package:gs_analyzer_ui/providers/drive_stats_provider.dart'; import 'package:gs_analyzer_ui/utils/globals.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; class TempCleanerPanel extends ConsumerStatefulWidget { const TempCleanerPanel({super.key}); diff --git a/frontend/gs_analyzer_ui/lib/widgets/undo_history_panel.dart b/frontend/gs_analyzer_ui/lib/widgets/undo_history_panel.dart index 0939748..b68e171 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/undo_history_panel.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/undo_history_panel.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gs_analyzer_ui/models/nuke_result.dart'; import 'package:gs_analyzer_ui/services/api_service.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/utils/globals.dart'; import 'package:gs_analyzer_ui/providers/directory_provider.dart'; import 'package:gs_analyzer_ui/providers/root_tree_provider.dart'; diff --git a/frontend/gs_analyzer_ui/lib/widgets/watcher_event_log_panel.dart b/frontend/gs_analyzer_ui/lib/widgets/watcher_event_log_panel.dart index ff806e3..64f9dfd 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/watcher_event_log_panel.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/watcher_event_log_panel.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gs_analyzer_ui/models/watcher_event.dart'; import 'package:gs_analyzer_ui/providers/watcher_log_provider.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:intl/intl.dart'; import 'dart:convert'; import 'dart:ui' as ui; diff --git a/frontend/gs_analyzer_ui/test/widgets/disk_io_card_test.dart b/frontend/gs_analyzer_ui/test/widgets/disk_io_card_test.dart index bf13709..8677e05 100644 --- a/frontend/gs_analyzer_ui/test/widgets/disk_io_card_test.dart +++ b/frontend/gs_analyzer_ui/test/widgets/disk_io_card_test.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:gs_analyzer_ui/models/disk_io_telemetry.dart'; -import 'package:gs_analyzer_ui/utils/hud_theme.dart'; +import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/widgets/disk_io_card.dart'; void main() { diff --git a/frontend/gs_analyzer_ui/test/widgets/settings_appearance_test.dart b/frontend/gs_analyzer_ui/test/widgets/settings_appearance_test.dart new file mode 100644 index 0000000..0642eb6 --- /dev/null +++ b/frontend/gs_analyzer_ui/test/widgets/settings_appearance_test.dart @@ -0,0 +1,139 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:gs_analyzer_ui/models/app_settings.dart'; +import 'package:gs_analyzer_ui/models/cache_stats.dart'; +import 'package:gs_analyzer_ui/providers/cache_stats_provider.dart'; +import 'package:gs_analyzer_ui/providers/settings_provider.dart'; +import 'package:gs_analyzer_ui/screen/settings_screen.dart'; +import 'package:gs_analyzer_ui/services/api_service.dart'; + +class MockApiService extends Mock implements ApiService {} + +class TestSettingsNotifier extends StateNotifier + implements SettingsNotifier { + TestSettingsNotifier(SettingsState state) : super(state); + + @override + void updateUI() { + state = state.copyWith(currentSettings: state.currentSettings); + } + + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} + +void main() { + late MockApiService mockApi; + + setUp(() { + mockApi = MockApiService(); + when(() => mockApi.getCacheStats()).thenAnswer( + (_) async => CacheStats( + activeEntries: 100, + rootCount: 1, + approximateMemoryBytes: 1024, + hitRate: 0.9, + ), + ); + }); + + bool getSwitchValueForLabel(WidgetTester tester, String label) { + final rowFinder = find.ancestor( + of: find.text(label), + matching: find.byType(Row), + ); + final switchFinder = find.descendant( + of: rowFinder, + matching: find.byType(Switch), + ); + final switchWidget = tester.widget(switchFinder); + return switchWidget.value; + } + + testWidgets( + 'Appearance section switches modes mutually exclusively between Dark, Light, and System', + (tester) async { + tester.view.physicalSize = const Size(1920, 2400); + tester.view.devicePixelRatio = 1.0; + addTearDown(tester.view.resetPhysicalSize); + addTearDown(tester.view.resetDevicePixelRatio); + + final initialSettings = AppSettings.fromjson({ + 'appearance': { + 'theme': 'cyber_dark', + 'accentColor': 'cyan', + 'compactMode': true, + 'showAnimations': true, + }, + }); + + final notifier = TestSettingsNotifier( + SettingsState( + isLoading: false, + savedSettings: initialSettings, + currentSettings: initialSettings, + ), + ); + + await tester.pumpWidget( + ProviderScope( + overrides: [ + settingsProvider.overrideWith((ref) => notifier), + cacheStatsApiProvider.overrideWithValue(mockApi), + ], + child: const MaterialApp( + home: SettingsScreen(), + ), + ), + ); + + await tester.pumpAndSettle(); + + // 1. Initially Dark Mode is ON, Light Mode is OFF, System Mode is OFF + expect(getSwitchValueForLabel(tester, 'DARK MODE'), isTrue); + expect(getSwitchValueForLabel(tester, 'LIGHT MODE'), isFalse); + expect(getSwitchValueForLabel(tester, 'SYSTEM MODE'), isFalse); + + // 2. Tap LIGHT MODE toggle + await tester.tap(find.text('LIGHT MODE')); + await tester.pumpAndSettle(); + + // Light Mode should now be ON, Dark Mode OFF, System Mode OFF + expect(getSwitchValueForLabel(tester, 'DARK MODE'), isFalse); + expect(getSwitchValueForLabel(tester, 'LIGHT MODE'), isTrue); + expect(getSwitchValueForLabel(tester, 'SYSTEM MODE'), isFalse); + expect(notifier.state.currentSettings?.appearance.theme, 'cyber_light'); + + // 3. Tap SYSTEM MODE toggle + await tester.tap(find.text('SYSTEM MODE')); + await tester.pumpAndSettle(); + + // System Mode should now be ON, Dark Mode OFF, Light Mode OFF + expect(getSwitchValueForLabel(tester, 'DARK MODE'), isFalse); + expect(getSwitchValueForLabel(tester, 'LIGHT MODE'), isFalse); + expect(getSwitchValueForLabel(tester, 'SYSTEM MODE'), isTrue); + expect(notifier.state.currentSettings?.appearance.theme, 'system'); + + // 4. Tap SYSTEM MODE again (tapping already active mode) + await tester.tap(find.text('SYSTEM MODE')); + await tester.pumpAndSettle(); + + // System Mode must remain active, ensuring modes never turn all-off + expect(getSwitchValueForLabel(tester, 'DARK MODE'), isFalse); + expect(getSwitchValueForLabel(tester, 'LIGHT MODE'), isFalse); + expect(getSwitchValueForLabel(tester, 'SYSTEM MODE'), isTrue); + expect(notifier.state.currentSettings?.appearance.theme, 'system'); + + // 5. Tap DARK MODE toggle to switch back + await tester.tap(find.text('DARK MODE')); + await tester.pumpAndSettle(); + + expect(getSwitchValueForLabel(tester, 'DARK MODE'), isTrue); + expect(getSwitchValueForLabel(tester, 'LIGHT MODE'), isFalse); + expect(getSwitchValueForLabel(tester, 'SYSTEM MODE'), isFalse); + expect(notifier.state.currentSettings?.appearance.theme, 'cyber_dark'); + }); +} diff --git a/frontend/gs_analyzer_ui/test/widgets/theme_mode_rebuild_test.dart b/frontend/gs_analyzer_ui/test/widgets/theme_mode_rebuild_test.dart new file mode 100644 index 0000000..678b355 --- /dev/null +++ b/frontend/gs_analyzer_ui/test/widgets/theme_mode_rebuild_test.dart @@ -0,0 +1,100 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_riverpod/legacy.dart'; +import 'package:gs_analyzer_ui/models/app_settings.dart'; +import 'package:gs_analyzer_ui/providers/settings_provider.dart'; +import 'package:gs_analyzer_ui/core/theme/hud_colors.dart'; +import 'package:gs_analyzer_ui/core/theme/hud_theme.dart'; + +class TestSettingsNotifier extends StateNotifier + implements SettingsNotifier { + TestSettingsNotifier(SettingsState state) : super(state); + + @override + void updateUI() { + state = state.copyWith( + currentSettings: state.currentSettings, + validationErrors: [], + ); + } + + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} + +class TestApp extends ConsumerWidget { + const TestApp({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final theme = ref.watch( + settingsProvider.select((s) => s.currentSettings?.appearance.theme), + ); + final accentKey = ref.watch( + settingsProvider.select((s) => s.currentSettings?.appearance.accentColor), + ); + + final lightAccent = HudColors.resolveAccent(accentKey, Brightness.light); + final darkAccent = HudColors.resolveAccent(accentKey, Brightness.dark); + final themeMode = HudTheme.resolveThemeMode(theme); + + return MaterialApp( + theme: HudTheme.lightTheme(lightAccent), + darkTheme: HudTheme.darkTheme(darkAccent), + themeMode: themeMode, + home: const SizedBox(), + ); + } +} + +void main() { + testWidgets('TestApp updates themeMode immediately when theme changes', + (tester) async { + final initialSettings = AppSettings.fromjson({ + 'appearance': { + 'theme': 'cyber_dark', + 'accentColor': 'cyan', + }, + }); + + final notifier = TestSettingsNotifier( + SettingsState( + isLoading: false, + savedSettings: initialSettings, + currentSettings: initialSettings, + ), + ); + + await tester.pumpWidget( + ProviderScope( + overrides: [ + settingsProvider.overrideWith((ref) => notifier), + ], + child: const TestApp(), + ), + ); + await tester.pump(); + + // Verify initial themeMode is dark + MaterialApp app = tester.widget(find.byType(MaterialApp)); + expect(app.themeMode, ThemeMode.dark); + + // Now mutate theme and call updateUI (simulating what SettingsScreen does) + notifier.state.currentSettings!.appearance.theme = 'cyber_light'; + notifier.updateUI(); + await tester.pump(); + + // Check if MaterialApp received the updated themeMode immediately + app = tester.widget(find.byType(MaterialApp)); + expect(app.themeMode, ThemeMode.light); + + // Switch to system mode + notifier.state.currentSettings!.appearance.theme = 'system'; + notifier.updateUI(); + await tester.pump(); + + app = tester.widget(find.byType(MaterialApp)); + expect(app.themeMode, ThemeMode.system); + }); +} From ca876cc0b36230b06fc43ed10168ba3d0adf0a27 Mon Sep 17 00:00:00 2001 From: joyy_dev Date: Sat, 19 Sep 2026 22:54:32 +0100 Subject: [PATCH 2/4] refactor: update theme colors and text styles across dashboard components --- .../lib/core/theme/hud_colors.dart | 32 +++++++++---------- .../lib/core/theme/hud_text_theme.dart | 16 ---------- .../dashboard/screens/dashboard_screen.dart | 7 +++- .../dashboard/widgets/cpu_bar_chart.dart | 10 +++--- .../dashboard/widgets/cpu_load_report.dart | 22 +++++-------- .../dashboard/widgets/cpu_memory.dart | 18 +++++------ .../features/dashboard/widgets/net_rate.dart | 26 +++++++-------- .../dashboard/widgets/process_filter.dart | 5 +-- .../gs_analyzer_ui/lib/utils/hud_label.dart | 3 +- .../lib/widgets/global_sidebar_widget.dart | 9 +++--- 10 files changed, 64 insertions(+), 84 deletions(-) diff --git a/frontend/gs_analyzer_ui/lib/core/theme/hud_colors.dart b/frontend/gs_analyzer_ui/lib/core/theme/hud_colors.dart index 7359c10..d5989f0 100644 --- a/frontend/gs_analyzer_ui/lib/core/theme/hud_colors.dart +++ b/frontend/gs_analyzer_ui/lib/core/theme/hud_colors.dart @@ -14,32 +14,32 @@ class HudColors { // --------- HUD LIGHT THEME COLOR ----------------- static const lightBgBase = Color(0xFFF4F7F8); static const lightBgPanel = Color(0xFFF0F6FF); - static const lightPrimaryBorder = Color(0XFF00545D); + static const lightPrimaryBorder = Colors.cyan; // Text Colors - static const lightTextMain = Color(0xFF172026); - static const lightTextMuted = Color(0xFF5F6B72); - static const lightTextDim = Color(0xFF8A969D); + static const lightTextMain = Colors.black; + static const lightTextMuted = Colors.black87; + static const lightTextDim = Colors.black54; // --------- ACCENT COLORS ---------------- - static const Color lightAccentCyan = Color(0xFF00E5FF); - static const Color darkAccentCyan = Color(0xFF00545D); + static const Color lightAccentCyan = Colors.cyan; + static const Color darkAccentCyan = Colors.cyanAccent; - static const Color lightAccentGreen = Color(0xFF69F0AE); - static const Color darkAccentGreen = Color(0xFF006F3A); + static const Color lightAccentGreen = Colors.green; + static const Color darkAccentGreen = Colors.greenAccent; - static const Color lightAccentRed = Color(0xFFFF5252); - static const Color darkAccentRed = Color(0xFF9D0E0E); + static const Color lightAccentRed = Colors.red; + static const Color darkAccentRed = Colors.redAccent; - static const Color lightAccentAmber = Color(0xFFFFC107); - static const Color darkAccentAmber = Color(0xFF896700); + static const Color lightAccentAmber = Colors.amber; + static const Color darkAccentAmber = Colors.amberAccent; - static const Color lightAccentPurple = Color(0xFFE040FB); - static const Color darkAccentPurple = Color(0xFF7E0095); + static const Color lightAccentPurple = Colors.purple; + static const Color darkAccentPurple = Colors.purpleAccent; - static const Color lightAccentBlue = Color(0xFF448AFF); - static const Color darkAccentBlue = Color(0xFF003082); + static const Color lightAccentBlue = Colors.blue; + static const Color darkAccentBlue = Colors.blueAccent; static Color resolveAccent(String? accentKey, Brightness brightness) { diff --git a/frontend/gs_analyzer_ui/lib/core/theme/hud_text_theme.dart b/frontend/gs_analyzer_ui/lib/core/theme/hud_text_theme.dart index ead01f2..1b11217 100644 --- a/frontend/gs_analyzer_ui/lib/core/theme/hud_text_theme.dart +++ b/frontend/gs_analyzer_ui/lib/core/theme/hud_text_theme.dart @@ -6,14 +6,6 @@ class HudTextTheme { static const String fontCore = 'Courier'; static const light = TextTheme( - headlineLarge: TextStyle( - fontFamily: fontCore, - color: HudColors.lightPrimaryBorder, - fontWeight: FontWeight.bold, - letterSpacing: 2, - fontSize: 18 - ), - headlineMedium: TextStyle( fontFamily: fontCore, color: HudColors.lightPrimaryBorder, @@ -64,14 +56,6 @@ class HudTextTheme { ); static const dark = TextTheme( - headlineLarge: TextStyle( - fontFamily: fontCore, - color: HudColors.darkPrimaryBorder, - fontWeight: FontWeight.bold, - letterSpacing: 2, - fontSize: 18 - ), - headlineMedium: TextStyle( fontFamily: fontCore, color: HudColors.darkPrimaryBorder, diff --git a/frontend/gs_analyzer_ui/lib/features/dashboard/screens/dashboard_screen.dart b/frontend/gs_analyzer_ui/lib/features/dashboard/screens/dashboard_screen.dart index 49fa8d5..3fb4cfc 100644 --- a/frontend/gs_analyzer_ui/lib/features/dashboard/screens/dashboard_screen.dart +++ b/frontend/gs_analyzer_ui/lib/features/dashboard/screens/dashboard_screen.dart @@ -50,7 +50,12 @@ class _DashboardScreenState extends ConsumerState { children: [ Text( 'System_Overview'.toUpperCase(), - style:theme.textTheme.headlineLarge + style:TextStyle( + fontSize: 19, + color: theme.colorScheme.primary, + fontWeight: FontWeight.bold, + letterSpacing: 2 + ) ), Text( 'Real-Time Telemetry Stream'.toUpperCase(), diff --git a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_bar_chart.dart b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_bar_chart.dart index 02b0544..139b57f 100644 --- a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_bar_chart.dart +++ b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_bar_chart.dart @@ -1,6 +1,5 @@ import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; -import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; class CpuBarChart extends StatelessWidget { final Map> coreGroups; @@ -11,6 +10,7 @@ class CpuBarChart extends StatelessWidget { Widget build(BuildContext context) { final labels = coreGroups.keys.toList(); final barGroups = []; + final theme = Theme.of(context); int x = 0; for (final entry in coreGroups.entries) { @@ -21,7 +21,7 @@ class CpuBarChart extends StatelessWidget { barRods: entry.value.map((load) { return BarChartRodData( toY: load, - color: load > 80 ? HudTheme.accentAmber : HudTheme.accentCyan, + color: load > 80 ? theme.colorScheme.tertiary : theme.colorScheme.onPrimary, width: 14, borderRadius: BorderRadius.circular(3), ); @@ -40,8 +40,8 @@ class CpuBarChart extends StatelessWidget { show: true, drawVerticalLine: false, getDrawingHorizontalLine: (value) { - return const FlLine( - color: Colors.white12, + return FlLine( + color: theme.dividerColor, strokeWidth: 1, dashArray: [4, 4], ); @@ -66,7 +66,7 @@ class CpuBarChart extends StatelessWidget { } return SideTitleWidget( meta: meta, - child: Text(labels[index], style: HudTheme.labelMuted), + child: Text(labels[index], style: theme.textTheme.titleMedium), ); }, ), diff --git a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_load_report.dart b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_load_report.dart index 082a450..d74eccc 100644 --- a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_load_report.dart +++ b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_load_report.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gs_analyzer_ui/features/dashboard/widgets/cpu_bar_chart.dart'; import 'package:gs_analyzer_ui/providers/cpu_provider.dart'; -import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/widgets/custom_container.dart'; class CpuLoadReport extends ConsumerStatefulWidget { @@ -18,22 +17,23 @@ class _CpuLoadReportState extends ConsumerState { Widget build(BuildContext context) { final cpuState = ref.watch(cpuProvider); final snapShot = cpuState.snapshot; + final theme = Theme.of(context); if (snapShot == null) { return CustomContainer( - color: HudTheme.bgPanel, + color: theme.colorScheme.surface, padding: const EdgeInsets.all(20), - child: const SizedBox( + child: SizedBox( height: 240, child: Center( - child: Text('AWAITING CPU TELEMETRY...', style: HudTheme.labelMuted), + child: Text('AWAITING CPU TELEMETRY...', style: theme.textTheme.titleMedium), ), ), ); } return CustomContainer( - color: HudTheme.bgPanel, + color: theme.colorScheme.surface, padding: EdgeInsets.all(20), child: Column( children: [ @@ -42,7 +42,7 @@ class _CpuLoadReportState extends ConsumerState { contentPadding: EdgeInsets.zero, title: Text( 'CPU LOAD [AVG]', - style: HudTheme.labelMuted, + style: theme.textTheme.titleMedium, ), subtitle: Row( children: [ @@ -50,7 +50,7 @@ class _CpuLoadReportState extends ConsumerState { text: TextSpan( text: '${snapShot.averageLoad.toStringAsFixed(1)}', style: TextStyle( - color: HudTheme.accentCyan, + color: theme.colorScheme.onPrimary, fontSize: 28 ), children: [ @@ -64,18 +64,12 @@ class _CpuLoadReportState extends ConsumerState { ] ) ), - // const SizedBox(width: 5,), - // Icon(Icons.arrow_upward, size: 14, color: Colors.greenAccent,), - // Text( - // '2.4%', - // style: HudTheme.statGreen, - // ) ], ), trailing: Icon( Icons.memory, size: 40, - color: HudTheme.accentCyan.withValues(alpha: 0.1), + color: theme.colorScheme.onPrimary.withValues(alpha: 0.1), ), ), const SizedBox(height: 10,), diff --git a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_memory.dart b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_memory.dart index f9d886d..def341c 100644 --- a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_memory.dart +++ b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/cpu_memory.dart @@ -4,7 +4,6 @@ import 'package:gs_analyzer_ui/features/dashboard/widgets/custom_progress_indica import 'package:gs_analyzer_ui/providers/ram_provider.dart'; import 'package:gs_analyzer_ui/providers/ram_alert_provider.dart'; import 'package:gs_analyzer_ui/utils/hud_label.dart'; -import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/widgets/custom_container.dart'; class CpuMemory extends ConsumerStatefulWidget { @@ -15,16 +14,15 @@ class CpuMemory extends ConsumerStatefulWidget { } class _CpuMemoryState extends ConsumerState{ - // String _formatGB(int bytes) => (bytes / (1024 * 1024 * 1024)).toStringAsFixed(1); @override Widget build(BuildContext context) { final ramstate = ref.watch(ramProvider); final ramAlert = ref.watch(ramAlertProvider); - // final drive = ref.watsch(currentDriveProvider)!; + final theme = Theme.of(context); return CustomContainer( - color: HudTheme.bgPanel, + color: theme.colorScheme.surface, padding: EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -40,7 +38,7 @@ class _CpuMemoryState extends ConsumerState{ Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( - color: ramAlert.severity == 'critical' ? HudTheme.accentRed : HudTheme.accentAmber, + color: ramAlert.severity == 'critical' ? theme.colorScheme.error : theme.colorScheme.tertiary, borderRadius: BorderRadius.circular(4), ), child: Text( @@ -58,7 +56,7 @@ class _CpuMemoryState extends ConsumerState{ Icon( Icons.memory, size: 40, - color: HudTheme.accentGreen.withValues(alpha: 0.1), + color: theme.colorScheme.secondary.withValues(alpha: 0.1), ) ], ), @@ -107,12 +105,12 @@ class _CpuMemoryState extends ConsumerState{ color: AlwaysStoppedAnimation(Colors.greenAccent), ), const SizedBox(height: 20,), - // Spacer(), + Spacer(), Row( children: [ Expanded( child: CustomContainer( - color: Colors.black, + color: theme.dividerTheme.color!, padding: EdgeInsets.all(8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -138,7 +136,7 @@ class _CpuMemoryState extends ConsumerState{ const SizedBox(width: 10,), Expanded( child: CustomContainer( - color: Colors.black, + color: theme.dividerTheme.color!, padding: EdgeInsets.all(8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -158,7 +156,7 @@ class _CpuMemoryState extends ConsumerState{ ), ) ], - ) + ) ), ) ], diff --git a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/net_rate.dart b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/net_rate.dart index cd04669..155dae5 100644 --- a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/net_rate.dart +++ b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/net_rate.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gs_analyzer_ui/providers/network_provider.dart'; import 'package:gs_analyzer_ui/utils/formatters.dart'; -import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/widgets/custom_container.dart'; class NetRate extends ConsumerStatefulWidget { @@ -32,10 +31,11 @@ class _NetRateState extends ConsumerState { Widget build(BuildContext context) { final netState = ref.watch(networkProvider); final primary = netState.primaryInterface; + final theme = Theme.of(context); if (primary == null) { return CustomContainer( - color: HudTheme.bgPanel, + color: theme.colorScheme.surface, padding: EdgeInsets.all(20), child: const Center( child: CircularProgressIndicator(), @@ -45,19 +45,19 @@ class _NetRateState extends ConsumerState { return CustomContainer( - color: HudTheme.bgPanel, + color: theme.colorScheme.surface, padding: EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'NET IO', - style: HudTheme.labelMuted, + style: theme.textTheme.titleMedium, ), const SizedBox(height: 10,), Text( 'ACTIVE: ${_getSimplifiedName(primary.name)}', - style: HudTheme.statCyan.copyWith( + style: theme.textTheme.bodyMedium?.copyWith( fontSize: 15, fontWeight: FontWeight.bold ), @@ -66,8 +66,8 @@ class _NetRateState extends ConsumerState { ListTile( contentPadding: EdgeInsets.zero, leading: CircleAvatar( - backgroundColor: HudTheme.accentCyan.withValues(alpha: 0.1), - foregroundColor: HudTheme.accentCyan, + backgroundColor: theme.colorScheme.onPrimary.withValues(alpha: 0.1), + foregroundColor: theme.colorScheme.onPrimary, radius: 18, child: Icon( Icons.arrow_downward @@ -77,12 +77,12 @@ class _NetRateState extends ConsumerState { 'RX RATE', style: TextStyle( fontSize: 12, - color: Colors.white60 + color: theme.colorScheme.surfaceDim ), ), subtitle: Text( formatRate(primary.rxBytesPerSec), - style: HudTheme.statCyan.copyWith( + style: theme.textTheme.bodyMedium?.copyWith( fontSize: 16, fontWeight: FontWeight.bold ), @@ -91,8 +91,8 @@ class _NetRateState extends ConsumerState { ListTile( contentPadding: EdgeInsets.zero, leading: CircleAvatar( - backgroundColor: HudTheme.accentAmber.withValues(alpha: 0.1), - foregroundColor: HudTheme.accentAmber, + backgroundColor: theme.colorScheme.tertiary.withValues(alpha: 0.1), + foregroundColor: theme.colorScheme.tertiary, radius: 18, child: Icon( Icons.arrow_upward @@ -102,14 +102,14 @@ class _NetRateState extends ConsumerState { 'TX RATE', style: TextStyle( fontSize: 12, - color: Colors.white60 + color: theme.colorScheme.surfaceDim ), ), subtitle: Text( formatRate(primary.txBytesPerSec), style: TextStyle( fontSize: 16, - color: HudTheme.accentAmber, + color: theme.colorScheme.tertiary, fontWeight: FontWeight.bold ), ) diff --git a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/process_filter.dart b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/process_filter.dart index 47d9e8d..f059aa8 100644 --- a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/process_filter.dart +++ b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/process_filter.dart @@ -15,6 +15,7 @@ class _ProcessFilterState extends ConsumerState { Widget build(BuildContext context) { final sort = ref.watch(processSortModeProvider); final status = ref.watch(processStatusFilterProvider); + final theme = Theme.of(context); String sortLabel; switch (sort) { @@ -49,7 +50,7 @@ class _ProcessFilterState extends ConsumerState { PopupMenuButton( tooltip: 'Filter by status', child: CustomContainer( - color: Colors.black, + color: theme.dividerTheme.color!, padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), child: Text( 'FILTER BY: $statusLabel' @@ -75,7 +76,7 @@ class _ProcessFilterState extends ConsumerState { PopupMenuButton( tooltip: 'Sort by:', child: CustomContainer( - color: Colors.black, + color: theme.dividerTheme.color!, padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), child: Text( 'SORT BY: $sortLabel' diff --git a/frontend/gs_analyzer_ui/lib/utils/hud_label.dart b/frontend/gs_analyzer_ui/lib/utils/hud_label.dart index bba6e0d..cbc3e54 100644 --- a/frontend/gs_analyzer_ui/lib/utils/hud_label.dart +++ b/frontend/gs_analyzer_ui/lib/utils/hud_label.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; class HudLabel extends StatelessWidget { final String text; @@ -19,7 +18,7 @@ class HudLabel extends StatelessWidget { Widget build(BuildContext context) { return Text( text.toUpperCase(), - style: HudTheme.labelMuted, + style: Theme.of(context).textTheme.titleMedium, textAlign: textAlign, overflow: overflow, maxLines: maxLines, diff --git a/frontend/gs_analyzer_ui/lib/widgets/global_sidebar_widget.dart b/frontend/gs_analyzer_ui/lib/widgets/global_sidebar_widget.dart index cfdab97..c04ff04 100644 --- a/frontend/gs_analyzer_ui/lib/widgets/global_sidebar_widget.dart +++ b/frontend/gs_analyzer_ui/lib/widgets/global_sidebar_widget.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:gs_analyzer_ui/core/theme/hud_colors.dart'; import 'package:gs_analyzer_ui/core/theme/hud_text_theme.dart'; import 'package:gs_analyzer_ui/providers/navigation_provider.dart'; import 'package:gs_analyzer_ui/providers/settings_provider.dart'; @@ -183,14 +182,14 @@ class _GlobalSidebarWidgetState extends ConsumerState { }) { final theme = Theme.of(context); final isActive = route == currentRoute && !isAction; - final color = isActive ? theme.colorScheme.onPrimary : theme.colorScheme.surfaceDim; + final color = isActive ? theme.colorScheme.primary : theme.colorScheme.surfaceDim; // Windows 11 style accent line final accentLine = Container( width: 3, height: 16, decoration: BoxDecoration( - color: isActive ? theme.colorScheme.onPrimary : Colors.transparent, + color: isActive ? theme.colorScheme.primary : Colors.transparent, borderRadius: BorderRadius.circular(2), ), ); @@ -261,13 +260,13 @@ class _GlobalSidebarWidgetState extends ConsumerState { .watch(settingsProvider) .hasUnsavedChanges; final theme = Theme.of(context); - final color = isSelected ? theme.colorScheme.onPrimary : theme.colorScheme.surfaceDim; + final color = isSelected ? theme.colorScheme.primary : theme.colorScheme.surfaceDim; final accentLine = Container( width: 3, height: 16, decoration: BoxDecoration( - color: isSelected ? theme.colorScheme.onPrimary : Colors.transparent, + color: isSelected ? theme.colorScheme.primary : Colors.transparent, borderRadius: BorderRadius.circular(2), ), ); From 0377bcdec7eae5249f12d5761d669458aa80503d Mon Sep 17 00:00:00 2001 From: joyy_dev Date: Tue, 22 Sep 2026 11:07:37 +0100 Subject: [PATCH 3/4] refactor: replace HudTheme with Theme.of(context) for consistent theming in status and process explorer screens --- .../features/dashboard/widgets/status.dart | 14 +- .../lib/screen/process_explorer_screen.dart | 144 ++++++++++-------- 2 files changed, 85 insertions(+), 73 deletions(-) diff --git a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/status.dart b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/status.dart index a903b2e..060e19b 100644 --- a/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/status.dart +++ b/frontend/gs_analyzer_ui/lib/features/dashboard/widgets/status.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; +import 'package:gs_analyzer_ui/utils/theme/hud_text_theme.dart'; class Status extends StatelessWidget { final String status; @@ -11,11 +11,13 @@ class Status extends StatelessWidget { @override Widget build(BuildContext context) { + final theme = Theme.of(context); + final (bg, fg) = switch(status) { - 'RUNNING' => (HudTheme.accentGreen.withValues(alpha: 0.15), HudTheme.accentGreen), - 'SLEEPING' => (Colors.white.withValues(alpha: 0.06), HudTheme.textDim), - 'ZOMBIE' => (HudTheme.accentRed.withValues(alpha: 0.15), HudTheme.accentRed), - _ => (HudTheme.accentRed.withValues(alpha: 0.15), HudTheme.accentRed) + 'RUNNING' => (theme.colorScheme.secondary.withValues(alpha: 0.15), theme.colorScheme.secondary), + 'SLEEPING' => (theme.colorScheme.onSurface.withValues(alpha: 0.06), theme.colorScheme.surfaceDim), + 'ZOMBIE' => (theme.colorScheme.error.withValues(alpha: 0.15), theme.colorScheme.error), + _ => (theme.colorScheme.error.withValues(alpha: 0.15), theme.colorScheme.error) }; return Container( padding: EdgeInsets.symmetric(horizontal: 7, vertical: 3), @@ -28,7 +30,7 @@ class Status extends StatelessWidget { style: TextStyle( color: fg, fontSize: 11, - fontFamily: HudTheme.fontCore, + fontFamily: HudTextTheme.fontCore, fontWeight: FontWeight.bold, letterSpacing: 0.8 ), diff --git a/frontend/gs_analyzer_ui/lib/screen/process_explorer_screen.dart b/frontend/gs_analyzer_ui/lib/screen/process_explorer_screen.dart index 8e24e87..0a394bb 100644 --- a/frontend/gs_analyzer_ui/lib/screen/process_explorer_screen.dart +++ b/frontend/gs_analyzer_ui/lib/screen/process_explorer_screen.dart @@ -6,7 +6,6 @@ import 'package:gs_analyzer_ui/models/process_telemetry.dart'; import 'package:gs_analyzer_ui/providers/cpu_provider.dart'; import 'package:gs_analyzer_ui/providers/process_explorer_provider.dart'; import 'package:gs_analyzer_ui/providers/ram_provider.dart'; -import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/utils/hud_label.dart'; import 'package:gs_analyzer_ui/providers/hud_density_provider.dart'; @@ -23,6 +22,7 @@ class ProcessExplorerScreen extends ConsumerWidget { final showAll = ref.watch(showAllProcessesProvider); final totalCount = ramState.groupedProcesses.length; final d = ref.watch(hudDensityProvider); + final theme = Theme.of(context); return Column( children: [ @@ -35,9 +35,9 @@ class ProcessExplorerScreen extends ConsumerWidget { // Table Expanded( child: ramState.isLoading && ramState.groupedProcesses.isEmpty - ? const Center( + ? Center( child: CircularProgressIndicator( - color: HudTheme.primaryBorder, + color: theme.colorScheme.outline, ), ) : Column( @@ -96,11 +96,12 @@ class _SystemLoadBar extends ConsumerWidget { final cpuText = cpuState.snapshot != null ? '${load.toStringAsFixed(1)}%' : '--'; + final theme = Theme.of(context); return Container( padding: EdgeInsets.symmetric(horizontal: d.panelPad, vertical: d.gap), - decoration: const BoxDecoration( - border: Border(bottom: BorderSide(color: Colors.white10)), + decoration: BoxDecoration( + border: Border(bottom: BorderSide(color: theme.dividerTheme.color!)), ), child: LayoutBuilder( builder: (context, constraints) { @@ -109,13 +110,13 @@ class _SystemLoadBar extends ConsumerWidget { 'CPU', cpuText, cpuPct.clamp(0.0, 1.0), - HudTheme.accentCyan, + theme.colorScheme.onPrimary, ); final ram = _LoadMetric( 'RAM', '${(ramPct * 100).toStringAsFixed(1)}%', ramPct.clamp(0.0, 1.0), - HudTheme.accentGreen, + theme.colorScheme.secondary, ); if (wide) { @@ -162,12 +163,12 @@ class _LoadMetric extends StatelessWidget { child: LinearProgressIndicator( value: pct.clamp(0.0, 1.0), color: color, - backgroundColor: Colors.white10, + backgroundColor: Theme.of(context).dividerTheme.color!, minHeight: 4, ), ), const SizedBox(width: 8), - Text(value, style: HudTheme.bodyText.copyWith(color: color)), + Text(value, style: Theme.of(context).textTheme.bodyMedium?.copyWith(color: color)), ], ); } @@ -179,6 +180,7 @@ class _Toolbar extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final sort = ref.watch(processSortModeProvider); final status = ref.watch(processStatusFilterProvider); + final theme = Theme.of(context); String sortLabel; switch (sort) { @@ -211,8 +213,8 @@ class _Toolbar extends ConsumerWidget { return Container( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10), - decoration: const BoxDecoration( - border: Border(bottom: BorderSide(color: Colors.white10)), + decoration: BoxDecoration( + border: Border(bottom: BorderSide(color: theme.dividerTheme.color!)), ), child: Row( children: [ @@ -221,25 +223,25 @@ class _Toolbar extends ConsumerWidget { flex: 3, child: TextFormField( initialValue: ref.read(processFilterProvider), - style: HudTheme.bodyText, - cursorColor: HudTheme.accentCyan, + style: theme.textTheme.bodyMedium, + cursorColor: theme.colorScheme.onPrimary, decoration: InputDecoration( hintText: 'FILTER BY NAME OR PID...', - hintStyle: HudTheme.bodyText.copyWith(color: HudTheme.textDim), - prefixIcon: const Icon( + hintStyle: theme.textTheme.bodyMedium?.copyWith(color: theme.colorScheme.surfaceDim), + prefixIcon: Icon( Icons.search, - color: HudTheme.textDim, + color: theme.colorScheme.surfaceDim, size: 18, ), filled: true, - fillColor: Colors.white.withValues(alpha: 0.04), + fillColor: theme.colorScheme.onSurface.withValues(alpha: 0.04), border: OutlineInputBorder( borderRadius: BorderRadius.circular(4), - borderSide: const BorderSide(color: Colors.white12), + borderSide: BorderSide(color: theme.dividerTheme.color!), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(4), - borderSide: const BorderSide(color: HudTheme.accentCyan), + borderSide: BorderSide(color: theme.colorScheme.onPrimary), ), contentPadding: const EdgeInsets.symmetric(vertical: 8), ), @@ -310,24 +312,25 @@ class _ToolbarChip extends StatelessWidget { @override Widget build(BuildContext context) { + final theme = Theme.of(context); return Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( - color: Colors.white.withValues(alpha: 0.04), - border: Border.all(color: Colors.white12), + color: theme.colorScheme.onSurface.withValues(alpha: 0.04), + border: Border.all(color: theme.dividerTheme.color!), borderRadius: BorderRadius.circular(4), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ - Icon(icon, color: HudTheme.textDim, size: 14), + Icon(icon, color: theme.colorScheme.surfaceDim, size: 14), const SizedBox(width: 6), Text( label, - style: HudTheme.bodyText.copyWith(color: HudTheme.textDim), + style: theme.textTheme.bodyMedium?.copyWith(color: theme.colorScheme.surfaceDim), ), const SizedBox(width: 4), - const Icon(Icons.arrow_drop_down, color: HudTheme.textDim, size: 14), + Icon(Icons.arrow_drop_down, color: theme.colorScheme.surfaceDim, size: 14), ], ), ); @@ -343,8 +346,8 @@ class _TableHeader extends ConsumerWidget { padding: EdgeInsets.symmetric(horizontal: d.panelPad, vertical: 8), height: d.rowHeight + 16, decoration: BoxDecoration( - color: HudTheme.bgPanel, - border: const Border(bottom: BorderSide(color: Colors.white10)), + color: Theme.of(context).colorScheme.surface, + border: Border(bottom: BorderSide(color: Theme.of(context).dividerTheme.color!)), ), child: const Row( children: [ @@ -401,13 +404,14 @@ class _ProcessRow extends ConsumerWidget { final isCpuHot = group.totalCpuPercent > 10.0; final isMemHot = group.totalPercentMem > 10.0; final isHot = isCpuHot || isMemHot; + final theme = Theme.of(context); final rowColor = isSelected - ? HudTheme.accentCyan.withValues(alpha: 0.06) + ? theme.colorScheme.onPrimary.withValues(alpha: 0.06) : isHot - ? HudTheme.accentAmber.withValues(alpha: 0.05) + ? theme.colorScheme.tertiary.withValues(alpha: 0.05) : Colors.transparent; - final textColor = isHot ? HudTheme.accentAmber : HudTheme.textMain; + final textColor = isHot ? theme.colorScheme.tertiary : theme.colorScheme.onSurface; final displayName = group.count > 1 ? '${group.name} (x${group.count})' : group.name; @@ -417,7 +421,7 @@ class _ProcessRow extends ConsumerWidget { // ── Main row InkWell( onTap: onTap, - hoverColor: Colors.white.withValues(alpha: 0.03), + hoverColor: theme.colorScheme.onSurface.withValues(alpha: 0.03), child: Container( height: d.rowHeight + 16, padding: EdgeInsets.symmetric(horizontal: d.panelPad, vertical: 8), @@ -425,10 +429,10 @@ class _ProcessRow extends ConsumerWidget { color: rowColor, border: Border( left: BorderSide( - color: isSelected ? HudTheme.accentCyan : Colors.transparent, + color: isSelected ? theme.colorScheme.onPrimary : Colors.transparent, width: 3, ), - bottom: const BorderSide(color: Colors.white10), + bottom: BorderSide(color: theme.dividerTheme.color!), ), ), child: Row( @@ -437,7 +441,7 @@ class _ProcessRow extends ConsumerWidget { flex: 2, child: Text( group.count > 1 ? 'GRP' : group.primaryPid.toString(), - style: HudTheme.bodyText.copyWith(color: HudTheme.textDim), + style: theme.textTheme.bodyMedium?.copyWith(color: theme.colorScheme.surfaceDim), textAlign: TextAlign.center, ), ), @@ -445,8 +449,8 @@ class _ProcessRow extends ConsumerWidget { flex: 4, child: Text( displayName, - style: HudTheme.bodyText.copyWith( - color: isSelected ? HudTheme.accentCyan : textColor, + style: theme.textTheme.bodyMedium?.copyWith( + color: isSelected ? theme.colorScheme.onPrimary : textColor, fontWeight: FontWeight.bold, ), overflow: TextOverflow.ellipsis, @@ -457,7 +461,7 @@ class _ProcessRow extends ConsumerWidget { flex: 3, child: Text( group.primaryUser, - style: HudTheme.bodyText.copyWith(color: HudTheme.textDim), + style: theme.textTheme.bodyMedium?.copyWith(color: theme.colorScheme.surfaceDim), overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, ), @@ -466,10 +470,10 @@ class _ProcessRow extends ConsumerWidget { flex: 2, child: Text( '${group.totalCpuPercent.toStringAsFixed(1)}%', - style: HudTheme.statGreen.copyWith( + style: theme.textTheme.bodyLarge?.copyWith( color: isCpuHot - ? HudTheme.accentAmber - : HudTheme.accentCyan, + ? theme.colorScheme.tertiary + : theme.colorScheme.onPrimary, ), textAlign: TextAlign.center, ), @@ -478,7 +482,7 @@ class _ProcessRow extends ConsumerWidget { flex: 2, child: Text( '${group.totalPercentMem.toStringAsFixed(1)}%', - style: HudTheme.statGreen.copyWith(color: textColor), + style: theme.textTheme.bodyLarge?.copyWith(color: textColor), textAlign: TextAlign.center, ), ), @@ -489,9 +493,9 @@ class _ProcessRow extends ConsumerWidget { Expanded( flex: 1, child: IconButton( - icon: const Icon( + icon: Icon( Icons.cancel_outlined, - color: HudTheme.accentRed, + color: theme.colorScheme.error, size: 18, ), tooltip: group.count > 1 @@ -516,6 +520,7 @@ class _ProcessRow extends ConsumerWidget { final label = group.count > 1 ? 'Kill all ${group.count} instances of ${group.name}?' : 'Kill PID ${group.primaryPid} (${group.name})?'; + final theme = Theme.of(context); showDialog( context: context, @@ -524,18 +529,18 @@ class _ProcessRow extends ConsumerWidget { backgroundColor: const Color(0xFF1A1A1A), title: Text( 'CONFIRM KILL', - style: HudTheme.bodyText.copyWith( - color: HudTheme.accentRed, + style: theme.textTheme.bodyMedium?.copyWith( + color: theme.colorScheme.error, fontWeight: FontWeight.bold, ), ), - content: Text(label, style: HudTheme.bodyText), + content: Text(label, style: theme.textTheme.bodyMedium), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text( 'CANCEL', - style: HudTheme.bodyText.copyWith(color: HudTheme.textDim), + style: theme.textTheme.bodyMedium?.copyWith(color: theme.colorScheme.surfaceDim), ), ), TextButton( @@ -550,8 +555,8 @@ class _ProcessRow extends ConsumerWidget { }, child: Text( 'KILL', - style: HudTheme.bodyText.copyWith( - color: HudTheme.accentRed, + style: theme.textTheme.bodyMedium?.copyWith( + color: theme.colorScheme.error, fontWeight: FontWeight.bold, ), ), @@ -570,15 +575,17 @@ class _DetailDrawer extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final theme = Theme.of(context); + return AnimatedContainer( duration: const Duration(milliseconds: 180), curve: Curves.easeOut, padding: EdgeInsets.symmetric(horizontal: d.panelPad, vertical: d.gap), decoration: BoxDecoration( - color: HudTheme.accentCyan.withValues(alpha: 0.04), - border: const Border( - left: BorderSide(color: HudTheme.accentCyan, width: 3), - bottom: BorderSide(color: Colors.white10), + color: theme.colorScheme.onPrimary.withValues(alpha: 0.04), + border: Border( + left: BorderSide(color: theme.colorScheme.onPrimary, width: 3), + bottom: BorderSide(color: theme.dividerTheme.color!), ), ), child: SingleChildScrollView( @@ -616,13 +623,13 @@ class _DetailDrawer extends ConsumerWidget { label: group.count > 1 ? 'KILL ALL ${group.name.toUpperCase()} (${group.count})' : 'KILL PID ${group.primaryPid}', - color: HudTheme.accentRed, + color: theme.colorScheme.error, onTap: () => _showKillDialog(context, ref), ), const SizedBox(width: 8), _ActionButton( label: 'COPY NAME', - color: HudTheme.textDim, + color: theme.colorScheme.surfaceDim, onTap: () => Clipboard.setData(ClipboardData(text: group.name)), ), @@ -635,6 +642,7 @@ class _DetailDrawer extends ConsumerWidget { } void _showKillDialog(BuildContext context, WidgetRef ref) { + final theme = Theme.of(context); showDialog( context: context, barrierDismissible: false, @@ -642,8 +650,8 @@ class _DetailDrawer extends ConsumerWidget { backgroundColor: const Color(0xFF1A1A1A), title: Text( 'CONFIRM KILL', - style: HudTheme.bodyText.copyWith( - color: HudTheme.accentRed, + style: theme.textTheme.bodyMedium?.copyWith( + color: theme.colorScheme.error, fontWeight: FontWeight.bold, ), ), @@ -651,14 +659,14 @@ class _DetailDrawer extends ConsumerWidget { group.count > 1 ? 'Terminate all ${group.count} instances of ${group.name}?' : 'Terminate PID ${group.primaryPid} (${group.name})?', - style: HudTheme.bodyText, + style: theme.textTheme.bodyMedium, ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text( 'CANCEL', - style: HudTheme.bodyText.copyWith(color: HudTheme.textDim), + style: theme.textTheme.bodyMedium?.copyWith(color: theme.colorScheme.surfaceDim), ), ), TextButton( @@ -673,8 +681,8 @@ class _DetailDrawer extends ConsumerWidget { }, child: Text( 'EXECUTE', - style: HudTheme.bodyText.copyWith( - color: HudTheme.accentRed, + style: theme.textTheme.bodyMedium?.copyWith( + color: theme.colorScheme.error, fontWeight: FontWeight.bold, ), ), @@ -692,6 +700,7 @@ class _DrawerStat extends StatelessWidget { @override Widget build(BuildContext context) { + final theme = Theme.of(context); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -699,7 +708,7 @@ class _DrawerStat extends StatelessWidget { const SizedBox(height: 2), Text( value, - style: HudTheme.bodyText.copyWith(color: HudTheme.accentCyan), + style: theme.textTheme.bodyMedium?.copyWith(color: theme.colorScheme.onPrimary), ), ], ); @@ -729,7 +738,7 @@ class _ActionButton extends StatelessWidget { ), child: Text( label, - style: HudTheme.bodyText.copyWith(color: color, fontSize: 11), + style: Theme.of(context).textTheme.bodyMedium?.copyWith(color: color, fontSize: 11), ), ), ); @@ -749,16 +758,17 @@ class _Footer extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final theme = Theme.of(context); return Container( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10), - decoration: const BoxDecoration( - border: Border(top: BorderSide(color: Colors.white10)), + decoration: BoxDecoration( + border: Border(top: BorderSide(color: theme.dividerTheme.color!)), ), child: Row( children: [ Text( 'Showing $shown of $total processes', - style: HudTheme.bodyText.copyWith(color: HudTheme.textDim), + style: theme.textTheme.bodyMedium?.copyWith(color: theme.colorScheme.surfaceDim), ), const Spacer(), if (total > 100) @@ -767,8 +777,8 @@ class _Footer extends ConsumerWidget { ref.read(showAllProcessesProvider.notifier).state = !showAll, child: Text( showAll ? 'SHOW TOP 100' : 'SHOW ALL ($total)', - style: HudTheme.bodyText.copyWith( - color: HudTheme.accentCyan, + style: theme.textTheme.bodyMedium?.copyWith( + color: theme.colorScheme.onPrimary, decoration: TextDecoration.underline, ), ), From 5ddfdbb231d56ea8a243bdde713752fb32608a09 Mon Sep 17 00:00:00 2001 From: joyy_dev Date: Fri, 25 Sep 2026 11:09:06 +0100 Subject: [PATCH 4/4] Enhanced theme resolution logic in hud_theme.dart to support dynamic theme switching. --- .../lib/screen/analyzer_dashboard.dart | 100 +++---- .../lib/screen/automation_screen.dart | 263 +++++++++--------- .../lib/utils/theme/hud_colors.dart | 85 ++++++ .../lib/utils/theme/hud_label.dart | 27 ++ .../lib/utils/theme/hud_text_theme.dart | 107 +++++++ .../lib/utils/theme/hud_theme.dart | 100 +++++++ .../lib/utils/theme/hud_theme_context.dart | 10 + .../lib/utils/theme/hud_theme_extension.dart | 94 +++++++ 8 files changed, 609 insertions(+), 177 deletions(-) create mode 100644 frontend/gs_analyzer_ui/lib/utils/theme/hud_colors.dart create mode 100644 frontend/gs_analyzer_ui/lib/utils/theme/hud_label.dart create mode 100644 frontend/gs_analyzer_ui/lib/utils/theme/hud_text_theme.dart create mode 100644 frontend/gs_analyzer_ui/lib/utils/theme/hud_theme.dart create mode 100644 frontend/gs_analyzer_ui/lib/utils/theme/hud_theme_context.dart create mode 100644 frontend/gs_analyzer_ui/lib/utils/theme/hud_theme_extension.dart diff --git a/frontend/gs_analyzer_ui/lib/screen/analyzer_dashboard.dart b/frontend/gs_analyzer_ui/lib/screen/analyzer_dashboard.dart index 1750c8a..f6f5cc9 100644 --- a/frontend/gs_analyzer_ui/lib/screen/analyzer_dashboard.dart +++ b/frontend/gs_analyzer_ui/lib/screen/analyzer_dashboard.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:gs_analyzer_ui/services/api_service.dart'; -import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; +import 'package:gs_analyzer_ui/utils/theme/hud_theme_context.dart'; import 'package:gs_analyzer_ui/widgets/_directory_search_widget.dart'; import 'package:gs_analyzer_ui/widgets/age_heatmap_overlay.dart'; import 'package:gs_analyzer_ui/widgets/directory_node_widget.dart'; @@ -35,14 +35,16 @@ class _AnalyzerDashboardState extends ConsumerState { final dirNotifier = ref.read(directoryProvider.notifier); final activeDrive = ref.watch(currentDriveProvider); final currentMode = ref.watch(storageModeProvider); + final theme = context.HudTheme; + final text = context.HudTextTheme; return Scaffold( - backgroundColor: HudTheme.bgBase, + backgroundColor: theme.panel, appBar: AppBar( title: Row( children: [ IconButton( - icon: const Icon(Icons.arrow_back, color: HudTheme.accentCyan), + icon: Icon(Icons.arrow_back, color: theme.accentCyan), tooltip: 'Back to Drives', onPressed: () => ref.read(storageViewProvider.notifier).state = StorageView.drivePicker, @@ -52,7 +54,7 @@ class _AnalyzerDashboardState extends ConsumerState { ref.watch(treeExpandedProvider) ? Icons.menu_open_outlined : Icons.menu_outlined, - color: HudTheme.accentCyan, + color: theme.accentCyan ), tooltip: 'Toggle Data Tree', onPressed: () { @@ -64,8 +66,8 @@ class _AnalyzerDashboardState extends ConsumerState { Expanded( child: Text( dirState.currentPath, - style: HudTheme.bodyText.copyWith( - color: HudTheme.textMain, + style: text.bodyMedium?.copyWith( + color: theme.text, fontWeight: FontWeight.bold, ), overflow: TextOverflow.ellipsis, @@ -73,16 +75,16 @@ class _AnalyzerDashboardState extends ConsumerState { ), ], ), - backgroundColor: HudTheme.bgPanel, + backgroundColor: theme.panel, elevation: 0, actions: [ PopupMenuButton( - icon: const Icon( + icon: Icon( Icons.build_circle_outlined, - color: HudTheme.accentAmber, + color: theme.accentAmber, ), tooltip: 'Storage Tools', - color: HudTheme.bgPanel, + color: theme.panel, offset: const Offset(0, 50), onSelected: (mode) { ref.read(storageModeProvider.notifier).state = mode; @@ -92,8 +94,8 @@ class _AnalyzerDashboardState extends ConsumerState { value: StorageMode.duplicateScanner, child: Text( 'DUPLICATE HUNTER', - style: HudTheme.bodyText.copyWith( - color: HudTheme.accentAmber, + style: text.bodyMedium?.copyWith( + color: theme.accentAmber, fontWeight: FontWeight.bold, ), ), @@ -102,8 +104,8 @@ class _AnalyzerDashboardState extends ConsumerState { value: StorageMode.largeFileScanner, child: Text( 'LARGE FILE SCANNER', - style: HudTheme.bodyText.copyWith( - color: HudTheme.accentAmber, + style: text.bodyMedium?.copyWith( + color: theme.accentAmber, fontWeight: FontWeight.bold, ), ), @@ -112,8 +114,8 @@ class _AnalyzerDashboardState extends ConsumerState { value: StorageMode.tempFileCleaner, child: Text( 'TEMP FILE CLEANER', - style: HudTheme.bodyText.copyWith( - color: HudTheme.accentAmber, + style: text.bodyMedium?.copyWith( + color: theme.accentAmber, fontWeight: FontWeight.bold, ), ), @@ -122,8 +124,8 @@ class _AnalyzerDashboardState extends ConsumerState { value: StorageMode.permissionAudit, child: Text( 'PERMISSION AUDIT', - style: HudTheme.bodyText.copyWith( - color: HudTheme.accentAmber, + style: text.titleMedium?.copyWith( + color: theme.accentAmber, fontWeight: FontWeight.bold, ), ), @@ -139,16 +141,16 @@ class _AnalyzerDashboardState extends ConsumerState { icon: Icon( isHeatmapOn ? Icons.thermostat : Icons.thermostat_outlined, color: isHeatmapOn - ? HudTheme.accentAmber - : HudTheme.textDim, + ? theme.accentAmber + : theme.textDim, size: 18, ), label: Text( 'AGE MAP', - style: HudTheme.bodyText.copyWith( + style: text.titleMedium?.copyWith( color: isHeatmapOn - ? HudTheme.accentAmber - : HudTheme.textDim, + ? theme.accentAmber + : theme.textDim, fontWeight: FontWeight.bold, fontSize: 11, letterSpacing: 1, @@ -162,9 +164,9 @@ class _AnalyzerDashboardState extends ConsumerState { }, ), PopupMenuButton( - icon: const Icon(Icons.sort_outlined), + icon: Icon(Icons.sort_outlined), tooltip: 'Sort Option', - color: HudTheme.bgPanel, + color: theme.panel, onSelected: (value) { if (value is SortMethod) { ref.read(directoryProvider.notifier).setSortMethod(value); @@ -176,28 +178,28 @@ class _AnalyzerDashboardState extends ConsumerState { CheckedPopupMenuItem( value: SortMethod.name, checked: dirState.sortMethod == SortMethod.name, - child: Text('Name', style: HudTheme.bodyText), + child: Text('Name', style: text.titleMedium), ), CheckedPopupMenuItem( value: SortMethod.size, checked: dirState.sortMethod == SortMethod.size, - child: Text('Total Size', style: HudTheme.bodyText), + child: Text('Total Size', style: text.titleMedium), ), CheckedPopupMenuItem( value: SortMethod.date, checked: dirState.sortMethod == SortMethod.date, - child: Text('DateModified', style: HudTheme.bodyText), + child: Text('DateModified', style: text.titleMedium), ), const PopupMenuDivider(), CheckedPopupMenuItem( value: true, checked: dirState.isAscending == true, - child: Text('Ascending', style: HudTheme.bodyText), + child: Text('Ascending', style:text.titleMedium), ), CheckedPopupMenuItem( value: false, checked: dirState.isAscending == false, - child: Text('Descending', style: HudTheme.bodyText), + child: Text('Descending', style: text.titleMedium), ), ], ), @@ -209,25 +211,25 @@ class _AnalyzerDashboardState extends ConsumerState { dirState.isSelectionMode ? 'CANCEL SELECTION' : 'SELECT MULTIPLE', - style: HudTheme.bodyText.copyWith( - color: HudTheme.accentCyan, + style: context.HudTextTheme.titleMedium?.copyWith( + color: theme.accentCyan, fontWeight: FontWeight.bold, ), ), ), if (dirState.isSelectionMode && dirState.selectedPath.isNotEmpty) IconButton( - icon: const Icon( + icon: Icon( Icons.delete_forever_outlined, - color: HudTheme.accentRed, + color: theme.accentRed, ), tooltip: 'Nuke Selected (${dirState.selectedPath.length})', onPressed: () => executeNukeProtocol(context, ref), ), IconButton( - icon: const Icon( + icon: Icon( Icons.refresh_outlined, - color: HudTheme.accentCyan, + color: theme.accentCyan, ), tooltip: 'Refresh', onPressed: () => dirNotifier.scanDirectory( @@ -311,7 +313,7 @@ class _AnalyzerDashboardState extends ConsumerState { return Center( child: Text( 'BRIDGE FAILURE: ${dirState.errorMessage}', - style: HudTheme.actionRed, + style: context.HudTextTheme.displayMedium, ), ); } @@ -319,7 +321,7 @@ class _AnalyzerDashboardState extends ConsumerState { return Column( children: [ - // Age Heatmap overlay (legend + summary) — shown when toggle is on + // Age Heatmap overlay (legend + summary) — shown when toggle is on if (isHeatmapOn) const AgeHeatmapOverlay(), DirectoryTableHeader(), if (dirState.currentPath != 'C:/' && dirState.searchQuery.isEmpty) @@ -327,10 +329,10 @@ class _AnalyzerDashboardState extends ConsumerState { Expanded( child: dirState.displayNodes.isEmpty && dirState.searchQuery.isNotEmpty - ? const Center( + ? Center( child: Text( 'NO DATA FOUND IN SECTOR', - style: HudTheme.labelMuted, + style: context.HudTextTheme.bodySmall, ), ) : ListView.builder( @@ -359,28 +361,28 @@ class _AnalyzerDashboardState extends ConsumerState { return Center( child: Container( padding: const EdgeInsets.all(40), - decoration: HudTheme.hudPanelDecoration, + decoration: context.HudTheme.hudPanelDecoration, child: Column( mainAxisSize: MainAxisSize.min, children: [ - const Icon(Icons.storage, size: 64, color: Colors.white24), + Icon(Icons.storage, size: 64, color: Colors.white24), const SizedBox(height: 24), Text( 'SYSTEM STANDBY', - style: HudTheme.headerCyan.copyWith( - color: HudTheme.accentCyan, + style: context.HudTextTheme.bodyMedium?.copyWith( + color: context.HudTheme.accentCyan, fontSize: 24, ), ), const SizedBox(height: 8), Text( 'Awaiting target matrix assignment for Directory Indexing.', - style: HudTheme.bodyText.copyWith(color: HudTheme.textDim), + style: context.HudTextTheme.titleMedium?.copyWith(color: context.HudTheme.textDim), ), const SizedBox(height: 32), OutlinedButton.icon( - icon: const Icon(Icons.touch_app, color: HudTheme.accentCyan), - label: const Text( + icon: Icon(Icons.touch_app, color: context.HudTheme.accentCyan), + label: Text( 'ASSIGN TARGET DRIVE', style: TextStyle(letterSpacing: 2), ), @@ -389,8 +391,8 @@ class _AnalyzerDashboardState extends ConsumerState { horizontal: 24, vertical: 16, ), - side: BorderSide(color: HudTheme.accentCyan), - foregroundColor: HudTheme.accentCyan, + side: BorderSide(color: context.HudTheme.accentCyan), + foregroundColor: context.HudTheme.accentCyan, ), onPressed: () { // Flips the sidebar to the Storage Screen (Index 1) diff --git a/frontend/gs_analyzer_ui/lib/screen/automation_screen.dart b/frontend/gs_analyzer_ui/lib/screen/automation_screen.dart index 5e70742..1d2aa04 100644 --- a/frontend/gs_analyzer_ui/lib/screen/automation_screen.dart +++ b/frontend/gs_analyzer_ui/lib/screen/automation_screen.dart @@ -1,11 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:gs_analyzer_ui/utils/theme/hud_text_theme.dart'; +import 'package:gs_analyzer_ui/utils/theme/hud_theme_context.dart'; import 'package:intl/intl.dart'; import 'package:gs_analyzer_ui/models/automation_rule.dart'; import 'package:gs_analyzer_ui/providers/automation_provider.dart'; import 'package:gs_analyzer_ui/providers/hud_density_provider.dart'; import 'package:gs_analyzer_ui/utils/formatters.dart'; -import 'package:gs_analyzer_ui/core/theme/hudd_theme.dart'; import 'package:gs_analyzer_ui/widgets/rule_editor_dialog.dart'; class AutomationScreen extends ConsumerStatefulWidget { @@ -21,11 +22,13 @@ class _AutomationScreenState extends ConsumerState { final rulesAsync = ref.watch(automationRulesProvider); final auditAsync = ref.watch(automationAuditProvider); final d = ref.watch(hudDensityProvider); + final theme = context.HudTheme; + final text = context.HudTextTheme; return Material( - color: HudTheme.bgBase, + color: theme.background, child: Container( - color: HudTheme.bgBase, + color: theme.background, padding: EdgeInsets.all(d.panelPad), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -33,29 +36,29 @@ class _AutomationScreenState extends ConsumerState { // Header Bar Row( children: [ - const Icon( + Icon( Icons.auto_mode_outlined, - color: HudTheme.accentCyan, + color: theme.accentCyan, size: 22, ), const SizedBox(width: 10), - const Text('AUTOMATION PROTOCOL', style: HudTheme.headerCyan), + Text('AUTOMATION PROTOCOL', style: context.HudTextTheme.headlineMedium), const Spacer(), ElevatedButton.icon( - icon: const Icon(Icons.add, size: 16, color: HudTheme.accentCyan), - label: const Text( + icon: Icon(Icons.add, size: 16, color: theme.accentCyan), + label: Text( '+ NEW RULE', style: TextStyle( - fontFamily: HudTheme.fontCore, - color: HudTheme.accentCyan, + fontFamily: HudTextTheme.fontCore, + color: theme.accentCyan, fontWeight: FontWeight.bold, fontSize: 12, letterSpacing: 1, ), ), style: ElevatedButton.styleFrom( - backgroundColor: HudTheme.accentCyan.withValues(alpha: 0.15), - side: const BorderSide(color: HudTheme.accentCyan), + backgroundColor: theme.accentCyan.withValues(alpha: 0.15), + side: BorderSide(color: theme.accentCyan), padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(4), @@ -65,7 +68,7 @@ class _AutomationScreenState extends ConsumerState { ), const SizedBox(width: 8), IconButton( - icon: const Icon(Icons.refresh, color: HudTheme.textDim, size: 20), + icon: Icon(Icons.refresh, color: theme.textDim, size: 20), tooltip: 'Refresh', onPressed: () { ref.read(automationRulesProvider.notifier).reload(); @@ -75,9 +78,9 @@ class _AutomationScreenState extends ConsumerState { ], ), const SizedBox(height: 4), - const Text( + Text( 'SCHEDULED BACKGROUND CLEANUP & AUDIT LOG', - style: HudTheme.labelMuted, + style: text.titleMedium, ), SizedBox(height: d.gap * 2), @@ -98,28 +101,29 @@ class _AutomationScreenState extends ConsumerState { } Widget _buildRulesPanel(AsyncValue> rulesAsync) { + final theme = context.HudTheme; return Container( - decoration: HudTheme.hudPanelDecoration, + decoration: theme.hudPanelDecoration, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - decoration: const BoxDecoration( + decoration: BoxDecoration( border: Border(bottom: BorderSide(color: Colors.white10)), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Row( + Row( children: [ - Icon(Icons.rule_outlined, color: HudTheme.accentCyan, size: 16), + Icon(Icons.rule_outlined, color: theme.accentCyan, size: 16), SizedBox(width: 8), Text( 'RULES', style: TextStyle( - fontFamily: HudTheme.fontCore, - color: HudTheme.accentCyan, + fontFamily: HudTextTheme.fontCore, + color: theme.accentCyan, fontWeight: FontWeight.bold, fontSize: 13, letterSpacing: 1.2, @@ -134,11 +138,11 @@ class _AutomationScreenState extends ConsumerState { tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), onPressed: () => _openRuleEditor(context), - child: const Text( + child: Text( '+ NEW RULE', style: TextStyle( - fontFamily: HudTheme.fontCore, - color: HudTheme.accentCyan, + fontFamily: HudTextTheme.fontCore, + color: theme.accentCyan, fontSize: 11, ), ), @@ -150,12 +154,12 @@ class _AutomationScreenState extends ConsumerState { child: rulesAsync.when( data: (rules) { if (rules.isEmpty) { - return const Center( + return Center( child: Text( - 'NO AUTOMATION RULES CONFIGURED — CLICK + NEW RULE TO ADD ONE', + 'NO AUTOMATION RULES CONFIGURED — CLICK + NEW RULE TO ADD ONE', style: TextStyle( - fontFamily: HudTheme.fontCore, - color: HudTheme.textDim, + fontFamily: HudTextTheme.fontCore, + color: theme.textDim, fontSize: 12, letterSpacing: 1, ), @@ -169,15 +173,15 @@ class _AutomationScreenState extends ConsumerState { itemBuilder: (context, index) => _buildRuleCard(rules[index]), ); }, - loading: () => const Center( - child: CircularProgressIndicator(color: HudTheme.accentCyan), + loading: () => Center( + child: CircularProgressIndicator(color: theme.accentCyan), ), error: (err, _) => Center( child: Text( 'ERROR: $err', - style: const TextStyle( - fontFamily: HudTheme.fontCore, - color: HudTheme.accentRed, + style: TextStyle( + fontFamily: HudTextTheme.fontCore, + color: theme.accentRed, fontSize: 12, ), ), @@ -196,17 +200,18 @@ class _AutomationScreenState extends ConsumerState { : (rule.criteria.largerThanBytes != null ? '> ${formatBytes(rule.criteria.largerThanBytes!)}' : 'ALL FILES'); + final theme = context.HudTheme; return Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12), decoration: BoxDecoration( - color: HudTheme.bgBase, + color: theme.background, borderRadius: BorderRadius.circular(6), border: Border.all( color: !rule.isArmed - ? HudTheme.accentAmber.withValues(alpha: 0.5) + ? theme.accentAmber.withValues(alpha: 0.5) : (rule.isEnabled - ? HudTheme.accentCyan.withValues(alpha: 0.3) + ? theme.accentCyan.withValues(alpha: 0.3) : Colors.white10), ), ), @@ -217,16 +222,16 @@ class _AutomationScreenState extends ConsumerState { children: [ Icon( rule.isEnabled ? Icons.circle : Icons.circle_outlined, - color: rule.isEnabled ? HudTheme.accentGreen : HudTheme.textDim, + color: rule.isEnabled ? theme.accentGreen : theme.textDim, size: 12, ), const SizedBox(width: 8), Expanded( child: Text( rule.name.toUpperCase(), - style: const TextStyle( - fontFamily: HudTheme.fontCore, - color: HudTheme.textMain, + style: TextStyle( + fontFamily: HudTextTheme.fontCore, + color: theme.text, fontWeight: FontWeight.bold, fontSize: 13, letterSpacing: 1, @@ -234,10 +239,10 @@ class _AutomationScreenState extends ConsumerState { ), ), Text( - '${rule.root} · $criteriaSummary', - style: const TextStyle( - fontFamily: HudTheme.fontCore, - color: HudTheme.textDim, + '${rule.root} · $criteriaSummary', + style: TextStyle( + fontFamily: HudTextTheme.fontCore, + color: theme.textDim, fontSize: 11, ), ), @@ -245,17 +250,17 @@ class _AutomationScreenState extends ConsumerState { Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( - color: HudTheme.bgPanel, + color: theme.panel, borderRadius: BorderRadius.circular(2), border: Border.all( - color: HudTheme.accentCyan.withValues(alpha: 0.3), + color: theme.accentCyan.withValues(alpha: 0.3), ), ), child: Text( scheduleLabel, - style: const TextStyle( - fontFamily: HudTheme.fontCore, - color: HudTheme.accentCyan, + style: TextStyle( + fontFamily: HudTextTheme.fontCore, + color: theme.accentCyan, fontSize: 10, fontWeight: FontWeight.bold, ), @@ -273,25 +278,25 @@ class _AutomationScreenState extends ConsumerState { Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( - color: HudTheme.accentAmber.withValues(alpha: 0.15), + color: theme.accentAmber.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(2), border: Border.all( - color: HudTheme.accentAmber.withValues(alpha: 0.4), + color: theme.accentAmber.withValues(alpha: 0.4), ), ), - child: const Row( + child: Row( children: [ Icon( Icons.warning_amber, - color: HudTheme.accentAmber, + color: theme.accentAmber, size: 12, ), SizedBox(width: 4), Text( - 'NOT ARMED — REVIEW DRY RUN', + 'NOT ARMED — REVIEW DRY RUN', style: TextStyle( - fontFamily: HudTheme.fontCore, - color: HudTheme.accentAmber, + fontFamily: HudTextTheme.fontCore, + color: theme.accentAmber, fontSize: 10, fontWeight: FontWeight.bold, letterSpacing: 0.8, @@ -305,18 +310,18 @@ class _AutomationScreenState extends ConsumerState { rule.lastRunUtc != null ? 'Last run: ${DateFormat('yyyy-MM-dd HH:mm').format(rule.lastRunUtc!.toLocal())}' : 'Never run', - style: const TextStyle( - fontFamily: HudTheme.fontCore, - color: HudTheme.textDim, + style: TextStyle( + fontFamily: HudTextTheme.fontCore, + color: theme.textDim, fontSize: 11, ), ), if (rule.nextRunUtc != null && rule.isArmed) Text( 'Next run: ${DateFormat('yyyy-MM-dd HH:mm').format(rule.nextRunUtc!.toLocal())}', - style: const TextStyle( - fontFamily: HudTheme.fontCore, - color: HudTheme.textDim, + style: TextStyle( + fontFamily: HudTextTheme.fontCore, + color: theme.textDim, fontSize: 11, ), ), @@ -328,12 +333,13 @@ class _AutomationScreenState extends ConsumerState { } Widget _buildRuleActionsMenu(AutomationRule rule) { + final theme = context.HudTheme; return PopupMenuButton( - icon: const Icon(Icons.more_horiz, color: HudTheme.textDim, size: 18), - color: HudTheme.bgPanel, + icon: Icon(Icons.more_horiz, color: theme.textDim, size: 18), + color: theme.panel, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(4), - side: BorderSide(color: HudTheme.primaryBorder.withValues(alpha: 0.3)), + side: BorderSide(color: theme.border.withValues(alpha: 0.3)), ), onSelected: (val) async { final notifier = ref.read(automationRulesProvider.notifier); @@ -361,34 +367,34 @@ class _AutomationScreenState extends ConsumerState { }, itemBuilder: (context) => [ if (rule.isArmed) - const PopupMenuItem( + PopupMenuItem( value: 'run', child: Row( children: [ - Icon(Icons.play_arrow, color: HudTheme.accentGreen, size: 16), + Icon(Icons.play_arrow, color: theme.accentGreen, size: 16), SizedBox(width: 8), Text( 'RUN NOW', style: TextStyle( - fontFamily: HudTheme.fontCore, - color: HudTheme.textMain, + fontFamily: HudTextTheme.fontCore, + color: theme.text, fontSize: 12, ), ), ], ), ), - const PopupMenuItem( + PopupMenuItem( value: 'dryrun', child: Row( children: [ - Icon(Icons.remove_red_eye_outlined, color: HudTheme.accentCyan, size: 16), + Icon(Icons.remove_red_eye_outlined, color: theme.accentCyan, size: 16), SizedBox(width: 8), Text( 'DRY RUN PREVIEW', style: TextStyle( - fontFamily: HudTheme.fontCore, - color: HudTheme.textMain, + fontFamily: HudTextTheme.fontCore, + color: theme.text, fontSize: 12, ), ), @@ -396,51 +402,51 @@ class _AutomationScreenState extends ConsumerState { ), ), if (!rule.isArmed) - const PopupMenuItem( + PopupMenuItem( value: 'arm', child: Row( children: [ - Icon(Icons.security, color: HudTheme.accentAmber, size: 16), + Icon(Icons.security, color: theme.accentAmber, size: 16), SizedBox(width: 8), Text( 'ARM RULE', style: TextStyle( - fontFamily: HudTheme.fontCore, - color: HudTheme.accentAmber, + fontFamily: HudTextTheme.fontCore, + color: theme.accentAmber, fontSize: 12, ), ), ], ), ), - const PopupMenuItem( + PopupMenuItem( value: 'edit', child: Row( children: [ - Icon(Icons.edit, color: HudTheme.accentCyan, size: 16), + Icon(Icons.edit, color: theme.accentCyan, size: 16), SizedBox(width: 8), Text( 'EDIT', style: TextStyle( - fontFamily: HudTheme.fontCore, - color: HudTheme.textMain, + fontFamily: HudTextTheme.fontCore, + color: theme.text, fontSize: 12, ), ), ], ), ), - const PopupMenuItem( + PopupMenuItem( value: 'delete', child: Row( children: [ - Icon(Icons.delete_outline, color: HudTheme.accentRed, size: 16), + Icon(Icons.delete_outline, color: theme.accentRed, size: 16), SizedBox(width: 8), Text( 'DELETE', style: TextStyle( - fontFamily: HudTheme.fontCore, - color: HudTheme.accentRed, + fontFamily: HudTextTheme.fontCore, + color: theme.accentRed, fontSize: 12, ), ), @@ -452,28 +458,29 @@ class _AutomationScreenState extends ConsumerState { } Widget _buildAuditPanel(AsyncValue> auditAsync) { + final theme = context.HudTheme; return Container( - decoration: HudTheme.hudPanelDecoration, + decoration: theme.hudPanelDecoration, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - decoration: const BoxDecoration( + decoration: BoxDecoration( border: Border(bottom: BorderSide(color: Colors.white10)), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Row( + Row( children: [ - Icon(Icons.history_outlined, color: HudTheme.accentCyan, size: 16), + Icon(Icons.history_outlined, color: theme.accentCyan, size: 16), SizedBox(width: 8), Text( 'AUDIT LOG', style: TextStyle( - fontFamily: HudTheme.fontCore, - color: HudTheme.accentCyan, + fontFamily: HudTextTheme.fontCore, + color: theme.accentCyan, fontWeight: FontWeight.bold, fontSize: 13, letterSpacing: 1.2, @@ -486,16 +493,16 @@ class _AutomationScreenState extends ConsumerState { final days = ref.watch(auditDaysProvider); return DropdownButton( value: days, - dropdownColor: HudTheme.bgPanel, + dropdownColor: theme.panel, underline: const SizedBox.shrink(), - style: const TextStyle( - fontFamily: HudTheme.fontCore, - color: HudTheme.accentCyan, + style: TextStyle( + fontFamily: HudTextTheme.fontCore, + color: theme.accentCyan, fontSize: 11, ), - icon: const Icon( + icon: Icon( Icons.arrow_drop_down, - color: HudTheme.accentCyan, + color: theme.accentCyan, size: 18, ), items: const [ @@ -503,21 +510,21 @@ class _AutomationScreenState extends ConsumerState { value: 7, child: Text( 'PAST 7 DAYS', - style: TextStyle(fontFamily: HudTheme.fontCore), + style: TextStyle(fontFamily: HudTextTheme.fontCore), ), ), DropdownMenuItem( value: 30, child: Text( 'PAST 30 DAYS', - style: TextStyle(fontFamily: HudTheme.fontCore), + style: TextStyle(fontFamily: HudTextTheme.fontCore), ), ), DropdownMenuItem( value: 90, child: Text( 'PAST 90 DAYS', - style: TextStyle(fontFamily: HudTheme.fontCore), + style: TextStyle(fontFamily: HudTextTheme.fontCore), ), ), ], @@ -536,12 +543,12 @@ class _AutomationScreenState extends ConsumerState { child: auditAsync.when( data: (entries) { if (entries.isEmpty) { - return const Center( + return Center( child: Text( 'NO AUDIT ENTRIES RECORDED', style: TextStyle( - fontFamily: HudTheme.fontCore, - color: HudTheme.textDim, + fontFamily: HudTextTheme.fontCore, + color: theme.textDim, fontSize: 12, letterSpacing: 1, ), @@ -566,9 +573,9 @@ class _AutomationScreenState extends ConsumerState { children: [ Text( dateStr, - style: const TextStyle( - fontFamily: HudTheme.fontCore, - color: HudTheme.textDim, + style: TextStyle( + fontFamily: HudTextTheme.fontCore, + color: theme.textDim, fontSize: 11, ), ), @@ -576,9 +583,9 @@ class _AutomationScreenState extends ConsumerState { Expanded( child: Text( e.ruleName.toUpperCase(), - style: const TextStyle( - fontFamily: HudTheme.fontCore, - color: HudTheme.textMain, + style: TextStyle( + fontFamily: HudTextTheme.fontCore, + color: theme.text, fontWeight: FontWeight.bold, fontSize: 12, ), @@ -586,17 +593,17 @@ class _AutomationScreenState extends ConsumerState { ), Text( e.isDryRun - ? 'DRY-RUN: ${e.matchedCount} files · $bytesStr' + ? 'DRY-RUN: ${e.matchedCount} files · $bytesStr' : (e.wasAborted - ? 'ABORTED — ${e.abortReason ?? 'FAILED'}' - : '${e.deletedCount} files · $bytesStr'), + ? 'ABORTED — ${e.abortReason ?? 'FAILED'}' + : '${e.deletedCount} files · $bytesStr'), style: TextStyle( - fontFamily: HudTheme.fontCore, + fontFamily: HudTextTheme.fontCore, color: e.wasAborted - ? HudTheme.accentRed + ? theme.accentRed : (e.isDryRun - ? HudTheme.accentCyan - : HudTheme.textDim), + ? theme.accentCyan + : theme.textDim), fontSize: 11, ), ), @@ -606,8 +613,8 @@ class _AutomationScreenState extends ConsumerState { ? Icons.warning_amber : Icons.check_circle_outline, color: e.wasAborted - ? HudTheme.accentAmber - : HudTheme.accentGreen, + ? theme.accentAmber + : theme.accentGreen, size: 16, ), ], @@ -616,15 +623,15 @@ class _AutomationScreenState extends ConsumerState { }, ); }, - loading: () => const Center( - child: CircularProgressIndicator(color: HudTheme.accentCyan), + loading: () => Center( + child: CircularProgressIndicator(color: theme.accentCyan), ), error: (err, _) => Center( child: Text( 'ERROR: $err', - style: const TextStyle( - fontFamily: HudTheme.fontCore, - color: HudTheme.accentRed, + style: TextStyle( + fontFamily: HudTextTheme.fontCore, + color: theme.accentRed, fontSize: 12, ), ), @@ -648,14 +655,14 @@ class _AutomationScreenState extends ConsumerState { SnackBar( content: Text( msg, - style: const TextStyle( - fontFamily: HudTheme.fontCore, - color: HudTheme.textMain, + style: TextStyle( + fontFamily: HudTextTheme.fontCore, + color: context.HudTheme.text, ), ), - backgroundColor: HudTheme.bgPanel, + backgroundColor: context.HudTheme.panel, shape: RoundedRectangleBorder( - side: BorderSide(color: HudTheme.primaryBorder.withValues(alpha: 0.3)), + side: BorderSide(color: context.HudTheme.border.withValues(alpha: 0.3)), borderRadius: BorderRadius.circular(4), ), duration: const Duration(seconds: 3), diff --git a/frontend/gs_analyzer_ui/lib/utils/theme/hud_colors.dart b/frontend/gs_analyzer_ui/lib/utils/theme/hud_colors.dart new file mode 100644 index 0000000..d0e27f2 --- /dev/null +++ b/frontend/gs_analyzer_ui/lib/utils/theme/hud_colors.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; + +class HudColors { + // --------- HUD DARK THEME COLOR ------------------ + static const Color darkBgBase = Color(0xFF161616); + static const Color darkBgPanel = Color(0xFF1E1E1E); + static const darkPrimaryBorder = Colors.cyan; + + // Text Colors + static const Color darkTextMain = Colors.white; + static const Color darkTextMuted = Colors.white70; + static const Color darkTextDim = Colors.white54; + + // --------- HUD LIGHT THEME COLOR ----------------- + static const lightBgBase = Color(0xFFF5F5F5); + static const lightBgPanel = Color(0xFFFFFFFFF); + static const lightPrimaryBorder = Color(0xFF0097A7); + + // Text Colors + static const lightTextMain = Color(0xFF111111); + static const lightTextMuted = Color(0xFF555555); + static const lightTextDim = Color(0xFF8888888); + + // --------- ACCENT COLORS ---------------- + + static const Color lightAccentCyan = Colors.cyan; + static const Color darkAccentCyan = Color(0xFF0097A7); + + static const Color lightAccentGreen = Colors.green; + static const Color darkAccentGreen = Color(0xFF2E7D32); + + static const Color lightAccentRed = Colors.red; + static const Color darkAccentRed = Color(0xFFC62828); + + static const Color lightAccentAmber = Colors.amber; + static const Color darkAccentAmber = Color(0xFFE65100); + + static const Color lightAccentPurple = Colors.purple; + static const Color darkAccentPurple = Colors.purpleAccent; + + static const Color lightAccentBlue = Colors.blue; + static const Color darkAccentBlue = Colors.blueAccent; + + + static Color resolveAccent(String? accentKey, Brightness brightness) { + final isdark = brightness == Brightness.dark; + + switch (accentKey?.toLowerCase()) { + case 'cyan': + return isdark ? darkAccentCyan : lightAccentCyan; + case 'green': + return isdark ? darkAccentGreen : lightAccentGreen; + case 'amber': + return isdark ? darkAccentAmber : lightAccentAmber; + case 'red': + return isdark ? darkAccentRed : lightAccentRed; + case 'purple': + return isdark ? darkAccentPurple : lightAccentPurple; + case 'blue': + return isdark ? darkAccentBlue : lightAccentBlue; + default: + return isdark ? darkAccentCyan : lightAccentCyan; + } + } + + static Color fileTypeColor(String category) { + switch (category.toLowerCase()) { + case 'media': + return const Color(0xFF00FFFF); + case 'documents': + return const Color(0xFF4CAF50); + case 'executables': + return const Color(0xFFFF5252); + case 'archives': + return const Color(0xFFFFB300); + case 'code': + return const Color(0xFF9C27B0); + case 'system': + return Colors.white38; + default: + return Colors.white12; + } + } + +} \ No newline at end of file diff --git a/frontend/gs_analyzer_ui/lib/utils/theme/hud_label.dart b/frontend/gs_analyzer_ui/lib/utils/theme/hud_label.dart new file mode 100644 index 0000000..cbc3e54 --- /dev/null +++ b/frontend/gs_analyzer_ui/lib/utils/theme/hud_label.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; + +class HudLabel extends StatelessWidget { + final String text; + final TextAlign? textAlign; + final TextOverflow? overflow; + final int? maxLines; + + const HudLabel( + this.text, { + super.key, + this.textAlign, + this.overflow = TextOverflow.ellipsis, + this.maxLines = 1, + }); + + @override + Widget build(BuildContext context) { + return Text( + text.toUpperCase(), + style: Theme.of(context).textTheme.titleMedium, + textAlign: textAlign, + overflow: overflow, + maxLines: maxLines, + ); + } +} diff --git a/frontend/gs_analyzer_ui/lib/utils/theme/hud_text_theme.dart b/frontend/gs_analyzer_ui/lib/utils/theme/hud_text_theme.dart new file mode 100644 index 0000000..8c1d6c9 --- /dev/null +++ b/frontend/gs_analyzer_ui/lib/utils/theme/hud_text_theme.dart @@ -0,0 +1,107 @@ +import 'package:flutter/material.dart'; +import 'package:gs_analyzer_ui/utils/theme/hud_colors.dart'; + +class HudTextTheme { + + static const String fontCore = 'Courier'; + + static const light = TextTheme( + headlineMedium: TextStyle( + fontFamily: fontCore, + color: HudColors.lightPrimaryBorder, + fontWeight: FontWeight.bold, + letterSpacing: 2, + fontSize: 18 + ), + + headlineSmall: TextStyle( + fontFamily: fontCore, + fontWeight: FontWeight.w600 + ), + + bodyLarge: TextStyle( + fontFamily: fontCore, + color: HudColors.lightAccentGreen, + fontSize: 14, + fontWeight: FontWeight.bold + ), + + bodyMedium: TextStyle( + fontFamily: fontCore, + color: HudColors.lightAccentCyan, + fontSize: 13, + fontWeight: FontWeight.bold + ), + + bodySmall: TextStyle( + fontFamily: fontCore, + color: HudColors.lightTextDim, + fontSize: 12, + letterSpacing: 1 + ), + + displayMedium: TextStyle( + fontFamily: fontCore, + color: HudColors.lightAccentRed, + fontSize: 14, + fontWeight: FontWeight.bold, + letterSpacing: 1.5 + ), + + titleMedium: TextStyle( + fontFamily: fontCore, + color: HudColors.lightTextMuted, + fontSize: 13 + ), + ); + + static const dark = TextTheme( + headlineMedium: TextStyle( + fontFamily: fontCore, + color: HudColors.darkPrimaryBorder, + fontWeight: FontWeight.bold, + letterSpacing: 2, + fontSize: 18 + ), + + headlineSmall: TextStyle( + fontFamily: fontCore, + fontWeight: FontWeight.w600 + ), + + bodyLarge: TextStyle( + fontFamily: fontCore, + color: HudColors.darkAccentGreen, + fontSize: 14, + fontWeight: FontWeight.bold + ), + + bodyMedium: TextStyle( + fontFamily: fontCore, + color: HudColors.darkAccentCyan, + fontSize: 13, + fontWeight: FontWeight.bold + ), + + bodySmall: TextStyle( + fontFamily: fontCore, + color: HudColors.darkTextDim, + fontSize: 12, + letterSpacing: 1 + ), + + displayMedium: TextStyle( + fontFamily: fontCore, + color: HudColors.darkAccentRed, + fontSize: 14, + fontWeight: FontWeight.bold, + letterSpacing: 1.5 + ), + + titleMedium: TextStyle( + fontFamily: fontCore, + color: HudColors.darkTextMuted, + fontSize: 13 + ), + ); +} \ No newline at end of file diff --git a/frontend/gs_analyzer_ui/lib/utils/theme/hud_theme.dart b/frontend/gs_analyzer_ui/lib/utils/theme/hud_theme.dart new file mode 100644 index 0000000..0f9a4bd --- /dev/null +++ b/frontend/gs_analyzer_ui/lib/utils/theme/hud_theme.dart @@ -0,0 +1,100 @@ +import 'package:flutter/material.dart'; +import 'package:gs_analyzer_ui/utils/theme/hud_colors.dart'; +import 'package:gs_analyzer_ui/utils/theme/hud_text_theme.dart'; +import 'package:gs_analyzer_ui/utils/theme/hud_theme_extension.dart'; + +class HudTheme { + static ThemeData lightTheme(Color accentColor) { + return ThemeData( + brightness: Brightness.light, + scaffoldBackgroundColor: HudColors.lightBgBase, + textTheme: HudTextTheme.light, + colorScheme: ColorScheme.light( + primary: accentColor, + surface: HudColors.lightBgPanel, + onSurface: HudColors.lightTextMain, + outline: HudColors.lightPrimaryBorder, + error: HudColors.lightAccentRed, + onPrimary: HudColors.lightAccentCyan, + secondary: HudColors.lightAccentGreen, + tertiary: HudColors.lightAccentAmber, + onSecondary: HudColors.lightAccentPurple, + onTertiary: HudColors.lightAccentBlue, + surfaceDim: HudColors.lightTextDim, + ), + cardTheme: CardThemeData(color: HudColors.lightBgPanel), + dividerTheme: const DividerThemeData(color: Colors.black12), + extensions: >[ + HudThemeExtension( + background: HudColors.lightBgBase, + panel: HudColors.lightBgPanel, + text: HudColors.lightTextMain, + border: HudColors.lightPrimaryBorder, + accentRed: HudColors.lightAccentRed, + accentCyan: HudColors.lightAccentCyan, + accentGreen: HudColors.lightAccentGreen, + accentAmber: HudColors.lightAccentAmber, + accentPurple: HudColors.lightAccentPurple, + accentBlue: HudColors.lightAccentBlue, + textDim: HudColors.lightTextDim, + hudPanelDecoration: BoxDecoration(), + listItemDecoration: BoxDecoration() + ), + ], + ); + } + + static ThemeData darkTheme(Color accentColor) { + return ThemeData( + brightness: Brightness.dark, + scaffoldBackgroundColor: HudColors.darkBgBase, + textTheme: HudTextTheme.dark, + colorScheme: ColorScheme.dark( + primary: accentColor, + surface: HudColors.darkBgPanel, + onSurface: HudColors.darkTextMain, + outline: HudColors.darkPrimaryBorder, + error: HudColors.darkAccentRed, + onPrimary: HudColors.darkAccentCyan, + secondary: HudColors.darkAccentGreen, + tertiary: HudColors.darkAccentAmber, + onSecondary: HudColors.darkAccentPurple, + onTertiary: HudColors.darkAccentBlue, + surfaceDim: HudColors.darkTextDim, + ), + cardTheme: CardThemeData(color: HudColors.darkBgPanel), + dividerTheme: const DividerThemeData(color: Colors.white10), + extensions: >[ + HudThemeExtension( + background: HudColors.darkBgBase, + panel: HudColors.darkBgPanel, + text: HudColors.darkTextMain, + border: HudColors.darkPrimaryBorder, + accentRed: HudColors.darkAccentRed, + accentCyan: HudColors.darkAccentCyan, + accentGreen: HudColors.darkAccentGreen, + accentAmber: HudColors.darkAccentAmber, + accentPurple: HudColors.darkAccentPurple, + accentBlue: HudColors.darkAccentBlue, + textDim: HudColors.darkTextDim, + hudPanelDecoration: BoxDecoration(), + listItemDecoration: BoxDecoration() + ), + ], + ); + } + + static ThemeMode resolveThemeMode(String? theme) { + switch (theme?.toLowerCase()) { + case 'cyber_light': + case 'light': + return ThemeMode.light; + case 'cyber_dark': + case 'dark': + return ThemeMode.dark; + case 'system': + default: + return ThemeMode.system; + } + } +} \ No newline at end of file diff --git a/frontend/gs_analyzer_ui/lib/utils/theme/hud_theme_context.dart b/frontend/gs_analyzer_ui/lib/utils/theme/hud_theme_context.dart new file mode 100644 index 0000000..1657f5d --- /dev/null +++ b/frontend/gs_analyzer_ui/lib/utils/theme/hud_theme_context.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; +import 'package:gs_analyzer_ui/utils/theme/hud_theme_extension.dart'; + +extension HudThemeContext on BuildContext { + HudThemeExtension get HudTheme => Theme.of(this).extension()!; +} + +extension hudTextThemeContext on BuildContext { + TextTheme get HudTextTheme => Theme.of(this).textTheme; +} \ No newline at end of file diff --git a/frontend/gs_analyzer_ui/lib/utils/theme/hud_theme_extension.dart b/frontend/gs_analyzer_ui/lib/utils/theme/hud_theme_extension.dart new file mode 100644 index 0000000..886615a --- /dev/null +++ b/frontend/gs_analyzer_ui/lib/utils/theme/hud_theme_extension.dart @@ -0,0 +1,94 @@ +import 'package:flutter/material.dart'; + +@immutable +class HudThemeExtension extends ThemeExtension { + final Color background; + final Color panel; + final Color text; + final Color border; + final Color accentRed; + final Color accentCyan; + final Color accentGreen; + final Color accentAmber; + final Color accentPurple; + final Color accentBlue; + final Color textDim; + final BoxDecoration hudPanelDecoration; + final BoxDecoration listItemDecoration; + + const HudThemeExtension({ + required this.background, + required this.panel, + required this.text, + required this.border, + required this.accentRed, + required this.accentCyan, + required this.accentGreen, + required this.accentAmber, + required this.accentPurple, + required this.accentBlue, + required this.textDim, + required this.hudPanelDecoration, + required this.listItemDecoration + }); + + @override + HudThemeExtension copyWith({ + Color? background, + Color? panel, + Color? text, + Color? border, + Color? accentRed, + Color? accentCyan, + Color? accentGreen, + Color? accentAmber, + Color? accentPurple, + Color? accentBlue, + Color? textDim, + BoxDecoration? hudPanelDecoration, + BoxDecoration? listItemDecoration + }) { + return HudThemeExtension( + background: background ?? this.background, + panel: panel ?? this.panel, + text: text ?? this.text, + border: border ?? this.border, + accentRed: accentRed ?? this.accentRed, + accentCyan: accentCyan ?? this.accentCyan, + accentGreen: accentGreen ?? this.accentGreen, + accentAmber: accentAmber ?? this.accentAmber, + accentPurple: accentPurple ?? this.accentPurple, + accentBlue: accentBlue ?? this.accentBlue, + textDim: textDim ?? this.textDim, + hudPanelDecoration: BoxDecoration( + color: panel, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: border!.withValues(alpha: 0.3)), + ), + listItemDecoration: BoxDecoration( + border: Border(bottom: BorderSide(color: (text ?? Colors.white10).withValues(alpha: 0.1))) + ) + ); + } + + @override + HudThemeExtension lerp(covariant HudThemeExtension? other, double t) { + if (other == null) return this; + + return HudThemeExtension( + background: Color.lerp(background, other.background, t)!, + panel: Color.lerp(panel, other.panel, t)!, + text: Color.lerp(text, other.text, t)!, + border: Color.lerp(border, other.border, t)!, + accentRed: Color.lerp(accentRed, other.accentRed, t)!, + accentCyan: Color.lerp(accentCyan, other.accentCyan, t)!, + accentGreen: Color.lerp(accentGreen, other.accentGreen, t)!, + accentAmber: Color.lerp(accentAmber, other.accentAmber, t)!, + accentPurple: Color.lerp(accentPurple, other.accentPurple, t)!, + accentBlue: Color.lerp(accentBlue, other.accentBlue, t)!, + textDim: Color.lerp(textDim, other.textDim, t)!, + hudPanelDecoration: BoxDecoration.lerp(hudPanelDecoration, other.hudPanelDecoration, t)!, + listItemDecoration: BoxDecoration.lerp(listItemDecoration, other.listItemDecoration, t)! + ); + } +} \ No newline at end of file