147 lines
3.8 KiB
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()],
|
|
),
|
|
),
|
|
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|