refactor(settings): ClideSettings — one live-preferences facade (D-101)

Introduce ClideSettings, a single widget-facing facade for the app's live
user preferences, namespaced by concern: ClideSettings.fonts.monoOf(context)
/ .fonts.uiOf(context), ClideSettings.theme.of(context),
ClideSettings.i18n.of(context). "Plumb once, use many."

Fonts are carried by a new root-provided ClideSettingsScope (resolved from the
font settings in root_shell, rebuilt on change); theme and i18n delegate to
their existing live providers (ClideTheme / the I18n service) so there's one
source of truth and their many consumers migrate incrementally rather than in
a big-bang. Reads outside a scope fall back to the bundled font defaults, so a
widget renders without a provider (isolated tests).

Migrate ~93 monospace-font call sites across 33 files from the clideMonoFamily
const to ClideSettings.fonts.monoOf(context) — pure refactor, identical family
when no override is set. 11 context-less helper sites (markdown static spans,
a few top-level/static builders) keep the const for now and are tracked in
T-472. Records D-101; updates the ui-design skill's font-family rule.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-17 20:57:16 +02:00
co-authored by Claude Opus 4.8
parent 8ad5c9ba6e
commit a7ea38acde
44 changed files with 452 additions and 206 deletions
@@ -148,16 +148,17 @@ class _TicketHeader extends StatelessWidget {
),
),
const SizedBox(width: 8),
ClideText(detail.id, fontSize: clideFontSmall, color: typeColor, fontFamily: clideMonoFamily),
ClideText(detail.id, fontSize: clideFontSmall, color: typeColor, fontFamily: ClideSettings.fonts.monoOf(context)),
const Spacer(),
if (detail.priority != null) ClideText(detail.priority!, fontSize: clideFontSmall, color: tokens.globalTextMuted, fontFamily: clideMonoFamily),
if (detail.priority != null)
ClideText(detail.priority!, fontSize: clideFontSmall, color: tokens.globalTextMuted, fontFamily: ClideSettings.fonts.monoOf(context)),
],
),
const SizedBox(height: 8),
ClideText(detail.title, fontSize: 15, fontWeight: FontWeight.w500),
if (detail.assignedTo != null) ...[
const SizedBox(height: 6),
ClideText('assigned: ${detail.assignedTo}', muted: true, fontSize: clideFontSmall, fontFamily: clideMonoFamily),
ClideText('assigned: ${detail.assignedTo}', muted: true, fontSize: clideFontSmall, fontFamily: ClideSettings.fonts.monoOf(context)),
],
],
),
@@ -206,7 +207,7 @@ class _StatusControls extends StatelessWidget {
border: Border.all(color: active ? tokens.statusInfo : tokens.panelBorder),
),
alignment: Alignment.center,
child: ClideText(_shortLabel(s), fontSize: clideFontBadge, color: color, fontFamily: clideMonoFamily),
child: ClideText(_shortLabel(s), fontSize: clideFontBadge, color: color, fontFamily: ClideSettings.fonts.monoOf(context)),
);
},
),
@@ -234,7 +235,7 @@ class _SectionLabel extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ClideText(label, fontSize: clideFontSmall, color: tokens.sidebarSectionHeader, fontFamily: clideMonoFamily);
return ClideText(label, fontSize: clideFontSmall, color: tokens.sidebarSectionHeader, fontFamily: ClideSettings.fonts.monoOf(context));
}
}
@@ -270,7 +271,7 @@ class _CompactCard extends StatelessWidget {
decoration: BoxDecoration(color: typeColor, shape: BoxShape.circle),
),
const SizedBox(width: 6),
ClideText(id, fontSize: clideFontSmall, color: tokens.globalTextMuted, fontFamily: clideMonoFamily),
ClideText(id, fontSize: clideFontSmall, color: tokens.globalTextMuted, fontFamily: ClideSettings.fonts.monoOf(context)),
const SizedBox(width: 8),
Expanded(child: ClideText(title, fontSize: clideFontSmall)),
],
@@ -320,9 +321,10 @@ class _DecisionRefCard extends StatelessWidget {
decoration: BoxDecoration(color: color, shape: BoxShape.circle),
),
const SizedBox(width: 6),
ClideText(id, fontSize: clideFontSmall, color: color, fontFamily: clideMonoFamily),
ClideText(id, fontSize: clideFontSmall, color: color, fontFamily: ClideSettings.fonts.monoOf(context)),
const Spacer(),
if (domain != null) ClideText(domain, fontSize: clideFontBadge, color: tokens.globalTextMuted, fontFamily: clideMonoFamily),
if (domain != null)
ClideText(domain, fontSize: clideFontBadge, color: tokens.globalTextMuted, fontFamily: ClideSettings.fonts.monoOf(context)),
],
),
const SizedBox(height: 3),
+15 -4
View File
@@ -314,7 +314,12 @@ class _TypeChip extends StatelessWidget {
decoration: BoxDecoration(color: dotColor, shape: BoxShape.circle),
),
const SizedBox(width: 6),
ClideText(label, fontSize: clideFontSmall, fontFamily: clideMonoFamily, color: active ? tokens.globalForeground : tokens.globalTextMuted),
ClideText(
label,
fontSize: clideFontSmall,
fontFamily: ClideSettings.fonts.monoOf(context),
color: active ? tokens.globalForeground : tokens.globalTextMuted,
),
],
),
),
@@ -386,7 +391,7 @@ class _TicketCard extends StatelessWidget {
entry.parentId!,
fontSize: clideFontSmall,
color: hovered ? tokens.globalForeground : tokens.globalTextMuted,
fontFamily: clideMonoFamily,
fontFamily: ClideSettings.fonts.monoOf(context),
),
),
),
@@ -402,7 +407,13 @@ class _TicketCard extends StatelessWidget {
),
),
const SizedBox(width: 6),
ClideText(entry.id, fontSize: clideFontSmall, color: tokens.globalForeground, fontFamily: clideMonoFamily, fontWeight: FontWeight.w600),
ClideText(
entry.id,
fontSize: clideFontSmall,
color: tokens.globalForeground,
fontFamily: ClideSettings.fonts.monoOf(context),
fontWeight: FontWeight.w600,
),
],
),
const SizedBox(height: 4),
@@ -480,7 +491,7 @@ class _StatusBadge extends StatelessWidget {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(color: color.withAlpha(0x30), borderRadius: BorderRadius.circular(3)),
child: ClideText(label, fontSize: clideFontBadge, color: color, fontFamily: clideMonoFamily),
child: ClideText(label, fontSize: clideFontBadge, color: color, fontFamily: ClideSettings.fonts.monoOf(context)),
);
}
}