25ed765a03
6 Wochen uncommittete Arbeit (Voice-Channels, LiveKit-Manager, Settings-Modal u.v.m.) als ein WIP-Commit gesichert, damit nichts verloren geht und der Pi den aktuellen Stand klonen kann. Thematische Aufarbeitung: siehe ROADMAP M0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CPrAGBxBT6GfPXzeWQ4AXb
152 lines
5.6 KiB
Dart
152 lines
5.6 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
// Pyramid design tokens — matching mockup CSS variables exactly
|
|
class PyramidColors {
|
|
const PyramidColors._();
|
|
|
|
// Dark theme
|
|
static const bg0 = Color(0xFF0A0A0C);
|
|
static const bg1 = Color(0xFF111114);
|
|
static const bg2 = Color(0xFF17171C);
|
|
static const bg3 = Color(0xFF1E1E25);
|
|
static const bgHover = Color(0xFF232330);
|
|
static const bgActive = Color(0xFF2A2A38);
|
|
static const border = Color(0xFF25252E);
|
|
static const borderStrong = Color(0xFF32323D);
|
|
static const fg = Color(0xFFECECF0);
|
|
static const fgMuted = Color(0xFFA4A4B0);
|
|
static const fgDim = Color(0xFF6F6F7D);
|
|
|
|
// Light theme
|
|
static const bg0Light = Color(0xFFF5F4F0);
|
|
static const bg1Light = Color(0xFFFFFFFF);
|
|
static const bg2Light = Color(0xFFFAF9F5);
|
|
static const bg3Light = Color(0xFFF0EEEA);
|
|
static const bgHoverLight = Color(0xFFE9E7E1);
|
|
static const bgActiveLight = Color(0xFFDFDCD3);
|
|
static const borderLight = Color(0xFFE5E2DC);
|
|
static const borderStrongLight = Color(0xFFD3CFC6);
|
|
static const fgLight = Color(0xFF1A1A1F);
|
|
static const fgMutedLight = Color(0xFF54545E);
|
|
static const fgDimLight = Color(0xFF8A8A92);
|
|
|
|
// Semantic colors (same in both themes)
|
|
static const danger = Color(0xFFE5484D);
|
|
static const success = Color(0xFF30A46C);
|
|
static const online = Color(0xFF4ADE80);
|
|
static const away = Color(0xFFFACC15);
|
|
static const busy = Color(0xFFF87171);
|
|
|
|
// Accent presets — default: amber hsl(42 95% 58%)
|
|
static const accentAmber = Color(0xFFF5A614);
|
|
static const accentCoral = Color(0xFFED6951);
|
|
static const accentMint = Color(0xFF3DB88A);
|
|
static const accentViolet = Color(0xFFB67EEA);
|
|
static const accentCobalt = Color(0xFF5BA3F5);
|
|
static const accentLime = Color(0xFF8EC63F);
|
|
|
|
static const accentFg = Color(0xFF0B0B0D);
|
|
}
|
|
|
|
class PyramidTheme extends InheritedWidget {
|
|
final bool isDark;
|
|
final Color accent;
|
|
final double radius;
|
|
final double density;
|
|
final double motion;
|
|
|
|
const PyramidTheme({
|
|
super.key,
|
|
required super.child,
|
|
this.isDark = true,
|
|
this.accent = PyramidColors.accentAmber,
|
|
this.radius = 12,
|
|
this.density = 1,
|
|
this.motion = 1,
|
|
});
|
|
|
|
static PyramidTheme of(BuildContext context) {
|
|
return context.dependOnInheritedWidgetOfExactType<PyramidTheme>()!;
|
|
}
|
|
|
|
Color get bg0 => isDark ? PyramidColors.bg0 : PyramidColors.bg0Light;
|
|
Color get bg1 => isDark ? PyramidColors.bg1 : PyramidColors.bg1Light;
|
|
Color get bg2 => isDark ? PyramidColors.bg2 : PyramidColors.bg2Light;
|
|
Color get bg3 => isDark ? PyramidColors.bg3 : PyramidColors.bg3Light;
|
|
Color get bgHover => isDark ? PyramidColors.bgHover : PyramidColors.bgHoverLight;
|
|
Color get bgActive => isDark ? PyramidColors.bgActive : PyramidColors.bgActiveLight;
|
|
Color get border => isDark ? PyramidColors.border : PyramidColors.borderLight;
|
|
Color get borderStrong => isDark ? PyramidColors.borderStrong : PyramidColors.borderStrongLight;
|
|
Color get fg => isDark ? PyramidColors.fg : PyramidColors.fgLight;
|
|
Color get fgMuted => isDark ? PyramidColors.fgMuted : PyramidColors.fgMutedLight;
|
|
Color get fgDim => isDark ? PyramidColors.fgDim : PyramidColors.fgDimLight;
|
|
Color get danger => PyramidColors.danger;
|
|
Color get online => PyramidColors.online;
|
|
Color get away => PyramidColors.away;
|
|
Color get busy => PyramidColors.busy;
|
|
Color get accentSoft => accent.withAlpha(36); // ~14%
|
|
Color get accentGlow => accent.withAlpha(89); // ~35%
|
|
Color get accentFg => PyramidColors.accentFg;
|
|
|
|
double get rSm => radius * 0.6;
|
|
double get rBase => radius;
|
|
double get rLg => radius * 1.5;
|
|
double get rXl => radius * 2;
|
|
|
|
double get rowPadY => 10 * density;
|
|
double get rowPadX => 12 * density;
|
|
double get msgGap => 16 * density;
|
|
|
|
Duration get durationFast => const Duration(milliseconds: 150);
|
|
Duration get durationNormal => const Duration(milliseconds: 220);
|
|
Curve get springBounce => Curves.easeOutBack;
|
|
Curve get springSoft => Curves.easeOutCubic;
|
|
|
|
@override
|
|
bool updateShouldNotify(PyramidTheme oldWidget) =>
|
|
isDark != oldWidget.isDark ||
|
|
accent != oldWidget.accent ||
|
|
radius != oldWidget.radius ||
|
|
density != oldWidget.density ||
|
|
motion != oldWidget.motion;
|
|
}
|
|
|
|
// Inter ist als Asset gebündelt (pubspec `fonts:`) — kein Laufzeit-Download.
|
|
TextStyle _base(Color color) => TextStyle(fontFamily: 'Inter', color: color);
|
|
|
|
ThemeData buildPyramidThemeData(bool isDark) {
|
|
final fg = isDark ? PyramidColors.fg : PyramidColors.fgLight;
|
|
final bg1 = isDark ? PyramidColors.bg1 : PyramidColors.bg1Light;
|
|
final accent = PyramidColors.accentAmber;
|
|
|
|
return ThemeData(
|
|
useMaterial3: true,
|
|
fontFamily: 'Inter',
|
|
brightness: isDark ? Brightness.dark : Brightness.light,
|
|
scaffoldBackgroundColor: bg1,
|
|
colorScheme: ColorScheme(
|
|
brightness: isDark ? Brightness.dark : Brightness.light,
|
|
primary: accent,
|
|
onPrimary: PyramidColors.accentFg,
|
|
secondary: accent,
|
|
onSecondary: PyramidColors.accentFg,
|
|
error: PyramidColors.danger,
|
|
onError: Colors.white,
|
|
surface: bg1,
|
|
onSurface: fg,
|
|
),
|
|
textTheme: TextTheme(
|
|
bodyMedium: _base(fg).copyWith(fontSize: 14),
|
|
bodySmall: _base(fg).copyWith(fontSize: 12, color: isDark ? PyramidColors.fgMuted : PyramidColors.fgMutedLight),
|
|
labelSmall: _base(fg).copyWith(fontSize: 11, color: isDark ? PyramidColors.fgDim : PyramidColors.fgDimLight),
|
|
),
|
|
dividerTheme: DividerThemeData(
|
|
color: isDark ? PyramidColors.border : PyramidColors.borderLight,
|
|
thickness: 1,
|
|
space: 1,
|
|
),
|
|
splashFactory: NoSplash.splashFactory,
|
|
highlightColor: Colors.transparent,
|
|
);
|
|
}
|