From 092411630a843eb4559a2d8bb46398864e069418 Mon Sep 17 00:00:00 2001 From: Luna <60090391+blingblin-g@users.noreply.github.com> Date: Fri, 21 Mar 2025 06:28:08 +0900 Subject: [PATCH 01/14] Enhance RedirectsPanel with customizable redirect response hook (#2104) * Add interception_response hook * Fix comments for SimpleTemplateResponse * Docs for Custom RedirectPanel * Moved the changelog entry to the correct place. --- debug_toolbar/panels/redirects.py | 35 ++++++++++++++++++------------- docs/changes.rst | 2 ++ docs/panels.rst | 5 +++++ tests/panels/test_redirects.py | 13 ++++++++++++ 4 files changed, 41 insertions(+), 14 deletions(-) diff --git a/debug_toolbar/panels/redirects.py b/debug_toolbar/panels/redirects.py index 27cce4c17..8055c67ad 100644 --- a/debug_toolbar/panels/redirects.py +++ b/debug_toolbar/panels/redirects.py @@ -22,20 +22,8 @@ def _process_response(self, response): Common response processing logic. """ if 300 <= response.status_code < 400: - redirect_to = response.get("Location") - if redirect_to: - status_line = f"{response.status_code} {response.reason_phrase}" - cookies = response.cookies - context = { - "redirect_to": redirect_to, - "status_line": status_line, - "toolbar": self.toolbar, - } - # Using SimpleTemplateResponse avoids running global context processors. - response = SimpleTemplateResponse( - "debug_toolbar/redirect.html", context - ) - response.cookies = cookies + if redirect_to := response.get("Location"): + response = self.get_interception_response(response, redirect_to) response.render() return response @@ -53,3 +41,22 @@ def process_request(self, request): if iscoroutine(response): return self.aprocess_request(request, response) return self._process_response(response) + + def get_interception_response(self, response, redirect_to): + """ + Hook method to allow subclasses to customize the interception response. + """ + status_line = f"{response.status_code} {response.reason_phrase}" + cookies = response.cookies + original_response = response + context = { + "redirect_to": redirect_to, + "status_line": status_line, + "toolbar": self.toolbar, + "original_response": original_response, + } + # Using SimpleTemplateResponse avoids running global context processors. + response = SimpleTemplateResponse("debug_toolbar/redirect.html", context) + response.cookies = cookies + response.original_response = original_response + return response diff --git a/docs/changes.rst b/docs/changes.rst index dd52a09e1..5be81b2cb 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -4,6 +4,8 @@ Change log Pending ------- +* Added hook to RedirectsPanel for subclass customization. + 5.1.0 (2025-03-20) ------------------ * Added Django 5.2 to the tox matrix. diff --git a/docs/panels.rst b/docs/panels.rst index be481fb6e..a116bff1e 100644 --- a/docs/panels.rst +++ b/docs/panels.rst @@ -122,6 +122,11 @@ Since this behavior is annoying when you aren't debugging a redirect, this panel is included but inactive by default. You can activate it by default with the ``DISABLE_PANELS`` configuration option. +To further customize the behavior, you can subclass the ``RedirectsPanel`` +and override the ``get_interception_response`` method to manipulate the +response directly. To use a custom ``RedirectsPanel``, you need to replace +the original one in ``DEBUG_TOOLBAR_PANELS`` in your ``settings.py``. + .. _profiling-panel: Profiling diff --git a/tests/panels/test_redirects.py b/tests/panels/test_redirects.py index 2abed9fd0..7d6d5ac06 100644 --- a/tests/panels/test_redirects.py +++ b/tests/panels/test_redirects.py @@ -85,3 +85,16 @@ async def get_response(request): response = await self.panel.process_request(self.request) self.assertIsInstance(response, HttpResponse) self.assertTrue(response is await_response) + + def test_original_response_preserved(self): + redirect = HttpResponse(status=302) + redirect["Location"] = "http://somewhere/else/" + self._get_response = lambda request: redirect + response = self.panel.process_request(self.request) + self.assertFalse(response is redirect) + self.assertTrue(hasattr(response, "original_response")) + self.assertTrue(response.original_response is redirect) + self.assertIsNone(response.get("Location")) + self.assertEqual( + response.original_response.get("Location"), "http://somewhere/else/" + ) From e6076b5007cd753c0106aad2a4eac0557197b149 Mon Sep 17 00:00:00 2001 From: Felipe Villegas Date: Thu, 20 Mar 2025 17:34:48 -0400 Subject: [PATCH 02/14] Sanitize sensitive variables in RequestPanel (#2105) --- debug_toolbar/panels/request.py | 20 +++------ debug_toolbar/utils.py | 46 +++++++++++++++++---- docs/changes.rst | 1 + tests/panels/test_request.py | 73 +++++++++++++++++++++++++++++++++ tests/test_utils.py | 62 ++++++++++++++++++++++++++++ 5 files changed, 181 insertions(+), 21 deletions(-) diff --git a/debug_toolbar/panels/request.py b/debug_toolbar/panels/request.py index b77788637..5a24d6179 100644 --- a/debug_toolbar/panels/request.py +++ b/debug_toolbar/panels/request.py @@ -3,7 +3,7 @@ from django.utils.translation import gettext_lazy as _ from debug_toolbar.panels import Panel -from debug_toolbar.utils import get_name_from_obj, get_sorted_request_variable +from debug_toolbar.utils import get_name_from_obj, sanitize_and_sort_request_vars class RequestPanel(Panel): @@ -26,9 +26,9 @@ def nav_subtitle(self): def generate_stats(self, request, response): self.record_stats( { - "get": get_sorted_request_variable(request.GET), - "post": get_sorted_request_variable(request.POST), - "cookies": get_sorted_request_variable(request.COOKIES), + "get": sanitize_and_sort_request_vars(request.GET), + "post": sanitize_and_sort_request_vars(request.POST), + "cookies": sanitize_and_sort_request_vars(request.COOKIES), } ) @@ -59,13 +59,5 @@ def generate_stats(self, request, response): self.record_stats(view_info) if hasattr(request, "session"): - try: - session_list = [ - (k, request.session.get(k)) for k in sorted(request.session.keys()) - ] - except TypeError: - session_list = [ - (k, request.session.get(k)) - for k in request.session.keys() # (it's not a dict) - ] - self.record_stats({"session": {"list": session_list}}) + session_data = dict(request.session) + self.record_stats({"session": sanitize_and_sort_request_vars(session_data)}) diff --git a/debug_toolbar/utils.py b/debug_toolbar/utils.py index dc3cc1adc..f4b3eac38 100644 --- a/debug_toolbar/utils.py +++ b/debug_toolbar/utils.py @@ -14,10 +14,12 @@ from django.template import Node from django.utils.html import format_html from django.utils.safestring import SafeString, mark_safe +from django.views.debug import get_default_exception_reporter_filter from debug_toolbar import _stubs as stubs, settings as dt_settings _local_data = Local() +safe_filter = get_default_exception_reporter_filter() def _is_excluded_frame(frame: Any, excluded_modules: Sequence[str] | None) -> bool: @@ -215,20 +217,50 @@ def getframeinfo(frame: Any, context: int = 1) -> inspect.Traceback: return inspect.Traceback(filename, lineno, frame.f_code.co_name, lines, index) -def get_sorted_request_variable( +def sanitize_and_sort_request_vars( variable: dict[str, Any] | QueryDict, ) -> dict[str, list[tuple[str, Any]] | Any]: """ Get a data structure for showing a sorted list of variables from the - request data. + request data with sensitive values redacted. """ + if not isinstance(variable, (dict, QueryDict)): + return {"raw": variable} + + # Get sorted keys if possible, otherwise just list them + keys = _get_sorted_keys(variable) + + # Process the variable based on its type + if isinstance(variable, QueryDict): + result = _process_query_dict(variable, keys) + else: + result = _process_dict(variable, keys) + + return {"list": result} + + +def _get_sorted_keys(variable): + """Helper function to get sorted keys if possible.""" try: - if isinstance(variable, dict): - return {"list": [(k, variable.get(k)) for k in sorted(variable)]} - else: - return {"list": [(k, variable.getlist(k)) for k in sorted(variable)]} + return sorted(variable) except TypeError: - return {"raw": variable} + return list(variable) + + +def _process_query_dict(query_dict, keys): + """Process a QueryDict into a list of (key, sanitized_value) tuples.""" + result = [] + for k in keys: + values = query_dict.getlist(k) + # Return single value if there's only one, otherwise keep as list + value = values[0] if len(values) == 1 else values + result.append((k, safe_filter.cleanse_setting(k, value))) + return result + + +def _process_dict(dictionary, keys): + """Process a dictionary into a list of (key, sanitized_value) tuples.""" + return [(k, safe_filter.cleanse_setting(k, dictionary.get(k))) for k in keys] def get_stack(context=1) -> list[stubs.InspectStack]: diff --git a/docs/changes.rst b/docs/changes.rst index 5be81b2cb..7f8fabbc5 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -5,6 +5,7 @@ Pending ------- * Added hook to RedirectsPanel for subclass customization. +* Added feature to sanitize sensitive data in the Request Panel. 5.1.0 (2025-03-20) ------------------ diff --git a/tests/panels/test_request.py b/tests/panels/test_request.py index 707b50bb4..2eb7ba610 100644 --- a/tests/panels/test_request.py +++ b/tests/panels/test_request.py @@ -136,3 +136,76 @@ def test_session_list_sorted_or_not(self): self.panel.generate_stats(self.request, response) panel_stats = self.panel.get_stats() self.assertEqual(panel_stats["session"], data) + + def test_sensitive_post_data_sanitized(self): + """Test that sensitive POST data is redacted.""" + self.request.POST = {"username": "testuser", "password": "secret123"} + response = self.panel.process_request(self.request) + self.panel.generate_stats(self.request, response) + + # Check that password is redacted in panel content + content = self.panel.content + self.assertIn("username", content) + self.assertIn("testuser", content) + self.assertIn("password", content) + self.assertNotIn("secret123", content) + self.assertIn("********************", content) + + def test_sensitive_get_data_sanitized(self): + """Test that sensitive GET data is redacted.""" + self.request.GET = {"api_key": "abc123", "q": "search term"} + response = self.panel.process_request(self.request) + self.panel.generate_stats(self.request, response) + + # Check that api_key is redacted in panel content + content = self.panel.content + self.assertIn("api_key", content) + self.assertNotIn("abc123", content) + self.assertIn("********************", content) + self.assertIn("q", content) + self.assertIn("search term", content) + + def test_sensitive_cookie_data_sanitized(self): + """Test that sensitive cookie data is redacted.""" + self.request.COOKIES = {"session_id": "abc123", "auth_token": "xyz789"} + response = self.panel.process_request(self.request) + self.panel.generate_stats(self.request, response) + + # Check that auth_token is redacted in panel content + content = self.panel.content + self.assertIn("session_id", content) + self.assertIn("abc123", content) + self.assertIn("auth_token", content) + self.assertNotIn("xyz789", content) + self.assertIn("********************", content) + + def test_sensitive_session_data_sanitized(self): + """Test that sensitive session data is redacted.""" + self.request.session = {"user_id": 123, "auth_token": "xyz789"} + response = self.panel.process_request(self.request) + self.panel.generate_stats(self.request, response) + + # Check that auth_token is redacted in panel content + content = self.panel.content + self.assertIn("user_id", content) + self.assertIn("123", content) + self.assertIn("auth_token", content) + self.assertNotIn("xyz789", content) + self.assertIn("********************", content) + + def test_querydict_sanitized(self): + """Test that sensitive data in QueryDict objects is properly redacted.""" + query_dict = QueryDict("username=testuser&password=secret123&token=abc456") + self.request.GET = query_dict + response = self.panel.process_request(self.request) + self.panel.generate_stats(self.request, response) + + # Check that sensitive data is redacted in panel content + content = self.panel.content + self.assertIn("username", content) + self.assertIn("testuser", content) + self.assertIn("password", content) + self.assertNotIn("secret123", content) + self.assertIn("token", content) + self.assertNotIn("abc456", content) + self.assertIn("********************", content) diff --git a/tests/test_utils.py b/tests/test_utils.py index 26bfce005..646b6a5ad 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,5 +1,6 @@ import unittest +from django.http import QueryDict from django.test import override_settings import debug_toolbar.utils @@ -8,6 +9,7 @@ get_stack, get_stack_trace, render_stacktrace, + sanitize_and_sort_request_vars, tidy_stacktrace, ) @@ -109,3 +111,63 @@ def __init__(self, value): rendered_stack_2 = render_stacktrace(stack_2_wrapper.value) self.assertNotIn("test_locals_value_1", rendered_stack_2) self.assertIn("test_locals_value_2", rendered_stack_2) + + +class SanitizeAndSortRequestVarsTestCase(unittest.TestCase): + """Tests for the sanitize_and_sort_request_vars function.""" + + def test_dict_sanitization(self): + """Test sanitization of a regular dictionary.""" + test_dict = { + "username": "testuser", + "password": "secret123", + "api_key": "abc123", + } + result = sanitize_and_sort_request_vars(test_dict) + + # Convert to dict for easier testing + result_dict = dict(result["list"]) + + self.assertEqual(result_dict["username"], "testuser") + self.assertEqual(result_dict["password"], "********************") + self.assertEqual(result_dict["api_key"], "********************") + + def test_querydict_sanitization(self): + """Test sanitization of a QueryDict.""" + query_dict = QueryDict("username=testuser&password=secret123&api_key=abc123") + result = sanitize_and_sort_request_vars(query_dict) + + # Convert to dict for easier testing + result_dict = dict(result["list"]) + + self.assertEqual(result_dict["username"], "testuser") + self.assertEqual(result_dict["password"], "********************") + self.assertEqual(result_dict["api_key"], "********************") + + def test_non_sortable_dict_keys(self): + """Test dictionary with keys that can't be sorted.""" + test_dict = { + 1: "one", + "2": "two", + None: "none", + } + result = sanitize_and_sort_request_vars(test_dict) + self.assertEqual(len(result["list"]), 3) + result_dict = dict(result["list"]) + self.assertEqual(result_dict[1], "one") + self.assertEqual(result_dict["2"], "two") + self.assertEqual(result_dict[None], "none") + + def test_querydict_multiple_values(self): + """Test QueryDict with multiple values for the same key.""" + query_dict = QueryDict("name=bar1&name=bar2&title=value") + result = sanitize_and_sort_request_vars(query_dict) + result_dict = dict(result["list"]) + self.assertEqual(result_dict["name"], ["bar1", "bar2"]) + self.assertEqual(result_dict["title"], "value") + + def test_non_dict_input(self): + """Test handling of non-dict input.""" + test_input = ["not", "a", "dict"] + result = sanitize_and_sort_request_vars(test_input) + self.assertEqual(result["raw"], test_input) From fcae84dca5cb6b22fdbc05191e09da46e263574a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 25 Mar 2025 07:38:41 +0100 Subject: [PATCH 03/14] [pre-commit.ci] pre-commit autoupdate (#2112) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.0 → v0.11.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.0...v0.11.2) - [github.com/abravalheri/validate-pyproject: v0.24 → v0.24.1](https://github.com/abravalheri/validate-pyproject/compare/v0.24...v0.24.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ee54d2d5d..9d2134b82 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,7 +29,7 @@ repos: - id: biome-check verbose: true - repo: https://github.com/astral-sh/ruff-pre-commit - rev: 'v0.11.0' + rev: 'v0.11.2' hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] @@ -39,6 +39,6 @@ repos: hooks: - id: pyproject-fmt - repo: https://github.com/abravalheri/validate-pyproject - rev: v0.24 + rev: v0.24.1 hooks: - id: validate-pyproject From 7de5a30550b5871d8afdd11cf8feb78e8c6f3948 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 22:09:07 +0200 Subject: [PATCH 04/14] [pre-commit.ci] pre-commit autoupdate (#2115) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9d2134b82..7978a3ba9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,7 +14,7 @@ repos: hooks: - id: doc8 - repo: https://github.com/adamchainz/django-upgrade - rev: 1.23.1 + rev: 1.24.0 hooks: - id: django-upgrade args: [--target-version, "4.2"] From 11e28f7e629feed807c3e5519a4b0f4195fbbc31 Mon Sep 17 00:00:00 2001 From: Prashant Andoriya <121665385+andoriyaprashant@users.noreply.github.com> Date: Tue, 1 Apr 2025 01:48:13 +0530 Subject: [PATCH 05/14] Dark Mode Conflict in Pygments Fixed (#2111) --- .../static/debug_toolbar/css/toolbar.css | 500 +++++++++++++++++- docs/changes.rst | 1 + 2 files changed, 500 insertions(+), 1 deletion(-) diff --git a/debug_toolbar/static/debug_toolbar/css/toolbar.css b/debug_toolbar/static/debug_toolbar/css/toolbar.css index 3d0d34e6c..a45e8a670 100644 --- a/debug_toolbar/static/debug_toolbar/css/toolbar.css +++ b/debug_toolbar/static/debug_toolbar/css/toolbar.css @@ -59,7 +59,7 @@ } #djDebug[data-theme="dark"] { - --djdt-font-color: #8393a7; + --djdt-font-color: #f8f8f2; --djdt-background-color: #1e293bff; --djdt-panel-content-background-color: #0f1729ff; --djdt-panel-content-table-background-color: var(--djdt-background-color); @@ -829,6 +829,504 @@ To regenerate: color: #666666; } /* Literal.Number.Integer.Long */ +@media (prefers-color-scheme: dark) { + :root { + #djDebug .highlight .hll { + background-color: #f1fa8c; + } + #djDebug .highlight { + background: #282a36; + color: #f8f8f2; + } + #djDebug .highlight .c { + color: #6272a4; + } /* Comment */ + #djDebug .highlight .err { + color: #f8f8f2; + } /* Error */ + #djDebug .highlight .g { + color: #f8f8f2; + } /* Generic */ + #djDebug .highlight .k { + color: #ff79c6; + } /* Keyword */ + #djDebug .highlight .l { + color: #f8f8f2; + } /* Literal */ + #djDebug .highlight .n { + color: #f8f8f2; + } /* Name */ + #djDebug .highlight .o { + color: #ff79c6; + } /* Operator */ + #djDebug .highlight .x { + color: #f8f8f2; + } /* Other */ + #djDebug .highlight .p { + color: #f8f8f2; + } /* Punctuation */ + #djDebug .highlight .ch { + color: #6272a4; + } /* Comment.Hashbang */ + #djDebug .highlight .cm { + color: #6272a4; + } /* Comment.Multiline */ + #djDebug .highlight .cp { + color: #ff79c6; + } /* Comment.Preproc */ + #djDebug .highlight .cpf { + color: #6272a4; + } /* Comment.PreprocFile */ + #djDebug .highlight .c1 { + color: #6272a4; + } /* Comment.Single */ + #djDebug .highlight .cs { + color: #6272a4; + } /* Comment.Special */ + #djDebug .highlight .gd { + color: #8b080b; + } /* Generic.Deleted */ + #djDebug .highlight .ge { + color: #f8f8f2; + text-decoration: underline; + } /* Generic.Emph */ + #djDebug .highlight .gr { + color: #f8f8f2; + } /* Generic.Error */ + #djDebug .highlight .gh { + color: #f8f8f2; + font-weight: bold; + } /* Generic.Heading */ + #djDebug .highlight .gi { + color: #f8f8f2; + font-weight: bold; + } /* Generic.Inserted */ + #djDebug .highlight .go { + color: #44475a; + } /* Generic.Output */ + #djDebug .highlight .gp { + color: #f8f8f2; + } /* Generic.Prompt */ + #djDebug .highlight .gs { + color: #f8f8f2; + } /* Generic.Strong */ + #djDebug .highlight .gu { + color: #f8f8f2; + font-weight: bold; + } /* Generic.Subheading */ + #djDebug .highlight .gt { + color: #f8f8f2; + } /* Generic.Traceback */ + #djDebug .highlight .kc { + color: #ff79c6; + } /* Keyword.Constant */ + #djDebug .highlight .kd { + color: #8be9fd; + font-style: italic; + } /* Keyword.Declaration */ + #djDebug .highlight .kn { + color: #ff79c6; + } /* Keyword.Namespace */ + #djDebug .highlight .kp { + color: #ff79c6; + } /* Keyword.Pseudo */ + #djDebug .highlight .kr { + color: #ff79c6; + } /* Keyword.Reserved */ + #djDebug .highlight .kt { + color: #8be9fd; + } /* Keyword.Type */ + #djDebug .highlight .ld { + color: #f8f8f2; + } /* Literal.Date */ + #djDebug .highlight .m { + color: #bd93f9; + } /* Literal.Number */ + #djDebug .highlight .s { + color: #f1fa8c; + } /* Literal.String */ + #djDebug .highlight .na { + color: #50fa7b; + } /* Name.Attribute */ + #djDebug .highlight .nb { + color: #8be9fd; + font-style: italic; + } /* Name.Builtin */ + #djDebug .highlight .nc { + color: #50fa7b; + } /* Name.Class */ + #djDebug .highlight .no { + color: #f8f8f2; + } /* Name.Constant */ + #djDebug .highlight .nd { + color: #f8f8f2; + } /* Name.Decorator */ + #djDebug .highlight .ni { + color: #f8f8f2; + } /* Name.Entity */ + #djDebug .highlight .ne { + color: #f8f8f2; + } /* Name.Exception */ + #djDebug .highlight .nf { + color: #50fa7b; + } /* Name.Function */ + #djDebug .highlight .nl { + color: #8be9fd; + font-style: italic; + } /* Name.Label */ + #djDebug .highlight .nn { + color: #f8f8f2; + } /* Name.Namespace */ + #djDebug .highlight .nx { + color: #f8f8f2; + } /* Name.Other */ + #djDebug .highlight .py { + color: #f8f8f2; + } /* Name.Property */ + #djDebug .highlight .nt { + color: #ff79c6; + } /* Name.Tag */ + #djDebug .highlight .nv { + color: #8be9fd; + font-style: italic; + } /* Name.Variable */ + #djDebug .highlight .ow { + color: #ff79c6; + } /* Operator.Word */ + #djDebug .highlight .w { + color: #f8f8f2; + } /* Text.Whitespace */ + #djDebug .highlight .mb { + color: #bd93f9; + } /* Literal.Number.Bin */ + #djDebug .highlight .mf { + color: #bd93f9; + } /* Literal.Number.Float */ + #djDebug .highlight .mh { + color: #bd93f9; + } /* Literal.Number.Hex */ + #djDebug .highlight .mi { + color: #bd93f9; + } /* Literal.Number.Integer */ + #djDebug .highlight .mo { + color: #bd93f9; + } /* Literal.Number.Oct */ + #djDebug .highlight .sa { + color: #f1fa8c; + } /* Literal.String.Affix */ + #djDebug .highlight .sb { + color: #f1fa8c; + } /* Literal.String.Backtick */ + #djDebug .highlight .sc { + color: #f1fa8c; + } /* Literal.String.Char */ + #djDebug .highlight .dl { + color: #f1fa8c; + } /* Literal.String.Delimiter */ + #djDebug .highlight .sd { + color: #f1fa8c; + } /* Literal.String.Doc */ + #djDebug .highlight .s2 { + color: #f1fa8c; + } /* Literal.String.Double */ + #djDebug .highlight .se { + color: #f1fa8c; + } /* Literal.String.Escape */ + #djDebug .highlight .sh { + color: #f1fa8c; + } /* Literal.String.Heredoc */ + #djDebug .highlight .si { + color: #f1fa8c; + } /* Literal.String.Interpol */ + #djDebug .highlight .sx { + color: #f1fa8c; + } /* Literal.String.Other */ + #djDebug .highlight .sr { + color: #f1fa8c; + } /* Literal.String.Regex */ + #djDebug .highlight .s1 { + color: #f1fa8c; + } /* Literal.String.Single */ + #djDebug .highlight .ss { + color: #f1fa8c; + } /* Literal.String.Symbol */ + #djDebug .highlight .bp { + color: #f8f8f2; + font-style: italic; + } /* Name.Builtin.Pseudo */ + #djDebug .highlight .fm { + color: #50fa7b; + } /* Name.Function.Magic */ + #djDebug .highlight .vc { + color: #8be9fd; + font-style: italic; + } /* Name.Variable.Class */ + #djDebug .highlight .vg { + color: #8be9fd; + font-style: italic; + } /* Name.Variable.Global */ + #djDebug .highlight .vi { + color: #8be9fd; + font-style: italic; + } /* Name.Variable.Instance */ + #djDebug .highlight .vm { + color: #8be9fd; + font-style: italic; + } /* Name.Variable.Magic */ + #djDebug .highlight .il { + color: #bd93f9; + } /* Literal.Number.Integer.Long */ + } +} + +#djDebug[data-theme="dark"] { + #djDebug .highlight .hll { + background-color: #f1fa8c; + } + #djDebug .highlight { + background: #282a36; + color: #f8f8f2; + } + #djDebug .highlight .c { + color: #6272a4; + } /* Comment */ + #djDebug .highlight .err { + color: #f8f8f2; + } /* Error */ + #djDebug .highlight .g { + color: #f8f8f2; + } /* Generic */ + #djDebug .highlight .k { + color: #ff79c6; + } /* Keyword */ + #djDebug .highlight .l { + color: #f8f8f2; + } /* Literal */ + #djDebug .highlight .n { + color: #f8f8f2; + } /* Name */ + #djDebug .highlight .o { + color: #ff79c6; + } /* Operator */ + #djDebug .highlight .x { + color: #f8f8f2; + } /* Other */ + #djDebug .highlight .p { + color: #f8f8f2; + } /* Punctuation */ + #djDebug .highlight .ch { + color: #6272a4; + } /* Comment.Hashbang */ + #djDebug .highlight .cm { + color: #6272a4; + } /* Comment.Multiline */ + #djDebug .highlight .cp { + color: #ff79c6; + } /* Comment.Preproc */ + #djDebug .highlight .cpf { + color: #6272a4; + } /* Comment.PreprocFile */ + #djDebug .highlight .c1 { + color: #6272a4; + } /* Comment.Single */ + #djDebug .highlight .cs { + color: #6272a4; + } /* Comment.Special */ + #djDebug .highlight .gd { + color: #8b080b; + } /* Generic.Deleted */ + #djDebug .highlight .ge { + color: #f8f8f2; + text-decoration: underline; + } /* Generic.Emph */ + #djDebug .highlight .gr { + color: #f8f8f2; + } /* Generic.Error */ + #djDebug .highlight .gh { + color: #f8f8f2; + font-weight: bold; + } /* Generic.Heading */ + #djDebug .highlight .gi { + color: #f8f8f2; + font-weight: bold; + } /* Generic.Inserted */ + #djDebug .highlight .go { + color: #44475a; + } /* Generic.Output */ + #djDebug .highlight .gp { + color: #f8f8f2; + } /* Generic.Prompt */ + #djDebug .highlight .gs { + color: #f8f8f2; + } /* Generic.Strong */ + #djDebug .highlight .gu { + color: #f8f8f2; + font-weight: bold; + } /* Generic.Subheading */ + #djDebug .highlight .gt { + color: #f8f8f2; + } /* Generic.Traceback */ + #djDebug .highlight .kc { + color: #ff79c6; + } /* Keyword.Constant */ + #djDebug .highlight .kd { + color: #8be9fd; + font-style: italic; + } /* Keyword.Declaration */ + #djDebug .highlight .kn { + color: #ff79c6; + } /* Keyword.Namespace */ + #djDebug .highlight .kp { + color: #ff79c6; + } /* Keyword.Pseudo */ + #djDebug .highlight .kr { + color: #ff79c6; + } /* Keyword.Reserved */ + #djDebug .highlight .kt { + color: #8be9fd; + } /* Keyword.Type */ + #djDebug .highlight .ld { + color: #f8f8f2; + } /* Literal.Date */ + #djDebug .highlight .m { + color: #bd93f9; + } /* Literal.Number */ + #djDebug .highlight .s { + color: #f1fa8c; + } /* Literal.String */ + #djDebug .highlight .na { + color: #50fa7b; + } /* Name.Attribute */ + #djDebug .highlight .nb { + color: #8be9fd; + font-style: italic; + } /* Name.Builtin */ + #djDebug .highlight .nc { + color: #50fa7b; + } /* Name.Class */ + #djDebug .highlight .no { + color: #f8f8f2; + } /* Name.Constant */ + #djDebug .highlight .nd { + color: #f8f8f2; + } /* Name.Decorator */ + #djDebug .highlight .ni { + color: #f8f8f2; + } /* Name.Entity */ + #djDebug .highlight .ne { + color: #f8f8f2; + } /* Name.Exception */ + #djDebug .highlight .nf { + color: #50fa7b; + } /* Name.Function */ + #djDebug .highlight .nl { + color: #8be9fd; + font-style: italic; + } /* Name.Label */ + #djDebug .highlight .nn { + color: #f8f8f2; + } /* Name.Namespace */ + #djDebug .highlight .nx { + color: #f8f8f2; + } /* Name.Other */ + #djDebug .highlight .py { + color: #f8f8f2; + } /* Name.Property */ + #djDebug .highlight .nt { + color: #ff79c6; + } /* Name.Tag */ + #djDebug .highlight .nv { + color: #8be9fd; + font-style: italic; + } /* Name.Variable */ + #djDebug .highlight .ow { + color: #ff79c6; + } /* Operator.Word */ + #djDebug .highlight .w { + color: #f8f8f2; + } /* Text.Whitespace */ + #djDebug .highlight .mb { + color: #bd93f9; + } /* Literal.Number.Bin */ + #djDebug .highlight .mf { + color: #bd93f9; + } /* Literal.Number.Float */ + #djDebug .highlight .mh { + color: #bd93f9; + } /* Literal.Number.Hex */ + #djDebug .highlight .mi { + color: #bd93f9; + } /* Literal.Number.Integer */ + #djDebug .highlight .mo { + color: #bd93f9; + } /* Literal.Number.Oct */ + #djDebug .highlight .sa { + color: #f1fa8c; + } /* Literal.String.Affix */ + #djDebug .highlight .sb { + color: #f1fa8c; + } /* Literal.String.Backtick */ + #djDebug .highlight .sc { + color: #f1fa8c; + } /* Literal.String.Char */ + #djDebug .highlight .dl { + color: #f1fa8c; + } /* Literal.String.Delimiter */ + #djDebug .highlight .sd { + color: #f1fa8c; + } /* Literal.String.Doc */ + #djDebug .highlight .s2 { + color: #f1fa8c; + } /* Literal.String.Double */ + #djDebug .highlight .se { + color: #f1fa8c; + } /* Literal.String.Escape */ + #djDebug .highlight .sh { + color: #f1fa8c; + } /* Literal.String.Heredoc */ + #djDebug .highlight .si { + color: #f1fa8c; + } /* Literal.String.Interpol */ + #djDebug .highlight .sx { + color: #f1fa8c; + } /* Literal.String.Other */ + #djDebug .highlight .sr { + color: #f1fa8c; + } /* Literal.String.Regex */ + #djDebug .highlight .s1 { + color: #f1fa8c; + } /* Literal.String.Single */ + #djDebug .highlight .ss { + color: #f1fa8c; + } /* Literal.String.Symbol */ + #djDebug .highlight .bp { + color: #f8f8f2; + font-style: italic; + } /* Name.Builtin.Pseudo */ + #djDebug .highlight .fm { + color: #50fa7b; + } /* Name.Function.Magic */ + #djDebug .highlight .vc { + color: #8be9fd; + font-style: italic; + } /* Name.Variable.Class */ + #djDebug .highlight .vg { + color: #8be9fd; + font-style: italic; + } /* Name.Variable.Global */ + #djDebug .highlight .vi { + color: #8be9fd; + font-style: italic; + } /* Name.Variable.Instance */ + #djDebug .highlight .vm { + color: #8be9fd; + font-style: italic; + } /* Name.Variable.Magic */ + #djDebug .highlight .il { + color: #bd93f9; + } /* Literal.Number.Integer.Long */ +} + #djDebug svg.djDebugLineChart { width: 100%; height: 1.5em; diff --git a/docs/changes.rst b/docs/changes.rst index 7f8fabbc5..7911e9e4d 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -6,6 +6,7 @@ Pending * Added hook to RedirectsPanel for subclass customization. * Added feature to sanitize sensitive data in the Request Panel. +* Fixed dark mode conflict in code block toolbar CSS 5.1.0 (2025-03-20) ------------------ From 1ea7c95b835c70356a558d010407759109032a71 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Wed, 2 Apr 2025 13:26:35 +0200 Subject: [PATCH 06/14] Fix #2109: Recursively unwrap loaders to support template partials (#2117) --- debug_toolbar/panels/templates/views.py | 15 +++++++++------ docs/changes.rst | 3 +++ tests/panels/test_template.py | 18 ++++++++++++++++++ tests/settings.py | 5 +++++ tox.ini | 1 + 5 files changed, 36 insertions(+), 6 deletions(-) diff --git a/debug_toolbar/panels/templates/views.py b/debug_toolbar/panels/templates/views.py index 898639c54..b8a0a376f 100644 --- a/debug_toolbar/panels/templates/views.py +++ b/debug_toolbar/panels/templates/views.py @@ -27,15 +27,18 @@ def template_source(request): template_name = request.GET.get("template", template_origin_name) final_loaders = [] - loaders = Engine.get_default().template_loaders + loaders = list(Engine.get_default().template_loaders) + + while loaders: + loader = loaders.pop(0) - for loader in loaders: if loader is not None: - # When the loader has loaders associated with it, - # append those loaders to the list. This occurs with - # django.template.loaders.cached.Loader + # Recursively unwrap loaders until we get to loaders which do not + # themselves wrap other loaders. This adds support for + # django.template.loaders.cached.Loader and the + # django-template-partials loader (possibly among others) if hasattr(loader, "loaders"): - final_loaders += loader.loaders + loaders.extend(loader.loaders) else: final_loaders.append(loader) diff --git a/docs/changes.rst b/docs/changes.rst index 7911e9e4d..a4a45afc0 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -7,6 +7,9 @@ Pending * Added hook to RedirectsPanel for subclass customization. * Added feature to sanitize sensitive data in the Request Panel. * Fixed dark mode conflict in code block toolbar CSS +* Added support for using django-template-partials with the template panel's + source view functionality. The same change possibly adds support for other + template loaders. 5.1.0 (2025-03-20) ------------------ diff --git a/tests/panels/test_template.py b/tests/panels/test_template.py index 636e88a23..44ac4ff0d 100644 --- a/tests/panels/test_template.py +++ b/tests/panels/test_template.py @@ -132,6 +132,24 @@ def test_lazyobject_eval(self): self.panel.generate_stats(self.request, response) self.assertIn("lazy_value", self.panel.content) + @override_settings( + DEBUG=True, + DEBUG_TOOLBAR_PANELS=["debug_toolbar.panels.templates.TemplatesPanel"], + ) + def test_template_source(self): + from django.core import signing + from django.template.loader import get_template + + template = get_template("basic.html") + url = "/__debug__/template_source/" + data = { + "template": template.template.name, + "template_origin": signing.dumps(template.template.origin.name), + } + + response = self.client.get(url, data) + self.assertEqual(response.status_code, 200) + @override_settings( DEBUG=True, DEBUG_TOOLBAR_PANELS=["debug_toolbar.panels.templates.TemplatesPanel"] diff --git a/tests/settings.py b/tests/settings.py index 0bf88bec1..12561fb11 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -7,6 +7,7 @@ # Quick-start development settings - unsuitable for production +DEBUG = False SECRET_KEY = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" INTERNAL_IPS = ["127.0.0.1"] @@ -27,6 +28,10 @@ "django.contrib.messages", "django.contrib.staticfiles", "debug_toolbar", + # We are not actively using template-partials; we just want more nesting + # in our template loader configuration, see + # https://github.com/django-commons/django-debug-toolbar/issues/2109 + "template_partials", "tests", ] diff --git a/tox.ini b/tox.ini index c8f4a6815..e2dcdd6c6 100644 --- a/tox.ini +++ b/tox.ini @@ -26,6 +26,7 @@ deps = selenium>=4.8.0 sqlparse django-csp + django-template-partials passenv= CI COVERAGE_ARGS From 66361c53224428666006438d1de68bd6fba96fdb Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Wed, 2 Apr 2025 13:30:18 +0200 Subject: [PATCH 07/14] Refs #2096: The theme selector now controls all colors (#2116) Previously, if the system preference was dark mode and the user explicitly selected the light theme, the @media block still interferred with the styling. This is fixed by only evaluating the color scheme preference when initializing the toolbar and later only looking at our own selected theme. Also, removed the DEFAULT_THEME setting; falling back to system defaults seems much better to me. --- debug_toolbar/settings.py | 1 - .../static/debug_toolbar/css/toolbar.css | 915 ++++++------------ .../static/debug_toolbar/js/toolbar.js | 39 +- .../templates/debug_toolbar/base.html | 3 +- docs/changes.rst | 5 +- docs/configuration.rst | 7 - tests/test_integration.py | 28 +- 7 files changed, 366 insertions(+), 632 deletions(-) diff --git a/debug_toolbar/settings.py b/debug_toolbar/settings.py index e0be35ea8..59d538a0b 100644 --- a/debug_toolbar/settings.py +++ b/debug_toolbar/settings.py @@ -45,7 +45,6 @@ "TOOLBAR_LANGUAGE": None, "IS_RUNNING_TESTS": "test" in sys.argv, "UPDATE_ON_FETCH": False, - "DEFAULT_THEME": "auto", } diff --git a/debug_toolbar/static/debug_toolbar/css/toolbar.css b/debug_toolbar/static/debug_toolbar/css/toolbar.css index a45e8a670..e47dcc975 100644 --- a/debug_toolbar/static/debug_toolbar/css/toolbar.css +++ b/debug_toolbar/static/debug_toolbar/css/toolbar.css @@ -35,29 +35,6 @@ --djdt-raw-border-color: var(--djdt-table-border-color); } -@media (prefers-color-scheme: dark) { - :root { - --djdt-font-color: #f8f8f2; - --djdt-background-color: #1e293bff; - --djdt-panel-content-background-color: #0f1729ff; - --djdt-panel-title-background-color: #242432; - --djdt-djdt-panel-content-table-strip-background-color: #324154ff; - --djdt--highlighted-background-color: #2c2a7dff; - --djdt-toggle-template-background-color: #282755; - - --djdt-sql-font-color: var(--djdt-font-color); - --djdt-pre-text-color: var(--djdt-font-color); - --djdt-path-and-locals: #65758cff; - --djdt-stack-span-color: #7c8fa4; - --djdt-template-highlight-color: var(--djdt-stack-span-color); - - --djdt-table-border-color: #324154ff; - --djdt-button-border-color: var(--djdt-table-border-color); - --djdt-pre-border-color: var(--djdt-table-border-color); - --djdt-raw-border-color: var(--djdt-table-border-color); - } -} - #djDebug[data-theme="dark"] { --djdt-font-color: #f8f8f2; --djdt-background-color: #1e293bff; @@ -569,763 +546,511 @@ To regenerate: from pygments.formatters import HtmlFormatter print(HtmlFormatter(wrapcode=True).get_style_defs()) */ -#djDebug .highlight pre { +#djDebug[data-theme="light"] .highlight pre { line-height: 125%; } -#djDebug .highlight td.linenos .normal { +#djDebug[data-theme="light"] .highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -#djDebug .highlight span.linenos { +#djDebug[data-theme="light"] .highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -#djDebug .highlight td.linenos .special { +#djDebug[data-theme="light"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -#djDebug .highlight span.linenos.special { +#djDebug[data-theme="light"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -#djDebug .highlight .hll { +#djDebug[data-theme="light"] .highlight .hll { background-color: #ffffcc; } -#djDebug .highlight .c { +#djDebug[data-theme="light"] .highlight .c { color: #3d7b7b; font-style: italic; } /* Comment */ -#djDebug .highlight .err { +#djDebug[data-theme="light"] .highlight .err { border: 1px solid #ff0000; } /* Error */ -#djDebug .highlight .k { +#djDebug[data-theme="light"] .highlight .k { color: #008000; font-weight: bold; } /* Keyword */ -#djDebug .highlight .o { +#djDebug[data-theme="light"] .highlight .o { color: #666666; } /* Operator */ -#djDebug .highlight .ch { +#djDebug[data-theme="light"] .highlight .ch { color: #3d7b7b; font-style: italic; } /* Comment.Hashbang */ -#djDebug .highlight .cm { +#djDebug[data-theme="light"] .highlight .cm { color: #3d7b7b; font-style: italic; } /* Comment.Multiline */ -#djDebug .highlight .cp { +#djDebug[data-theme="light"] .highlight .cp { color: #9c6500; } /* Comment.Preproc */ -#djDebug .highlight .cpf { +#djDebug[data-theme="light"] .highlight .cpf { color: #3d7b7b; font-style: italic; } /* Comment.PreprocFile */ -#djDebug .highlight .c1 { +#djDebug[data-theme="light"] .highlight .c1 { color: #3d7b7b; font-style: italic; } /* Comment.Single */ -#djDebug .highlight .cs { +#djDebug[data-theme="light"] .highlight .cs { color: #3d7b7b; font-style: italic; } /* Comment.Special */ -#djDebug .highlight .gd { +#djDebug[data-theme="light"] .highlight .gd { color: #a00000; } /* Generic.Deleted */ -#djDebug .highlight .ge { +#djDebug[data-theme="light"] .highlight .ge { font-style: italic; } /* Generic.Emph */ -#djDebug .highlight .ges { +#djDebug[data-theme="light"] .highlight .ges { font-weight: bold; font-style: italic; } /* Generic.EmphStrong */ -#djDebug .highlight .gr { +#djDebug[data-theme="light"] .highlight .gr { color: #e40000; } /* Generic.Error */ -#djDebug .highlight .gh { +#djDebug[data-theme="light"] .highlight .gh { color: #000080; font-weight: bold; } /* Generic.Heading */ -#djDebug .highlight .gi { +#djDebug[data-theme="light"] .highlight .gi { color: #008400; } /* Generic.Inserted */ -#djDebug .highlight .go { +#djDebug[data-theme="light"] .highlight .go { color: #717171; } /* Generic.Output */ -#djDebug .highlight .gp { +#djDebug[data-theme="light"] .highlight .gp { color: #000080; font-weight: bold; } /* Generic.Prompt */ -#djDebug .highlight .gs { +#djDebug[data-theme="light"] .highlight .gs { font-weight: bold; } /* Generic.Strong */ -#djDebug .highlight .gu { +#djDebug[data-theme="light"] .highlight .gu { color: #800080; font-weight: bold; } /* Generic.Subheading */ -#djDebug .highlight .gt { +#djDebug[data-theme="light"] .highlight .gt { color: #0044dd; } /* Generic.Traceback */ -#djDebug .highlight .kc { +#djDebug[data-theme="light"] .highlight .kc { color: #008000; font-weight: bold; } /* Keyword.Constant */ -#djDebug .highlight .kd { +#djDebug[data-theme="light"] .highlight .kd { color: #008000; font-weight: bold; } /* Keyword.Declaration */ -#djDebug .highlight .kn { +#djDebug[data-theme="light"] .highlight .kn { color: #008000; font-weight: bold; } /* Keyword.Namespace */ -#djDebug .highlight .kp { +#djDebug[data-theme="light"] .highlight .kp { color: #008000; } /* Keyword.Pseudo */ -#djDebug .highlight .kr { +#djDebug[data-theme="light"] .highlight .kr { color: #008000; font-weight: bold; } /* Keyword.Reserved */ -#djDebug .highlight .kt { +#djDebug[data-theme="light"] .highlight .kt { color: #b00040; } /* Keyword.Type */ -#djDebug .highlight .m { +#djDebug[data-theme="light"] .highlight .m { color: #666666; } /* Literal.Number */ -#djDebug .highlight .s { +#djDebug[data-theme="light"] .highlight .s { color: #ba2121; } /* Literal.String */ -#djDebug .highlight .na { +#djDebug[data-theme="light"] .highlight .na { color: #687822; } /* Name.Attribute */ -#djDebug .highlight .nb { +#djDebug[data-theme="light"] .highlight .nb { color: #008000; } /* Name.Builtin */ -#djDebug .highlight .nc { +#djDebug[data-theme="light"] .highlight .nc { color: #0000ff; font-weight: bold; } /* Name.Class */ -#djDebug .highlight .no { +#djDebug[data-theme="light"] .highlight .no { color: #880000; } /* Name.Constant */ -#djDebug .highlight .nd { +#djDebug[data-theme="light"] .highlight .nd { color: #aa22ff; } /* Name.Decorator */ -#djDebug .highlight .ni { +#djDebug[data-theme="light"] .highlight .ni { color: #717171; font-weight: bold; } /* Name.Entity */ -#djDebug .highlight .ne { +#djDebug[data-theme="light"] .highlight .ne { color: #cb3f38; font-weight: bold; } /* Name.Exception */ -#djDebug .highlight .nf { +#djDebug[data-theme="light"] .highlight .nf { color: #0000ff; } /* Name.Function */ -#djDebug .highlight .nl { +#djDebug[data-theme="light"] .highlight .nl { color: #767600; } /* Name.Label */ -#djDebug .highlight .nn { +#djDebug[data-theme="light"] .highlight .nn { color: #0000ff; font-weight: bold; } /* Name.Namespace */ -#djDebug .highlight .nt { +#djDebug[data-theme="light"] .highlight .nt { color: #008000; font-weight: bold; } /* Name.Tag */ -#djDebug .highlight .nv { +#djDebug[data-theme="light"] .highlight .nv { color: #19177c; } /* Name.Variable */ -#djDebug .highlight .ow { +#djDebug[data-theme="light"] .highlight .ow { color: #aa22ff; font-weight: bold; } /* Operator.Word */ -#djDebug .highlight .w { +#djDebug[data-theme="light"] .highlight .w { color: #bbbbbb; white-space: pre-wrap; } /* Text.Whitespace */ -#djDebug .highlight .mb { +#djDebug[data-theme="light"] .highlight .mb { color: #666666; } /* Literal.Number.Bin */ -#djDebug .highlight .mf { +#djDebug[data-theme="light"] .highlight .mf { color: #666666; } /* Literal.Number.Float */ -#djDebug .highlight .mh { +#djDebug[data-theme="light"] .highlight .mh { color: #666666; } /* Literal.Number.Hex */ -#djDebug .highlight .mi { +#djDebug[data-theme="light"] .highlight .mi { color: #666666; } /* Literal.Number.Integer */ -#djDebug .highlight .mo { +#djDebug[data-theme="light"] .highlight .mo { color: #666666; } /* Literal.Number.Oct */ -#djDebug .highlight .sa { +#djDebug[data-theme="light"] .highlight .sa { color: #ba2121; } /* Literal.String.Affix */ -#djDebug .highlight .sb { +#djDebug[data-theme="light"] .highlight .sb { color: #ba2121; } /* Literal.String.Backtick */ -#djDebug .highlight .sc { +#djDebug[data-theme="light"] .highlight .sc { color: #ba2121; } /* Literal.String.Char */ -#djDebug .highlight .dl { +#djDebug[data-theme="light"] .highlight .dl { color: #ba2121; } /* Literal.String.Delimiter */ -#djDebug .highlight .sd { +#djDebug[data-theme="light"] .highlight .sd { color: #ba2121; font-style: italic; } /* Literal.String.Doc */ -#djDebug .highlight .s2 { +#djDebug[data-theme="light"] .highlight .s2 { color: #ba2121; } /* Literal.String.Double */ -#djDebug .highlight .se { +#djDebug[data-theme="light"] .highlight .se { color: #aa5d1f; font-weight: bold; } /* Literal.String.Escape */ -#djDebug .highlight .sh { +#djDebug[data-theme="light"] .highlight .sh { color: #ba2121; } /* Literal.String.Heredoc */ -#djDebug .highlight .si { +#djDebug[data-theme="light"] .highlight .si { color: #a45a77; font-weight: bold; } /* Literal.String.Interpol */ -#djDebug .highlight .sx { +#djDebug[data-theme="light"] .highlight .sx { color: #008000; } /* Literal.String.Other */ -#djDebug .highlight .sr { +#djDebug[data-theme="light"] .highlight .sr { color: #a45a77; } /* Literal.String.Regex */ -#djDebug .highlight .s1 { +#djDebug[data-theme="light"] .highlight .s1 { color: #ba2121; } /* Literal.String.Single */ -#djDebug .highlight .ss { +#djDebug[data-theme="light"] .highlight .ss { color: #19177c; } /* Literal.String.Symbol */ -#djDebug .highlight .bp { +#djDebug[data-theme="light"] .highlight .bp { color: #008000; } /* Name.Builtin.Pseudo */ -#djDebug .highlight .fm { +#djDebug[data-theme="light"] .highlight .fm { color: #0000ff; } /* Name.Function.Magic */ -#djDebug .highlight .vc { +#djDebug[data-theme="light"] .highlight .vc { color: #19177c; } /* Name.Variable.Class */ -#djDebug .highlight .vg { +#djDebug[data-theme="light"] .highlight .vg { color: #19177c; } /* Name.Variable.Global */ -#djDebug .highlight .vi { +#djDebug[data-theme="light"] .highlight .vi { color: #19177c; } /* Name.Variable.Instance */ -#djDebug .highlight .vm { +#djDebug[data-theme="light"] .highlight .vm { color: #19177c; } /* Name.Variable.Magic */ -#djDebug .highlight .il { +#djDebug[data-theme="light"] .highlight .il { color: #666666; } /* Literal.Number.Integer.Long */ -@media (prefers-color-scheme: dark) { - :root { - #djDebug .highlight .hll { - background-color: #f1fa8c; - } - #djDebug .highlight { - background: #282a36; - color: #f8f8f2; - } - #djDebug .highlight .c { - color: #6272a4; - } /* Comment */ - #djDebug .highlight .err { - color: #f8f8f2; - } /* Error */ - #djDebug .highlight .g { - color: #f8f8f2; - } /* Generic */ - #djDebug .highlight .k { - color: #ff79c6; - } /* Keyword */ - #djDebug .highlight .l { - color: #f8f8f2; - } /* Literal */ - #djDebug .highlight .n { - color: #f8f8f2; - } /* Name */ - #djDebug .highlight .o { - color: #ff79c6; - } /* Operator */ - #djDebug .highlight .x { - color: #f8f8f2; - } /* Other */ - #djDebug .highlight .p { - color: #f8f8f2; - } /* Punctuation */ - #djDebug .highlight .ch { - color: #6272a4; - } /* Comment.Hashbang */ - #djDebug .highlight .cm { - color: #6272a4; - } /* Comment.Multiline */ - #djDebug .highlight .cp { - color: #ff79c6; - } /* Comment.Preproc */ - #djDebug .highlight .cpf { - color: #6272a4; - } /* Comment.PreprocFile */ - #djDebug .highlight .c1 { - color: #6272a4; - } /* Comment.Single */ - #djDebug .highlight .cs { - color: #6272a4; - } /* Comment.Special */ - #djDebug .highlight .gd { - color: #8b080b; - } /* Generic.Deleted */ - #djDebug .highlight .ge { - color: #f8f8f2; - text-decoration: underline; - } /* Generic.Emph */ - #djDebug .highlight .gr { - color: #f8f8f2; - } /* Generic.Error */ - #djDebug .highlight .gh { - color: #f8f8f2; - font-weight: bold; - } /* Generic.Heading */ - #djDebug .highlight .gi { - color: #f8f8f2; - font-weight: bold; - } /* Generic.Inserted */ - #djDebug .highlight .go { - color: #44475a; - } /* Generic.Output */ - #djDebug .highlight .gp { - color: #f8f8f2; - } /* Generic.Prompt */ - #djDebug .highlight .gs { - color: #f8f8f2; - } /* Generic.Strong */ - #djDebug .highlight .gu { - color: #f8f8f2; - font-weight: bold; - } /* Generic.Subheading */ - #djDebug .highlight .gt { - color: #f8f8f2; - } /* Generic.Traceback */ - #djDebug .highlight .kc { - color: #ff79c6; - } /* Keyword.Constant */ - #djDebug .highlight .kd { - color: #8be9fd; - font-style: italic; - } /* Keyword.Declaration */ - #djDebug .highlight .kn { - color: #ff79c6; - } /* Keyword.Namespace */ - #djDebug .highlight .kp { - color: #ff79c6; - } /* Keyword.Pseudo */ - #djDebug .highlight .kr { - color: #ff79c6; - } /* Keyword.Reserved */ - #djDebug .highlight .kt { - color: #8be9fd; - } /* Keyword.Type */ - #djDebug .highlight .ld { - color: #f8f8f2; - } /* Literal.Date */ - #djDebug .highlight .m { - color: #bd93f9; - } /* Literal.Number */ - #djDebug .highlight .s { - color: #f1fa8c; - } /* Literal.String */ - #djDebug .highlight .na { - color: #50fa7b; - } /* Name.Attribute */ - #djDebug .highlight .nb { - color: #8be9fd; - font-style: italic; - } /* Name.Builtin */ - #djDebug .highlight .nc { - color: #50fa7b; - } /* Name.Class */ - #djDebug .highlight .no { - color: #f8f8f2; - } /* Name.Constant */ - #djDebug .highlight .nd { - color: #f8f8f2; - } /* Name.Decorator */ - #djDebug .highlight .ni { - color: #f8f8f2; - } /* Name.Entity */ - #djDebug .highlight .ne { - color: #f8f8f2; - } /* Name.Exception */ - #djDebug .highlight .nf { - color: #50fa7b; - } /* Name.Function */ - #djDebug .highlight .nl { - color: #8be9fd; - font-style: italic; - } /* Name.Label */ - #djDebug .highlight .nn { - color: #f8f8f2; - } /* Name.Namespace */ - #djDebug .highlight .nx { - color: #f8f8f2; - } /* Name.Other */ - #djDebug .highlight .py { - color: #f8f8f2; - } /* Name.Property */ - #djDebug .highlight .nt { - color: #ff79c6; - } /* Name.Tag */ - #djDebug .highlight .nv { - color: #8be9fd; - font-style: italic; - } /* Name.Variable */ - #djDebug .highlight .ow { - color: #ff79c6; - } /* Operator.Word */ - #djDebug .highlight .w { - color: #f8f8f2; - } /* Text.Whitespace */ - #djDebug .highlight .mb { - color: #bd93f9; - } /* Literal.Number.Bin */ - #djDebug .highlight .mf { - color: #bd93f9; - } /* Literal.Number.Float */ - #djDebug .highlight .mh { - color: #bd93f9; - } /* Literal.Number.Hex */ - #djDebug .highlight .mi { - color: #bd93f9; - } /* Literal.Number.Integer */ - #djDebug .highlight .mo { - color: #bd93f9; - } /* Literal.Number.Oct */ - #djDebug .highlight .sa { - color: #f1fa8c; - } /* Literal.String.Affix */ - #djDebug .highlight .sb { - color: #f1fa8c; - } /* Literal.String.Backtick */ - #djDebug .highlight .sc { - color: #f1fa8c; - } /* Literal.String.Char */ - #djDebug .highlight .dl { - color: #f1fa8c; - } /* Literal.String.Delimiter */ - #djDebug .highlight .sd { - color: #f1fa8c; - } /* Literal.String.Doc */ - #djDebug .highlight .s2 { - color: #f1fa8c; - } /* Literal.String.Double */ - #djDebug .highlight .se { - color: #f1fa8c; - } /* Literal.String.Escape */ - #djDebug .highlight .sh { - color: #f1fa8c; - } /* Literal.String.Heredoc */ - #djDebug .highlight .si { - color: #f1fa8c; - } /* Literal.String.Interpol */ - #djDebug .highlight .sx { - color: #f1fa8c; - } /* Literal.String.Other */ - #djDebug .highlight .sr { - color: #f1fa8c; - } /* Literal.String.Regex */ - #djDebug .highlight .s1 { - color: #f1fa8c; - } /* Literal.String.Single */ - #djDebug .highlight .ss { - color: #f1fa8c; - } /* Literal.String.Symbol */ - #djDebug .highlight .bp { - color: #f8f8f2; - font-style: italic; - } /* Name.Builtin.Pseudo */ - #djDebug .highlight .fm { - color: #50fa7b; - } /* Name.Function.Magic */ - #djDebug .highlight .vc { - color: #8be9fd; - font-style: italic; - } /* Name.Variable.Class */ - #djDebug .highlight .vg { - color: #8be9fd; - font-style: italic; - } /* Name.Variable.Global */ - #djDebug .highlight .vi { - color: #8be9fd; - font-style: italic; - } /* Name.Variable.Instance */ - #djDebug .highlight .vm { - color: #8be9fd; - font-style: italic; - } /* Name.Variable.Magic */ - #djDebug .highlight .il { - color: #bd93f9; - } /* Literal.Number.Integer.Long */ - } +#djDebug[data-theme="dark"] .highlight .hll { + background-color: #f1fa8c; } - -#djDebug[data-theme="dark"] { - #djDebug .highlight .hll { - background-color: #f1fa8c; - } - #djDebug .highlight { - background: #282a36; - color: #f8f8f2; - } - #djDebug .highlight .c { - color: #6272a4; - } /* Comment */ - #djDebug .highlight .err { - color: #f8f8f2; - } /* Error */ - #djDebug .highlight .g { - color: #f8f8f2; - } /* Generic */ - #djDebug .highlight .k { - color: #ff79c6; - } /* Keyword */ - #djDebug .highlight .l { - color: #f8f8f2; - } /* Literal */ - #djDebug .highlight .n { - color: #f8f8f2; - } /* Name */ - #djDebug .highlight .o { - color: #ff79c6; - } /* Operator */ - #djDebug .highlight .x { - color: #f8f8f2; - } /* Other */ - #djDebug .highlight .p { - color: #f8f8f2; - } /* Punctuation */ - #djDebug .highlight .ch { - color: #6272a4; - } /* Comment.Hashbang */ - #djDebug .highlight .cm { - color: #6272a4; - } /* Comment.Multiline */ - #djDebug .highlight .cp { - color: #ff79c6; - } /* Comment.Preproc */ - #djDebug .highlight .cpf { - color: #6272a4; - } /* Comment.PreprocFile */ - #djDebug .highlight .c1 { - color: #6272a4; - } /* Comment.Single */ - #djDebug .highlight .cs { - color: #6272a4; - } /* Comment.Special */ - #djDebug .highlight .gd { - color: #8b080b; - } /* Generic.Deleted */ - #djDebug .highlight .ge { - color: #f8f8f2; - text-decoration: underline; - } /* Generic.Emph */ - #djDebug .highlight .gr { - color: #f8f8f2; - } /* Generic.Error */ - #djDebug .highlight .gh { - color: #f8f8f2; - font-weight: bold; - } /* Generic.Heading */ - #djDebug .highlight .gi { - color: #f8f8f2; - font-weight: bold; - } /* Generic.Inserted */ - #djDebug .highlight .go { - color: #44475a; - } /* Generic.Output */ - #djDebug .highlight .gp { - color: #f8f8f2; - } /* Generic.Prompt */ - #djDebug .highlight .gs { - color: #f8f8f2; - } /* Generic.Strong */ - #djDebug .highlight .gu { - color: #f8f8f2; - font-weight: bold; - } /* Generic.Subheading */ - #djDebug .highlight .gt { - color: #f8f8f2; - } /* Generic.Traceback */ - #djDebug .highlight .kc { - color: #ff79c6; - } /* Keyword.Constant */ - #djDebug .highlight .kd { - color: #8be9fd; - font-style: italic; - } /* Keyword.Declaration */ - #djDebug .highlight .kn { - color: #ff79c6; - } /* Keyword.Namespace */ - #djDebug .highlight .kp { - color: #ff79c6; - } /* Keyword.Pseudo */ - #djDebug .highlight .kr { - color: #ff79c6; - } /* Keyword.Reserved */ - #djDebug .highlight .kt { - color: #8be9fd; - } /* Keyword.Type */ - #djDebug .highlight .ld { - color: #f8f8f2; - } /* Literal.Date */ - #djDebug .highlight .m { - color: #bd93f9; - } /* Literal.Number */ - #djDebug .highlight .s { - color: #f1fa8c; - } /* Literal.String */ - #djDebug .highlight .na { - color: #50fa7b; - } /* Name.Attribute */ - #djDebug .highlight .nb { - color: #8be9fd; - font-style: italic; - } /* Name.Builtin */ - #djDebug .highlight .nc { - color: #50fa7b; - } /* Name.Class */ - #djDebug .highlight .no { - color: #f8f8f2; - } /* Name.Constant */ - #djDebug .highlight .nd { - color: #f8f8f2; - } /* Name.Decorator */ - #djDebug .highlight .ni { - color: #f8f8f2; - } /* Name.Entity */ - #djDebug .highlight .ne { - color: #f8f8f2; - } /* Name.Exception */ - #djDebug .highlight .nf { - color: #50fa7b; - } /* Name.Function */ - #djDebug .highlight .nl { - color: #8be9fd; - font-style: italic; - } /* Name.Label */ - #djDebug .highlight .nn { - color: #f8f8f2; - } /* Name.Namespace */ - #djDebug .highlight .nx { - color: #f8f8f2; - } /* Name.Other */ - #djDebug .highlight .py { - color: #f8f8f2; - } /* Name.Property */ - #djDebug .highlight .nt { - color: #ff79c6; - } /* Name.Tag */ - #djDebug .highlight .nv { - color: #8be9fd; - font-style: italic; - } /* Name.Variable */ - #djDebug .highlight .ow { - color: #ff79c6; - } /* Operator.Word */ - #djDebug .highlight .w { - color: #f8f8f2; - } /* Text.Whitespace */ - #djDebug .highlight .mb { - color: #bd93f9; - } /* Literal.Number.Bin */ - #djDebug .highlight .mf { - color: #bd93f9; - } /* Literal.Number.Float */ - #djDebug .highlight .mh { - color: #bd93f9; - } /* Literal.Number.Hex */ - #djDebug .highlight .mi { - color: #bd93f9; - } /* Literal.Number.Integer */ - #djDebug .highlight .mo { - color: #bd93f9; - } /* Literal.Number.Oct */ - #djDebug .highlight .sa { - color: #f1fa8c; - } /* Literal.String.Affix */ - #djDebug .highlight .sb { - color: #f1fa8c; - } /* Literal.String.Backtick */ - #djDebug .highlight .sc { - color: #f1fa8c; - } /* Literal.String.Char */ - #djDebug .highlight .dl { - color: #f1fa8c; - } /* Literal.String.Delimiter */ - #djDebug .highlight .sd { - color: #f1fa8c; - } /* Literal.String.Doc */ - #djDebug .highlight .s2 { - color: #f1fa8c; - } /* Literal.String.Double */ - #djDebug .highlight .se { - color: #f1fa8c; - } /* Literal.String.Escape */ - #djDebug .highlight .sh { - color: #f1fa8c; - } /* Literal.String.Heredoc */ - #djDebug .highlight .si { - color: #f1fa8c; - } /* Literal.String.Interpol */ - #djDebug .highlight .sx { - color: #f1fa8c; - } /* Literal.String.Other */ - #djDebug .highlight .sr { - color: #f1fa8c; - } /* Literal.String.Regex */ - #djDebug .highlight .s1 { - color: #f1fa8c; - } /* Literal.String.Single */ - #djDebug .highlight .ss { - color: #f1fa8c; - } /* Literal.String.Symbol */ - #djDebug .highlight .bp { - color: #f8f8f2; - font-style: italic; - } /* Name.Builtin.Pseudo */ - #djDebug .highlight .fm { - color: #50fa7b; - } /* Name.Function.Magic */ - #djDebug .highlight .vc { - color: #8be9fd; - font-style: italic; - } /* Name.Variable.Class */ - #djDebug .highlight .vg { - color: #8be9fd; - font-style: italic; - } /* Name.Variable.Global */ - #djDebug .highlight .vi { - color: #8be9fd; - font-style: italic; - } /* Name.Variable.Instance */ - #djDebug .highlight .vm { - color: #8be9fd; - font-style: italic; - } /* Name.Variable.Magic */ - #djDebug .highlight .il { - color: #bd93f9; - } /* Literal.Number.Integer.Long */ +#djDebug[data-theme="dark"] .highlight { + background: #282a36; + color: #f8f8f2; } +#djDebug[data-theme="dark"] .highlight .c { + color: #6272a4; +} /* Comment */ +#djDebug[data-theme="dark"] .highlight .err { + color: #f8f8f2; +} /* Error */ +#djDebug[data-theme="dark"] .highlight .g { + color: #f8f8f2; +} /* Generic */ +#djDebug[data-theme="dark"] .highlight .k { + color: #ff79c6; +} /* Keyword */ +#djDebug[data-theme="dark"] .highlight .l { + color: #f8f8f2; +} /* Literal */ +#djDebug[data-theme="dark"] .highlight .n { + color: #f8f8f2; +} /* Name */ +#djDebug[data-theme="dark"] .highlight .o { + color: #ff79c6; +} /* Operator */ +#djDebug[data-theme="dark"] .highlight .x { + color: #f8f8f2; +} /* Other */ +#djDebug[data-theme="dark"] .highlight .p { + color: #f8f8f2; +} /* Punctuation */ +#djDebug[data-theme="dark"] .highlight .ch { + color: #6272a4; +} /* Comment.Hashbang */ +#djDebug[data-theme="dark"] .highlight .cm { + color: #6272a4; +} /* Comment.Multiline */ +#djDebug[data-theme="dark"] .highlight .cp { + color: #ff79c6; +} /* Comment.Preproc */ +#djDebug[data-theme="dark"] .highlight .cpf { + color: #6272a4; +} /* Comment.PreprocFile */ +#djDebug[data-theme="dark"] .highlight .c1 { + color: #6272a4; +} /* Comment.Single */ +#djDebug[data-theme="dark"] .highlight .cs { + color: #6272a4; +} /* Comment.Special */ +#djDebug[data-theme="dark"] .highlight .gd { + color: #8b080b; +} /* Generic.Deleted */ +#djDebug[data-theme="dark"] .highlight .ge { + color: #f8f8f2; + text-decoration: underline; +} /* Generic.Emph */ +#djDebug[data-theme="dark"] .highlight .gr { + color: #f8f8f2; +} /* Generic.Error */ +#djDebug[data-theme="dark"] .highlight .gh { + color: #f8f8f2; + font-weight: bold; +} /* Generic.Heading */ +#djDebug[data-theme="dark"] .highlight .gi { + color: #f8f8f2; + font-weight: bold; +} /* Generic.Inserted */ +#djDebug[data-theme="dark"] .highlight .go { + color: #44475a; +} /* Generic.Output */ +#djDebug[data-theme="dark"] .highlight .gp { + color: #f8f8f2; +} /* Generic.Prompt */ +#djDebug[data-theme="dark"] .highlight .gs { + color: #f8f8f2; +} /* Generic.Strong */ +#djDebug[data-theme="dark"] .highlight .gu { + color: #f8f8f2; + font-weight: bold; +} /* Generic.Subheading */ +#djDebug[data-theme="dark"] .highlight .gt { + color: #f8f8f2; +} /* Generic.Traceback */ +#djDebug[data-theme="dark"] .highlight .kc { + color: #ff79c6; +} /* Keyword.Constant */ +#djDebug[data-theme="dark"] .highlight .kd { + color: #8be9fd; + font-style: italic; +} /* Keyword.Declaration */ +#djDebug[data-theme="dark"] .highlight .kn { + color: #ff79c6; +} /* Keyword.Namespace */ +#djDebug[data-theme="dark"] .highlight .kp { + color: #ff79c6; +} /* Keyword.Pseudo */ +#djDebug[data-theme="dark"] .highlight .kr { + color: #ff79c6; +} /* Keyword.Reserved */ +#djDebug[data-theme="dark"] .highlight .kt { + color: #8be9fd; +} /* Keyword.Type */ +#djDebug[data-theme="dark"] .highlight .ld { + color: #f8f8f2; +} /* Literal.Date */ +#djDebug[data-theme="dark"] .highlight .m { + color: #bd93f9; +} /* Literal.Number */ +#djDebug[data-theme="dark"] .highlight .s { + color: #f1fa8c; +} /* Literal.String */ +#djDebug[data-theme="dark"] .highlight .na { + color: #50fa7b; +} /* Name.Attribute */ +#djDebug[data-theme="dark"] .highlight .nb { + color: #8be9fd; + font-style: italic; +} /* Name.Builtin */ +#djDebug[data-theme="dark"] .highlight .nc { + color: #50fa7b; +} /* Name.Class */ +#djDebug[data-theme="dark"] .highlight .no { + color: #f8f8f2; +} /* Name.Constant */ +#djDebug[data-theme="dark"] .highlight .nd { + color: #f8f8f2; +} /* Name.Decorator */ +#djDebug[data-theme="dark"] .highlight .ni { + color: #f8f8f2; +} /* Name.Entity */ +#djDebug[data-theme="dark"] .highlight .ne { + color: #f8f8f2; +} /* Name.Exception */ +#djDebug[data-theme="dark"] .highlight .nf { + color: #50fa7b; +} /* Name.Function */ +#djDebug[data-theme="dark"] .highlight .nl { + color: #8be9fd; + font-style: italic; +} /* Name.Label */ +#djDebug[data-theme="dark"] .highlight .nn { + color: #f8f8f2; +} /* Name.Namespace */ +#djDebug[data-theme="dark"] .highlight .nx { + color: #f8f8f2; +} /* Name.Other */ +#djDebug[data-theme="dark"] .highlight .py { + color: #f8f8f2; +} /* Name.Property */ +#djDebug[data-theme="dark"] .highlight .nt { + color: #ff79c6; +} /* Name.Tag */ +#djDebug[data-theme="dark"] .highlight .nv { + color: #8be9fd; + font-style: italic; +} /* Name.Variable */ +#djDebug[data-theme="dark"] .highlight .ow { + color: #ff79c6; +} /* Operator.Word */ +#djDebug[data-theme="dark"] .highlight .w { + color: #f8f8f2; +} /* Text.Whitespace */ +#djDebug[data-theme="dark"] .highlight .mb { + color: #bd93f9; +} /* Literal.Number.Bin */ +#djDebug[data-theme="dark"] .highlight .mf { + color: #bd93f9; +} /* Literal.Number.Float */ +#djDebug[data-theme="dark"] .highlight .mh { + color: #bd93f9; +} /* Literal.Number.Hex */ +#djDebug[data-theme="dark"] .highlight .mi { + color: #bd93f9; +} /* Literal.Number.Integer */ +#djDebug[data-theme="dark"] .highlight .mo { + color: #bd93f9; +} /* Literal.Number.Oct */ +#djDebug[data-theme="dark"] .highlight .sa { + color: #f1fa8c; +} /* Literal.String.Affix */ +#djDebug[data-theme="dark"] .highlight .sb { + color: #f1fa8c; +} /* Literal.String.Backtick */ +#djDebug[data-theme="dark"] .highlight .sc { + color: #f1fa8c; +} /* Literal.String.Char */ +#djDebug[data-theme="dark"] .highlight .dl { + color: #f1fa8c; +} /* Literal.String.Delimiter */ +#djDebug[data-theme="dark"] .highlight .sd { + color: #f1fa8c; +} /* Literal.String.Doc */ +#djDebug[data-theme="dark"] .highlight .s2 { + color: #f1fa8c; +} /* Literal.String.Double */ +#djDebug[data-theme="dark"] .highlight .se { + color: #f1fa8c; +} /* Literal.String.Escape */ +#djDebug[data-theme="dark"] .highlight .sh { + color: #f1fa8c; +} /* Literal.String.Heredoc */ +#djDebug[data-theme="dark"] .highlight .si { + color: #f1fa8c; +} /* Literal.String.Interpol */ +#djDebug[data-theme="dark"] .highlight .sx { + color: #f1fa8c; +} /* Literal.String.Other */ +#djDebug[data-theme="dark"] .highlight .sr { + color: #f1fa8c; +} /* Literal.String.Regex */ +#djDebug[data-theme="dark"] .highlight .s1 { + color: #f1fa8c; +} /* Literal.String.Single */ +#djDebug[data-theme="dark"] .highlight .ss { + color: #f1fa8c; +} /* Literal.String.Symbol */ +#djDebug[data-theme="dark"] .highlight .bp { + color: #f8f8f2; + font-style: italic; +} /* Name.Builtin.Pseudo */ +#djDebug[data-theme="dark"] .highlight .fm { + color: #50fa7b; +} /* Name.Function.Magic */ +#djDebug[data-theme="dark"] .highlight .vc { + color: #8be9fd; + font-style: italic; +} /* Name.Variable.Class */ +#djDebug[data-theme="dark"] .highlight .vg { + color: #8be9fd; + font-style: italic; +} /* Name.Variable.Global */ +#djDebug[data-theme="dark"] .highlight .vi { + color: #8be9fd; + font-style: italic; +} /* Name.Variable.Instance */ +#djDebug[data-theme="dark"] .highlight .vm { + color: #8be9fd; + font-style: italic; +} /* Name.Variable.Magic */ +#djDebug[data-theme="dark"] .highlight .il { + color: #bd93f9; +} /* Literal.Number.Integer.Long */ #djDebug svg.djDebugLineChart { width: 100%; @@ -1445,9 +1170,9 @@ To regenerate: #djToggleThemeButton > svg { margin-left: auto; } -#djDebug[data-theme="light"] #djToggleThemeButton svg.theme-light, -#djDebug[data-theme="dark"] #djToggleThemeButton svg.theme-dark, -#djDebug[data-theme="auto"] #djToggleThemeButton svg.theme-auto { +#djDebug[data-user-theme="light"] #djToggleThemeButton svg.theme-light, +#djDebug[data-user-theme="dark"] #djToggleThemeButton svg.theme-dark, +#djDebug[data-user-theme="auto"] #djToggleThemeButton svg.theme-auto { display: block; height: 1rem; width: 1rem; diff --git a/debug_toolbar/static/debug_toolbar/js/toolbar.js b/debug_toolbar/static/debug_toolbar/js/toolbar.js index 077bc930a..19658f76e 100644 --- a/debug_toolbar/static/debug_toolbar/js/toolbar.js +++ b/debug_toolbar/static/debug_toolbar/js/toolbar.js @@ -212,27 +212,30 @@ const djdt = { djdt.updateOnAjax(); } + const prefersDark = window.matchMedia( + "(prefers-color-scheme: dark)" + ).matches; + const themeList = prefersDark + ? ["auto", "light", "dark"] + : ["auto", "dark", "light"]; + const setTheme = (theme) => { + djDebug.setAttribute( + "data-theme", + theme === "auto" ? (prefersDark ? "dark" : "light") : theme + ); + djDebug.setAttribute("data-user-theme", theme); + }; + // Updates the theme using user settings - const userTheme = localStorage.getItem("djdt.user-theme"); - if (userTheme !== null) { - djDebug.setAttribute("data-theme", userTheme); - } + let userTheme = localStorage.getItem("djdt.user-theme") || "auto"; + setTheme(userTheme); + // Adds the listener to the Theme Toggle Button $$.on(djDebug, "click", "#djToggleThemeButton", () => { - switch (djDebug.getAttribute("data-theme")) { - case "auto": - djDebug.setAttribute("data-theme", "light"); - localStorage.setItem("djdt.user-theme", "light"); - break; - case "light": - djDebug.setAttribute("data-theme", "dark"); - localStorage.setItem("djdt.user-theme", "dark"); - break; - default: /* dark is the default */ - djDebug.setAttribute("data-theme", "auto"); - localStorage.setItem("djdt.user-theme", "auto"); - break; - } + const index = themeList.indexOf(userTheme); + userTheme = themeList[(index + 1) % themeList.length]; + localStorage.setItem("djdt.user-theme", userTheme); + setTheme(userTheme); }); }, hidePanels() { diff --git a/debug_toolbar/templates/debug_toolbar/base.html b/debug_toolbar/templates/debug_toolbar/base.html index a9983250d..b5c225ac8 100644 --- a/debug_toolbar/templates/debug_toolbar/base.html +++ b/debug_toolbar/templates/debug_toolbar/base.html @@ -16,8 +16,7 @@ data-sidebar-url="{{ history_url }}" {% endif %} data-default-show="{% if toolbar.config.SHOW_COLLAPSED %}false{% else %}true{% endif %}" - {{ toolbar.config.ROOT_TAG_EXTRA_ATTRS|safe }} data-update-on-fetch="{{ toolbar.config.UPDATE_ON_FETCH }}" - data-theme="{{ toolbar.config.DEFAULT_THEME }}"> + {{ toolbar.config.ROOT_TAG_EXTRA_ATTRS|safe }} data-update-on-fetch="{{ toolbar.config.UPDATE_ON_FETCH }}">