dissolve app/ into repo root (D-056)
Single Flutter package at the repo root. All code, tests, assets, and platform directories moved from app/ to root. Package renamed from clide_app to clide — all imports rewritten. Merged pubspec combines core (ffi) and app (flutter, yaml, xterm) dependencies. Makefile simplified: no APP_PRESENT conditionals, no cd, no daemon lifecycle. 317 tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
import 'dart:math' as math;
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:clide/kernel/src/theme/tokens.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
/// A foreground/background token pair the a11y contrast suite walks.
|
||||
@immutable
|
||||
class ContrastPair {
|
||||
const ContrastPair({
|
||||
required this.name,
|
||||
required this.foreground,
|
||||
required this.background,
|
||||
this.largeText = false,
|
||||
});
|
||||
|
||||
final String name;
|
||||
final Color foreground;
|
||||
final Color background;
|
||||
|
||||
/// WCAG AA threshold for "large text" (18pt, or 14pt bold) is 3:1;
|
||||
/// normal text is 4.5:1. Mark a pair as [largeText] when the rendered
|
||||
/// typography qualifies.
|
||||
final bool largeText;
|
||||
}
|
||||
|
||||
/// Compute the WCAG 2.x relative-luminance ratio between two colors.
|
||||
///
|
||||
/// Alpha is pre-composited against a neutral grey so semi-transparent
|
||||
/// tokens don't spuriously pass. Returns a value in `[1, 21]`.
|
||||
double contrastRatio(Color a, Color b, {Color onto = const Color(0xFF808080)}) {
|
||||
final la = _relativeLuminance(_composite(a, onto));
|
||||
final lb = _relativeLuminance(_composite(b, onto));
|
||||
final brighter = math.max(la, lb);
|
||||
final darker = math.min(la, lb);
|
||||
return (brighter + 0.05) / (darker + 0.05);
|
||||
}
|
||||
|
||||
/// Minimum ratio required for this pair per WCAG AA.
|
||||
double minimumRatio(ContrastPair pair) => pair.largeText ? 3.0 : 4.5;
|
||||
|
||||
/// Canonical set of token pairs each bundled theme must honour.
|
||||
///
|
||||
/// The a11y contrast test walks this list per-theme.
|
||||
List<ContrastPair> canonicalPairs(SurfaceTokens s) => [
|
||||
ContrastPair(
|
||||
name: 'global.text_on_background',
|
||||
foreground: s.globalForeground,
|
||||
background: s.globalBackground,
|
||||
),
|
||||
ContrastPair(
|
||||
name: 'panel.header_foreground_on_panel',
|
||||
foreground: s.panelHeaderForeground,
|
||||
background: s.panelHeader,
|
||||
),
|
||||
ContrastPair(
|
||||
name: 'sidebar.foreground_on_sidebar',
|
||||
foreground: s.sidebarForeground,
|
||||
background: s.sidebarBackground,
|
||||
),
|
||||
ContrastPair(
|
||||
name: 'statusbar.foreground_on_statusbar',
|
||||
foreground: s.statusBarForeground,
|
||||
background: s.statusBarBackground,
|
||||
),
|
||||
ContrastPair(
|
||||
name: 'tab.active_text_on_active_bg',
|
||||
foreground: s.tabActiveForeground,
|
||||
background: s.tabActive,
|
||||
),
|
||||
ContrastPair(
|
||||
name: 'tab.inactive_text_on_inactive_bg',
|
||||
foreground: s.tabInactiveForeground,
|
||||
background: s.tabInactive,
|
||||
),
|
||||
ContrastPair(
|
||||
name: 'button.text_on_button',
|
||||
foreground: s.buttonForeground,
|
||||
background: s.buttonBackground,
|
||||
),
|
||||
ContrastPair(
|
||||
name: 'listItem.selected_text_on_selected_bg',
|
||||
foreground: s.listItemSelectedForeground,
|
||||
background: s.listItemSelectedBackground,
|
||||
),
|
||||
ContrastPair(
|
||||
name: 'listItem.text_on_list',
|
||||
foreground: s.listItemForeground,
|
||||
background: s.listItemBackground,
|
||||
),
|
||||
ContrastPair(
|
||||
name: 'tooltip.text_on_tooltip',
|
||||
foreground: s.tooltipForeground,
|
||||
background: s.tooltipBackground,
|
||||
),
|
||||
ContrastPair(
|
||||
name: 'dropdown.text_on_dropdown',
|
||||
foreground: s.dropdownForeground,
|
||||
background: s.dropdownBackground,
|
||||
),
|
||||
];
|
||||
|
||||
/// Convenience for tests: returns the list of pairs that fail WCAG AA.
|
||||
List<ContrastFailure> failingPairs(SurfaceTokens tokens) {
|
||||
final out = <ContrastFailure>[];
|
||||
for (final p in canonicalPairs(tokens)) {
|
||||
final ratio = contrastRatio(p.foreground, p.background);
|
||||
final need = minimumRatio(p);
|
||||
if (ratio < need) {
|
||||
out.add(ContrastFailure(pair: p, ratio: ratio, minimum: need));
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@immutable
|
||||
class ContrastFailure {
|
||||
const ContrastFailure({
|
||||
required this.pair,
|
||||
required this.ratio,
|
||||
required this.minimum,
|
||||
});
|
||||
|
||||
final ContrastPair pair;
|
||||
final double ratio;
|
||||
final double minimum;
|
||||
|
||||
@override
|
||||
String toString() => 'contrast ${pair.name}: ${ratio.toStringAsFixed(2)} < '
|
||||
'${minimum.toStringAsFixed(1)}';
|
||||
}
|
||||
|
||||
// -- internals ---------------------------------------------------------------
|
||||
|
||||
Color _composite(Color src, Color dst) {
|
||||
final a = src.a;
|
||||
if (a >= 0.999) return src;
|
||||
double mix(double s, double d) => s * a + d * (1 - a);
|
||||
return Color.from(
|
||||
alpha: 1.0,
|
||||
red: mix(src.r, dst.r),
|
||||
green: mix(src.g, dst.g),
|
||||
blue: mix(src.b, dst.b),
|
||||
);
|
||||
}
|
||||
|
||||
double _relativeLuminance(Color c) {
|
||||
double chan(double v) =>
|
||||
v <= 0.03928 ? v / 12.92 : math.pow((v + 0.055) / 1.055, 2.4).toDouble();
|
||||
return 0.2126 * chan(c.r) + 0.7152 * chan(c.g) + 0.0722 * chan(c.b);
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import 'package:clide/kernel/src/theme/loader.dart';
|
||||
import 'package:clide/kernel/src/theme/resolver.dart';
|
||||
import 'package:clide/kernel/src/theme/tokens.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
@immutable
|
||||
class ClideThemeData {
|
||||
const ClideThemeData({
|
||||
required this.name,
|
||||
required this.displayName,
|
||||
required this.dark,
|
||||
required this.surface,
|
||||
});
|
||||
|
||||
final String name;
|
||||
final String displayName;
|
||||
final bool dark;
|
||||
final SurfaceTokens surface;
|
||||
}
|
||||
|
||||
class ThemeController extends ChangeNotifier {
|
||||
ThemeController({
|
||||
required List<ThemeDefinition> bundled,
|
||||
ThemeResolver resolver = const ThemeResolver(),
|
||||
String? initialName,
|
||||
}) : _resolver = resolver,
|
||||
_defs = Map.fromEntries(bundled.map((d) => MapEntry(d.name, d))) {
|
||||
final first = initialName != null && _defs.containsKey(initialName)
|
||||
? initialName
|
||||
: bundled.first.name;
|
||||
_currentName = first;
|
||||
_current = _build(first);
|
||||
}
|
||||
|
||||
final ThemeResolver _resolver;
|
||||
final Map<String, ThemeDefinition> _defs;
|
||||
|
||||
late String _currentName;
|
||||
late ClideThemeData _current;
|
||||
|
||||
ClideThemeData get current => _current;
|
||||
String get currentName => _currentName;
|
||||
List<ThemeDefinition> get available => _defs.values.toList(growable: false);
|
||||
|
||||
void select(String name) {
|
||||
if (!_defs.containsKey(name)) {
|
||||
throw ArgumentError('Unknown theme: $name');
|
||||
}
|
||||
if (name == _currentName) return;
|
||||
_currentName = name;
|
||||
_current = _build(name);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void registerTheme(ThemeDefinition def) {
|
||||
_defs[def.name] = def;
|
||||
// If the user re-imported the current theme, rebuild so overrides
|
||||
// take effect without a select().
|
||||
if (def.name == _currentName) {
|
||||
_current = _build(def.name);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
ClideThemeData _build(String name) {
|
||||
final def = _defs[name]!;
|
||||
final tokens = _resolver.resolve(
|
||||
palette: def.palette,
|
||||
semanticOverride: def.semanticOverride,
|
||||
surfaceOverride: def.surfaceOverride,
|
||||
extensionOverride: def.extensionOverride,
|
||||
);
|
||||
return ClideThemeData(
|
||||
name: def.name,
|
||||
displayName: def.displayName,
|
||||
dark: def.dark,
|
||||
surface: tokens,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ClideTheme extends InheritedNotifier<ThemeController> {
|
||||
const ClideTheme({
|
||||
super.key,
|
||||
required ThemeController controller,
|
||||
required super.child,
|
||||
}) : super(notifier: controller);
|
||||
|
||||
static ClideThemeData of(BuildContext context) {
|
||||
final w = context.dependOnInheritedWidgetOfExactType<ClideTheme>();
|
||||
if (w == null) {
|
||||
throw FlutterError(
|
||||
'ClideTheme.of() called with a context that is not a descendant of a ClideTheme.');
|
||||
}
|
||||
return w.notifier!.current;
|
||||
}
|
||||
|
||||
static ThemeController controllerOf(BuildContext context) {
|
||||
final w = context.dependOnInheritedWidgetOfExactType<ClideTheme>();
|
||||
if (w == null) {
|
||||
throw FlutterError(
|
||||
'ClideTheme.controllerOf() called with a context that is not a descendant of a ClideTheme.');
|
||||
}
|
||||
return w.notifier!;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:clide/kernel/src/theme/palette.dart';
|
||||
import 'package:clide/kernel/src/theme/semantic.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
class ThemeDefinition {
|
||||
const ThemeDefinition({
|
||||
required this.name,
|
||||
required this.displayName,
|
||||
required this.dark,
|
||||
required this.palette,
|
||||
this.semanticOverride,
|
||||
this.surfaceOverride,
|
||||
this.extensionOverride,
|
||||
});
|
||||
|
||||
final String name;
|
||||
final String displayName;
|
||||
final bool dark;
|
||||
final Palette palette;
|
||||
final SemanticRoles? semanticOverride;
|
||||
final Map<String, String>? surfaceOverride;
|
||||
final Map<String, String>? extensionOverride;
|
||||
}
|
||||
|
||||
class ThemeLoader {
|
||||
const ThemeLoader();
|
||||
|
||||
ThemeDefinition fromYamlString(String text, {String? fallbackName}) {
|
||||
final doc = loadYaml(text);
|
||||
if (doc is! Map) {
|
||||
throw FormatException('Theme root is not a map');
|
||||
}
|
||||
final name = (doc['name'] as String?) ?? fallbackName;
|
||||
if (name == null || name.isEmpty) {
|
||||
throw const FormatException('Theme missing `name`');
|
||||
}
|
||||
final displayName = (doc['display_name'] as String?) ?? name;
|
||||
final dark = (doc['dark'] as bool?) ?? true;
|
||||
|
||||
final paletteYaml = doc['palette'];
|
||||
if (paletteYaml is! Map) {
|
||||
throw const FormatException('Theme missing `palette`');
|
||||
}
|
||||
final palette = _parsePalette(paletteYaml);
|
||||
|
||||
// Syntax colours inject into the surface override layer so
|
||||
// TokenKeys.syntax* resolve directly from the theme YAML.
|
||||
final syntaxYaml = doc['syntax'];
|
||||
final syntaxSurface = <String, String>{};
|
||||
if (syntaxYaml is Map) {
|
||||
const syntaxMap = {
|
||||
'keyword': 'syntax.keyword',
|
||||
'type': 'syntax.type',
|
||||
'string': 'syntax.string',
|
||||
'number': 'syntax.number',
|
||||
'comment': 'syntax.comment',
|
||||
'method': 'syntax.method',
|
||||
'punct': 'syntax.punct',
|
||||
};
|
||||
syntaxYaml.forEach((k, v) {
|
||||
final key = syntaxMap['$k'];
|
||||
if (key != null && v is String) syntaxSurface[key] = v;
|
||||
});
|
||||
}
|
||||
|
||||
final semantic = doc['semantic'];
|
||||
final surface = doc['surface'];
|
||||
final extension = doc['extension'];
|
||||
|
||||
final mergedSurface = <String, String>{
|
||||
...syntaxSurface,
|
||||
if (surface is Map) ..._parseRefMap(surface),
|
||||
};
|
||||
|
||||
return ThemeDefinition(
|
||||
name: name,
|
||||
displayName: displayName,
|
||||
dark: dark,
|
||||
palette: palette,
|
||||
semanticOverride:
|
||||
semantic is Map ? _parseSemantic(semantic, palette) : null,
|
||||
surfaceOverride: mergedSurface.isNotEmpty ? mergedSurface : null,
|
||||
extensionOverride: extension is Map ? _parseRefMap(extension) : null,
|
||||
);
|
||||
}
|
||||
|
||||
Future<ThemeDefinition> fromAsset(
|
||||
AssetBundle bundle, String assetPath) async {
|
||||
final txt = await bundle.loadString(assetPath);
|
||||
final fallback = assetPath.split('/').last.replaceAll('.yaml', '');
|
||||
return fromYamlString(txt, fallbackName: fallback);
|
||||
}
|
||||
|
||||
Future<ThemeDefinition> fromFile(File f) async {
|
||||
final txt = await f.readAsString();
|
||||
final fallback = f.uri.pathSegments.last.replaceAll('.yaml', '');
|
||||
return fromYamlString(txt, fallbackName: fallback);
|
||||
}
|
||||
}
|
||||
|
||||
Palette _parsePalette(Map src) {
|
||||
final colors = <String, Color>{};
|
||||
src.forEach((k, v) {
|
||||
if (v is! String) return;
|
||||
final c = Palette.parseHex(v);
|
||||
if (c != null) colors['$k'] = c;
|
||||
});
|
||||
return Palette(colors);
|
||||
}
|
||||
|
||||
SemanticRoles _parseSemantic(Map src, Palette palette) {
|
||||
final roles = <String, Color>{};
|
||||
src.forEach((k, v) {
|
||||
if (v is! String) return;
|
||||
final resolved =
|
||||
v.startsWith('#') ? Palette.parseHex(v) : palette.lookup(v);
|
||||
if (resolved != null) roles['$k'] = resolved;
|
||||
});
|
||||
return SemanticRoles(roles);
|
||||
}
|
||||
|
||||
Map<String, String> _parseRefMap(Map src) {
|
||||
final out = <String, String>{};
|
||||
src.forEach((k, v) {
|
||||
if (v is String) out['$k'] = v;
|
||||
});
|
||||
return out;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
@immutable
|
||||
class Palette {
|
||||
const Palette(this._colors);
|
||||
final Map<String, Color> _colors;
|
||||
|
||||
Color? lookup(String name) => _colors[name];
|
||||
Iterable<String> get names => _colors.keys;
|
||||
|
||||
static Color? parseHex(String s) {
|
||||
var v = s.trim();
|
||||
if (v.startsWith('#')) v = v.substring(1);
|
||||
if (v.length == 6) v = 'FF$v';
|
||||
if (v.length != 8) return null;
|
||||
final n = int.tryParse(v, radix: 16);
|
||||
if (n == null) return null;
|
||||
return Color(n);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:clide/kernel/src/theme/palette.dart';
|
||||
import 'package:clide/kernel/src/theme/semantic.dart';
|
||||
import 'package:clide/kernel/src/theme/tokens.dart';
|
||||
|
||||
/// Three-tier theme resolution.
|
||||
///
|
||||
/// palette (raw colors)
|
||||
/// ↓ (ref-chain; defaults inherited)
|
||||
/// semantic (role → palette)
|
||||
/// ↓ (ref-chain; defaults inherited)
|
||||
/// surface (token → semantic|palette|literal)
|
||||
///
|
||||
/// References take the form:
|
||||
/// `semantic.<role>` — look up in [semantic]
|
||||
/// `#rrggbb` / `#aarrggbb` — literal hex
|
||||
/// bare name — palette lookup
|
||||
class ThemeResolver {
|
||||
const ThemeResolver();
|
||||
|
||||
SurfaceTokens resolve({
|
||||
required Palette palette,
|
||||
SemanticRoles? semanticOverride,
|
||||
Map<String, String>? surfaceOverride,
|
||||
Map<String, String>? extensionOverride,
|
||||
}) {
|
||||
final semantic = _buildSemantic(palette, semanticOverride);
|
||||
final surface = <String, Color>{};
|
||||
for (final key in TokenKeys.all) {
|
||||
surface[key] = _resolveSurface(
|
||||
key: key,
|
||||
palette: palette,
|
||||
semantic: semantic,
|
||||
surfaceOverride: surfaceOverride,
|
||||
);
|
||||
}
|
||||
|
||||
final extTokens = <String, Color>{};
|
||||
if (extensionOverride != null) {
|
||||
for (final entry in extensionOverride.entries) {
|
||||
final resolved = _resolveRef(entry.value, palette, semantic);
|
||||
if (resolved != null) extTokens[entry.key] = resolved;
|
||||
}
|
||||
}
|
||||
|
||||
return SurfaceTokens(
|
||||
globalForeground: surface[TokenKeys.globalForeground]!,
|
||||
globalBackground: surface[TokenKeys.globalBackground]!,
|
||||
globalBorder: surface[TokenKeys.globalBorder]!,
|
||||
globalFocus: surface[TokenKeys.globalFocus]!,
|
||||
globalTextMuted: surface[TokenKeys.globalTextMuted]!,
|
||||
panelBackground: surface[TokenKeys.panelBackground]!,
|
||||
panelBorder: surface[TokenKeys.panelBorder]!,
|
||||
panelActiveBorder: surface[TokenKeys.panelActiveBorder]!,
|
||||
panelHeader: surface[TokenKeys.panelHeader]!,
|
||||
panelHeaderForeground: surface[TokenKeys.panelHeaderForeground]!,
|
||||
sidebarBackground: surface[TokenKeys.sidebarBackground]!,
|
||||
sidebarForeground: surface[TokenKeys.sidebarForeground]!,
|
||||
sidebarItemHover: surface[TokenKeys.sidebarItemHover]!,
|
||||
sidebarItemSelected: surface[TokenKeys.sidebarItemSelected]!,
|
||||
sidebarSectionHeader: surface[TokenKeys.sidebarSectionHeader]!,
|
||||
statusBarBackground: surface[TokenKeys.statusBarBackground]!,
|
||||
statusBarForeground: surface[TokenKeys.statusBarForeground]!,
|
||||
statusBarItemActiveBackground:
|
||||
surface[TokenKeys.statusBarItemActiveBackground]!,
|
||||
statusBarItemHoverBackground:
|
||||
surface[TokenKeys.statusBarItemHoverBackground]!,
|
||||
tabBarBackground: surface[TokenKeys.tabBarBackground]!,
|
||||
tabActive: surface[TokenKeys.tabActive]!,
|
||||
tabInactive: surface[TokenKeys.tabInactive]!,
|
||||
tabActiveForeground: surface[TokenKeys.tabActiveForeground]!,
|
||||
tabInactiveForeground: surface[TokenKeys.tabInactiveForeground]!,
|
||||
tabActiveBorder: surface[TokenKeys.tabActiveBorder]!,
|
||||
tabCloseHover: surface[TokenKeys.tabCloseHover]!,
|
||||
buttonBackground: surface[TokenKeys.buttonBackground]!,
|
||||
buttonForeground: surface[TokenKeys.buttonForeground]!,
|
||||
buttonHoverBackground: surface[TokenKeys.buttonHoverBackground]!,
|
||||
buttonActiveBackground: surface[TokenKeys.buttonActiveBackground]!,
|
||||
buttonBorder: surface[TokenKeys.buttonBorder]!,
|
||||
listItemBackground: surface[TokenKeys.listItemBackground]!,
|
||||
listItemForeground: surface[TokenKeys.listItemForeground]!,
|
||||
listItemHoverBackground: surface[TokenKeys.listItemHoverBackground]!,
|
||||
listItemSelectedBackground:
|
||||
surface[TokenKeys.listItemSelectedBackground]!,
|
||||
listItemSelectedForeground:
|
||||
surface[TokenKeys.listItemSelectedForeground]!,
|
||||
scrollbarSlider: surface[TokenKeys.scrollbarSlider]!,
|
||||
scrollbarSliderHover: surface[TokenKeys.scrollbarSliderHover]!,
|
||||
scrollbarTrack: surface[TokenKeys.scrollbarTrack]!,
|
||||
tooltipBackground: surface[TokenKeys.tooltipBackground]!,
|
||||
tooltipForeground: surface[TokenKeys.tooltipForeground]!,
|
||||
tooltipBorder: surface[TokenKeys.tooltipBorder]!,
|
||||
dropdownBackground: surface[TokenKeys.dropdownBackground]!,
|
||||
dropdownForeground: surface[TokenKeys.dropdownForeground]!,
|
||||
dropdownBorder: surface[TokenKeys.dropdownBorder]!,
|
||||
modalOverlayBackground: surface[TokenKeys.modalOverlayBackground]!,
|
||||
modalSurfaceBackground: surface[TokenKeys.modalSurfaceBackground]!,
|
||||
modalSurfaceBorder: surface[TokenKeys.modalSurfaceBorder]!,
|
||||
dividerColor: surface[TokenKeys.dividerColor]!,
|
||||
statusSuccess: surface[TokenKeys.statusSuccess]!,
|
||||
statusWarning: surface[TokenKeys.statusWarning]!,
|
||||
statusError: surface[TokenKeys.statusError]!,
|
||||
statusInfo: surface[TokenKeys.statusInfo]!,
|
||||
syntaxKeyword: surface[TokenKeys.syntaxKeyword]!,
|
||||
syntaxType: surface[TokenKeys.syntaxType]!,
|
||||
syntaxString: surface[TokenKeys.syntaxString]!,
|
||||
syntaxNumber: surface[TokenKeys.syntaxNumber]!,
|
||||
syntaxComment: surface[TokenKeys.syntaxComment]!,
|
||||
syntaxMethod: surface[TokenKeys.syntaxMethod]!,
|
||||
syntaxPunct: surface[TokenKeys.syntaxPunct]!,
|
||||
extensionTokens: extTokens,
|
||||
);
|
||||
}
|
||||
|
||||
SemanticRoles _buildSemantic(Palette palette, SemanticRoles? override) {
|
||||
final roles = <String, Color>{};
|
||||
for (final role in SemanticKeys.all) {
|
||||
final fromOverride = override?.lookup(role);
|
||||
if (fromOverride != null) {
|
||||
roles[role] = fromOverride;
|
||||
continue;
|
||||
}
|
||||
for (final candidate in _defaultSemanticFallbacks[role] ?? [role]) {
|
||||
final fromPalette = palette.lookup(candidate);
|
||||
if (fromPalette != null) {
|
||||
roles[role] = fromPalette;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If still unresolved, fall back to foreground/background so the
|
||||
// theme never has a null surface color. Themes that omit these
|
||||
// will land readable if uninspired.
|
||||
roles.putIfAbsent(role, () {
|
||||
return palette.lookup('foreground') ??
|
||||
palette.lookup('background') ??
|
||||
const Color(0xFFFFFFFF);
|
||||
});
|
||||
}
|
||||
return SemanticRoles(roles);
|
||||
}
|
||||
|
||||
Color _resolveSurface({
|
||||
required String key,
|
||||
required Palette palette,
|
||||
required SemanticRoles semantic,
|
||||
Map<String, String>? surfaceOverride,
|
||||
}) {
|
||||
final override = surfaceOverride?[key];
|
||||
if (override != null) {
|
||||
final resolved = _resolveRef(override, palette, semantic);
|
||||
if (resolved != null) return resolved;
|
||||
}
|
||||
final candidates = _defaultSurfaceMap[key];
|
||||
if (candidates != null) {
|
||||
for (final ref in candidates) {
|
||||
final resolved = _resolveRef(ref, palette, semantic);
|
||||
if (resolved != null) return resolved;
|
||||
}
|
||||
}
|
||||
return semantic.lookup(SemanticKeys.text) ?? const Color(0xFFFFFFFF);
|
||||
}
|
||||
|
||||
Color? _resolveRef(String ref, Palette palette, SemanticRoles semantic) {
|
||||
if (ref.startsWith('#')) return Palette.parseHex(ref);
|
||||
if (ref.startsWith('semantic.')) {
|
||||
return semantic.lookup(ref.substring('semantic.'.length));
|
||||
}
|
||||
return palette.lookup(ref);
|
||||
}
|
||||
}
|
||||
|
||||
/// Default palette names a semantic role will try, in order, when the
|
||||
/// theme doesn't override the role explicitly.
|
||||
const Map<String, List<String>> _defaultSemanticFallbacks = {
|
||||
SemanticKeys.mainchrome: ['bgSunken', 'panel', 'surface', 'background'],
|
||||
SemanticKeys.calltoaction: ['accent', 'primary'],
|
||||
SemanticKeys.focus: ['accent', 'primary'],
|
||||
SemanticKeys.background: ['bg', 'background'],
|
||||
SemanticKeys.surface: ['surface', 'panel'],
|
||||
SemanticKeys.text: ['textHi', 'foreground'],
|
||||
SemanticKeys.textMuted: ['textDim', 'muted', 'secondary', 'foreground'],
|
||||
SemanticKeys.success: ['ok', 'success'],
|
||||
SemanticKeys.warning: ['warn', 'warning'],
|
||||
SemanticKeys.error: ['err', 'error'],
|
||||
SemanticKeys.info: ['info', 'primary'],
|
||||
};
|
||||
|
||||
/// Default surface map. Every entry resolves through the semantic layer
|
||||
/// where it makes sense; raw palette refs are used only where the
|
||||
/// semantic layer doesn't have a role that fits.
|
||||
const Map<String, List<String>> _defaultSurfaceMap = {
|
||||
// global — try design keys first, then legacy semantic
|
||||
TokenKeys.globalForeground: ['textHi', 'semantic.text'],
|
||||
TokenKeys.globalBackground: ['bg', 'semantic.background'],
|
||||
TokenKeys.globalBorder: ['border', 'semantic.surface'],
|
||||
TokenKeys.globalFocus: ['accent', 'semantic.focus'],
|
||||
TokenKeys.globalTextMuted: ['textDim', 'semantic.text_muted'],
|
||||
// panel
|
||||
TokenKeys.panelBackground: ['bgSunken', 'semantic.mainchrome'],
|
||||
TokenKeys.panelBorder: ['border', 'semantic.surface'],
|
||||
TokenKeys.panelActiveBorder: ['borderHi', 'semantic.focus'],
|
||||
TokenKeys.panelHeader: ['surface', 'semantic.mainchrome'],
|
||||
TokenKeys.panelHeaderForeground: ['text', 'semantic.text'],
|
||||
// sidebar
|
||||
TokenKeys.sidebarBackground: ['bgSunken', 'semantic.mainchrome'],
|
||||
TokenKeys.sidebarForeground: ['text', 'semantic.text'],
|
||||
TokenKeys.sidebarItemHover: ['surface', 'semantic.surface'],
|
||||
TokenKeys.sidebarItemSelected: ['surfaceHi', 'semantic.focus'],
|
||||
TokenKeys.sidebarSectionHeader: ['textMute', 'semantic.text_muted'],
|
||||
// statusbar
|
||||
TokenKeys.statusBarBackground: ['bgSunken', 'semantic.mainchrome'],
|
||||
TokenKeys.statusBarForeground: ['text', 'semantic.text'],
|
||||
TokenKeys.statusBarItemActiveBackground: ['accent', 'semantic.focus'],
|
||||
TokenKeys.statusBarItemHoverBackground: ['surface', 'semantic.surface'],
|
||||
// tabs
|
||||
TokenKeys.tabBarBackground: ['bgSunken', 'semantic.mainchrome'],
|
||||
TokenKeys.tabActive: ['bg', 'semantic.background'],
|
||||
TokenKeys.tabInactive: ['bgSunken', 'semantic.mainchrome'],
|
||||
TokenKeys.tabActiveForeground: ['textHi', 'semantic.text'],
|
||||
TokenKeys.tabInactiveForeground: ['textDim', 'semantic.text_muted'],
|
||||
TokenKeys.tabActiveBorder: ['accent', 'semantic.focus'],
|
||||
TokenKeys.tabCloseHover: ['err', 'semantic.error'],
|
||||
// buttons
|
||||
TokenKeys.buttonBackground: ['accent', 'semantic.calltoaction'],
|
||||
TokenKeys.buttonForeground: ['onAccent', 'semantic.background'],
|
||||
TokenKeys.buttonHoverBackground: ['accentPress', 'semantic.focus'],
|
||||
TokenKeys.buttonActiveBackground: ['accentPress', 'semantic.focus'],
|
||||
TokenKeys.buttonBorder: ['border', 'semantic.surface'],
|
||||
// list items
|
||||
TokenKeys.listItemBackground: ['bg', 'semantic.background'],
|
||||
TokenKeys.listItemForeground: ['text', 'semantic.text'],
|
||||
TokenKeys.listItemHoverBackground: ['surface', 'semantic.surface'],
|
||||
TokenKeys.listItemSelectedBackground: ['surfaceHi', 'semantic.focus'],
|
||||
TokenKeys.listItemSelectedForeground: ['textHi', 'semantic.text'],
|
||||
// scrollbar
|
||||
TokenKeys.scrollbarSlider: ['border', 'semantic.surface'],
|
||||
TokenKeys.scrollbarSliderHover: ['borderHi', 'semantic.text_muted'],
|
||||
TokenKeys.scrollbarTrack: ['bgSunken', 'semantic.mainchrome'],
|
||||
// tooltip
|
||||
TokenKeys.tooltipBackground: ['surface', 'semantic.surface'],
|
||||
TokenKeys.tooltipForeground: ['textHi', 'semantic.text'],
|
||||
TokenKeys.tooltipBorder: ['borderHi', 'semantic.mainchrome'],
|
||||
// dropdown
|
||||
TokenKeys.dropdownBackground: ['surface', 'semantic.surface'],
|
||||
TokenKeys.dropdownForeground: ['text', 'semantic.text'],
|
||||
TokenKeys.dropdownBorder: ['border', 'semantic.mainchrome'],
|
||||
// modal
|
||||
TokenKeys.modalOverlayBackground: ['#C0000000'],
|
||||
TokenKeys.modalSurfaceBackground: ['surface', 'semantic.mainchrome'],
|
||||
TokenKeys.modalSurfaceBorder: ['accent', 'semantic.focus'],
|
||||
// divider
|
||||
TokenKeys.dividerColor: ['border', 'semantic.surface'],
|
||||
// status
|
||||
TokenKeys.statusSuccess: ['ok', 'semantic.success'],
|
||||
TokenKeys.statusWarning: ['warn', 'semantic.warning'],
|
||||
TokenKeys.statusError: ['err', 'semantic.error'],
|
||||
TokenKeys.statusInfo: ['info', 'semantic.info'],
|
||||
TokenKeys.syntaxKeyword: ['semantic.calltoaction'],
|
||||
TokenKeys.syntaxType: ['semantic.info'],
|
||||
TokenKeys.syntaxString: ['semantic.success'],
|
||||
TokenKeys.syntaxNumber: ['semantic.warning'],
|
||||
TokenKeys.syntaxComment: ['semantic.text_muted'],
|
||||
TokenKeys.syntaxMethod: ['semantic.focus'],
|
||||
TokenKeys.syntaxPunct: ['semantic.text_muted'],
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
@immutable
|
||||
class SemanticRoles {
|
||||
const SemanticRoles(this._roles);
|
||||
final Map<String, Color> _roles;
|
||||
|
||||
Color? lookup(String role) => _roles[role];
|
||||
Iterable<String> get roles => _roles.keys;
|
||||
}
|
||||
|
||||
abstract class SemanticKeys {
|
||||
static const mainchrome = 'mainchrome';
|
||||
static const calltoaction = 'calltoaction';
|
||||
static const focus = 'focus';
|
||||
static const background = 'background';
|
||||
static const surface = 'surface';
|
||||
static const text = 'text';
|
||||
static const textMuted = 'text_muted';
|
||||
static const success = 'success';
|
||||
static const warning = 'warning';
|
||||
static const error = 'error';
|
||||
static const info = 'info';
|
||||
|
||||
static const all = <String>[
|
||||
mainchrome,
|
||||
calltoaction,
|
||||
focus,
|
||||
background,
|
||||
surface,
|
||||
text,
|
||||
textMuted,
|
||||
success,
|
||||
warning,
|
||||
error,
|
||||
info,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
# clide — cool near-black + periwinkle (default)
|
||||
# Source: docs/claude-design/tokens/clide.yaml
|
||||
|
||||
name: clide
|
||||
display_name: Clide
|
||||
dark: true
|
||||
|
||||
palette:
|
||||
background: "#20202C"
|
||||
panel: "#1A1A24"
|
||||
surface: "#242838"
|
||||
muted: "#78809C"
|
||||
foreground: "#E6E8F2"
|
||||
secondary: "#B1BBE3"
|
||||
primary: "#78A0F8"
|
||||
accent: "#6C90DC"
|
||||
success: "#7DD3A8"
|
||||
warning: "#E6C370"
|
||||
error: "#E87D7D"
|
||||
info: "#78A0F8"
|
||||
# extended palette keys for the design's richer vocabulary
|
||||
surfaceHi: "#2C3046"
|
||||
border: "#343850"
|
||||
borderHi: "#3C445C"
|
||||
textDim: "#8890AC"
|
||||
textMute: "#545C84"
|
||||
accentSoft: "#2178A0F8"
|
||||
|
||||
syntax:
|
||||
keyword: "#C792EA"
|
||||
type: "#78A0F8"
|
||||
string: "#A8D99B"
|
||||
number: "#E6C370"
|
||||
comment: "#545C84"
|
||||
method: "#82B1FF"
|
||||
punct: "#78809C"
|
||||
@@ -0,0 +1,36 @@
|
||||
# midnight — VS Code-adjacent muted dark
|
||||
# Source: docs/claude-design/tokens/midnight.yaml
|
||||
|
||||
name: midnight
|
||||
display_name: Midnight
|
||||
dark: true
|
||||
|
||||
palette:
|
||||
background: "#1E1E1E"
|
||||
panel: "#181818"
|
||||
surface: "#252526"
|
||||
muted: "#858585"
|
||||
foreground: "#D4D4D4"
|
||||
secondary: "#BBBBBB"
|
||||
primary: "#569CD6"
|
||||
accent: "#4785BD"
|
||||
success: "#89D185"
|
||||
warning: "#D7BA7D"
|
||||
error: "#F48771"
|
||||
info: "#569CD6"
|
||||
onAccent: "#0B1220"
|
||||
surfaceHi: "#2D2D2E"
|
||||
border: "#333333"
|
||||
borderHi: "#3F3F3F"
|
||||
textDim: "#858585"
|
||||
textMute: "#6A6A6A"
|
||||
accentSoft: "#21569CD6"
|
||||
|
||||
syntax:
|
||||
keyword: "#C586C0"
|
||||
type: "#4EC9B0"
|
||||
string: "#CE9178"
|
||||
number: "#B5CEA8"
|
||||
comment: "#6A9955"
|
||||
method: "#DCDCAA"
|
||||
punct: "#858585"
|
||||
@@ -0,0 +1,35 @@
|
||||
# paper — drafting sheet, red-pencil accent, light
|
||||
# Source: docs/claude-design/tokens/paper.yaml
|
||||
|
||||
name: paper
|
||||
display_name: Paper
|
||||
dark: false
|
||||
|
||||
palette:
|
||||
background: "#F4F1EA"
|
||||
panel: "#ECE7DB"
|
||||
surface: "#FBF8F1"
|
||||
muted: "#8A8A82"
|
||||
foreground: "#1A1A1A"
|
||||
secondary: "#4A4A4A"
|
||||
primary: "#C14B2A"
|
||||
accent: "#A03D20"
|
||||
success: "#2D8A52"
|
||||
warning: "#B88A2A"
|
||||
error: "#B03A2A"
|
||||
info: "#2A6FC1"
|
||||
surfaceHi: "#ECE7DB"
|
||||
border: "#1A1A1A"
|
||||
borderHi: "#4A4A4A"
|
||||
textDim: "#5E5E56"
|
||||
textMute: "#A8A89E"
|
||||
accentSoft: "#21C14B2A"
|
||||
|
||||
syntax:
|
||||
keyword: "#7B3F8C"
|
||||
type: "#2A6FC1"
|
||||
string: "#2D8A52"
|
||||
number: "#B88A2A"
|
||||
comment: "#8A8A82"
|
||||
method: "#1E5D9E"
|
||||
punct: "#4A4A4A"
|
||||
@@ -0,0 +1,29 @@
|
||||
# Summer Night — ported from legacy clide v1.2.0.
|
||||
# Palette-only; the three-tier resolver fills semantic + surface from
|
||||
# defaults. Override sections land here as the token surface grows.
|
||||
|
||||
name: summer-night
|
||||
display_name: Summer Night
|
||||
dark: true
|
||||
|
||||
palette:
|
||||
# Accents (legacy names: primary/secondary/accent)
|
||||
primary: "#00a3d2" # cyan
|
||||
secondary: "#00a9b9" # teal
|
||||
accent: "#fa5f8b" # pink
|
||||
|
||||
# Backgrounds
|
||||
background: "#21262f"
|
||||
surface: "#393e48"
|
||||
panel: "#292e38"
|
||||
|
||||
# Text. `muted` is WCAG-AA-calibrated against `panel` — don't darken
|
||||
# without re-running the a11y/contrast suite.
|
||||
foreground: "#e2e8f5"
|
||||
muted: "#a6adbb"
|
||||
|
||||
# Status
|
||||
success: "#00ab9a"
|
||||
warning: "#d08447"
|
||||
error: "#f06c6f"
|
||||
info: "#00a3d2"
|
||||
@@ -0,0 +1,35 @@
|
||||
# terminal — near-black + amber, tmux feel
|
||||
# Source: docs/claude-design/tokens/terminal.yaml
|
||||
|
||||
name: terminal
|
||||
display_name: Terminal
|
||||
dark: true
|
||||
|
||||
palette:
|
||||
background: "#0A0A0A"
|
||||
panel: "#000000"
|
||||
surface: "#111111"
|
||||
muted: "#7A7A7A"
|
||||
foreground: "#E6E6E6"
|
||||
secondary: "#BDBDBD"
|
||||
primary: "#E0B050"
|
||||
accent: "#C29438"
|
||||
success: "#8FDC9B"
|
||||
warning: "#E0B050"
|
||||
error: "#E05050"
|
||||
info: "#A3C4FF"
|
||||
surfaceHi: "#181818"
|
||||
border: "#242424"
|
||||
borderHi: "#2E2E2E"
|
||||
textDim: "#7A7A7A"
|
||||
textMute: "#4A4A4A"
|
||||
accentSoft: "#21E0B050"
|
||||
|
||||
syntax:
|
||||
keyword: "#E05050"
|
||||
type: "#E0B050"
|
||||
string: "#8FDC9B"
|
||||
number: "#C792EA"
|
||||
comment: "#4A4A4A"
|
||||
method: "#A3C4FF"
|
||||
punct: "#7A7A7A"
|
||||
@@ -0,0 +1,325 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
/// Resolved surface tokens — the only thing widgets consume.
|
||||
///
|
||||
/// The token surface grows as features need more of it. Every token
|
||||
/// declared here must have a default resolution in
|
||||
/// [DefaultSurfaceMap] so legacy palette-only themes produce a complete
|
||||
/// SurfaceTokens without declaring the full surface.
|
||||
@immutable
|
||||
class SurfaceTokens {
|
||||
const SurfaceTokens({
|
||||
// global
|
||||
required this.globalForeground,
|
||||
required this.globalBackground,
|
||||
required this.globalBorder,
|
||||
required this.globalFocus,
|
||||
required this.globalTextMuted,
|
||||
// panel
|
||||
required this.panelBackground,
|
||||
required this.panelBorder,
|
||||
required this.panelActiveBorder,
|
||||
required this.panelHeader,
|
||||
required this.panelHeaderForeground,
|
||||
// sidebar
|
||||
required this.sidebarBackground,
|
||||
required this.sidebarForeground,
|
||||
required this.sidebarItemHover,
|
||||
required this.sidebarItemSelected,
|
||||
required this.sidebarSectionHeader,
|
||||
// statusbar
|
||||
required this.statusBarBackground,
|
||||
required this.statusBarForeground,
|
||||
required this.statusBarItemActiveBackground,
|
||||
required this.statusBarItemHoverBackground,
|
||||
// tabs
|
||||
required this.tabBarBackground,
|
||||
required this.tabActive,
|
||||
required this.tabInactive,
|
||||
required this.tabActiveForeground,
|
||||
required this.tabInactiveForeground,
|
||||
required this.tabActiveBorder,
|
||||
required this.tabCloseHover,
|
||||
// buttons
|
||||
required this.buttonBackground,
|
||||
required this.buttonForeground,
|
||||
required this.buttonHoverBackground,
|
||||
required this.buttonActiveBackground,
|
||||
required this.buttonBorder,
|
||||
// list items
|
||||
required this.listItemBackground,
|
||||
required this.listItemForeground,
|
||||
required this.listItemHoverBackground,
|
||||
required this.listItemSelectedBackground,
|
||||
required this.listItemSelectedForeground,
|
||||
// scrollbar
|
||||
required this.scrollbarSlider,
|
||||
required this.scrollbarSliderHover,
|
||||
required this.scrollbarTrack,
|
||||
// tooltip
|
||||
required this.tooltipBackground,
|
||||
required this.tooltipForeground,
|
||||
required this.tooltipBorder,
|
||||
// dropdown
|
||||
required this.dropdownBackground,
|
||||
required this.dropdownForeground,
|
||||
required this.dropdownBorder,
|
||||
// modal
|
||||
required this.modalOverlayBackground,
|
||||
required this.modalSurfaceBackground,
|
||||
required this.modalSurfaceBorder,
|
||||
// divider
|
||||
required this.dividerColor,
|
||||
// status
|
||||
required this.statusSuccess,
|
||||
required this.statusWarning,
|
||||
required this.statusError,
|
||||
required this.statusInfo,
|
||||
// syntax
|
||||
required this.syntaxKeyword,
|
||||
required this.syntaxType,
|
||||
required this.syntaxString,
|
||||
required this.syntaxNumber,
|
||||
required this.syntaxComment,
|
||||
required this.syntaxMethod,
|
||||
required this.syntaxPunct,
|
||||
required this.extensionTokens,
|
||||
});
|
||||
|
||||
final Color globalForeground;
|
||||
final Color globalBackground;
|
||||
final Color globalBorder;
|
||||
final Color globalFocus;
|
||||
final Color globalTextMuted;
|
||||
|
||||
final Color panelBackground;
|
||||
final Color panelBorder;
|
||||
final Color panelActiveBorder;
|
||||
final Color panelHeader;
|
||||
final Color panelHeaderForeground;
|
||||
|
||||
final Color sidebarBackground;
|
||||
final Color sidebarForeground;
|
||||
final Color sidebarItemHover;
|
||||
final Color sidebarItemSelected;
|
||||
final Color sidebarSectionHeader;
|
||||
|
||||
final Color statusBarBackground;
|
||||
final Color statusBarForeground;
|
||||
final Color statusBarItemActiveBackground;
|
||||
final Color statusBarItemHoverBackground;
|
||||
|
||||
final Color tabBarBackground;
|
||||
final Color tabActive;
|
||||
final Color tabInactive;
|
||||
final Color tabActiveForeground;
|
||||
final Color tabInactiveForeground;
|
||||
final Color tabActiveBorder;
|
||||
final Color tabCloseHover;
|
||||
|
||||
final Color buttonBackground;
|
||||
final Color buttonForeground;
|
||||
final Color buttonHoverBackground;
|
||||
final Color buttonActiveBackground;
|
||||
final Color buttonBorder;
|
||||
|
||||
final Color listItemBackground;
|
||||
final Color listItemForeground;
|
||||
final Color listItemHoverBackground;
|
||||
final Color listItemSelectedBackground;
|
||||
final Color listItemSelectedForeground;
|
||||
|
||||
final Color scrollbarSlider;
|
||||
final Color scrollbarSliderHover;
|
||||
final Color scrollbarTrack;
|
||||
|
||||
final Color tooltipBackground;
|
||||
final Color tooltipForeground;
|
||||
final Color tooltipBorder;
|
||||
|
||||
final Color dropdownBackground;
|
||||
final Color dropdownForeground;
|
||||
final Color dropdownBorder;
|
||||
|
||||
final Color modalOverlayBackground;
|
||||
final Color modalSurfaceBackground;
|
||||
final Color modalSurfaceBorder;
|
||||
|
||||
final Color dividerColor;
|
||||
|
||||
final Color statusSuccess;
|
||||
final Color statusWarning;
|
||||
final Color statusError;
|
||||
final Color statusInfo;
|
||||
|
||||
final Color syntaxKeyword;
|
||||
final Color syntaxType;
|
||||
final Color syntaxString;
|
||||
final Color syntaxNumber;
|
||||
final Color syntaxComment;
|
||||
final Color syntaxMethod;
|
||||
final Color syntaxPunct;
|
||||
|
||||
/// Extension-declared tokens keyed by their dotted path
|
||||
/// (e.g. `ext.sqlite.table.background`).
|
||||
final Map<String, Color> extensionTokens;
|
||||
}
|
||||
|
||||
/// Canonical surface-token keys as they appear in YAML.
|
||||
///
|
||||
/// Keeping them in one place lets the loader, the resolver, and the
|
||||
/// default map reference the same strings without typos.
|
||||
abstract class TokenKeys {
|
||||
// global
|
||||
static const globalForeground = 'global.foreground';
|
||||
static const globalBackground = 'global.background';
|
||||
static const globalBorder = 'global.border';
|
||||
static const globalFocus = 'global.focus';
|
||||
static const globalTextMuted = 'global.textMuted';
|
||||
|
||||
// panel
|
||||
static const panelBackground = 'panel.background';
|
||||
static const panelBorder = 'panel.border';
|
||||
static const panelActiveBorder = 'panel.activeBorder';
|
||||
static const panelHeader = 'panel.header';
|
||||
static const panelHeaderForeground = 'panel.headerForeground';
|
||||
|
||||
// sidebar
|
||||
static const sidebarBackground = 'sidebar.background';
|
||||
static const sidebarForeground = 'sidebar.foreground';
|
||||
static const sidebarItemHover = 'sidebar.itemHover';
|
||||
static const sidebarItemSelected = 'sidebar.itemSelected';
|
||||
static const sidebarSectionHeader = 'sidebar.sectionHeader';
|
||||
|
||||
// statusbar
|
||||
static const statusBarBackground = 'statusBar.background';
|
||||
static const statusBarForeground = 'statusBar.foreground';
|
||||
static const statusBarItemActiveBackground = 'statusBar.itemActiveBackground';
|
||||
static const statusBarItemHoverBackground = 'statusBar.itemHoverBackground';
|
||||
|
||||
// tabs
|
||||
static const tabBarBackground = 'tabBar.background';
|
||||
static const tabActive = 'tabBar.tabActive';
|
||||
static const tabInactive = 'tabBar.tabInactive';
|
||||
static const tabActiveForeground = 'tabBar.tabActiveForeground';
|
||||
static const tabInactiveForeground = 'tabBar.tabInactiveForeground';
|
||||
static const tabActiveBorder = 'tabBar.tabActiveBorder';
|
||||
static const tabCloseHover = 'tabBar.tabCloseHover';
|
||||
|
||||
// buttons
|
||||
static const buttonBackground = 'button.background';
|
||||
static const buttonForeground = 'button.foreground';
|
||||
static const buttonHoverBackground = 'button.hoverBackground';
|
||||
static const buttonActiveBackground = 'button.activeBackground';
|
||||
static const buttonBorder = 'button.border';
|
||||
|
||||
// list items
|
||||
static const listItemBackground = 'listItem.background';
|
||||
static const listItemForeground = 'listItem.foreground';
|
||||
static const listItemHoverBackground = 'listItem.hoverBackground';
|
||||
static const listItemSelectedBackground = 'listItem.selectedBackground';
|
||||
static const listItemSelectedForeground = 'listItem.selectedForeground';
|
||||
|
||||
// scrollbar
|
||||
static const scrollbarSlider = 'scrollbar.slider';
|
||||
static const scrollbarSliderHover = 'scrollbar.sliderHover';
|
||||
static const scrollbarTrack = 'scrollbar.track';
|
||||
|
||||
// tooltip
|
||||
static const tooltipBackground = 'tooltip.background';
|
||||
static const tooltipForeground = 'tooltip.foreground';
|
||||
static const tooltipBorder = 'tooltip.border';
|
||||
|
||||
// dropdown
|
||||
static const dropdownBackground = 'dropdown.background';
|
||||
static const dropdownForeground = 'dropdown.foreground';
|
||||
static const dropdownBorder = 'dropdown.border';
|
||||
|
||||
// modal
|
||||
static const modalOverlayBackground = 'modal.overlayBackground';
|
||||
static const modalSurfaceBackground = 'modal.surfaceBackground';
|
||||
static const modalSurfaceBorder = 'modal.surfaceBorder';
|
||||
|
||||
// divider
|
||||
static const dividerColor = 'divider.color';
|
||||
|
||||
// status
|
||||
static const statusSuccess = 'status.success';
|
||||
static const statusWarning = 'status.warning';
|
||||
static const statusError = 'status.error';
|
||||
static const statusInfo = 'status.info';
|
||||
|
||||
// syntax
|
||||
static const syntaxKeyword = 'syntax.keyword';
|
||||
static const syntaxType = 'syntax.type';
|
||||
static const syntaxString = 'syntax.string';
|
||||
static const syntaxNumber = 'syntax.number';
|
||||
static const syntaxComment = 'syntax.comment';
|
||||
static const syntaxMethod = 'syntax.method';
|
||||
static const syntaxPunct = 'syntax.punct';
|
||||
|
||||
static const all = <String>[
|
||||
globalForeground,
|
||||
globalBackground,
|
||||
globalBorder,
|
||||
globalFocus,
|
||||
globalTextMuted,
|
||||
panelBackground,
|
||||
panelBorder,
|
||||
panelActiveBorder,
|
||||
panelHeader,
|
||||
panelHeaderForeground,
|
||||
sidebarBackground,
|
||||
sidebarForeground,
|
||||
sidebarItemHover,
|
||||
sidebarItemSelected,
|
||||
sidebarSectionHeader,
|
||||
statusBarBackground,
|
||||
statusBarForeground,
|
||||
statusBarItemActiveBackground,
|
||||
statusBarItemHoverBackground,
|
||||
tabBarBackground,
|
||||
tabActive,
|
||||
tabInactive,
|
||||
tabActiveForeground,
|
||||
tabInactiveForeground,
|
||||
tabActiveBorder,
|
||||
tabCloseHover,
|
||||
buttonBackground,
|
||||
buttonForeground,
|
||||
buttonHoverBackground,
|
||||
buttonActiveBackground,
|
||||
buttonBorder,
|
||||
listItemBackground,
|
||||
listItemForeground,
|
||||
listItemHoverBackground,
|
||||
listItemSelectedBackground,
|
||||
listItemSelectedForeground,
|
||||
scrollbarSlider,
|
||||
scrollbarSliderHover,
|
||||
scrollbarTrack,
|
||||
tooltipBackground,
|
||||
tooltipForeground,
|
||||
tooltipBorder,
|
||||
dropdownBackground,
|
||||
dropdownForeground,
|
||||
dropdownBorder,
|
||||
modalOverlayBackground,
|
||||
modalSurfaceBackground,
|
||||
modalSurfaceBorder,
|
||||
dividerColor,
|
||||
statusSuccess,
|
||||
statusWarning,
|
||||
statusError,
|
||||
statusInfo,
|
||||
syntaxKeyword,
|
||||
syntaxType,
|
||||
syntaxString,
|
||||
syntaxNumber,
|
||||
syntaxComment,
|
||||
syntaxMethod,
|
||||
syntaxPunct,
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user