This is an automated email from the ASF dual-hosted git repository.

apkhmv pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new c6a3e7df0ee IGNITE-27726 CLI: Add color theme configuration for 
improved readability (#7540)
c6a3e7df0ee is described below

commit c6a3e7df0ee0adbfb3b7dc9bc26c39f6ffc3f258
Author: Aleksandr Pakhomov <[email protected]>
AuthorDate: Mon Feb 9 15:06:03 2026 +0300

    IGNITE-27726 CLI: Add color theme configuration for improved readability 
(#7540)
---
 .../java/org/apache/ignite/internal/cli/Main.java  |  16 ++
 .../SqlAttributedStringHighlighter.java            |  53 +++--
 .../ignite/internal/cli/config/CliConfigKeys.java  |   7 +-
 .../internal/cli/core/style/AnsiStringSupport.java |  97 +++++++--
 .../internal/cli/core/style/ColorScheme.java       | 229 +++++++++++++++++++++
 .../cli/core/style/ColorSchemeProvider.java        |  30 +++
 .../cli/core/style/DefaultColorSchemeProvider.java |  43 ++++
 .../cli/core/style/AnsiStringSupportTest.java      |  95 +++++++++
 .../internal/cli/core/style/ColorSchemeTest.java   |  98 +++++++++
 9 files changed, 633 insertions(+), 35 deletions(-)

diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/Main.java 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/Main.java
index 5625b842840..2461a699b17 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/Main.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/Main.java
@@ -31,11 +31,14 @@ import java.util.logging.LogManager;
 import java.util.stream.Collectors;
 import org.apache.ignite.internal.cli.commands.TopLevelCliCommand;
 import org.apache.ignite.internal.cli.config.ConfigDefaultValueProvider;
+import org.apache.ignite.internal.cli.config.ConfigManagerProvider;
 import org.apache.ignite.internal.cli.config.StateFolderProvider;
 import 
org.apache.ignite.internal.cli.core.exception.handler.PicocliExecutionExceptionHandler;
 import 
org.apache.ignite.internal.cli.core.flow.question.JlineQuestionWriterReaderFactory;
 import org.apache.ignite.internal.cli.core.flow.question.QuestionAskerFactory;
 import 
org.apache.ignite.internal.cli.core.repl.executor.ReplExecutorProviderImpl;
+import org.apache.ignite.internal.cli.core.style.AnsiStringSupport;
+import org.apache.ignite.internal.cli.core.style.ColorScheme;
 import org.fusesource.jansi.AnsiConsole;
 import org.jline.terminal.Terminal;
 import picocli.CommandLine;
@@ -60,6 +63,7 @@ public class Main {
         int exitCode = 0;
         ApplicationContextBuilder builder = 
ApplicationContext.builder(Environment.CLI).deduceEnvironment(false);
         try (MicronautFactory micronautFactory = new 
MicronautFactory(builder.start())) {
+            initColorScheme(micronautFactory);
             if (interactiveMode) {
                 // REPL mode: full initialization with Jansi ANSI console and 
JLine terminal.
                 AnsiConsole.systemInstall();
@@ -94,6 +98,18 @@ public class Main {
         return System.console() != null;
     }
 
+    /** Initializes the color scheme provider to read from configuration 
dynamically. */
+    private static void initColorScheme(MicronautFactory micronautFactory) 
throws Exception {
+        ConfigManagerProvider configProvider = 
micronautFactory.create(ConfigManagerProvider.class);
+        // Set a provider that reads from config each time, so changes take 
effect immediately
+        AnsiStringSupport.setColorSchemeProvider(() -> {
+            String schemeName = 
configProvider.get().getCurrentProperty("ignite.cli.color-scheme");
+            ColorScheme scheme = ColorScheme.fromString(schemeName);
+            ColorScheme result = scheme != null ? scheme : 
ColorScheme.SOLARIZED_DARK;
+            return result;
+        });
+    }
+
     /** Needed for immediate REPL mode and for running a command which will 
stay in REPL mode so we need to init it once. */
     private static void initReplExecutor(MicronautFactory micronautFactory) 
throws Exception {
         ReplExecutorProviderImpl replExecutorProvider = 
micronautFactory.create(ReplExecutorProviderImpl.class);
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/highlighter/SqlAttributedStringHighlighter.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/highlighter/SqlAttributedStringHighlighter.java
index 11a956ba0f5..4735e9e94c8 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/highlighter/SqlAttributedStringHighlighter.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/treesitter/highlighter/SqlAttributedStringHighlighter.java
@@ -17,10 +17,11 @@
 
 package org.apache.ignite.internal.cli.commands.treesitter.highlighter;
 
-import java.util.Map;
 import org.apache.ignite.internal.cli.commands.treesitter.parser.Indexer;
 import org.apache.ignite.internal.cli.commands.treesitter.parser.Parser;
 import org.apache.ignite.internal.cli.commands.treesitter.parser.SqlTokenType;
+import org.apache.ignite.internal.cli.core.style.AnsiStringSupport;
+import org.apache.ignite.internal.cli.core.style.ColorScheme;
 import org.jline.utils.AttributedString;
 import org.jline.utils.AttributedStringBuilder;
 import org.jline.utils.AttributedStyle;
@@ -29,26 +30,24 @@ import org.jline.utils.AttributedStyle;
  * Highlighter for SQL text. The highlighted text is returned as an 
AttributedString.
  */
 public class SqlAttributedStringHighlighter {
-    private static final Map<SqlTokenType, Integer> colorMap = Map.of(
-            SqlTokenType.KEYWORD, 215,
-            SqlTokenType.IDENTIFIER, 254,
-            SqlTokenType.BRACKET, 248,
-            SqlTokenType.LITERAL, 106,
-            SqlTokenType.SPACE, 0,
-            SqlTokenType.COMMA, 248,
-            SqlTokenType.EQUAL, 248,
-            SqlTokenType.STAR, 248,
-            SqlTokenType.SEMICOLON, 248,
-            SqlTokenType.UNKNOWN, 248
-    );
-
     /**
-     * Highlights the input SQL text with ANSI colors.
+     * Highlights the input SQL text with ANSI colors using the current color 
scheme.
      *
      * @param text The input SQL text.
      * @return The highlighted SQL text.
      */
     public static AttributedString highlight(String text) {
+        return highlight(text, AnsiStringSupport.getColorScheme());
+    }
+
+    /**
+     * Highlights the input SQL text with ANSI colors using the specified 
color scheme.
+     *
+     * @param text The input SQL text.
+     * @param scheme The color scheme to use.
+     * @return The highlighted SQL text.
+     */
+    public static AttributedString highlight(String text, ColorScheme scheme) {
         var as = new AttributedStringBuilder();
 
         var tree = Parser.parseSql(text);
@@ -56,11 +55,33 @@ public class SqlAttributedStringHighlighter {
 
         for (int i = 0; i < text.length(); i++) {
             SqlTokenType token = tokens[i];
-            int color = colorMap.getOrDefault(token, 1);
+            int color = getColorForToken(token, scheme);
             var style = AttributedStyle.DEFAULT.foreground(color);
             as.style(style).append(text.charAt(i));
         }
 
         return as.toAttributedString();
     }
+
+    private static int getColorForToken(SqlTokenType token, ColorScheme 
scheme) {
+        switch (token) {
+            case KEYWORD:
+                return scheme.keywordColor();
+            case IDENTIFIER:
+                return scheme.identifierColor();
+            case LITERAL:
+                return scheme.stringColor();
+            case BRACKET:
+            case COMMA:
+            case EQUAL:
+            case STAR:
+            case SEMICOLON:
+                return scheme.punctuationColor();
+            case SPACE:
+                return 0;
+            case UNKNOWN:
+            default:
+                return scheme.punctuationColor();
+        }
+    }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CliConfigKeys.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CliConfigKeys.java
index c06178c5777..eb833611910 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CliConfigKeys.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CliConfigKeys.java
@@ -89,7 +89,10 @@ public enum CliConfigKeys {
     OUTPUT_TRUNCATE(Constants.OUTPUT_TRUNCATE),
 
     /** Maximum column width property name. */
-    OUTPUT_MAX_COLUMN_WIDTH(Constants.OUTPUT_MAX_COLUMN_WIDTH);
+    OUTPUT_MAX_COLUMN_WIDTH(Constants.OUTPUT_MAX_COLUMN_WIDTH),
+
+    /** Color scheme property name (dark, light). */
+    COLOR_SCHEME(Constants.COLOR_SCHEME);
 
     private final String value;
 
@@ -167,6 +170,8 @@ public enum CliConfigKeys {
         public static final String OUTPUT_TRUNCATE = 
"ignite.cli.output.truncate";
 
         public static final String OUTPUT_MAX_COLUMN_WIDTH = 
"ignite.cli.output.max-column-width";
+
+        public static final String COLOR_SCHEME = "ignite.cli.color-scheme";
     }
 
     CliConfigKeys(String value) {
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/AnsiStringSupport.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/AnsiStringSupport.java
index 5654659ab55..4f6d8defd52 100644
--- 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/AnsiStringSupport.java
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/AnsiStringSupport.java
@@ -17,20 +17,46 @@
 
 package org.apache.ignite.internal.cli.core.style;
 
+import java.util.concurrent.atomic.AtomicReference;
 import picocli.CommandLine.Help.Ansi;
 
 /**
  * Utility class with ANSI string support.
  */
 public final class AnsiStringSupport {
+    private static final AtomicReference<ColorSchemeProvider> SCHEME_PROVIDER =
+            new AtomicReference<>(() -> ColorScheme.SOLARIZED_DARK);
+
     private AnsiStringSupport() {}
 
+    /**
+     * Sets the color scheme provider for dynamic color scheme resolution.
+     *
+     * @param provider Color scheme provider.
+     */
+    public static void setColorSchemeProvider(ColorSchemeProvider provider) {
+        SCHEME_PROVIDER.set(provider != null ? provider : () -> 
ColorScheme.SOLARIZED_DARK);
+    }
+
+    /**
+     * Returns the current color scheme from the provider.
+     *
+     * @return Current color scheme.
+     */
+    public static ColorScheme getColorScheme() {
+        return SCHEME_PROVIDER.get().colorScheme();
+    }
+
     public static String ansi(String markupText) {
         return Ansi.AUTO.string(markupText);
     }
 
     public static Fg fg(Color color) {
-        return new Fg(color);
+        return new Fg(color, getColorScheme());
+    }
+
+    public static Fg fg(Color color, ColorScheme scheme) {
+        return new Fg(color, scheme);
     }
 
     /** Can mark the string as a ANSI string. */
@@ -43,11 +69,13 @@ public final class AnsiStringSupport {
      */
     public static class Fg implements Marker {
         private final Color color;
+        private final ColorScheme scheme;
 
         private Style style;
 
-        private Fg(Color color) {
+        private Fg(Color color, ColorScheme scheme) {
             this.color = color;
+            this.scheme = scheme;
         }
 
         public Fg with(Style style) {
@@ -58,10 +86,11 @@ public final class AnsiStringSupport {
         /** Marks given text with the configured before style. */
         @Override
         public String mark(String textToMark) {
+            int colorCode = color.getCode(scheme);
             if (style == Style.BOLD) {
-                return String.format("@|fg(%d),bold %s|@", color.code, 
textToMark);
+                return String.format("@|fg(%d),bold %s|@", colorCode, 
textToMark);
             }
-            return String.format("@|fg(%d) %s|@", color.code, textToMark);
+            return String.format("@|fg(%d) %s|@", colorCode, textToMark);
         }
     }
 
@@ -82,22 +111,54 @@ public final class AnsiStringSupport {
     }
 
     /**
-     * Represents ansi colors that are used in CLI.
+     * Represents semantic colors that are used in CLI.
+     * Actual ANSI color codes are resolved based on the current color scheme.
      */
     public enum Color {
-        RED(1),
-        GREEN(2),
-        YELLOW(3),
-        BLUE(31),
-        YELLOW_DARK(215),
-        GREEN_DARK(22),
-        GRAY(246),
-        WHITE(252);
-
-        Color(int code) {
-            this.code = code;
+        /** Error color (red variants). */
+        RED,
+        /** Success color (green variants). */
+        GREEN,
+        /** Warning/option color (yellow variants). */
+        YELLOW,
+        /** Info color (blue variants). */
+        BLUE,
+        /** Keyword color for syntax highlighting. */
+        YELLOW_DARK,
+        /** String literal color for syntax highlighting. */
+        GREEN_DARK,
+        /** Secondary/muted text color. */
+        GRAY,
+        /** Primary text color. */
+        WHITE;
+
+        /**
+         * Returns the ANSI color code for this semantic color in the given 
scheme.
+         *
+         * @param scheme Color scheme to use.
+         * @return ANSI color code.
+         */
+        public int getCode(ColorScheme scheme) {
+            switch (this) {
+                case RED:
+                    return scheme.errorColor();
+                case GREEN:
+                    return scheme.successColor();
+                case YELLOW:
+                    return scheme.warningColor();
+                case BLUE:
+                    return scheme.infoColor();
+                case YELLOW_DARK:
+                    return scheme.keywordColor();
+                case GREEN_DARK:
+                    return scheme.stringColor();
+                case GRAY:
+                    return scheme.mutedColor();
+                case WHITE:
+                    return scheme.primaryColor();
+                default:
+                    return scheme.primaryColor();
+            }
         }
-
-        private final int code;
     }
 }
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/ColorScheme.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/ColorScheme.java
new file mode 100644
index 00000000000..9c53cdd3a9b
--- /dev/null
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/ColorScheme.java
@@ -0,0 +1,229 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.cli.core.style;
+
+import java.util.Locale;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Defines color schemes for CLI output.
+ * Each scheme provides ANSI 256-color codes optimized for different terminal 
backgrounds.
+ */
+public enum ColorScheme {
+    /** Default color scheme for dark terminal backgrounds. */
+    DARK(
+            // UI colors
+            1,    // error: bright red
+            2,    // success: bright green
+            3,    // warning: bright yellow
+            33,   // info: bright blue
+            246,  // muted: gray
+            252,  // primary: white
+            // Syntax highlighting colors
+            214,  // keyword: orange
+            2,    // string: bright green
+            33,   // number: bright blue
+            252,  // bracket: white
+            246,  // punctuation: gray
+            254   // identifier: bright white
+    ),
+
+    /** Default color scheme for light terminal backgrounds. */
+    LIGHT(
+            // UI colors
+            124,  // error: dark red
+            22,   // success: dark green
+            130,  // warning: dark orange/brown
+            18,   // info: dark blue
+            242,  // muted: dark gray
+            0,    // primary: black
+            // Syntax highlighting colors
+            130,  // keyword: dark orange/brown
+            22,   // string: dark green
+            18,   // number: dark blue
+            0,    // bracket: black
+            242,  // punctuation: dark gray
+            0     // identifier: black
+    ),
+
+    /**
+     * Solarized Dark theme by Ethan Schoonover.
+     * Designed for dark backgrounds with carefully chosen colors for 
readability.
+     * See: https://ethanschoonover.com/solarized/
+     */
+    SOLARIZED_DARK(
+            // UI colors (Solarized accent colors)
+            160,  // error: red (#dc322f)
+            106,  // success: green (#859900)
+            136,  // warning: yellow (#b58900)
+            33,   // info: blue (#268bd2)
+            246,  // muted: base0 (#839496)
+            252,  // primary: bright white (for better visibility)
+            // Syntax highlighting colors
+            166,  // keyword: orange (#cb4b16)
+            37,   // string: cyan (#2aa198)
+            33,   // number: blue (#268bd2)
+            252,  // bracket: bright white
+            246,  // punctuation: base0 (#839496)
+            252   // identifier: bright white
+    ),
+
+    /**
+     * Solarized Light theme by Ethan Schoonover.
+     * Designed for light backgrounds with carefully chosen colors for 
readability.
+     * See: https://ethanschoonover.com/solarized/
+     */
+    SOLARIZED_LIGHT(
+            // UI colors (Solarized accent colors)
+            160,  // error: red (#dc322f)
+            106,  // success: green (#859900)
+            136,  // warning: yellow (#b58900)
+            33,   // info: blue (#268bd2)
+            247,  // muted: base1 (#93a1a1)
+            66,   // primary: base00 (#657b83)
+            // Syntax highlighting colors
+            166,  // keyword: orange (#cb4b16)
+            37,   // string: cyan (#2aa198)
+            33,   // number: blue (#268bd2)
+            66,   // bracket: base00 (#657b83)
+            247,  // punctuation: base1 (#93a1a1)
+            66    // identifier: base00 (#657b83)
+    );
+
+    private final int errorColor;
+    private final int successColor;
+    private final int warningColor;
+    private final int infoColor;
+    private final int mutedColor;
+    private final int primaryColor;
+    private final int keywordColor;
+    private final int stringColor;
+    private final int numberColor;
+    private final int bracketColor;
+    private final int punctuationColor;
+    private final int identifierColor;
+
+    ColorScheme(
+            int errorColor,
+            int successColor,
+            int warningColor,
+            int infoColor,
+            int mutedColor,
+            int primaryColor,
+            int keywordColor,
+            int stringColor,
+            int numberColor,
+            int bracketColor,
+            int punctuationColor,
+            int identifierColor
+    ) {
+        this.errorColor = errorColor;
+        this.successColor = successColor;
+        this.warningColor = warningColor;
+        this.infoColor = infoColor;
+        this.mutedColor = mutedColor;
+        this.primaryColor = primaryColor;
+        this.keywordColor = keywordColor;
+        this.stringColor = stringColor;
+        this.numberColor = numberColor;
+        this.bracketColor = bracketColor;
+        this.punctuationColor = punctuationColor;
+        this.identifierColor = identifierColor;
+    }
+
+    /** Returns ANSI color code for error messages. */
+    public int errorColor() {
+        return errorColor;
+    }
+
+    /** Returns ANSI color code for success messages. */
+    public int successColor() {
+        return successColor;
+    }
+
+    /** Returns ANSI color code for warning/option messages. */
+    public int warningColor() {
+        return warningColor;
+    }
+
+    /** Returns ANSI color code for info messages. */
+    public int infoColor() {
+        return infoColor;
+    }
+
+    /** Returns ANSI color code for muted/secondary text. */
+    public int mutedColor() {
+        return mutedColor;
+    }
+
+    /** Returns ANSI color code for primary text. */
+    public int primaryColor() {
+        return primaryColor;
+    }
+
+    /** Returns ANSI color code for syntax keywords. */
+    public int keywordColor() {
+        return keywordColor;
+    }
+
+    /** Returns ANSI color code for string literals. */
+    public int stringColor() {
+        return stringColor;
+    }
+
+    /** Returns ANSI color code for number literals. */
+    public int numberColor() {
+        return numberColor;
+    }
+
+    /** Returns ANSI color code for brackets. */
+    public int bracketColor() {
+        return bracketColor;
+    }
+
+    /** Returns ANSI color code for punctuation (comma, colon, etc.). */
+    public int punctuationColor() {
+        return punctuationColor;
+    }
+
+    /** Returns ANSI color code for identifiers. */
+    public int identifierColor() {
+        return identifierColor;
+    }
+
+    /**
+     * Parses a color scheme from string value.
+     * Supports both underscore and hyphen separators (e.g., "solarized-dark" 
or "solarized_dark").
+     *
+     * @param value Color scheme name (case-insensitive).
+     * @return Parsed color scheme or {@code null} if value is invalid.
+     */
+    @Nullable
+    public static ColorScheme fromString(@Nullable String value) {
+        if (value == null || value.isEmpty()) {
+            return null;
+        }
+        try {
+            // Convert hyphens to underscores to support both "solarized-dark" 
and "solarized_dark"
+            String normalized = value.toUpperCase(Locale.ROOT).replace('-', 
'_');
+            return valueOf(normalized);
+        } catch (IllegalArgumentException e) {
+            return null;
+        }
+    }
+}
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/ColorSchemeProvider.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/ColorSchemeProvider.java
new file mode 100644
index 00000000000..cbc95586a26
--- /dev/null
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/ColorSchemeProvider.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.cli.core.style;
+
+/**
+ * Provider for the current color scheme.
+ */
+public interface ColorSchemeProvider {
+    /**
+     * Returns the current color scheme.
+     *
+     * @return Current color scheme, never null.
+     */
+    ColorScheme colorScheme();
+}
diff --git 
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/DefaultColorSchemeProvider.java
 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/DefaultColorSchemeProvider.java
new file mode 100644
index 00000000000..9f53bc52304
--- /dev/null
+++ 
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/style/DefaultColorSchemeProvider.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.cli.core.style;
+
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+import org.apache.ignite.internal.cli.config.CliConfigKeys;
+import org.apache.ignite.internal.cli.config.ConfigManagerProvider;
+
+/**
+ * Default implementation of {@link ColorSchemeProvider} that reads from CLI 
configuration.
+ */
+@Singleton
+public class DefaultColorSchemeProvider implements ColorSchemeProvider {
+    private final ConfigManagerProvider configManagerProvider;
+
+    @Inject
+    public DefaultColorSchemeProvider(ConfigManagerProvider 
configManagerProvider) {
+        this.configManagerProvider = configManagerProvider;
+    }
+
+    @Override
+    public ColorScheme colorScheme() {
+        String schemeName = 
configManagerProvider.get().getCurrentProperty(CliConfigKeys.COLOR_SCHEME.value());
+        ColorScheme scheme = ColorScheme.fromString(schemeName);
+        return scheme != null ? scheme : ColorScheme.DARK;
+    }
+}
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/style/AnsiStringSupportTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/style/AnsiStringSupportTest.java
new file mode 100644
index 00000000000..6d00686e98b
--- /dev/null
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/style/AnsiStringSupportTest.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.cli.core.style;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.ignite.internal.cli.core.style.AnsiStringSupport.Color;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+class AnsiStringSupportTest {
+
+    @AfterEach
+    void tearDown() {
+        // Reset to default provider
+        AnsiStringSupport.setColorSchemeProvider(() -> 
ColorScheme.SOLARIZED_DARK);
+    }
+
+    @Test
+    void defaultSchemeIsSolarizedDark() {
+        AnsiStringSupport.setColorSchemeProvider(null);
+        assertEquals(ColorScheme.SOLARIZED_DARK, 
AnsiStringSupport.getColorScheme());
+    }
+
+    @Test
+    void providerIsCalledDynamically() {
+        // Use a mutable holder to change the scheme dynamically
+        final ColorScheme[] holder = {ColorScheme.DARK};
+        AnsiStringSupport.setColorSchemeProvider(() -> holder[0]);
+
+        assertEquals(ColorScheme.DARK, AnsiStringSupport.getColorScheme());
+
+        // Change the scheme
+        holder[0] = ColorScheme.LIGHT;
+        assertEquals(ColorScheme.LIGHT, AnsiStringSupport.getColorScheme());
+    }
+
+    @Test
+    void fgUsesCurrentSchemeColors() {
+        AnsiStringSupport.setColorSchemeProvider(() -> ColorScheme.DARK);
+        String darkResult = AnsiStringSupport.fg(Color.RED).mark("error");
+
+        AnsiStringSupport.setColorSchemeProvider(() -> ColorScheme.LIGHT);
+        String lightResult = AnsiStringSupport.fg(Color.RED).mark("error");
+
+        // The color codes should be different
+        assertTrue(darkResult.contains("fg(" + ColorScheme.DARK.errorColor() + 
")"));
+        assertTrue(lightResult.contains("fg(" + ColorScheme.LIGHT.errorColor() 
+ ")"));
+    }
+
+    @Test
+    void fgWithExplicitSchemeOverridesGlobal() {
+        AnsiStringSupport.setColorSchemeProvider(() -> ColorScheme.DARK);
+
+        // Even with DARK as global, we can explicitly use LIGHT
+        String lightResult = AnsiStringSupport.fg(Color.RED, 
ColorScheme.LIGHT).mark("error");
+        assertTrue(lightResult.contains("fg(" + ColorScheme.LIGHT.errorColor() 
+ ")"));
+    }
+
+    @Test
+    void fgWithBoldStyle() {
+        AnsiStringSupport.setColorSchemeProvider(() -> ColorScheme.DARK);
+        String result = 
AnsiStringSupport.fg(Color.GREEN).with(AnsiStringSupport.Style.BOLD).mark("success");
+        assertTrue(result.contains("bold"));
+        assertTrue(result.contains("fg(" + ColorScheme.DARK.successColor() + 
")"));
+    }
+
+    @Test
+    void colorEnumMapsToCorrectSchemeColors() {
+        assertEquals(ColorScheme.DARK.errorColor(), 
Color.RED.getCode(ColorScheme.DARK));
+        assertEquals(ColorScheme.DARK.successColor(), 
Color.GREEN.getCode(ColorScheme.DARK));
+        assertEquals(ColorScheme.DARK.warningColor(), 
Color.YELLOW.getCode(ColorScheme.DARK));
+        assertEquals(ColorScheme.DARK.infoColor(), 
Color.BLUE.getCode(ColorScheme.DARK));
+        assertEquals(ColorScheme.DARK.keywordColor(), 
Color.YELLOW_DARK.getCode(ColorScheme.DARK));
+        assertEquals(ColorScheme.DARK.stringColor(), 
Color.GREEN_DARK.getCode(ColorScheme.DARK));
+        assertEquals(ColorScheme.DARK.mutedColor(), 
Color.GRAY.getCode(ColorScheme.DARK));
+        assertEquals(ColorScheme.DARK.primaryColor(), 
Color.WHITE.getCode(ColorScheme.DARK));
+    }
+}
diff --git 
a/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/style/ColorSchemeTest.java
 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/style/ColorSchemeTest.java
new file mode 100644
index 00000000000..3fe874540be
--- /dev/null
+++ 
b/modules/cli/src/test/java/org/apache/ignite/internal/cli/core/style/ColorSchemeTest.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.cli.core.style;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import org.junit.jupiter.api.Test;
+
+class ColorSchemeTest {
+
+    @Test
+    void darkAndLightSchemesHaveDifferentColors() {
+        assertNotEquals(ColorScheme.DARK.errorColor(), 
ColorScheme.LIGHT.errorColor());
+        assertNotEquals(ColorScheme.DARK.successColor(), 
ColorScheme.LIGHT.successColor());
+        assertNotEquals(ColorScheme.DARK.primaryColor(), 
ColorScheme.LIGHT.primaryColor());
+    }
+
+    @Test
+    void solarizedSchemesExist() {
+        assertEquals(160, ColorScheme.SOLARIZED_DARK.errorColor());
+        assertEquals(106, ColorScheme.SOLARIZED_DARK.successColor());
+        assertEquals(160, ColorScheme.SOLARIZED_LIGHT.errorColor());
+        assertEquals(106, ColorScheme.SOLARIZED_LIGHT.successColor());
+    }
+
+    @Test
+    void solarizedDarkAndLightHaveDifferentBaseColors() {
+        // Primary text differs between solarized dark and light
+        assertNotEquals(ColorScheme.SOLARIZED_DARK.primaryColor(), 
ColorScheme.SOLARIZED_LIGHT.primaryColor());
+        // Muted text differs
+        assertNotEquals(ColorScheme.SOLARIZED_DARK.mutedColor(), 
ColorScheme.SOLARIZED_LIGHT.mutedColor());
+    }
+
+    @Test
+    void fromStringParsesValidSchemes() {
+        assertEquals(ColorScheme.DARK, ColorScheme.fromString("dark"));
+        assertEquals(ColorScheme.DARK, ColorScheme.fromString("DARK"));
+        assertEquals(ColorScheme.LIGHT, ColorScheme.fromString("light"));
+        assertEquals(ColorScheme.LIGHT, ColorScheme.fromString("LIGHT"));
+    }
+
+    @Test
+    void fromStringParsesSolarizedWithHyphen() {
+        assertEquals(ColorScheme.SOLARIZED_DARK, 
ColorScheme.fromString("solarized-dark"));
+        assertEquals(ColorScheme.SOLARIZED_DARK, 
ColorScheme.fromString("SOLARIZED-DARK"));
+        assertEquals(ColorScheme.SOLARIZED_LIGHT, 
ColorScheme.fromString("solarized-light"));
+        assertEquals(ColorScheme.SOLARIZED_LIGHT, 
ColorScheme.fromString("SOLARIZED-LIGHT"));
+    }
+
+    @Test
+    void fromStringParsesSolarizedWithUnderscore() {
+        assertEquals(ColorScheme.SOLARIZED_DARK, 
ColorScheme.fromString("solarized_dark"));
+        assertEquals(ColorScheme.SOLARIZED_DARK, 
ColorScheme.fromString("SOLARIZED_DARK"));
+        assertEquals(ColorScheme.SOLARIZED_LIGHT, 
ColorScheme.fromString("solarized_light"));
+        assertEquals(ColorScheme.SOLARIZED_LIGHT, 
ColorScheme.fromString("SOLARIZED_LIGHT"));
+    }
+
+    @Test
+    void fromStringReturnsNullForInvalidValues() {
+        assertNull(ColorScheme.fromString(null));
+        assertNull(ColorScheme.fromString(""));
+        assertNull(ColorScheme.fromString("invalid"));
+        assertNull(ColorScheme.fromString("auto"));
+    }
+
+    @Test
+    void lightSchemeHasDarkerColorsForLightBackgrounds() {
+        // Primary text should be black (0) on light, white-ish on dark
+        assertEquals(0, ColorScheme.LIGHT.primaryColor());
+        assertEquals(252, ColorScheme.DARK.primaryColor());
+    }
+
+    @Test
+    void solarizedUsesConsistentAccentColors() {
+        // Solarized uses the same accent colors for both dark and light themes
+        assertEquals(ColorScheme.SOLARIZED_DARK.errorColor(), 
ColorScheme.SOLARIZED_LIGHT.errorColor());
+        assertEquals(ColorScheme.SOLARIZED_DARK.successColor(), 
ColorScheme.SOLARIZED_LIGHT.successColor());
+        assertEquals(ColorScheme.SOLARIZED_DARK.warningColor(), 
ColorScheme.SOLARIZED_LIGHT.warningColor());
+        assertEquals(ColorScheme.SOLARIZED_DARK.infoColor(), 
ColorScheme.SOLARIZED_LIGHT.infoColor());
+    }
+}


Reply via email to