Files
Bernd Steckmeister 25ed765a03 wip: Sicherungs-Commit aller Änderungen seit April + Arbeitsstruktur (CLAUDE.md, ROADMAP.md, PROGRESS.md, Autopilot)
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
2026-07-03 05:47:18 +02:00

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,
);
}