add four design themes + syntax tokens to theme pipeline
test / unit + widget + golden + a11y (push) Failing after 39s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped

SurfaceTokens gains seven syntax-role fields (keyword, type,
string, number, comment, method, punct). Theme loader parses
the syntax: YAML block and injects values into the surface
override layer. Resolver defaults map syntax roles to semantic
colours so themes without explicit syntax blocks still compile.

Four new theme YAMLs (clide, midnight, paper, terminal) replace
summer-night. Palette keys mapped to the existing semantic layer;
syntax values pass through directly. Goldens regenerated for the
14→font-size change.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-04-22 16:29:41 +02:00
co-authored by Claude
parent de9a7b9745
commit 5869697297
14 changed files with 220 additions and 5 deletions
+26 -1
View File
@@ -46,10 +46,35 @@ class ThemeLoader {
}
final palette = _parsePalette(paletteYaml);
// Syntax colours inject into the surface override layer so
// TokenKeys.syntax* resolve directly from the theme YAML.
final syntaxYaml = doc['syntax'];
final syntaxSurface = <String, String>{};
if (syntaxYaml is Map) {
const syntaxMap = {
'keyword': 'syntax.keyword',
'type': 'syntax.type',
'string': 'syntax.string',
'number': 'syntax.number',
'comment': 'syntax.comment',
'method': 'syntax.method',
'punct': 'syntax.punct',
};
syntaxYaml.forEach((k, v) {
final key = syntaxMap['$k'];
if (key != null && v is String) syntaxSurface[key] = v;
});
}
final semantic = doc['semantic'];
final surface = doc['surface'];
final extension = doc['extension'];
final mergedSurface = <String, String>{
...syntaxSurface,
if (surface is Map) ..._parseRefMap(surface),
};
return ThemeDefinition(
name: name,
displayName: displayName,
@@ -57,7 +82,7 @@ class ThemeLoader {
palette: palette,
semanticOverride:
semantic is Map ? _parseSemantic(semantic, palette) : null,
surfaceOverride: surface is Map ? _parseRefMap(surface) : null,
surfaceOverride: mergedSurface.isNotEmpty ? mergedSurface : null,
extensionOverride: extension is Map ? _parseRefMap(extension) : null,
);
}
+14
View File
@@ -102,6 +102,13 @@ class ThemeResolver {
statusWarning: surface[TokenKeys.statusWarning]!,
statusError: surface[TokenKeys.statusError]!,
statusInfo: surface[TokenKeys.statusInfo]!,
syntaxKeyword: surface[TokenKeys.syntaxKeyword]!,
syntaxType: surface[TokenKeys.syntaxType]!,
syntaxString: surface[TokenKeys.syntaxString]!,
syntaxNumber: surface[TokenKeys.syntaxNumber]!,
syntaxComment: surface[TokenKeys.syntaxComment]!,
syntaxMethod: surface[TokenKeys.syntaxMethod]!,
syntaxPunct: surface[TokenKeys.syntaxPunct]!,
extensionTokens: extTokens,
);
}
@@ -235,4 +242,11 @@ const Map<String, String> _defaultSurfaceMap = {
TokenKeys.statusWarning: 'semantic.warning',
TokenKeys.statusError: 'semantic.error',
TokenKeys.statusInfo: 'semantic.info',
TokenKeys.syntaxKeyword: 'semantic.calltoaction',
TokenKeys.syntaxType: 'semantic.info',
TokenKeys.syntaxString: 'semantic.success',
TokenKeys.syntaxNumber: 'semantic.warning',
TokenKeys.syntaxComment: 'semantic.text_muted',
TokenKeys.syntaxMethod: 'semantic.focus',
TokenKeys.syntaxPunct: 'semantic.text_muted',
};
@@ -0,0 +1,36 @@
# clide — cool near-black + periwinkle (default)
# Source: docs/claude-design/tokens/clide.yaml
name: clide
display_name: Clide
dark: true
palette:
background: "#20202C"
panel: "#1A1A24"
surface: "#242838"
muted: "#78809C"
foreground: "#E6E8F2"
secondary: "#B1BBE3"
primary: "#78A0F8"
accent: "#6C90DC"
success: "#7DD3A8"
warning: "#E6C370"
error: "#E87D7D"
info: "#78A0F8"
# extended palette keys for the design's richer vocabulary
surfaceHi: "#2C3046"
border: "#343850"
borderHi: "#3C445C"
textDim: "#78809C"
textMute: "#545C84"
accentSoft: "#2178A0F8"
syntax:
keyword: "#C792EA"
type: "#78A0F8"
string: "#A8D99B"
number: "#E6C370"
comment: "#545C84"
method: "#82B1FF"
punct: "#78809C"
@@ -0,0 +1,35 @@
# midnight — VS Code-adjacent muted dark
# Source: docs/claude-design/tokens/midnight.yaml
name: midnight
display_name: Midnight
dark: true
palette:
background: "#1E1E1E"
panel: "#181818"
surface: "#252526"
muted: "#858585"
foreground: "#D4D4D4"
secondary: "#BBBBBB"
primary: "#569CD6"
accent: "#4785BD"
success: "#89D185"
warning: "#D7BA7D"
error: "#F48771"
info: "#569CD6"
surfaceHi: "#2D2D2E"
border: "#333333"
borderHi: "#3F3F3F"
textDim: "#858585"
textMute: "#6A6A6A"
accentSoft: "#21569CD6"
syntax:
keyword: "#C586C0"
type: "#4EC9B0"
string: "#CE9178"
number: "#B5CEA8"
comment: "#6A9955"
method: "#DCDCAA"
punct: "#858585"
@@ -0,0 +1,35 @@
# paper — drafting sheet, red-pencil accent, light
# Source: docs/claude-design/tokens/paper.yaml
name: paper
display_name: Paper
dark: false
palette:
background: "#F4F1EA"
panel: "#ECE7DB"
surface: "#FBF8F1"
muted: "#8A8A82"
foreground: "#1A1A1A"
secondary: "#4A4A4A"
primary: "#C14B2A"
accent: "#A03D20"
success: "#2D8A52"
warning: "#B88A2A"
error: "#B03A2A"
info: "#2A6FC1"
surfaceHi: "#ECE7DB"
border: "#1A1A1A"
borderHi: "#4A4A4A"
textDim: "#8A8A82"
textMute: "#A8A89E"
accentSoft: "#21C14B2A"
syntax:
keyword: "#7B3F8C"
type: "#2A6FC1"
string: "#2D8A52"
number: "#B88A2A"
comment: "#8A8A82"
method: "#1E5D9E"
punct: "#4A4A4A"
@@ -0,0 +1,35 @@
# terminal — near-black + amber, tmux feel
# Source: docs/claude-design/tokens/terminal.yaml
name: terminal
display_name: Terminal
dark: true
palette:
background: "#0A0A0A"
panel: "#000000"
surface: "#111111"
muted: "#7A7A7A"
foreground: "#E6E6E6"
secondary: "#BDBDBD"
primary: "#E0B050"
accent: "#C29438"
success: "#8FDC9B"
warning: "#E0B050"
error: "#E05050"
info: "#A3C4FF"
surfaceHi: "#181818"
border: "#242424"
borderHi: "#2E2E2E"
textDim: "#7A7A7A"
textMute: "#4A4A4A"
accentSoft: "#21E0B050"
syntax:
keyword: "#E05050"
type: "#E0B050"
string: "#8FDC9B"
number: "#C792EA"
comment: "#4A4A4A"
method: "#A3C4FF"
punct: "#7A7A7A"
+32
View File
@@ -77,6 +77,14 @@ class SurfaceTokens {
required this.statusWarning,
required this.statusError,
required this.statusInfo,
// syntax
required this.syntaxKeyword,
required this.syntaxType,
required this.syntaxString,
required this.syntaxNumber,
required this.syntaxComment,
required this.syntaxMethod,
required this.syntaxPunct,
required this.extensionTokens,
});
@@ -146,6 +154,14 @@ class SurfaceTokens {
final Color statusError;
final Color statusInfo;
final Color syntaxKeyword;
final Color syntaxType;
final Color syntaxString;
final Color syntaxNumber;
final Color syntaxComment;
final Color syntaxMethod;
final Color syntaxPunct;
/// Extension-declared tokens keyed by their dotted path
/// (e.g. `ext.sqlite.table.background`).
final Map<String, Color> extensionTokens;
@@ -235,6 +251,15 @@ abstract class TokenKeys {
static const statusError = 'status.error';
static const statusInfo = 'status.info';
// syntax
static const syntaxKeyword = 'syntax.keyword';
static const syntaxType = 'syntax.type';
static const syntaxString = 'syntax.string';
static const syntaxNumber = 'syntax.number';
static const syntaxComment = 'syntax.comment';
static const syntaxMethod = 'syntax.method';
static const syntaxPunct = 'syntax.punct';
static const all = <String>[
globalForeground,
globalBackground,
@@ -289,5 +314,12 @@ abstract class TokenKeys {
statusWarning,
statusError,
statusInfo,
syntaxKeyword,
syntaxType,
syntaxString,
syntaxNumber,
syntaxComment,
syntaxMethod,
syntaxPunct,
];
}
+4 -1
View File
@@ -105,7 +105,10 @@ Future<Directory> _resolveAppDir() async {
Future<List<ThemeDefinition>> _loadBundledThemes() async {
const loader = ThemeLoader();
const paths = [
'lib/kernel/src/theme/themes/summer-night.yaml',
'lib/kernel/src/theme/themes/clide.yaml',
'lib/kernel/src/theme/themes/midnight.yaml',
'lib/kernel/src/theme/themes/paper.yaml',
'lib/kernel/src/theme/themes/terminal.yaml',
];
final out = <ThemeDefinition>[];
for (final p in paths) {
+2 -2
View File
@@ -8,7 +8,7 @@ name: clide_app
description: >-
Flutter desktop IDE for Claude Code — renders the UI. Talks to the
Dart core (CLI + daemon) via IPC; all PTY/subprocess/filesystem work
lives in the daemon (ADR 0005).
lives in the daemon (D-005).
publish_to: none
version: 2.0.0-dev
repository: https://git.schweitz.net/jpmschweitzer/clide
@@ -23,7 +23,7 @@ dependencies:
# Dart core — shared types, IPC protocol, anything CLI + daemon + app
# all import. Kept Flutter-free so `dart compile exe` produces a lean
# static binary (see ADR 0005).
# static binary (see D-005).
clide:
path: ../
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 810 B

After

Width:  |  Height:  |  Size: 804 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

+1 -1
View File
@@ -6,7 +6,7 @@
# `dart compile exe` produces a lean static binary.
#
# The Flutter desktop UI lives under `app/` as a separate package that
# depends on this one via a path: dependency. See ADR 0005 for the
# depends on this one via a path: dependency. See D-005 for the
# layout rationale.
#
# Version is mirrored from project.yaml on every bump. project.yaml is