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
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
import 'dart:math' as math;
|
||||
import 'dart:ui' as ui;
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
// 3D Pyramid logo — two faces + base outline + spine
|
||||
class PyramidLogo extends StatelessWidget {
|
||||
final double size;
|
||||
final Color color;
|
||||
const PyramidLogo({super.key, this.size = 36, this.color = Colors.white});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) =>
|
||||
CustomPaint(size: Size(size, size), painter: _PyramidLogoPainter(color: color));
|
||||
}
|
||||
|
||||
class _PyramidLogoPainter extends CustomPainter {
|
||||
final Color color;
|
||||
const _PyramidLogoPainter({required this.color});
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final w = size.width;
|
||||
final h = size.height;
|
||||
final sx = w / 48;
|
||||
final sy = h / 48;
|
||||
Offset p(double x, double y) => Offset(x * sx, y * sy);
|
||||
|
||||
final apex = p(24, 6);
|
||||
final baseL = p(6, 40);
|
||||
final baseR = p(42, 40);
|
||||
final baseM = p(24, 34);
|
||||
|
||||
// Left face — full opacity → 55%
|
||||
final leftPath = Path()..moveTo(apex.dx, apex.dy)..lineTo(baseL.dx, baseL.dy)..lineTo(baseM.dx, baseM.dy)..close();
|
||||
canvas.drawPath(leftPath, Paint()..shader = ui.Gradient.linear(
|
||||
Offset(0, apex.dy), Offset(0, baseL.dy),
|
||||
[color.withAlpha(255), color.withAlpha(140)],
|
||||
));
|
||||
|
||||
// Right face — 85% → 25%
|
||||
final rightPath = Path()..moveTo(apex.dx, apex.dy)..lineTo(baseR.dx, baseR.dy)..lineTo(baseM.dx, baseM.dy)..close();
|
||||
canvas.drawPath(rightPath, Paint()..shader = ui.Gradient.linear(
|
||||
Offset(apex.dx, apex.dy), Offset(baseR.dx, baseR.dy),
|
||||
[color.withAlpha(217), color.withAlpha(64)],
|
||||
));
|
||||
|
||||
// Base outline
|
||||
canvas.drawPath(
|
||||
Path()..moveTo(baseL.dx, baseL.dy)..lineTo(baseM.dx, baseM.dy)..lineTo(baseR.dx, baseR.dy),
|
||||
Paint()..color = color.withAlpha(102)..style = PaintingStyle.stroke
|
||||
..strokeWidth = math.max(sx, 0.7)..strokeJoin = StrokeJoin.round,
|
||||
);
|
||||
|
||||
// Spine
|
||||
canvas.drawLine(apex, baseM, Paint()..color = color.withAlpha(230)
|
||||
..strokeWidth = math.max(sx * 0.8, 0.6));
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(_PyramidLogoPainter old) => old.color != color;
|
||||
}
|
||||
|
||||
// Pyramid mark — stylised stacked triangles
|
||||
class PyramidMark extends StatelessWidget {
|
||||
final double size;
|
||||
final Color? color;
|
||||
|
||||
const PyramidMark({super.key, this.size = 30, this.color});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final c = color ?? Colors.white;
|
||||
return SizedBox(
|
||||
width: size,
|
||||
height: size,
|
||||
child: CustomPaint(painter: _PyramidPainter(c)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PyramidPainter extends CustomPainter {
|
||||
final Color color;
|
||||
_PyramidPainter(this.color);
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final paint = Paint()..color = color..style = PaintingStyle.fill;
|
||||
final w = size.width;
|
||||
final h = size.height;
|
||||
|
||||
// bottom big triangle
|
||||
final big = Path()
|
||||
..moveTo(w * 0.5, h * 0.1)
|
||||
..lineTo(w * 0.92, h * 0.88)
|
||||
..lineTo(w * 0.08, h * 0.88)
|
||||
..close();
|
||||
canvas.drawPath(big, paint);
|
||||
|
||||
// inner cutout (dark) to create layered look
|
||||
final cut = Paint()
|
||||
..color = Colors.black.withAlpha(80)
|
||||
..style = PaintingStyle.fill;
|
||||
final inner = Path()
|
||||
..moveTo(w * 0.5, h * 0.28)
|
||||
..lineTo(w * 0.74, h * 0.72)
|
||||
..lineTo(w * 0.26, h * 0.72)
|
||||
..close();
|
||||
canvas.drawPath(inner, cut);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(_PyramidPainter old) => old.color != color;
|
||||
}
|
||||
|
||||
// Pyramid space glyph (letter + triangle overlay)
|
||||
class PyramidGlyph extends StatelessWidget {
|
||||
final double size;
|
||||
final Color color;
|
||||
final String label;
|
||||
|
||||
const PyramidGlyph({
|
||||
super.key,
|
||||
this.size = 30,
|
||||
required this.color,
|
||||
required this.label,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: size,
|
||||
height: size,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
CustomPaint(
|
||||
size: Size(size, size),
|
||||
painter: _PyramidPainter(color),
|
||||
),
|
||||
Text(
|
||||
label.isNotEmpty ? label[0].toUpperCase() : '?',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: size * 0.38,
|
||||
fontWeight: FontWeight.bold,
|
||||
height: 1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Presence indicator dot
|
||||
class PresenceDot extends StatelessWidget {
|
||||
final String status; // online, away, busy, offline
|
||||
final double size;
|
||||
final Color borderColor;
|
||||
|
||||
const PresenceDot({
|
||||
super.key,
|
||||
required this.status,
|
||||
this.size = 10,
|
||||
this.borderColor = const Color(0xFF111114),
|
||||
});
|
||||
|
||||
Color get _color => switch (status) {
|
||||
'online' => const Color(0xFF4ADE80),
|
||||
'away' => const Color(0xFFFACC15),
|
||||
'busy' => const Color(0xFFF87171),
|
||||
_ => const Color(0xFF6F6F7D),
|
||||
};
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: size,
|
||||
height: size,
|
||||
decoration: BoxDecoration(
|
||||
color: _color,
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(color: borderColor, width: 2),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user