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

pierrejeambrun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new acae180797b Clean custom theme defaults (#60226)
acae180797b is described below

commit acae180797b303fdeec1980796943c066c5b8d22
Author: Pierre Jeambrun <[email protected]>
AuthorDate: Thu Jan 8 09:54:30 2026 +0100

    Clean custom theme defaults (#60226)
---
 airflow-core/docs/howto/customize-ui.rst           |  2 +-
 .../api_fastapi/core_api/datamodels/ui/config.py   |  2 +-
 .../api_fastapi/core_api/openapi/_private_ui.yaml  |  4 +++-
 .../api_fastapi/core_api/routes/ui/config.py       | 23 +---------------------
 .../src/airflow/config_templates/config.yml        | 21 +-------------------
 .../airflow/ui/openapi-gen/requests/schemas.gen.ts |  9 ++++++++-
 .../airflow/ui/openapi-gen/requests/types.gen.ts   |  2 +-
 .../api_fastapi/core_api/routes/ui/test_config.py  | 11 +++++++----
 8 files changed, 23 insertions(+), 51 deletions(-)

diff --git a/airflow-core/docs/howto/customize-ui.rst 
b/airflow-core/docs/howto/customize-ui.rst
index 2833f389ca5..8b93b4b6178 100644
--- a/airflow-core/docs/howto/customize-ui.rst
+++ b/airflow-core/docs/howto/customize-ui.rst
@@ -112,7 +112,7 @@ To customize the UI, simply:
   The whitespace, particularly on the last line, is important so a multi-line 
value works properly. More details can be found in the
   the `configparser docs 
<https://docs.python.org/3/library/configparser.html#supported-ini-file-structure>`_.
 
-2.  Alternatively, you can set a custom title using the environment variable:
+2.  Alternatively, you can set a custom theme using the environment variable:
 
 .. code-block::
 
diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/config.py 
b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/config.py
index f70e233a6ef..045030269ce 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/config.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/ui/config.py
@@ -34,4 +34,4 @@ class ConfigResponse(BaseModel):
     dashboard_alert: list[UIAlert]
     show_external_log_redirect: bool
     external_log_name: str | None = None
-    theme: Theme
+    theme: Theme | None
diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml 
b/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml
index ccb45d06727..31e2a351036 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml
+++ b/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml
@@ -1352,7 +1352,9 @@ components:
           - type: 'null'
           title: External Log Name
         theme:
-          $ref: '#/components/schemas/Theme'
+          anyOf:
+          - $ref: '#/components/schemas/Theme'
+          - type: 'null'
       type: object
       required:
       - page_size
diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/config.py 
b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/config.py
index 98e506af0c1..28009f8343e 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/config.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/config.py
@@ -32,27 +32,6 @@ from airflow.utils.log.log_reader import TaskLogReader
 
 config_router = AirflowRouter(tags=["Config"])
 
-THEME_FALLBACK = """
-{
-    "tokens": {
-        "colors": {
-            "brand": {
-                "50": { "value": "oklch(0.98 0.006 248.717)" },
-                "100": { "value": "oklch(0.962 0.012 249.46)" },
-                "200": { "value": "oklch(0.923 0.023 255.082)" },
-                "300": { "value": "oklch(0.865 0.039 252.42)" },
-                "400": { "value": "oklch(0.705 0.066 256.378)" },
-                "500": { "value": "oklch(0.575 0.08 257.759)" },
-                "600": { "value": "oklch(0.469 0.084 257.657)" },
-                "700": { "value": "oklch(0.399 0.084 257.85)" },
-                "800": { "value": "oklch(0.324 0.072 260.329)" },
-                "900": { "value": "oklch(0.259 0.062 265.566)" },
-                "950": { "value": "oklch(0.179 0.05 265.487)" }
-            }
-        }
-    }
-}
-"""
 
 API_CONFIG_KEYS = [
     "enable_swagger_ui",
@@ -81,7 +60,7 @@ def get_configs() -> ConfigResponse:
         "dashboard_alert": [alert for alert in DASHBOARD_UIALERTS if 
isinstance(alert, UIAlert)],
         "show_external_log_redirect": task_log_reader.supports_external_link,
         "external_log_name": getattr(task_log_reader.log_handler, "log_name", 
None),
-        "theme": loads(conf.get("api", "theme", fallback=THEME_FALLBACK)),
+        "theme": loads(conf.get("api", "theme", fallback="{}")) or None,
     }
 
     config.update({key: value for key, value in additional_config.items()})
diff --git a/airflow-core/src/airflow/config_templates/config.yml 
b/airflow-core/src/airflow/config_templates/config.yml
index a4ed088865a..dac144f54cb 100644
--- a/airflow-core/src/airflow/config_templates/config.yml
+++ b/airflow-core/src/airflow/config_templates/config.yml
@@ -1400,26 +1400,7 @@ api:
                 }
               }
             }
-      default: >
-        {{
-              "tokens": {{
-                "colors": {{
-                  "brand": {{
-                    "50": {{ "value": "oklch(0.98 0.006 248.717)" }},
-                    "100": {{ "value": "oklch(0.962 0.012 249.460)" }},
-                    "200": {{ "value": "oklch(0.923 0.023 255.082)" }},
-                    "300": {{ "value": "oklch(0.865 0.039 252.420)" }},
-                    "400": {{ "value": "oklch(0.705 0.066 256.378)" }},
-                    "500": {{ "value": "oklch(0.575 0.08 257.759)" }},
-                    "600": {{ "value": "oklch(0.469 0.084 257.657)" }},
-                    "700": {{ "value": "oklch(0.399 0.084 257.850)" }},
-                    "800": {{ "value": "oklch(0.324 0.072 260.329)" }},
-                    "900": {{ "value": "oklch(0.259 0.062 265.566)" }},
-                    "950": {{ "value": "oklch(0.179 0.05 265.487)" }}
-                  }}
-                }}
-              }}
-            }}
+      default: ~
     enable_swagger_ui:
       description: |
         Boolean for running SwaggerUI in the webserver.
diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts 
b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
index 868a43380fc..c71112cc1a6 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
@@ -7154,7 +7154,14 @@ export const $ConfigResponse = {
             title: 'External Log Name'
         },
         theme: {
-            '$ref': '#/components/schemas/Theme'
+            anyOf: [
+                {
+                    '$ref': '#/components/schemas/Theme'
+                },
+                {
+                    type: 'null'
+                }
+            ]
         }
     },
     type: 'object',
diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts 
b/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts
index 59afa7eb842..69aeeda532f 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts
@@ -1772,7 +1772,7 @@ export type ConfigResponse = {
     dashboard_alert: Array<UIAlert>;
     show_external_log_redirect: boolean;
     external_log_name?: string | null;
-    theme: Theme;
+    theme: Theme | null;
 };
 
 /**
diff --git 
a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_config.py 
b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_config.py
index 3c5077b0b5b..6973ef97e42 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_config.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_config.py
@@ -16,6 +16,8 @@
 # under the License.
 from __future__ import annotations
 
+import json
+
 import pytest
 
 from tests_common.test_utils.asserts import assert_queries_count
@@ -43,7 +45,7 @@ THEME = {
     }
 }
 
-mock_config_response = {
+expected_config_response = {
     "page_size": 100,
     "auto_refresh_interval": 3,
     "hide_paused_dags_by_default": True,
@@ -73,6 +75,7 @@ def mock_config_data():
             ("api", "default_wrap"): "false",
             ("api", "auto_refresh_interval"): "3",
             ("api", "require_confirmation_dag_change"): "false",
+            ("api", "theme"): json.dumps(THEME),
         }
     ):
         yield
@@ -81,13 +84,13 @@ def mock_config_data():
 class TestGetConfig:
     def test_should_response_200(self, mock_config_data, test_client):
         """
-        Test the /config endpoint to verify response matches mock data.
+        Test the /config endpoint to verify response matches the expected data.
         """
         with assert_queries_count(0):
             response = test_client.get("/config")
 
         assert response.status_code == 200
-        assert response.json() == mock_config_response
+        assert response.json() == expected_config_response
 
     def test_get_config_should_response_401(self, unauthenticated_test_client):
         response = unauthenticated_test_client.get("/config")
@@ -97,4 +100,4 @@ class TestGetConfig:
         """Just being authenticated is enough to access the endpoint."""
         response = unauthorized_test_client.get("/config")
         assert response.status_code == 200
-        assert response.json() == mock_config_response
+        assert response.json() == expected_config_response

Reply via email to