feat(settings): Inter as default UI font + UI font picker (T-460)

Vendor Inter (variable + italic, OFL) under assets/fonts/inter/ and make it
the default application UI face, replacing Josefin Sans (which stays bundled
as a selectable option). pubspec font family + licenses.yaml entry per D-42.

Settings → Appearance gains a UI-font select (Inter / Josefin Sans). The root
DefaultTextStyle reads app.ui.font (kUiFontSettingKey) over the default and
re-applies live on settings change, so a pick takes effect immediately. Bump
the default UI weight to w400 — Inter reads better at Regular than Josefin's
Light.

The monospace picker is deferred to T-471: clideMonoFamily is a const at ~38
call sites (not inherited), so a live mono picker needs the family routed
through context first.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-17 20:15:01 +02:00
co-authored by Claude Opus 4.8
parent b1057b41c8
commit 21d435d1f1
12 changed files with 219 additions and 17 deletions
@@ -2,6 +2,7 @@ import 'package:clide/builtin/theme_picker/src/theme_status_item.dart';
import 'package:clide/builtin/theme_picker/theme_picker.dart';
import 'package:clide/extension/extension.dart';
import 'package:clide/kernel/kernel.dart';
import 'package:clide/widgets/widgets.dart' show kUiFontSettingKey;
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
@@ -195,6 +196,13 @@ void main() {
expect(ext.contributions.whereType<SettingsControlContribution>().any((c) => c.customId == 'theme.picker'), isTrue);
});
test('Appearance includes a UI font picker (T-460)', () {
final cat = ThemePickerExtension().contributions.whereType<SettingsCategoryContribution>().firstWhere((c) => c.id == 'appearance').category;
final font = cat.sections.expand((s) => s.fields).firstWhere((f) => f.key == kUiFontSettingKey);
expect(font.kind, SettingsFieldKind.select);
expect(font.options.map((o) => o.value), containsAll(['Inter', 'JosefinSans']));
});
testWidgets('AppearanceThemeControl lists base themes and applies a pick (T-452)', (tester) async {
await tester.pumpWidget(harness(f, const SizedBox(width: 420, child: AppearanceThemeControl())));
expect(find.text('summer-night'), findsOneWidget);