From fc837be107e33f5debe7fecc5c597a8dab69d83b Mon Sep 17 00:00:00 2001 From: Mirza Arnaut Date: Sat, 18 Oct 2025 03:16:46 +0200 Subject: [PATCH] opencode: add themes option Add themes option to configure custom themes through nix. Define a submodule for the settings option, to give the `theme` setting a description. --- modules/programs/opencode.nix | 25 +- tests/modules/programs/opencode/default.nix | 1 + .../programs/opencode/empty-themes.nix | 9 + tests/modules/programs/opencode/my-theme.json | 223 +++++++++++++++++ tests/modules/programs/opencode/themes.nix | 232 ++++++++++++++++++ 5 files changed, 488 insertions(+), 2 deletions(-) create mode 100644 tests/modules/programs/opencode/empty-themes.nix create mode 100644 tests/modules/programs/opencode/my-theme.json create mode 100644 tests/modules/programs/opencode/themes.nix diff --git a/modules/programs/opencode.nix b/modules/programs/opencode.nix index 040743ee0..47af7fd4d 100644 --- a/modules/programs/opencode.nix +++ b/modules/programs/opencode.nix @@ -114,7 +114,7 @@ in description = '' Custom agents for opencode. The attribute name becomes the agent filename, and the value is either: - - Inline content as a string + - Inline content as a string - A path to a file containing the agent content Agents are stored in ~/.config/opencode/agent/ directory. ''; @@ -137,6 +137,16 @@ in ''; }; + themes = mkOption { + inherit (jsonFormat) type; + default = { }; + description = '' + Custom themes for opencode. The attribute name becomes the theme filename. + Themes are stored in {file}`$XDG_CONFIG_HOME/opencode/themes/` directory. + Set `programs.opencode.settings.theme` to enable the custom theme. + See for the documentation. + ''; + }; }; config = mkIf cfg.enable { @@ -167,6 +177,17 @@ in lib.nameValuePair "opencode/agent/${name}.md" ( if lib.isPath content then { source = content; } else { text = content; } ) - ) cfg.agents; + ) cfg.agents + // lib.mapAttrs' ( + name: content: + lib.nameValuePair "opencode/themes/${name}.json" { + source = jsonFormat.generate "opencode-${name}.json" ( + { + "$schema" = "https://opencode.ai/theme.json"; + } + // content + ); + } + ) cfg.themes; }; } diff --git a/tests/modules/programs/opencode/default.nix b/tests/modules/programs/opencode/default.nix index 7fa1eaadd..22188c670 100644 --- a/tests/modules/programs/opencode/default.nix +++ b/tests/modules/programs/opencode/default.nix @@ -8,4 +8,5 @@ opencode-agents-path = ./agents-path.nix; opencode-commands-path = ./commands-path.nix; opencode-mixed-content = ./mixed-content.nix; + opencode-themes = ./themes.nix; } diff --git a/tests/modules/programs/opencode/empty-themes.nix b/tests/modules/programs/opencode/empty-themes.nix new file mode 100644 index 000000000..b436828af --- /dev/null +++ b/tests/modules/programs/opencode/empty-themes.nix @@ -0,0 +1,9 @@ +{ + programs.opencode = { + enable = true; + themes = { }; + }; + nmt.script = '' + assertPathNotExists home-files/.config/opencode/themes + ''; +} diff --git a/tests/modules/programs/opencode/my-theme.json b/tests/modules/programs/opencode/my-theme.json new file mode 100644 index 000000000..d123c556e --- /dev/null +++ b/tests/modules/programs/opencode/my-theme.json @@ -0,0 +1,223 @@ +{ + "$schema": "https://opencode.ai/theme.json", + "defs": { + "nord0": "#2E3440", + "nord1": "#3B4252", + "nord10": "#5E81AC", + "nord11": "#BF616A", + "nord12": "#D08770", + "nord13": "#EBCB8B", + "nord14": "#A3BE8C", + "nord15": "#B48EAD", + "nord2": "#434C5E", + "nord3": "#4C566A", + "nord4": "#D8DEE9", + "nord5": "#E5E9F0", + "nord6": "#ECEFF4", + "nord7": "#8FBCBB", + "nord8": "#88C0D0", + "nord9": "#81A1C1" + }, + "theme": { + "accent": { + "dark": "nord7", + "light": "nord7" + }, + "background": { + "dark": "nord0", + "light": "nord6" + }, + "backgroundElement": { + "dark": "nord1", + "light": "nord4" + }, + "backgroundPanel": { + "dark": "nord1", + "light": "nord5" + }, + "border": { + "dark": "nord2", + "light": "nord3" + }, + "borderActive": { + "dark": "nord3", + "light": "nord2" + }, + "borderSubtle": { + "dark": "nord2", + "light": "nord3" + }, + "diffAdded": { + "dark": "nord14", + "light": "nord14" + }, + "diffAddedBg": { + "dark": "#3B4252", + "light": "#E5E9F0" + }, + "diffAddedLineNumberBg": { + "dark": "#3B4252", + "light": "#E5E9F0" + }, + "diffContext": { + "dark": "nord3", + "light": "nord3" + }, + "diffContextBg": { + "dark": "nord1", + "light": "nord5" + }, + "diffHighlightAdded": { + "dark": "nord14", + "light": "nord14" + }, + "diffHighlightRemoved": { + "dark": "nord11", + "light": "nord11" + }, + "diffHunkHeader": { + "dark": "nord3", + "light": "nord3" + }, + "diffLineNumber": { + "dark": "nord2", + "light": "nord4" + }, + "diffRemoved": { + "dark": "nord11", + "light": "nord11" + }, + "diffRemovedBg": { + "dark": "#3B4252", + "light": "#E5E9F0" + }, + "diffRemovedLineNumberBg": { + "dark": "#3B4252", + "light": "#E5E9F0" + }, + "error": { + "dark": "nord11", + "light": "nord11" + }, + "info": { + "dark": "nord8", + "light": "nord10" + }, + "markdownBlockQuote": { + "dark": "nord3", + "light": "nord3" + }, + "markdownCode": { + "dark": "nord14", + "light": "nord14" + }, + "markdownCodeBlock": { + "dark": "nord4", + "light": "nord0" + }, + "markdownEmph": { + "dark": "nord12", + "light": "nord12" + }, + "markdownHeading": { + "dark": "nord8", + "light": "nord10" + }, + "markdownHorizontalRule": { + "dark": "nord3", + "light": "nord3" + }, + "markdownImage": { + "dark": "nord9", + "light": "nord9" + }, + "markdownImageText": { + "dark": "nord7", + "light": "nord7" + }, + "markdownLink": { + "dark": "nord9", + "light": "nord9" + }, + "markdownLinkText": { + "dark": "nord7", + "light": "nord7" + }, + "markdownListEnumeration": { + "dark": "nord7", + "light": "nord7" + }, + "markdownListItem": { + "dark": "nord8", + "light": "nord10" + }, + "markdownStrong": { + "dark": "nord13", + "light": "nord13" + }, + "markdownText": { + "dark": "nord4", + "light": "nord0" + }, + "primary": { + "dark": "nord8", + "light": "nord10" + }, + "secondary": { + "dark": "nord9", + "light": "nord9" + }, + "success": { + "dark": "nord14", + "light": "nord14" + }, + "syntaxComment": { + "dark": "nord3", + "light": "nord3" + }, + "syntaxFunction": { + "dark": "nord8", + "light": "nord8" + }, + "syntaxKeyword": { + "dark": "nord9", + "light": "nord9" + }, + "syntaxNumber": { + "dark": "nord15", + "light": "nord15" + }, + "syntaxOperator": { + "dark": "nord9", + "light": "nord9" + }, + "syntaxPunctuation": { + "dark": "nord4", + "light": "nord0" + }, + "syntaxString": { + "dark": "nord14", + "light": "nord14" + }, + "syntaxType": { + "dark": "nord7", + "light": "nord7" + }, + "syntaxVariable": { + "dark": "nord7", + "light": "nord7" + }, + "text": { + "dark": "nord4", + "light": "nord0" + }, + "textMuted": { + "dark": "nord3", + "light": "nord1" + }, + "warning": { + "dark": "nord12", + "light": "nord12" + } + } +} diff --git a/tests/modules/programs/opencode/themes.nix b/tests/modules/programs/opencode/themes.nix new file mode 100644 index 000000000..494943cd7 --- /dev/null +++ b/tests/modules/programs/opencode/themes.nix @@ -0,0 +1,232 @@ +{ + nmt.script = '' + assertFileExists home-files/.config/opencode/themes/my-theme.json + assertFileContent home-files/.config/opencode/themes/my-theme.json \ + ${./my-theme.json} + ''; + programs.opencode = { + enable = true; + themes.my-theme = { + defs = { + nord0 = "#2E3440"; + nord1 = "#3B4252"; + nord10 = "#5E81AC"; + nord11 = "#BF616A"; + nord12 = "#D08770"; + nord13 = "#EBCB8B"; + nord14 = "#A3BE8C"; + nord15 = "#B48EAD"; + nord2 = "#434C5E"; + nord3 = "#4C566A"; + nord4 = "#D8DEE9"; + nord5 = "#E5E9F0"; + nord6 = "#ECEFF4"; + nord7 = "#8FBCBB"; + nord8 = "#88C0D0"; + nord9 = "#81A1C1"; + }; + theme = { + accent = { + dark = "nord7"; + light = "nord7"; + }; + background = { + dark = "nord0"; + light = "nord6"; + }; + backgroundElement = { + dark = "nord1"; + light = "nord4"; + }; + backgroundPanel = { + dark = "nord1"; + light = "nord5"; + }; + border = { + dark = "nord2"; + light = "nord3"; + }; + borderActive = { + dark = "nord3"; + light = "nord2"; + }; + borderSubtle = { + dark = "nord2"; + light = "nord3"; + }; + diffAdded = { + dark = "nord14"; + light = "nord14"; + }; + diffAddedBg = { + dark = "#3B4252"; + light = "#E5E9F0"; + }; + diffAddedLineNumberBg = { + dark = "#3B4252"; + light = "#E5E9F0"; + }; + diffContext = { + dark = "nord3"; + light = "nord3"; + }; + diffContextBg = { + dark = "nord1"; + light = "nord5"; + }; + diffHighlightAdded = { + dark = "nord14"; + light = "nord14"; + }; + diffHighlightRemoved = { + dark = "nord11"; + light = "nord11"; + }; + diffHunkHeader = { + dark = "nord3"; + light = "nord3"; + }; + diffLineNumber = { + dark = "nord2"; + light = "nord4"; + }; + diffRemoved = { + dark = "nord11"; + light = "nord11"; + }; + diffRemovedBg = { + dark = "#3B4252"; + light = "#E5E9F0"; + }; + diffRemovedLineNumberBg = { + dark = "#3B4252"; + light = "#E5E9F0"; + }; + error = { + dark = "nord11"; + light = "nord11"; + }; + info = { + dark = "nord8"; + light = "nord10"; + }; + markdownBlockQuote = { + dark = "nord3"; + light = "nord3"; + }; + markdownCode = { + dark = "nord14"; + light = "nord14"; + }; + markdownCodeBlock = { + dark = "nord4"; + light = "nord0"; + }; + markdownEmph = { + dark = "nord12"; + light = "nord12"; + }; + markdownHeading = { + dark = "nord8"; + light = "nord10"; + }; + markdownHorizontalRule = { + dark = "nord3"; + light = "nord3"; + }; + markdownImage = { + dark = "nord9"; + light = "nord9"; + }; + markdownImageText = { + dark = "nord7"; + light = "nord7"; + }; + markdownLink = { + dark = "nord9"; + light = "nord9"; + }; + markdownLinkText = { + dark = "nord7"; + light = "nord7"; + }; + markdownListEnumeration = { + dark = "nord7"; + light = "nord7"; + }; + markdownListItem = { + dark = "nord8"; + light = "nord10"; + }; + markdownStrong = { + dark = "nord13"; + light = "nord13"; + }; + markdownText = { + dark = "nord4"; + light = "nord0"; + }; + primary = { + dark = "nord8"; + light = "nord10"; + }; + secondary = { + dark = "nord9"; + light = "nord9"; + }; + success = { + dark = "nord14"; + light = "nord14"; + }; + syntaxComment = { + dark = "nord3"; + light = "nord3"; + }; + syntaxFunction = { + dark = "nord8"; + light = "nord8"; + }; + syntaxKeyword = { + dark = "nord9"; + light = "nord9"; + }; + syntaxNumber = { + dark = "nord15"; + light = "nord15"; + }; + syntaxOperator = { + dark = "nord9"; + light = "nord9"; + }; + syntaxPunctuation = { + dark = "nord4"; + light = "nord0"; + }; + syntaxString = { + dark = "nord14"; + light = "nord14"; + }; + syntaxType = { + dark = "nord7"; + light = "nord7"; + }; + syntaxVariable = { + dark = "nord7"; + light = "nord7"; + }; + text = { + dark = "nord4"; + light = "nord0"; + }; + textMuted = { + dark = "nord3"; + light = "nord1"; + }; + warning = { + dark = "nord12"; + light = "nord12"; + }; + }; + }; + }; +}