Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion sqlit/domains/explorer/ui/mixins/tree_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,8 +344,10 @@ def _find_matching_nodes(
matches.append(node)
# Store original label and apply highlighting
self._tree_original_labels[id(node)] = str(node.label)
theme = getattr(self, "current_theme", None)
match_color = getattr(theme, "primary", "#1565C0")
highlighted = highlight_matches(
escape_markup(label_text), indices, style="bold #FFFF00"
escape_markup(label_text), indices, style=f"bold {match_color}"
)
# Preserve any existing markup prefix (like icons, colors)
node.set_label(self._rebuild_label_with_highlight(node, highlighted))
Expand Down
3 changes: 2 additions & 1 deletion sqlit/domains/shell/ui/mixins/ui_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@ def _update_status_bar(self: UINavigationMixinHost) -> None:
from rich.markup import escape as escape_markup

cmd_buffer = escape_markup(getattr(self, "_command_buffer", ""))
conn_info = f"[bold cyan]:{cmd_buffer}[/]"
command_color = getattr(self.current_theme, "primary", "#1565C0")
conn_info = f"[bold {command_color}]:{cmd_buffer}[/]"

# Build status indicators
status_parts = []
Expand Down
11 changes: 11 additions & 0 deletions tests/ui/explorer/test_tree_filter_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from __future__ import annotations

from types import SimpleNamespace
from unittest.mock import MagicMock

from sqlit.domains.explorer.domain.tree_nodes import (
Expand Down Expand Up @@ -127,6 +128,7 @@ class _FilterHost(TreeFilterMixin):

def __init__(self, connection_names: list[str]):
self._connection_names = connection_names
self.current_theme = SimpleNamespace(primary="#1565C0")
self.object_tree = MockTree()
self.tree_filter_input = MockFilterInput()
self._populate()
Expand Down Expand Up @@ -211,6 +213,15 @@ def test_typing_t_filters_to_t_matches(self):
f"Expected only 't'-matching connections visible, got {visible}"
)

def test_match_highlight_uses_current_theme_primary_color(self):
host = _FilterHost(["user-primary", "production"])

self._open_filter(host)
self._type(host, "user")

assert len(host._tree_filter_matches) == 1
assert "[bold #1565C0]" in str(host._tree_filter_matches[0].label)

def test_typing_tt_filters_out_everything(self):
host = _FilterHost(self.CONNECTION_NAMES)

Expand Down
60 changes: 60 additions & 0 deletions tests/ui/test_light_theme_contrast.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""Contrast regressions for command mode across Sqlit's light themes."""

from __future__ import annotations

import pytest

from sqlit.domains.shell.app.main import SSMSTUI

from .mocks import MockConnectionStore, MockSettingsStore, build_test_services

LIGHT_THEMES = [
"sqlit-light",
"textual-light",
"solarized-light",
"catppuccin-latte",
"rose-pine-dawn",
"gruvbox-light",
]


def _relative_luminance(color: str) -> float:
channels = [int(color[index : index + 2], 16) / 255 for index in (1, 3, 5)]

def linearize(channel: float) -> float:
if channel <= 0.04045:
return channel / 12.92
return ((channel + 0.055) / 1.055) ** 2.4

red, green, blue = (linearize(channel) for channel in channels)
return 0.2126 * red + 0.7152 * green + 0.0722 * blue


def _contrast_ratio(foreground: str, background: str) -> float:
lighter, darker = sorted(
(_relative_luminance(foreground), _relative_luminance(background)),
reverse=True,
)
return (lighter + 0.05) / (darker + 0.05)


@pytest.mark.parametrize("theme_name", LIGHT_THEMES)
@pytest.mark.asyncio
async def test_command_mode_uses_contrasting_theme_primary(theme_name: str) -> None:
services = build_test_services(
connection_store=MockConnectionStore(),
settings_store=MockSettingsStore({"theme": theme_name}),
)
app = SSMSTUI(services=services)

async with app.run_test() as pilot:
await pilot.pause()
app._command_mode = True
app._command_buffer = "theme"
app._update_status_bar()
await pilot.pause()

rendered = app.status_bar.render()
assert rendered.plain == ":theme"
assert rendered.spans[0].style == f"bold {app.current_theme.primary}"
assert _contrast_ratio(app.current_theme.primary, app.current_theme.surface) >= 3
Loading