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
@@ -16,7 +16,6 @@ import 'package:clide/builtin/claude/src/image_thumbnail.dart';
import 'package:clide/builtin/claude/src/permission_mode_control.dart';
import 'package:clide/builtin/claude/src/running_indicator.dart';
import 'package:clide/builtin/claude/src/slash_commands.dart';
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/kernel/src/theme/tokens.dart';
import 'package:clide/widgets/widgets.dart';
import 'package:flutter/services.dart';
@@ -415,7 +414,7 @@ class _ClaudeComposerState extends State<ClaudeComposer> {
@override
Widget build(BuildContext context) {
final theme = ClideTheme.of(context).surface;
final theme = ClideSettings.theme.of(context).surface;
final hasText = _controller.text.isNotEmpty;
final fg = widget.enabled ? theme.globalForeground : theme.globalTextMuted;
+1 -1
View File
@@ -729,7 +729,7 @@ class _ClaudePaneState extends State<ClaudePane> {
@override
Widget build(BuildContext context) {
final title = widget.isPrimary ? 'claude — primary' : 'claude — secondary ${widget.secondaryIndex}';
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final Widget body;
if (_error != null) {
+1 -2
View File
@@ -8,7 +8,6 @@
library;
import 'package:clide/builtin/claude/src/task_list.dart';
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/kernel/src/theme/tokens.dart';
import 'package:clide/widgets/widgets.dart';
import 'package:flutter/widgets.dart';
@@ -30,7 +29,7 @@ class _ClaudeTaskDockState extends State<ClaudeTaskDock> {
final tasks = widget.tasks;
if (tasks.isEmpty) return const SizedBox.shrink(); // no chrome when empty
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final done = tasks.where((t) => t.status == TaskStatus.completed).length;
final inProgress = tasks.where((t) => t.status == TaskStatus.inProgress);
final current = inProgress.isEmpty ? null : inProgress.first.text;
@@ -175,7 +175,7 @@ class _ConversationCardState extends State<ConversationCard> {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final content = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -27,7 +27,6 @@ import 'package:clide/kernel/src/facade.dart';
import 'package:clide/kernel/src/keymap/intents.dart';
import 'package:clide/kernel/src/keymap/pane_key_nav.dart';
import 'package:clide/kernel/src/syntax/language_map.dart';
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/kernel/src/theme/tokens.dart';
import 'package:clide/src/terminal/terminal.dart';
import 'package:clide/widgets/widgets.dart';
@@ -293,7 +292,7 @@ class _ConversationViewState extends State<ConversationView> {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final allItems = widget.controller.items;
// T-263/T-264: resolve each sidechain run → owning Agent card before
// culling, so the run is suppressed up top and folded into / nested under
+2 -2
View File
@@ -25,7 +25,7 @@ void openImageLightbox(BuildContext context, String path) {
}
Widget _placeholder(BuildContext context, double size) {
final t = ClideTheme.of(context).surface;
final t = ClideSettings.theme.of(context).surface;
return Container(
width: size,
height: size,
@@ -46,7 +46,7 @@ class ImageThumbnail extends StatelessWidget {
@override
Widget build(BuildContext context) {
final t = ClideTheme.of(context).surface;
final t = ClideSettings.theme.of(context).surface;
return Semantics(
button: true,
label: 'Image $_fileName',
@@ -45,7 +45,7 @@ class ActivityTabView extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final latest = stats.latest;
final u = usage;
final sections = <MetaSection>[
@@ -41,7 +41,7 @@ class ConfigTabView extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final cfg = config;
if (cfg == null) {
return metaPlaceholder('Claude environment not loaded.');
@@ -299,7 +299,7 @@ class _SettingControlRowState extends State<SettingControlRow> {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
return Padding(
padding: const EdgeInsets.symmetric(vertical: kMetaRowPitch),
child: Row(
@@ -3,7 +3,6 @@
/// lib/widgets/ only when a second consumer appears.
library;
import 'package:clide/kernel/kernel.dart';
import 'package:clide/widgets/widgets.dart';
import 'package:flutter/widgets.dart';
@@ -29,7 +28,7 @@ class MetaIconButton extends StatelessWidget {
onTap: onTap,
builder: (ctx, hovered, _) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 3, vertical: 2),
child: ClideIcon(painter, size: 12, color: hovered ? ClideTheme.of(ctx).surface.globalForeground : color),
child: ClideIcon(painter, size: 12, color: hovered ? ClideSettings.theme.of(ctx).surface.globalForeground : color),
),
),
);
@@ -70,7 +70,7 @@ class _AgentRosterRowState extends State<AgentRosterRow> {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final managed = widget.orchestrator?.byMemberName(widget.member.name);
final color = teamColor(widget.member.color, fallback: tokens.globalForeground);
final st = widget.status;
@@ -4,7 +4,6 @@
library;
import 'package:clide/builtin/claude/src/meta_sidebar/models.dart';
import 'package:clide/kernel/kernel.dart';
import 'package:clide/widgets/widgets.dart';
import 'package:flutter/widgets.dart';
@@ -16,7 +15,7 @@ class SidebarTabStrip extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
@@ -18,7 +18,7 @@ class TaskRow extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final marker = switch (task.status) {
'done' => '✓',
'claimed' => '◈',
@@ -48,7 +48,7 @@ class TeamTabView extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
if (members.isEmpty) {
return metaPlaceholder('No team active.');
}
@@ -9,7 +9,6 @@
library;
import 'package:clide/builtin/claude/src/stream_json_session.dart';
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/kernel/src/theme/tokens.dart';
import 'package:clide/widgets/widgets.dart';
import 'package:flutter/services.dart';
@@ -130,7 +129,7 @@ class _ModelPickerCardState extends State<ModelPickerCard> {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
return Focus(
autofocus: true,
onKeyEvent: _onKey,
@@ -88,13 +88,13 @@ class _PermissionModeControlState extends State<PermissionModeControl> {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
return ClideAnchoredOverlay(
controller: _overlay,
side: ClideAnchorSide.above,
align: ClideAnchorAlign.end,
offset: const Offset(0, -6),
overlayBuilder: (ctx, ctrl) => ClideMenu(onClose: ctrl.close, minWidth: 180, entries: _entries(ClideTheme.of(ctx).surface)),
overlayBuilder: (ctx, ctrl) => ClideMenu(onClose: ctrl.close, minWidth: 180, entries: _entries(ClideSettings.theme.of(ctx).surface)),
anchor: ListenableBuilder(
listenable: _overlay,
builder: (ctx, _) {
+2 -3
View File
@@ -15,7 +15,6 @@ import 'dart:convert';
import 'package:clide/builtin/claude/src/stream_json_session.dart';
import 'package:clide/kernel/src/syntax/language_map.dart';
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/kernel/src/theme/tokens.dart';
import 'package:clide/widgets/widgets.dart';
import 'package:flutter/services.dart';
@@ -103,7 +102,7 @@ class _ToolPromptCardState extends State<ToolPromptCard> {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final (accent, label, children) = widget.prompt.isQuestion ? _question(tokens) : _permission(tokens);
// Autofocus the card so number keys pick a button/option on appear (T-240).
// _onKey self-guards via hasPrimaryFocus, so once the user clicks a note
@@ -607,7 +606,7 @@ class _NoteFieldState extends State<_NoteField> {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
return Container(
width: double.infinity,
decoration: BoxDecoration(
+1 -2
View File
@@ -5,7 +5,6 @@
library;
import 'package:clide/builtin/claude/src/session_index.dart';
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/kernel/src/theme/tokens.dart';
import 'package:clide/widgets/widgets.dart';
import 'package:flutter/services.dart';
@@ -60,7 +59,7 @@ class _SessionPickerDialogState extends State<SessionPickerDialog> {
@override
Widget build(BuildContext context) {
final theme = ClideTheme.of(context).surface;
final theme = ClideSettings.theme.of(context).surface;
return Focus(
autofocus: true,
onKeyEvent: _onKey,
+1 -2
View File
@@ -8,7 +8,6 @@ import 'dart:io';
import 'package:clide/builtin/claude/src/session_index.dart';
import 'package:clide/builtin/claude/src/session_picker.dart' show relativeTime;
import 'package:clide/kernel/src/theme/controller.dart';
import 'package:clide/kernel/src/theme/tokens.dart';
import 'package:clide/widgets/widgets.dart';
import 'package:flutter/services.dart';
@@ -55,7 +54,7 @@ class _SessionStorageDialogState extends State<SessionStorageDialog> {
@override
Widget build(BuildContext context) {
final theme = ClideTheme.of(context).surface;
final theme = ClideSettings.theme.of(context).surface;
return Focus(
autofocus: true,
onKeyEvent: _onKey,
@@ -122,7 +122,7 @@ class _TeamChatSidebarState extends State<TeamChatSidebar> {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final messages = widget.model.messages;
return Column(
@@ -291,7 +291,7 @@ class _TeamChatPaneState extends State<TeamChatPane> {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
final messages = widget.model.messages;
return Column(
+1 -1
View File
@@ -73,7 +73,7 @@ class _TeamPanelHostState extends State<TeamPanelHost> {
@override
Widget build(BuildContext context) {
if (_members.isEmpty) return widget.lead;
final tokens = ClideTheme.of(context).surface;
final tokens = ClideSettings.theme.of(context).surface;
return LayoutBuilder(
builder: (context, constraints) {