The-Agency/lib/ui/widgets/common/footer_bar.dart

147 lines
3.8 KiB
Dart

import "package:flutter/widgets.dart" hide Tooltip;
import "package:shadcn_flutter/shadcn_flutter.dart" hide Row, Expanded;
import "../../providers/chat_provider.dart";
import "../../providers/cost_provider.dart";
import "../../providers/settings_provider.dart";
import "package:provider/provider.dart";
String _fmtTokens(int n) {
final s = n.toString();
final buf = StringBuffer();
for (var i = 0; i < s.length; i++) {
if (i > 0 && (s.length - i) % 3 == 0) buf.write(",");
buf.write(s[i]);
}
return buf.toString();
}
class FooterBar extends StatelessWidget {
const FooterBar({super.key});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final mutedFg = theme.colorScheme.mutedForeground;
final borderColor = theme.colorScheme.border;
final bg = theme.colorScheme.muted.scaleAlpha(0.3);
final costProvider = context.watch<CostProvider>();
final settingsProvider = context.watch<SettingsProvider>();
final chatProvider = context.watch<ChatProvider>();
final model = settingsProvider.settings.model ?? "unknown";
final costUsd = costProvider.getTotalCostUsd();
final cost = "\$${costUsd.toStringAsFixed(4)}";
final inputToks = costProvider.getTotalInputTokens();
final outputToks = costProvider.getTotalOutputTokens();
final isLoading = chatProvider.isLoading;
final contextTokens = chatProvider.contextTokens;
final textStyle = TextStyle(
fontFamily: "monospace",
fontSize: 11,
height: 1,
fontWeight: FontWeight.w600,
color: mutedFg,
);
Widget divider() => const Padding(
padding: EdgeInsets.symmetric(horizontal: 5),
child: SizedBox(height: 12, child: VerticalDivider(width: 1)),
);
Widget copyrightBlock() {
return Text(
"© 2026 IMBENJI.NET LTD - The Agency",
style: textStyle,
);
}
Widget statusBlock() {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
isLoading ? "running..." : "idle",
style: textStyle.copyWith(
color: isLoading
? theme.colorScheme.primary
: mutedFg,
),
),
divider(),
Text(model.split("/").last, style: textStyle),
],
);
}
Widget statsBlock() {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
if (contextTokens > 0) ...[
Text(_fmtTokens(contextTokens), style: textStyle),
Text(" tokens", style: textStyle),
divider(),
],
Tooltip(
tooltip: (_) => TooltipContainer(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
child: Text(
"In: $inputToks\nOut: $outputToks",
style: const TextStyle(
fontFamily: "monospace",
fontSize: 11,
height: 1.2,
),
),
),
child: Text(cost, style: textStyle),
),
],
);
}
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
width: double.infinity,
decoration: BoxDecoration(
color: bg,
border: Border(top: BorderSide(color: borderColor, width: 1)),
),
child: Row(
children: [
Expanded(child: Row(children: [copyrightBlock()])),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [statusBlock()],
),
),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [statsBlock()],
),
),
],
),
);
}
}