Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions lib/core/theme/app_theme.dart
Original file line number Diff line number Diff line change
@@ -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();
}
33 changes: 4 additions & 29 deletions lib/core/theme/querya_color_scheme.dart
Original file line number Diff line number Diff line change
@@ -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;
}
156 changes: 156 additions & 0 deletions lib/core/theme/querya_editor_theme.dart
Original file line number Diff line number Diff line change
@@ -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,
);
}
183 changes: 183 additions & 0 deletions lib/core/theme/querya_theme.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import 'package:shadcn_flutter/shadcn_flutter.dart';

import 'querya_colors.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 const QueryaTheme darkDefault = QueryaTheme(
workbench: QueryaWorkbenchTheme.darkDefault,
editor: QueryaEditorTheme.darkDefault,
brightness: Brightness.dark,
colorScheme: _darkColorScheme,
);

static const QueryaTheme lightDefault = QueryaTheme(
workbench: QueryaWorkbenchTheme.lightDefault,
editor: QueryaEditorTheme.lightDefault,
brightness: Brightness.light,
colorScheme: _lightColorScheme,
);

/// Matches [QueryaWorkbenchTheme.darkDefault] / legacy [QueryaColorScheme].
static const ColorScheme _darkColorScheme = 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),
);

/// Matches [QueryaWorkbenchTheme.lightDefault].
static const ColorScheme _lightColorScheme = ColorScheme(
brightness: Brightness.light,
background: Color(0xFFFAFAFA),
foreground: Color(0xFF0F172A),
card: Color(0xFFFFFFFF),
cardForeground: Color(0xFF0F172A),
popover: Color(0xFFFFFFFF),
popoverForeground: Color(0xFF0F172A),
primary: QueryaColors.accentCyan,
primaryForeground: QueryaColors.onAccent,
secondary: Color(0xFFF4F4F5),
secondaryForeground: Color(0xFF0F172A),
muted: Color(0xFFF4F4F5),
mutedForeground: Color(0xFF64748B),
accent: Color(0xFFE4E4E7),
accentForeground: Color(0xFF0F172A),
destructive: Color(0xFFDC2626),
destructiveForeground: Color(0xFFF8FAFC),
border: Color(0xFFE4E4E7),
input: Color(0xFFE4E4E7),
ring: QueryaColors.accentCyan,
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 ?? const 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);
}
Loading
Loading