feat(studio): hardware-aware model recommendations + sidebar clock (v0.14.0)
System-AI editor now fetches the detected hardware tier and the hub's curated model database on open. Each model chip's colour + tooltip combines both: green star when curated quality is good/excellent AND min_hw_tier ≤ host; orange when quality is fine but the host is below min_hw_tier; warning for "basic" curated entries. Tooltip exposes the curator's note, parameter count, context window, and licence. Banner above the form shows "Detected: <summary> · curation reviewed <date>". Sidebar footer gains a small monospaced live clock between the theme toggle and the settings icon. Tooltip exposes full local ISO + UTC for cross-checking against audit-log timestamps. Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
a83a138259
commit
942c29a395
5 changed files with 395 additions and 35 deletions
|
|
@ -23,7 +23,7 @@ import 'widgets/widgets.dart';
|
|||
/// Studio's own build version. Bump on every UI commit so the
|
||||
/// running app self-identifies — visible in the sidebar header
|
||||
/// and quick-glance proof that you're seeing the current build.
|
||||
const String kStudioVersion = '0.13.1';
|
||||
const String kStudioVersion = '0.14.0';
|
||||
|
||||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
|
@ -334,13 +334,20 @@ class _Sidebar extends StatelessWidget {
|
|||
],
|
||||
),
|
||||
),
|
||||
// Footer: theme toggle + settings.
|
||||
// Footer: theme toggle · live wall-clock · settings.
|
||||
// Clock sits between the two icons so it reads as a
|
||||
// status line — gives the operator a wall-clock
|
||||
// reference to cross-check audit-log timestamps
|
||||
// without taking sidebar real estate from anything
|
||||
// load-bearing.
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: FaiSpace.md),
|
||||
child: Row(
|
||||
children: [
|
||||
_ThemeToggle(),
|
||||
const Spacer(),
|
||||
const Expanded(
|
||||
child: Center(child: _SidebarClock()),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.settings_outlined, size: 16),
|
||||
tooltip: 'Settings (⌘;)',
|
||||
|
|
@ -546,3 +553,79 @@ class _ThemeToggle extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Small ticking wall-clock for the sidebar. Aligns the on-screen
|
||||
/// time with the timestamps the audit log writes — operators
|
||||
/// glance here when an event timestamp looks off and want a
|
||||
/// reference point. Updates on the second; tooltip exposes the
|
||||
/// full ISO + UTC offset for unambiguous comparison.
|
||||
class _SidebarClock extends StatefulWidget {
|
||||
const _SidebarClock();
|
||||
|
||||
@override
|
||||
State<_SidebarClock> createState() => _SidebarClockState();
|
||||
}
|
||||
|
||||
class _SidebarClockState extends State<_SidebarClock> {
|
||||
late DateTime _now;
|
||||
Timer? _ticker;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_now = DateTime.now();
|
||||
_scheduleNextTick();
|
||||
}
|
||||
|
||||
void _scheduleNextTick() {
|
||||
// Align ticks on the second boundary so the seconds digit
|
||||
// changes when the wall-clock second changes, not 0–999 ms
|
||||
// late depending on when the widget mounted.
|
||||
final delay = Duration(milliseconds: 1000 - _now.millisecond);
|
||||
_ticker = Timer(delay, () {
|
||||
if (!mounted) return;
|
||||
setState(() => _now = DateTime.now());
|
||||
_ticker = Timer.periodic(const Duration(seconds: 1), (_) {
|
||||
if (!mounted) return;
|
||||
setState(() => _now = DateTime.now());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_ticker?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final hh = _now.hour.toString().padLeft(2, '0');
|
||||
final mm = _now.minute.toString().padLeft(2, '0');
|
||||
final ss = _now.second.toString().padLeft(2, '0');
|
||||
final yyyy = _now.year.toString().padLeft(4, '0');
|
||||
final mo = _now.month.toString().padLeft(2, '0');
|
||||
final dd = _now.day.toString().padLeft(2, '0');
|
||||
|
||||
final offsetMin = _now.timeZoneOffset.inMinutes;
|
||||
final sign = offsetMin >= 0 ? '+' : '-';
|
||||
final absMin = offsetMin.abs();
|
||||
final offH = (absMin ~/ 60).toString().padLeft(2, '0');
|
||||
final offM = (absMin % 60).toString().padLeft(2, '0');
|
||||
final tooltip =
|
||||
'Local: $yyyy-$mo-$dd $hh:$mm:$ss $sign$offH:$offM\n'
|
||||
'UTC: ${_now.toUtc().toIso8601String()}';
|
||||
|
||||
return Tooltip(
|
||||
message: tooltip,
|
||||
child: Text(
|
||||
'$hh:$mm:$ss',
|
||||
style: FaiTheme.mono(
|
||||
size: 11,
|
||||
color: theme.colorScheme.onSurfaceVariant.withValues(alpha: 0.85),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue