// The link to the Orin at a glance: the state chip, and the details behind it. import 'package:flutter/material.dart'; import '../link/boundary.dart'; import '../link/net.dart'; import '../theme/palette.dart'; /// CONNECTED / CONNECTING / DOWN in good / caution / alarm. class LinkChip extends StatelessWidget { const LinkChip({super.key, required this.boundary}); final Boundary boundary; @override Widget build(BuildContext context) { final p = AppTheme.of(context); final (color, label) = switch (boundary.state) { LinkState.up => (p.good, 'CONNECTED'), LinkState.connecting => (p.caution, 'CONNECTING'), LinkState.down => (p.alarm, 'DOWN'), }; return Chip( label: Text( label, style: TextStyle(color: color, fontWeight: FontWeight.w600), ), backgroundColor: color.withValues(alpha: 0.3), side: BorderSide.none, visualDensity: VisualDensity.compact, ); } } /// Address, gateway, last-message age, clock offset, USB link. Fixed-width numbers, /// padded with figure spaces (U+2007, digit-wide): nothing reflows as a value gains a digit. class LinkDetails extends StatelessWidget { const LinkDetails({super.key, required this.boundary, required this.usb}); final Boundary boundary; final UsbLink usb; @override Widget build(BuildContext context) { final b = boundary; String pad(String v, int n) => v.padLeft(n, ' '); final age = b.lastMessage == null ? pad('—', 4) : pad( '${DateTime.now().difference(b.lastMessage!).inMilliseconds.clamp(0, 9999)}', 4, ); final offset = b.clockOffset == null ? pad('—', 6) : pad((b.clockOffset!.inMilliseconds / 1000).toStringAsFixed(1), 6); const fixed = TextStyle(fontFeatures: [FontFeature.tabularFigures()]); return Wrap( spacing: 16, runSpacing: 4, crossAxisAlignment: WrapCrossAlignment.center, children: [ Text(b.url, style: const TextStyle(fontFamily: 'monospace')), Text(b.detail), Text('last msg $age ms', style: fixed), Text('Orin−tablet clock $offset s', style: fixed), if (usb.supported) Text( 'USB link ${usb.up ? 'up' : 'absent'}${usb.bound ? ', bound' : ''}', ), ], ); } }