refactor(theme): route consumer reads through ClideSettings.theme (T-473)

Migrate ClideTheme.of(context) reads across the widget, feature, and shell
layers to the unified ClideSettings.theme.of(context) facade (D-101), so
theme/i18n/fonts/settings share one widget-facing entry. The facade delegates
straight to ClideTheme, so behaviour is unchanged — goldens are unmoved.

The low-level theme provider keeps its direct ClideTheme.of: the facade is
built on it, and the two kernel sites (ClideTheme's own definition + the
panels drag-resize widget) stay direct to avoid a widgets→kernel import cycle.
Dead controller.dart/kernel.dart imports left by the sweep removed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-17 21:47:15 +02:00
co-authored by Claude Opus 4.8
parent 1c79c9c4dc
commit 18cd945380
83 changed files with 174 additions and 187 deletions
+1 -2
View File
@@ -1,4 +1,3 @@
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/widgets/src/clide_icon.dart';
import 'package:clide/widgets/src/clide_settings.dart';
import 'package:clide/widgets/src/clide_tappable.dart';
@@ -27,7 +26,7 @@ class ClideAccordion extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
+2 -2
View File
@@ -1,4 +1,4 @@
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/widgets/src/clide_settings.dart';
import 'package:clide/widgets/src/clide_tappable.dart';
import 'package:clide/widgets/src/clide_text.dart';
import 'package:flutter/widgets.dart';
@@ -27,7 +27,7 @@ class ClideButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final enabled = onPressed != null;
return Semantics(
+1 -2
View File
@@ -1,7 +1,6 @@
import 'dart:convert';
import 'package:clide/kernel/src/syntax/tree_sitter_service.dart';
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/widgets/src/typography.dart';
import 'package:flutter/widgets.dart';
@@ -51,7 +50,7 @@ class _ClideCodeBlockState extends State<ClideCodeBlock> {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final style = TextStyle(
fontFamily: ClideSettings.fonts.monoOf(context),
fontFamilyFallback: clideMonoFamilyFallback,
+1 -2
View File
@@ -23,7 +23,6 @@
/// caller and passed in — this widget stays free of conversation semantics.
library;
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/kernel/src/theme/tokens.dart';
import 'package:clide/widgets/src/clide_card_metrics.dart';
import 'package:clide/widgets/src/clide_icon.dart';
@@ -88,7 +87,7 @@ class _ClideCollapserCardState extends State<ClideCollapserCard> {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
return Padding(
padding: const EdgeInsets.only(bottom: kClideCardGap),
child: _expanded ? _expandedFrame(tokens) : _tickerRow(tokens),
+2 -2
View File
@@ -1,4 +1,4 @@
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/widgets/src/clide_settings.dart';
import 'package:flutter/widgets.dart';
class ClideDivider extends StatelessWidget {
@@ -9,7 +9,7 @@ class ClideDivider extends StatelessWidget {
@override
Widget build(BuildContext context) {
final color = ClideTheme.of(context).surface.dividerColor;
final color = ClideSettings.theme.of(context).surface.dividerColor;
return Container(width: axis == Axis.vertical ? thickness : null, height: axis == Axis.horizontal ? thickness : null, color: color);
}
}
+2 -2
View File
@@ -1,8 +1,8 @@
import 'package:clide/widgets/src/clide_settings.dart';
import 'dart:async';
import 'package:clide/kernel/src/events/message_bus.dart';
import 'package:clide/kernel/src/facade.dart';
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/widgets/src/clide_icon.dart';
import 'package:clide/widgets/src/icons/phosphor.dart';
import 'package:clide/widgets/src/typography.dart';
@@ -115,7 +115,7 @@ class _ClideFilterBoxState extends State<ClideFilterBox> {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final hasText = _controller.text.isNotEmpty;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
+2 -2
View File
@@ -1,4 +1,4 @@
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/widgets/src/clide_settings.dart';
import 'package:flutter/widgets.dart';
/// Stateless painter producing a single-color icon. Every icon in
@@ -37,7 +37,7 @@ class ClideIcon extends StatelessWidget {
@override
Widget build(BuildContext context) {
final resolved = color ?? ClideTheme.of(context).surface.globalForeground;
final resolved = color ?? ClideSettings.theme.of(context).surface.globalForeground;
return SizedBox(
width: size,
height: size,
+2 -2
View File
@@ -1,4 +1,4 @@
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/widgets/src/clide_settings.dart';
import 'package:clide/widgets/src/clide_icon.dart';
import 'package:clide/widgets/src/clide_tappable.dart';
import 'package:flutter/widgets.dart';
@@ -56,7 +56,7 @@ class _RailButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
return Semantics(
button: true,
selected: active,
+2 -2
View File
@@ -10,7 +10,7 @@
/// kept here so the UX is ours.
library;
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/widgets/src/clide_settings.dart';
import 'package:clide/kernel/src/theme/tokens.dart';
import 'package:clide/widgets/src/clide_icon.dart';
import 'package:clide/widgets/src/clide_text.dart';
@@ -115,7 +115,7 @@ class _ClideLightboxState extends State<ClideLightbox> {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final size = MediaQuery.of(context).size;
return Focus(
focusNode: _focus,
+1 -2
View File
@@ -1,4 +1,3 @@
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/kernel/src/theme/tokens.dart';
import 'package:clide/widgets/src/clide_code_block.dart';
import 'package:clide/widgets/src/clide_divider.dart';
@@ -114,7 +113,7 @@ class ClideMarkdown extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final doc = md.Document(extensionSet: md.ExtensionSet.gitHubFlavored);
final nodes = doc.parseLines(source.split('\n'));
final hooks = ClideMarkdownHooks(
+2 -1
View File
@@ -8,6 +8,7 @@
/// quick-open) reuse identical key handling without [ClideMenu]'s rendering.
library;
import 'package:clide/widgets/src/clide_settings.dart';
import 'package:clide/kernel/kernel.dart';
import 'package:clide/widgets/src/clide_icon.dart';
import 'package:clide/widgets/src/clide_tappable.dart';
@@ -250,7 +251,7 @@ class _ClideMenuState extends State<ClideMenu> {
@override
Widget build(BuildContext context) {
final t = ClideTheme.of(context).surface;
final t = ClideSettings.theme.of(context).surface;
final col = Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
+2 -2
View File
@@ -104,7 +104,7 @@ class _ClidePaletteState extends State<ClidePalette> {
@override
Widget build(BuildContext context) {
final kernel = ClideKernel.of(context);
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
return ListenableBuilder(
listenable: kernel.palette,
builder: (ctx, _) {
@@ -205,7 +205,7 @@ class _PaletteItemState extends State<_PaletteItem> {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final selected = widget.highlighted;
return MouseRegion(
cursor: SystemMouseCursors.click,
+4 -4
View File
@@ -1,4 +1,4 @@
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/widgets/src/clide_settings.dart';
import 'package:flutter/widgets.dart';
import 'clide_divider.dart';
@@ -39,7 +39,7 @@ class ClidePaneChrome extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
return ColoredBox(
color: tokens.panelBackground,
child: Column(
@@ -59,7 +59,7 @@ class _CloseButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
return Semantics(
button: true,
label: 'Close pane',
@@ -90,7 +90,7 @@ class _Header extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
return Semantics(
container: true,
explicitChildNodes: true,
+1 -2
View File
@@ -1,4 +1,3 @@
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/kernel/src/theme/tokens.dart';
import 'package:clide/widgets/src/typography.dart';
import 'package:flutter/widgets.dart';
@@ -30,7 +29,7 @@ class ClidePtyView extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
return Semantics(
label: label,
textField: true,
+2 -2
View File
@@ -1,4 +1,4 @@
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/widgets/src/clide_settings.dart';
import 'package:flutter/widgets.dart';
/// Thin themed scrollbar. Tier-0 shell; more refined scrolling (velocity
@@ -12,7 +12,7 @@ class ClideScrollbar extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
return ScrollbarTheme(
slider: tokens.scrollbarSlider,
sliderHover: tokens.scrollbarSliderHover,
+2 -2
View File
@@ -1,6 +1,6 @@
import 'package:clide/widgets/src/clide_settings.dart';
import 'dart:math' as math;
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/widgets/src/clide_tappable.dart';
import 'package:flutter/widgets.dart';
@@ -16,7 +16,7 @@ class ClideSpine extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final borderSide = BorderSide(color: tokens.dividerColor);
return Semantics(
+2 -2
View File
@@ -9,9 +9,9 @@
/// with bounded pumps.
library;
import 'package:clide/widgets/src/clide_settings.dart';
import 'dart:math' as math;
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/widgets/src/clide_svg_view.dart';
import 'package:flutter/widgets.dart';
@@ -61,7 +61,7 @@ class _ClideSpinnerState extends State<ClideSpinner> with SingleTickerProviderSt
@override
Widget build(BuildContext context) {
final color = widget.color ?? ClideTheme.of(context).surface.globalForeground;
final color = widget.color ?? ClideSettings.theme.of(context).surface.globalForeground;
// Tint every stroke of the multi-colour logo to one colour, keeping alpha.
final mark = ColorFiltered(
colorFilter: ColorFilter.mode(color, BlendMode.srcIn),
+2 -2
View File
@@ -8,7 +8,7 @@
/// children leave the seam for a fuller transition later.
library;
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/widgets/src/clide_settings.dart';
import 'package:clide/widgets/src/clide_icon.dart';
import 'package:clide/widgets/src/clide_spinner.dart';
import 'package:clide/widgets/src/icons/check.dart';
@@ -45,7 +45,7 @@ class _ClideStatusIndicatorState extends State<ClideStatusIndicator> {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final key = ValueKey('${widget.status.name}-$_seq');
final (Widget glyph, String label) = switch (widget.status) {
ClideRunStatus.running => (ClideSpinner(size: widget.size, color: tokens.globalTextMuted, key: key), 'running'),
+2 -2
View File
@@ -1,4 +1,4 @@
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/widgets/src/clide_settings.dart';
import 'package:flutter/widgets.dart';
/// Themed container. Replaces Material's Card / Scaffold body surfaces
@@ -17,7 +17,7 @@ class ClideSurface extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
return Container(
width: width,
height: height,
+3 -3
View File
@@ -1,4 +1,4 @@
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/widgets/src/clide_settings.dart';
import 'package:clide/widgets/src/clide_icon.dart';
import 'package:clide/widgets/src/clide_tappable.dart';
import 'package:clide/widgets/src/clide_text.dart';
@@ -26,7 +26,7 @@ class ClideTabBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
return Semantics(
container: true,
label: semanticContainerLabel,
@@ -54,7 +54,7 @@ class _Tab extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final fg = active ? tokens.tabActiveForeground : tokens.tabInactiveForeground;
return Semantics(
+2 -2
View File
@@ -1,4 +1,4 @@
import 'package:clide/kernel/kernel.dart' show ClideTheme;
import 'package:clide/widgets/src/clide_settings.dart';
import 'package:clide/widgets/src/clide_tooltip.dart';
import 'package:flutter/widgets.dart';
@@ -68,7 +68,7 @@ class _ClideTappableState extends State<ClideTappable> {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final enabled = widget.onTap != null;
Widget child = MouseRegion(
cursor: enabled ? widget.cursor : SystemMouseCursors.forbidden,
+2 -2
View File
@@ -1,4 +1,4 @@
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/widgets/src/clide_settings.dart';
import 'package:clide/widgets/src/typography.dart';
import 'package:flutter/widgets.dart';
@@ -36,7 +36,7 @@ class ClideText extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final resolved = color ?? (muted ? tokens.globalTextMuted : tokens.globalForeground);
return Text(
data,
+2 -1
View File
@@ -1,3 +1,4 @@
import 'package:clide/widgets/src/clide_settings.dart';
import 'package:clide/kernel/kernel.dart';
import 'package:clide/widgets/src/clide_icon.dart';
import 'package:clide/widgets/src/clide_tappable.dart';
@@ -50,7 +51,7 @@ class _ClideToastState extends State<ClideToast> {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final style = _styleFor(widget.entry.severity, tokens);
return AnimatedSlide(
offset: _shown ? Offset.zero : const Offset(0.25, 0),
+2 -2
View File
@@ -1,4 +1,4 @@
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/widgets/src/clide_settings.dart';
import 'package:clide/widgets/src/clide_text.dart';
import 'package:flutter/widgets.dart';
@@ -29,7 +29,7 @@ class _ClideTooltipState extends State<ClideTooltip> {
_entry = OverlayEntry(
builder: (ctx) {
final tokens = ClideTheme.of(ctx).surface;
final tokens = ClideSettings.theme.of(ctx).surface;
final tooltip = Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
+1 -1
View File
@@ -133,7 +133,7 @@ class _ExLineOverlayState extends State<ExLineOverlay> {
@override
Widget build(BuildContext context) {
final kernel = ClideKernel.of(context);
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
return ListenableBuilder(
listenable: kernel.exLine,
builder: (ctx, _) {
+6 -6
View File
@@ -1,4 +1,4 @@
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/widgets/src/clide_settings.dart';
import 'package:clide/widgets/src/clide_icon.dart';
import 'package:clide/widgets/src/clide_tappable.dart';
import 'package:clide/widgets/src/clide_text.dart';
@@ -105,7 +105,7 @@ class _TabStrip<T> extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final entries = controller.entries;
final activeId = controller.activeId;
@@ -185,7 +185,7 @@ class _ReorderableTabState<T> extends State<_ReorderableTab<T>> {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final tabContent = _Tab<T>(entry: widget.entry, active: widget.active, onSelect: widget.onSelect, onClose: widget.onClose, tabHeight: widget.tabHeight);
@@ -238,7 +238,7 @@ class _DragFeedback extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
return Container(
height: tabHeight,
constraints: const BoxConstraints(minWidth: 96, maxWidth: 200),
@@ -271,7 +271,7 @@ class _TabState<T> extends State<_Tab<T>> {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final fg = widget.active ? tokens.tabActiveForeground : tokens.tabInactiveForeground;
// Active tabs sit on the elevated chrome surface (panelHeader);
// inactive tabs blend into the tab bar.
@@ -343,7 +343,7 @@ class _AddButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
return Semantics(
button: true,
label: 'New tab',
+2 -2
View File
@@ -150,7 +150,7 @@ class _QuickOpenOverlayState extends State<QuickOpenOverlay> {
@override
Widget build(BuildContext context) {
final kernel = ClideKernel.of(context);
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
return ListenableBuilder(
listenable: kernel.quickOpen,
builder: (ctx, _) {
@@ -262,7 +262,7 @@ class _QuickOpenItemState extends State<_QuickOpenItem> {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final selected = widget.highlighted;
// Split the basename from its directory so the filename reads first.
final slash = widget.path.lastIndexOf('/');