d6956395c8
Rename the whole project from CommGate to gsmnode and re-skin every surface onto the new signal-green + ink design system (Space Grotesk / IBM Plex Sans / JetBrains Mono, flat surfaces, two-arrow routing mark, lowercase gsm+node wordmark). - Web App + API panel: rewrite token layer, gn-* utilities, data-gsm-theme, new logo/favicon assets; both builds verified. - Phone App (Flutter): green theme, new mark/wordmark widgets, adaptive launcher icons; rename Android applicationId/package to app.gsmnode.phone; bump AGP to 8.6.0 and google_fonts to 8.1.0; debug APK build verified. - API Server (Go): panel routes, User-Agent, health service id, branding. - Home Assistant Plugin: rename integration domain sms_gateway to gsmnode (folder, DOMAIN, services, entity ids, classes); py_compile verified. - Update all READMEs; add .gitignore (excludes Design/, build artifacts). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
103 lines
3.1 KiB
Dart
103 lines
3.1 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
|
|
import '../theme.dart';
|
|
|
|
/// The gsmnode routing mark — two stacked arrows: the top (ink) routes right,
|
|
/// the bottom (green) routes left. Geometry matches assets/svg/mark-color.svg
|
|
/// (2.6px stroke on a 40px grid, rounded terminals).
|
|
///
|
|
/// When [color] is given the whole mark is drawn in that single color
|
|
/// (e.g. white on a filled tile); otherwise the top arrow takes the surface
|
|
/// ink color and the bottom arrow the signal green.
|
|
class GsmNodeMark extends StatelessWidget {
|
|
const GsmNodeMark({super.key, this.size = 32, this.color});
|
|
|
|
final double size;
|
|
final Color? color;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final ink = color ?? Theme.of(context).colorScheme.onSurface;
|
|
final green = color ?? GsmColors.green500;
|
|
return CustomPaint(
|
|
size: Size.square(size),
|
|
painter: _MarkPainter(ink: ink, green: green),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _MarkPainter extends CustomPainter {
|
|
_MarkPainter({required this.ink, required this.green});
|
|
|
|
final Color ink;
|
|
final Color green;
|
|
|
|
@override
|
|
void paint(Canvas canvas, Size size) {
|
|
final k = size.width / 40; // scale from the 40px design grid
|
|
Paint stroke(Color c) => Paint()
|
|
..color = c
|
|
..style = PaintingStyle.stroke
|
|
..strokeWidth = 2.6 * k
|
|
..strokeCap = StrokeCap.round
|
|
..strokeJoin = StrokeJoin.round;
|
|
|
|
final inkPaint = stroke(ink);
|
|
final greenPaint = stroke(green);
|
|
|
|
// Top arrow — routes right (ink).
|
|
canvas.drawLine(Offset(7 * k, 15 * k), Offset(31 * k, 15 * k), inkPaint);
|
|
canvas.drawPath(
|
|
Path()
|
|
..moveTo(26 * k, 10 * k)
|
|
..lineTo(32 * k, 15 * k)
|
|
..lineTo(26 * k, 20 * k),
|
|
inkPaint,
|
|
);
|
|
|
|
// Bottom arrow — routes left (green).
|
|
canvas.drawLine(Offset(9 * k, 25 * k), Offset(33 * k, 25 * k), greenPaint);
|
|
canvas.drawPath(
|
|
Path()
|
|
..moveTo(14 * k, 20 * k)
|
|
..lineTo(8 * k, 25 * k)
|
|
..lineTo(14 * k, 30 * k),
|
|
greenPaint,
|
|
);
|
|
}
|
|
|
|
@override
|
|
bool shouldRepaint(_MarkPainter old) => old.ink != ink || old.green != green;
|
|
}
|
|
|
|
/// The gsmnode wordmark: lowercase `gsm` in ink + `node` in green, monospace.
|
|
/// On dark surfaces `node` lifts to the on-dark green. Pass [color] to render
|
|
/// the whole wordmark in a single color.
|
|
class GsmNodeWordmark extends StatelessWidget {
|
|
const GsmNodeWordmark({super.key, this.size = 24, this.color});
|
|
|
|
final double size;
|
|
final Color? color;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
|
final ink = color ?? Theme.of(context).colorScheme.onSurface;
|
|
final green =
|
|
color ?? (isDark ? GsmColors.greenOnDark : GsmColors.green500);
|
|
return Text.rich(
|
|
TextSpan(children: [
|
|
TextSpan(text: 'gsm', style: TextStyle(color: ink)),
|
|
TextSpan(text: 'node', style: TextStyle(color: green)),
|
|
]),
|
|
style: GoogleFonts.jetBrainsMono(
|
|
fontSize: size,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: -0.01 * size,
|
|
height: 1,
|
|
),
|
|
);
|
|
}
|
|
}
|