From 633836e8e33491533c3c793a935c27eff56b3eac Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Thu, 28 May 2026 10:11:48 +0300 Subject: [PATCH 1/2] feat(theme): add QueryaTheme, workbench and editor token models Introduces QueryaWorkbenchTheme, QueryaEditorTheme, and QueryaTheme with dark/light defaults, lerp/copyWith, and shadcn ColorScheme mapping. AppTheme now builds from QueryaTheme.darkDefault (visual parity). Closes #38 --- lib/core/theme/app_theme.dart | 13 +- lib/core/theme/querya_color_scheme.dart | 33 +--- lib/core/theme/querya_editor_theme.dart | 156 ++++++++++++++++++ lib/core/theme/querya_theme.dart | 180 +++++++++++++++++++++ lib/core/theme/querya_workbench_theme.dart | 160 ++++++++++++++++++ test/core/theme/querya_theme_test.dart | 92 +++++++++++ 6 files changed, 597 insertions(+), 37 deletions(-) create mode 100644 lib/core/theme/querya_editor_theme.dart create mode 100644 lib/core/theme/querya_theme.dart create mode 100644 lib/core/theme/querya_workbench_theme.dart create mode 100644 test/core/theme/querya_theme_test.dart diff --git a/lib/core/theme/app_theme.dart b/lib/core/theme/app_theme.dart index 4e8fe381..51a9f060 100644 --- a/lib/core/theme/app_theme.dart +++ b/lib/core/theme/app_theme.dart @@ -1,13 +1,10 @@ import 'package:shadcn_flutter/shadcn_flutter.dart'; -import 'querya_color_scheme.dart'; +import 'querya_theme.dart'; -/// App theme: dark only. Used for both theme and darkTheme so the app is always dark. +/// App theme presets built from [QueryaTheme]. abstract class AppTheme { - static ThemeData get dark => const ThemeData.dark( - colorScheme: QueryaColorScheme.dark, - radius: 0.58, - scaling: 1, - typography: Typography.geist(), - ); + static ThemeData get dark => QueryaTheme.darkDefault.toShadcnThemeData(); + + static ThemeData get light => QueryaTheme.lightDefault.toShadcnThemeData(); } diff --git a/lib/core/theme/querya_color_scheme.dart b/lib/core/theme/querya_color_scheme.dart index f57e203b..a15f18a9 100644 --- a/lib/core/theme/querya_color_scheme.dart +++ b/lib/core/theme/querya_color_scheme.dart @@ -1,34 +1,9 @@ import 'package:shadcn_flutter/shadcn_flutter.dart'; -import 'querya_colors.dart'; +import 'querya_theme.dart'; -/// Dark [ColorScheme] for Querya desktop — matches [QueryaColors] tokens. +/// Shadcn [ColorScheme] presets — derived from [QueryaTheme] defaults. abstract class QueryaColorScheme { - static const ColorScheme dark = ColorScheme( - brightness: Brightness.dark, - background: QueryaColors.canvas, - foreground: Color(0xFFF8FAFC), - card: QueryaColors.surface, - cardForeground: Color(0xFFF8FAFC), - popover: QueryaColors.surface, - popoverForeground: Color(0xFFF8FAFC), - primary: QueryaColors.accentCyan, - primaryForeground: QueryaColors.onAccent, - secondary: Color(0xFF18181B), - secondaryForeground: Color(0xFFF8FAFC), - muted: Color(0xFF18181B), - mutedForeground: QueryaColors.mutedLabel, - accent: Color(0xFF27272A), - accentForeground: Color(0xFFF8FAFC), - destructive: Color(0xFFEF4444), - destructiveForeground: Color(0xFFF8FAFC), - border: QueryaColors.borderSubtle, - input: QueryaColors.borderSubtle, - ring: QueryaColors.accentCyan, - chart1: Color(0xFF2662D9), - chart2: Color(0xFF2EB88A), - chart3: Color(0xFFE88C30), - chart4: Color(0xFFAF57DB), - chart5: Color(0xFFE23670), - ); + static ColorScheme get dark => QueryaTheme.darkDefault.colorScheme; + static ColorScheme get light => QueryaTheme.lightDefault.colorScheme; } diff --git a/lib/core/theme/querya_editor_theme.dart b/lib/core/theme/querya_editor_theme.dart new file mode 100644 index 00000000..30e653f6 --- /dev/null +++ b/lib/core/theme/querya_editor_theme.dart @@ -0,0 +1,156 @@ +import 'dart:ui'; + +import 'querya_colors.dart'; +import 'querya_typography.dart'; + +/// Syntax and surface tokens for SQL/JSON code editors. +class QueryaEditorTheme { + const QueryaEditorTheme({ + required this.background, + required this.foreground, + required this.lineHighlight, + required this.selection, + required this.comment, + required this.keyword, + required this.string, + required this.number, + required this.operator, + required this.function, + required this.type, + this.fontFamily = QueryaTypography.mono, + this.fontSize = 13, + }); + + final Color background; + final Color foreground; + final Color lineHighlight; + final Color selection; + final Color comment; + final Color keyword; + final Color string; + final Color number; + final Color operator; + final Color function; + final Color type; + final String fontFamily; + final double fontSize; + + /// Aligned with dark workbench; VS Code Dark+–like token hues. + static const QueryaEditorTheme darkDefault = QueryaEditorTheme( + background: QueryaColors.surface, + foreground: Color(0xFFF8FAFC), + lineHighlight: Color(0xFF18181B), + selection: Color(0xFF264F78), + comment: Color(0xFF6A9955), + keyword: Color(0xFF569CD6), + string: Color(0xFFCE9178), + number: Color(0xFFB5CEA8), + operator: Color(0xFFD4D4D4), + function: Color(0xFFDCDCAA), + type: Color(0xFF4EC9B0), + ); + + static const QueryaEditorTheme lightDefault = QueryaEditorTheme( + background: Color(0xFFFFFFFF), + foreground: Color(0xFF1E293B), + lineHighlight: Color(0xFFF1F5F9), + selection: Color(0xFFADD6FF), + comment: Color(0xFF008000), + keyword: Color(0xFF0000FF), + string: Color(0xFFA31515), + number: Color(0xFF098658), + operator: Color(0xFF000000), + function: Color(0xFF795E26), + type: Color(0xFF267F99), + ); + + QueryaEditorTheme copyWith({ + Color? background, + Color? foreground, + Color? lineHighlight, + Color? selection, + Color? comment, + Color? keyword, + Color? string, + Color? number, + Color? operator, + Color? function, + Color? type, + String? fontFamily, + double? fontSize, + }) { + return QueryaEditorTheme( + background: background ?? this.background, + foreground: foreground ?? this.foreground, + lineHighlight: lineHighlight ?? this.lineHighlight, + selection: selection ?? this.selection, + comment: comment ?? this.comment, + keyword: keyword ?? this.keyword, + string: string ?? this.string, + number: number ?? this.number, + operator: operator ?? this.operator, + function: function ?? this.function, + type: type ?? this.type, + fontFamily: fontFamily ?? this.fontFamily, + fontSize: fontSize ?? this.fontSize, + ); + } + + static QueryaEditorTheme lerp( + QueryaEditorTheme a, + QueryaEditorTheme b, + double t, + ) { + Color c(Color x, Color y) => Color.lerp(x, y, t)!; + return QueryaEditorTheme( + background: c(a.background, b.background), + foreground: c(a.foreground, b.foreground), + lineHighlight: c(a.lineHighlight, b.lineHighlight), + selection: c(a.selection, b.selection), + comment: c(a.comment, b.comment), + keyword: c(a.keyword, b.keyword), + string: c(a.string, b.string), + number: c(a.number, b.number), + operator: c(a.operator, b.operator), + function: c(a.function, b.function), + type: c(a.type, b.type), + fontFamily: t < 0.5 ? a.fontFamily : b.fontFamily, + fontSize: a.fontSize + (b.fontSize - a.fontSize) * t, + ); + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is QueryaEditorTheme && + background == other.background && + foreground == other.foreground && + lineHighlight == other.lineHighlight && + selection == other.selection && + comment == other.comment && + keyword == other.keyword && + string == other.string && + number == other.number && + operator == other.operator && + function == other.function && + type == other.type && + fontFamily == other.fontFamily && + fontSize == other.fontSize; + + @override + int get hashCode => Object.hash( + background, + foreground, + lineHighlight, + selection, + comment, + keyword, + string, + number, + operator, + function, + type, + fontFamily, + fontSize, + ); +} diff --git a/lib/core/theme/querya_theme.dart b/lib/core/theme/querya_theme.dart new file mode 100644 index 00000000..8db6117f --- /dev/null +++ b/lib/core/theme/querya_theme.dart @@ -0,0 +1,180 @@ +import 'package:shadcn_flutter/shadcn_flutter.dart'; + +import 'querya_editor_theme.dart'; +import 'querya_workbench_theme.dart'; + +/// Full Querya theme: workbench chrome + editor tokens + shadcn [ColorScheme]. +class QueryaTheme { + const QueryaTheme({ + required this.workbench, + required this.editor, + required this.brightness, + required this.colorScheme, + }); + + final QueryaWorkbenchTheme workbench; + final QueryaEditorTheme editor; + final Brightness brightness; + final ColorScheme colorScheme; + + static final QueryaTheme darkDefault = QueryaTheme( + workbench: QueryaWorkbenchTheme.darkDefault, + editor: QueryaEditorTheme.darkDefault, + brightness: Brightness.dark, + colorScheme: _darkColorScheme, + ); + + static final QueryaTheme lightDefault = QueryaTheme( + workbench: QueryaWorkbenchTheme.lightDefault, + editor: QueryaEditorTheme.lightDefault, + brightness: Brightness.light, + colorScheme: _lightColorScheme, + ); + + static final ColorScheme _darkColorScheme = ColorScheme( + brightness: Brightness.dark, + background: QueryaWorkbenchTheme.darkDefault.canvas, + foreground: Color(0xFFF8FAFC), + card: QueryaWorkbenchTheme.darkDefault.surface, + cardForeground: Color(0xFFF8FAFC), + popover: QueryaWorkbenchTheme.darkDefault.surface, + popoverForeground: Color(0xFFF8FAFC), + primary: QueryaWorkbenchTheme.darkDefault.accent, + primaryForeground: QueryaWorkbenchTheme.darkDefault.onAccent, + secondary: Color(0xFF18181B), + secondaryForeground: Color(0xFFF8FAFC), + muted: Color(0xFF18181B), + mutedForeground: QueryaWorkbenchTheme.darkDefault.mutedForeground, + accent: Color(0xFF27272A), + accentForeground: Color(0xFFF8FAFC), + destructive: QueryaWorkbenchTheme.darkDefault.destructive, + destructiveForeground: Color(0xFFF8FAFC), + border: QueryaWorkbenchTheme.darkDefault.borderSubtle, + input: QueryaWorkbenchTheme.darkDefault.borderSubtle, + ring: QueryaWorkbenchTheme.darkDefault.accent, + chart1: Color(0xFF2662D9), + chart2: Color(0xFF2EB88A), + chart3: Color(0xFFE88C30), + chart4: Color(0xFFAF57DB), + chart5: Color(0xFFE23670), + ); + + static final ColorScheme _lightColorScheme = ColorScheme( + brightness: Brightness.light, + background: QueryaWorkbenchTheme.lightDefault.canvas, + foreground: Color(0xFF0F172A), + card: QueryaWorkbenchTheme.lightDefault.surface, + cardForeground: Color(0xFF0F172A), + popover: QueryaWorkbenchTheme.lightDefault.surface, + popoverForeground: Color(0xFF0F172A), + primary: QueryaWorkbenchTheme.lightDefault.accent, + primaryForeground: QueryaWorkbenchTheme.lightDefault.onAccent, + secondary: Color(0xFFF4F4F5), + secondaryForeground: Color(0xFF0F172A), + muted: Color(0xFFF4F4F5), + mutedForeground: QueryaWorkbenchTheme.lightDefault.mutedForeground, + accent: Color(0xFFE4E4E7), + accentForeground: Color(0xFF0F172A), + destructive: QueryaWorkbenchTheme.lightDefault.destructive, + destructiveForeground: Color(0xFFF8FAFC), + border: QueryaWorkbenchTheme.lightDefault.borderSubtle, + input: QueryaWorkbenchTheme.lightDefault.borderSubtle, + ring: QueryaWorkbenchTheme.lightDefault.accent, + chart1: Color(0xFF2662D9), + chart2: Color(0xFF2EB88A), + chart3: Color(0xFFE88C30), + chart4: Color(0xFFAF57DB), + chart5: Color(0xFFE23670), + ); + + /// Builds [ColorScheme] from [workbench] (for imported / overridden themes). + static ColorScheme colorSchemeFromWorkbench( + QueryaWorkbenchTheme w, { + required Brightness brightness, + }) { + final isDark = brightness == Brightness.dark; + final fg = isDark ? const Color(0xFFF8FAFC) : const Color(0xFF0F172A); + return ColorScheme( + brightness: brightness, + background: w.canvas, + foreground: fg, + card: w.surface, + cardForeground: fg, + popover: w.surface, + popoverForeground: fg, + primary: w.accent, + primaryForeground: w.onAccent, + secondary: isDark ? const Color(0xFF18181B) : const Color(0xFFF4F4F5), + secondaryForeground: fg, + muted: isDark ? const Color(0xFF18181B) : const Color(0xFFF4F4F5), + mutedForeground: w.mutedForeground, + accent: isDark ? const Color(0xFF27272A) : const Color(0xFFE4E4E7), + accentForeground: fg, + destructive: w.destructive, + destructiveForeground: const Color(0xFFF8FAFC), + border: w.borderSubtle, + input: w.borderSubtle, + ring: w.accent, + chart1: const Color(0xFF2662D9), + chart2: const Color(0xFF2EB88A), + chart3: const Color(0xFFE88C30), + chart4: const Color(0xFFAF57DB), + chart5: const Color(0xFFE23670), + ); + } + + QueryaTheme copyWith({ + QueryaWorkbenchTheme? workbench, + QueryaEditorTheme? editor, + Brightness? brightness, + ColorScheme? colorScheme, + }) { + return QueryaTheme( + workbench: workbench ?? this.workbench, + editor: editor ?? this.editor, + brightness: brightness ?? this.brightness, + colorScheme: colorScheme ?? this.colorScheme, + ); + } + + static QueryaTheme lerp(QueryaTheme a, QueryaTheme b, double t) { + final w = QueryaWorkbenchTheme.lerp(a.workbench, b.workbench, t); + final e = QueryaEditorTheme.lerp(a.editor, b.editor, t); + final brightness = t < 0.5 ? a.brightness : b.brightness; + return QueryaTheme( + workbench: w, + editor: e, + brightness: brightness, + colorScheme: ColorScheme.lerp(a.colorScheme, b.colorScheme, t), + ); + } + + ThemeData toShadcnThemeData({ + double radius = 0.58, + double scaling = 1, + Typography? typography, + }) { + final typo = typography ?? Typography.geist(); + final base = brightness == Brightness.dark + ? ThemeData.dark(colorScheme: colorScheme) + : ThemeData(colorScheme: colorScheme); + return base.copyWith( + radius: () => radius, + scaling: () => scaling, + typography: () => typo, + ); + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is QueryaTheme && + workbench == other.workbench && + editor == other.editor && + brightness == other.brightness && + colorScheme == other.colorScheme; + + @override + int get hashCode => + Object.hash(workbench, editor, brightness, colorScheme); +} diff --git a/lib/core/theme/querya_workbench_theme.dart b/lib/core/theme/querya_workbench_theme.dart new file mode 100644 index 00000000..ddfd783d --- /dev/null +++ b/lib/core/theme/querya_workbench_theme.dart @@ -0,0 +1,160 @@ +import 'dart:ui'; + +import 'querya_colors.dart'; + +/// Workbench (non-editor) color tokens — sidebar, chrome, status, git decorations. +class QueryaWorkbenchTheme { + const QueryaWorkbenchTheme({ + required this.canvas, + required this.surface, + required this.sidebarBackground, + required this.editorBackground, + required this.borderSubtle, + required this.accent, + required this.onAccent, + required this.mutedForeground, + required this.destructive, + required this.success, + required this.warning, + required this.gitModified, + required this.gitUntracked, + }); + + final Color canvas; + final Color surface; + final Color sidebarBackground; + final Color editorBackground; + final Color borderSubtle; + final Color accent; + final Color onAccent; + final Color mutedForeground; + final Color destructive; + final Color success; + final Color warning; + final Color gitModified; + final Color gitUntracked; + + /// Matches current [QueryaColors] / dark UI. + static const QueryaWorkbenchTheme darkDefault = QueryaWorkbenchTheme( + canvas: QueryaColors.canvas, + surface: QueryaColors.surface, + sidebarBackground: QueryaColors.canvas, + editorBackground: QueryaColors.surface, + borderSubtle: QueryaColors.borderSubtle, + accent: QueryaColors.accentCyan, + onAccent: QueryaColors.onAccent, + mutedForeground: QueryaColors.mutedLabel, + destructive: Color(0xFFEF4444), + success: Color(0xFF4CAF50), + warning: Color(0xFFE88C30), + gitModified: Color(0xFFE88C30), + gitUntracked: Color(0xFF2EB88A), + ); + + /// Placeholder light preset (#51 will refine). + static const QueryaWorkbenchTheme lightDefault = QueryaWorkbenchTheme( + canvas: Color(0xFFFAFAFA), + surface: Color(0xFFFFFFFF), + sidebarBackground: Color(0xFFF4F4F5), + editorBackground: Color(0xFFFFFFFF), + borderSubtle: Color(0xFFE4E4E7), + accent: QueryaColors.accentCyan, + onAccent: QueryaColors.onAccent, + mutedForeground: Color(0xFF64748B), + destructive: Color(0xFFDC2626), + success: Color(0xFF16A34A), + warning: Color(0xFFD97706), + gitModified: Color(0xFFD97706), + gitUntracked: Color(0xFF16A34A), + ); + + QueryaWorkbenchTheme copyWith({ + Color? canvas, + Color? surface, + Color? sidebarBackground, + Color? editorBackground, + Color? borderSubtle, + Color? accent, + Color? onAccent, + Color? mutedForeground, + Color? destructive, + Color? success, + Color? warning, + Color? gitModified, + Color? gitUntracked, + }) { + return QueryaWorkbenchTheme( + canvas: canvas ?? this.canvas, + surface: surface ?? this.surface, + sidebarBackground: sidebarBackground ?? this.sidebarBackground, + editorBackground: editorBackground ?? this.editorBackground, + borderSubtle: borderSubtle ?? this.borderSubtle, + accent: accent ?? this.accent, + onAccent: onAccent ?? this.onAccent, + mutedForeground: mutedForeground ?? this.mutedForeground, + destructive: destructive ?? this.destructive, + success: success ?? this.success, + warning: warning ?? this.warning, + gitModified: gitModified ?? this.gitModified, + gitUntracked: gitUntracked ?? this.gitUntracked, + ); + } + + static QueryaWorkbenchTheme lerp( + QueryaWorkbenchTheme a, + QueryaWorkbenchTheme b, + double t, + ) { + Color c(Color x, Color y) => Color.lerp(x, y, t)!; + return QueryaWorkbenchTheme( + canvas: c(a.canvas, b.canvas), + surface: c(a.surface, b.surface), + sidebarBackground: c(a.sidebarBackground, b.sidebarBackground), + editorBackground: c(a.editorBackground, b.editorBackground), + borderSubtle: c(a.borderSubtle, b.borderSubtle), + accent: c(a.accent, b.accent), + onAccent: c(a.onAccent, b.onAccent), + mutedForeground: c(a.mutedForeground, b.mutedForeground), + destructive: c(a.destructive, b.destructive), + success: c(a.success, b.success), + warning: c(a.warning, b.warning), + gitModified: c(a.gitModified, b.gitModified), + gitUntracked: c(a.gitUntracked, b.gitUntracked), + ); + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is QueryaWorkbenchTheme && + canvas == other.canvas && + surface == other.surface && + sidebarBackground == other.sidebarBackground && + editorBackground == other.editorBackground && + borderSubtle == other.borderSubtle && + accent == other.accent && + onAccent == other.onAccent && + mutedForeground == other.mutedForeground && + destructive == other.destructive && + success == other.success && + warning == other.warning && + gitModified == other.gitModified && + gitUntracked == other.gitUntracked; + + @override + int get hashCode => Object.hash( + canvas, + surface, + sidebarBackground, + editorBackground, + borderSubtle, + accent, + onAccent, + mutedForeground, + destructive, + success, + warning, + gitModified, + gitUntracked, + ); +} diff --git a/test/core/theme/querya_theme_test.dart b/test/core/theme/querya_theme_test.dart new file mode 100644 index 00000000..b9a3d434 --- /dev/null +++ b/test/core/theme/querya_theme_test.dart @@ -0,0 +1,92 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:querya_desktop/core/theme/querya_color_scheme.dart'; +import 'package:querya_desktop/core/theme/querya_colors.dart'; +import 'package:querya_desktop/core/theme/querya_editor_theme.dart'; +import 'package:querya_desktop/core/theme/querya_theme.dart'; +import 'package:querya_desktop/core/theme/querya_workbench_theme.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; + +void main() { + group('QueryaWorkbenchTheme', () { + test('darkDefault matches QueryaColors', () { + const w = QueryaWorkbenchTheme.darkDefault; + expect(w.canvas, QueryaColors.canvas); + expect(w.surface, QueryaColors.surface); + expect(w.accent, QueryaColors.accentCyan); + expect(w.borderSubtle, QueryaColors.borderSubtle); + }); + + test('copyWith overrides one field', () { + const w = QueryaWorkbenchTheme.darkDefault; + final next = w.copyWith(accent: const Color(0xFFFF0000)); + expect(next.accent, const Color(0xFFFF0000)); + expect(next.canvas, w.canvas); + }); + + test('lerp at 0 returns a', () { + const a = QueryaWorkbenchTheme.darkDefault; + const b = QueryaWorkbenchTheme.lightDefault; + final m = QueryaWorkbenchTheme.lerp(a, b, 0); + expect(m.canvas, a.canvas); + }); + }); + + group('QueryaEditorTheme', () { + test('copyWith and equality', () { + const a = QueryaEditorTheme.darkDefault; + final b = a.copyWith(fontSize: 14); + expect(b.fontSize, 14); + expect(b, isNot(equals(a))); + expect(b.copyWith(fontSize: 13), equals(a)); + }); + }); + + group('QueryaTheme', () { + test('darkDefault colorScheme matches legacy QueryaColorScheme.dark', () { + final legacy = QueryaColorScheme.dark; + final next = QueryaTheme.darkDefault.colorScheme; + expect(next.background, legacy.background); + expect(next.primary, legacy.primary); + expect(next.card, legacy.card); + expect(next.border, legacy.border); + }); + + test('colorSchemeFromWorkbench uses workbench tokens', () { + const w = QueryaWorkbenchTheme( + canvas: Color(0xFF111111), + surface: Color(0xFF222222), + sidebarBackground: Color(0xFF111111), + editorBackground: Color(0xFF222222), + borderSubtle: Color(0xFF333333), + accent: Color(0xFF00FFFF), + onAccent: Color(0xFF000000), + mutedForeground: Color(0xFFAAAAAA), + destructive: Color(0xFFFF0000), + success: Color(0xFF00FF00), + warning: Color(0xFFFFFF00), + gitModified: Color(0xFFFF00FF), + gitUntracked: Color(0xFF00FF00), + ); + final cs = QueryaTheme.colorSchemeFromWorkbench( + w, + brightness: Brightness.dark, + ); + expect(cs.background, w.canvas); + expect(cs.primary, w.accent); + }); + + test('lerp interpolates editor and workbench', () { + final a = QueryaTheme.darkDefault; + final b = QueryaTheme.lightDefault; + final mid = QueryaTheme.lerp(a, b, 0.5); + expect(mid.workbench, isNot(equals(a.workbench))); + expect(mid.editor.foreground, isNot(equals(a.editor.foreground))); + }); + + test('toShadcnThemeData preserves brightness', () { + final td = QueryaTheme.darkDefault.toShadcnThemeData(); + expect(td.brightness, Brightness.dark); + expect(td.colorScheme.primary, QueryaColors.accentCyan); + }); + }); +} From 3ec611103f6d509952e5f38fbff1a5b846d48f48 Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Thu, 28 May 2026 10:14:56 +0300 Subject: [PATCH 2/2] fix(theme): satisfy prefer_const_* lints in QueryaTheme Use const ColorScheme presets via QueryaColors and const Typography.geist() so flutter analyze exits cleanly in CI. --- lib/core/theme/querya_theme.dart | 53 ++++++++++++++------------ test/core/theme/querya_theme_test.dart | 4 +- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/lib/core/theme/querya_theme.dart b/lib/core/theme/querya_theme.dart index 8db6117f..7982b02b 100644 --- a/lib/core/theme/querya_theme.dart +++ b/lib/core/theme/querya_theme.dart @@ -1,5 +1,6 @@ import 'package:shadcn_flutter/shadcn_flutter.dart'; +import 'querya_colors.dart'; import 'querya_editor_theme.dart'; import 'querya_workbench_theme.dart'; @@ -17,41 +18,42 @@ class QueryaTheme { final Brightness brightness; final ColorScheme colorScheme; - static final QueryaTheme darkDefault = QueryaTheme( + static const QueryaTheme darkDefault = QueryaTheme( workbench: QueryaWorkbenchTheme.darkDefault, editor: QueryaEditorTheme.darkDefault, brightness: Brightness.dark, colorScheme: _darkColorScheme, ); - static final QueryaTheme lightDefault = QueryaTheme( + static const QueryaTheme lightDefault = QueryaTheme( workbench: QueryaWorkbenchTheme.lightDefault, editor: QueryaEditorTheme.lightDefault, brightness: Brightness.light, colorScheme: _lightColorScheme, ); - static final ColorScheme _darkColorScheme = ColorScheme( + /// Matches [QueryaWorkbenchTheme.darkDefault] / legacy [QueryaColorScheme]. + static const ColorScheme _darkColorScheme = ColorScheme( brightness: Brightness.dark, - background: QueryaWorkbenchTheme.darkDefault.canvas, + background: QueryaColors.canvas, foreground: Color(0xFFF8FAFC), - card: QueryaWorkbenchTheme.darkDefault.surface, + card: QueryaColors.surface, cardForeground: Color(0xFFF8FAFC), - popover: QueryaWorkbenchTheme.darkDefault.surface, + popover: QueryaColors.surface, popoverForeground: Color(0xFFF8FAFC), - primary: QueryaWorkbenchTheme.darkDefault.accent, - primaryForeground: QueryaWorkbenchTheme.darkDefault.onAccent, + primary: QueryaColors.accentCyan, + primaryForeground: QueryaColors.onAccent, secondary: Color(0xFF18181B), secondaryForeground: Color(0xFFF8FAFC), muted: Color(0xFF18181B), - mutedForeground: QueryaWorkbenchTheme.darkDefault.mutedForeground, + mutedForeground: QueryaColors.mutedLabel, accent: Color(0xFF27272A), accentForeground: Color(0xFFF8FAFC), - destructive: QueryaWorkbenchTheme.darkDefault.destructive, + destructive: Color(0xFFEF4444), destructiveForeground: Color(0xFFF8FAFC), - border: QueryaWorkbenchTheme.darkDefault.borderSubtle, - input: QueryaWorkbenchTheme.darkDefault.borderSubtle, - ring: QueryaWorkbenchTheme.darkDefault.accent, + border: QueryaColors.borderSubtle, + input: QueryaColors.borderSubtle, + ring: QueryaColors.accentCyan, chart1: Color(0xFF2662D9), chart2: Color(0xFF2EB88A), chart3: Color(0xFFE88C30), @@ -59,27 +61,28 @@ class QueryaTheme { chart5: Color(0xFFE23670), ); - static final ColorScheme _lightColorScheme = ColorScheme( + /// Matches [QueryaWorkbenchTheme.lightDefault]. + static const ColorScheme _lightColorScheme = ColorScheme( brightness: Brightness.light, - background: QueryaWorkbenchTheme.lightDefault.canvas, + background: Color(0xFFFAFAFA), foreground: Color(0xFF0F172A), - card: QueryaWorkbenchTheme.lightDefault.surface, + card: Color(0xFFFFFFFF), cardForeground: Color(0xFF0F172A), - popover: QueryaWorkbenchTheme.lightDefault.surface, + popover: Color(0xFFFFFFFF), popoverForeground: Color(0xFF0F172A), - primary: QueryaWorkbenchTheme.lightDefault.accent, - primaryForeground: QueryaWorkbenchTheme.lightDefault.onAccent, + primary: QueryaColors.accentCyan, + primaryForeground: QueryaColors.onAccent, secondary: Color(0xFFF4F4F5), secondaryForeground: Color(0xFF0F172A), muted: Color(0xFFF4F4F5), - mutedForeground: QueryaWorkbenchTheme.lightDefault.mutedForeground, + mutedForeground: Color(0xFF64748B), accent: Color(0xFFE4E4E7), accentForeground: Color(0xFF0F172A), - destructive: QueryaWorkbenchTheme.lightDefault.destructive, + destructive: Color(0xFFDC2626), destructiveForeground: Color(0xFFF8FAFC), - border: QueryaWorkbenchTheme.lightDefault.borderSubtle, - input: QueryaWorkbenchTheme.lightDefault.borderSubtle, - ring: QueryaWorkbenchTheme.lightDefault.accent, + border: Color(0xFFE4E4E7), + input: Color(0xFFE4E4E7), + ring: QueryaColors.accentCyan, chart1: Color(0xFF2662D9), chart2: Color(0xFF2EB88A), chart3: Color(0xFFE88C30), @@ -154,7 +157,7 @@ class QueryaTheme { double scaling = 1, Typography? typography, }) { - final typo = typography ?? Typography.geist(); + final typo = typography ?? const Typography.geist(); final base = brightness == Brightness.dark ? ThemeData.dark(colorScheme: colorScheme) : ThemeData(colorScheme: colorScheme); diff --git a/test/core/theme/querya_theme_test.dart b/test/core/theme/querya_theme_test.dart index b9a3d434..6e28b719 100644 --- a/test/core/theme/querya_theme_test.dart +++ b/test/core/theme/querya_theme_test.dart @@ -76,8 +76,8 @@ void main() { }); test('lerp interpolates editor and workbench', () { - final a = QueryaTheme.darkDefault; - final b = QueryaTheme.lightDefault; + const a = QueryaTheme.darkDefault; + const b = QueryaTheme.lightDefault; final mid = QueryaTheme.lerp(a, b, 0.5); expect(mid.workbench, isNot(equals(a.workbench))); expect(mid.editor.foreground, isNot(equals(a.editor.foreground)));