diff --git a/app/lib/kernel/src/theme/loader.dart b/app/lib/kernel/src/theme/loader.dart index 7ad6eb37..da301ded 100644 --- a/app/lib/kernel/src/theme/loader.dart +++ b/app/lib/kernel/src/theme/loader.dart @@ -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 = {}; + 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 = { + ...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, ); } diff --git a/app/lib/kernel/src/theme/resolver.dart b/app/lib/kernel/src/theme/resolver.dart index 2af9f4e7..854d0a39 100644 --- a/app/lib/kernel/src/theme/resolver.dart +++ b/app/lib/kernel/src/theme/resolver.dart @@ -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 _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', }; diff --git a/app/lib/kernel/src/theme/themes/clide.yaml b/app/lib/kernel/src/theme/themes/clide.yaml new file mode 100644 index 00000000..f3b94fe3 --- /dev/null +++ b/app/lib/kernel/src/theme/themes/clide.yaml @@ -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" diff --git a/app/lib/kernel/src/theme/themes/midnight.yaml b/app/lib/kernel/src/theme/themes/midnight.yaml new file mode 100644 index 00000000..e12fc89a --- /dev/null +++ b/app/lib/kernel/src/theme/themes/midnight.yaml @@ -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" diff --git a/app/lib/kernel/src/theme/themes/paper.yaml b/app/lib/kernel/src/theme/themes/paper.yaml new file mode 100644 index 00000000..6823ee79 --- /dev/null +++ b/app/lib/kernel/src/theme/themes/paper.yaml @@ -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" diff --git a/app/lib/kernel/src/theme/themes/terminal.yaml b/app/lib/kernel/src/theme/themes/terminal.yaml new file mode 100644 index 00000000..4fcfea59 --- /dev/null +++ b/app/lib/kernel/src/theme/themes/terminal.yaml @@ -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" diff --git a/app/lib/kernel/src/theme/tokens.dart b/app/lib/kernel/src/theme/tokens.dart index a18f5d3e..cd57a998 100644 --- a/app/lib/kernel/src/theme/tokens.dart +++ b/app/lib/kernel/src/theme/tokens.dart @@ -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 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 = [ globalForeground, globalBackground, @@ -289,5 +314,12 @@ abstract class TokenKeys { statusWarning, statusError, statusInfo, + syntaxKeyword, + syntaxType, + syntaxString, + syntaxNumber, + syntaxComment, + syntaxMethod, + syntaxPunct, ]; } diff --git a/app/lib/main.dart b/app/lib/main.dart index 94e2f874..edf32cea 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -105,7 +105,10 @@ Future _resolveAppDir() async { Future> _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 = []; for (final p in paths) { diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 6aec5085..7f26726e 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -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: ../ diff --git a/app/test/goldens/goldens/ci/clide_button.png b/app/test/goldens/goldens/ci/clide_button.png index dd315388..456cf3e1 100644 Binary files a/app/test/goldens/goldens/ci/clide_button.png and b/app/test/goldens/goldens/ci/clide_button.png differ diff --git a/app/test/goldens/goldens/ci/clide_tab_bar.png b/app/test/goldens/goldens/ci/clide_tab_bar.png index 36457abf..87fe4e02 100644 Binary files a/app/test/goldens/goldens/ci/clide_tab_bar.png and b/app/test/goldens/goldens/ci/clide_tab_bar.png differ diff --git a/app/test/goldens/goldens/linux/clide_button.png b/app/test/goldens/goldens/linux/clide_button.png index a489d817..06dabb88 100644 Binary files a/app/test/goldens/goldens/linux/clide_button.png and b/app/test/goldens/goldens/linux/clide_button.png differ diff --git a/app/test/goldens/goldens/linux/clide_tab_bar.png b/app/test/goldens/goldens/linux/clide_tab_bar.png index a2613874..6674a2a6 100644 Binary files a/app/test/goldens/goldens/linux/clide_tab_bar.png and b/app/test/goldens/goldens/linux/clide_tab_bar.png differ diff --git a/pubspec.yaml b/pubspec.yaml index 78f66a59..6d0c654f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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