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
Binary file added after-render/screenshots/all/catppuccin-latte.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added after-render/screenshots/all/catppuccin-mocha.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added after-render/screenshots/all/dracula.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added after-render/screenshots/all/everforest.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added after-render/screenshots/all/flexoki.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added after-render/screenshots/all/gruvbox.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added after-render/screenshots/all/hackerman.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added after-render/screenshots/all/kanagawa.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added after-render/screenshots/all/matte-black.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added after-render/screenshots/all/monokai.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added after-render/screenshots/all/nord.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added after-render/screenshots/all/osaka-jade.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added after-render/screenshots/all/ristretto.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added after-render/screenshots/all/rose-pine-dawn.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added after-render/screenshots/all/rose-pine-moon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added after-render/screenshots/all/rose-pine.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added after-render/screenshots/all/solarized-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added after-render/screenshots/all/solarized-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added after-render/screenshots/all/sqlit-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added after-render/screenshots/all/sqlit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added after-render/screenshots/all/textual-ansi.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added after-render/screenshots/all/textual-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added after-render/screenshots/all/textual-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added after-render/screenshots/all/tokyo-night.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions settings.template.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"_note": "Copy to .sqlit/settings.json (gitignored) and run: sqlit --settings .sqlit/settings.json",
"theme": "tokyo-night",
"custom_themes": [],
"expanded_nodes": [],
"allow_plaintext_credentials": false,
"mock": {
Expand Down
108 changes: 15 additions & 93 deletions sqlit/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,19 @@
from textual.containers import Container, Horizontal, Vertical
from textual.lazy import Lazy
from textual.screen import ModalScreen
from textual.theme import Theme
from textual.timer import Timer
from textual.widgets import Static, TextArea, Tree
from textual.worker import Worker

from .config import (
ConnectionConfig,
load_connections,
load_settings,
save_settings,
)
from .db import DatabaseAdapter
from .mock_settings import apply_mock_environment, build_mock_profile_from_settings
from .mocks import MockProfile
from .omarchy import (
DEFAULT_THEME,
get_current_theme_name,
get_matching_textual_theme,
is_omarchy_installed,
)
from .theme_manager import ThemeManager
from .omarchy import DEFAULT_THEME
from .state_machine import (
UIStateMachine,
get_leader_bindings,
Expand Down Expand Up @@ -75,31 +68,6 @@ class SSMSTUI(

TITLE = "sqlit"

_SQLIT_THEMES = [
Theme(
name="sqlit",
primary="#97CB93",
secondary="#6D8DC4",
accent="#6D8DC4",
warning="#f59e0b",
error="#BE728C",
success="#4ADE80",
foreground="#a9b1d6",
background="#1A1B26",
surface="#24283B",
panel="#414868",
dark=True,
variables={
"border": "#7a7f99",
"border-blurred": "#7a7f99",
"footer-background": "#24283B",
"footer-key-foreground": "#7FA1DE",
"button-color-foreground": "#1A1B26",
"input-selection-background": "#2a3144 40%",
},
),
]

CSS = """
Screen {
background: $surface;
Expand Down Expand Up @@ -374,6 +342,7 @@ def __init__(
self._query_worker: Worker[Any] | None = None
self._query_executing: bool = False
self._cancellable_query: Any | None = None
self._theme_manager = ThemeManager(self)
self._spinner_index: int = 0
self._spinner_timer: Timer | None = None
# Schema indexing state
Expand All @@ -387,8 +356,6 @@ def __init__(
self._session_factory: Any | None = None
self._last_query_table: dict | None = None
# Omarchy theme sync state
self._omarchy_theme_watcher: Timer | None = None
self._omarchy_last_theme_name: str | None = None

if mock_profile:
self._session_factory = self._create_mock_session_factory(mock_profile)
Expand Down Expand Up @@ -564,15 +531,12 @@ def on_mount(self) -> None:
self._startup_stamp("on_mount_start")
self._restart_argv = self._compute_restart_argv()

for theme in self._SQLIT_THEMES:
self.register_theme(theme)
self._theme_manager.register_builtin_themes()
self._theme_manager.register_textarea_themes()

settings = load_settings()
settings = self._theme_manager.initialize()
self._startup_stamp("settings_loaded")

# Initialize Omarchy theme sync
self._init_omarchy_theme(settings)

self._expanded_paths = set(settings.get("expanded_nodes", []))
self._startup_stamp("settings_applied")

Expand Down Expand Up @@ -755,31 +719,19 @@ def _maybe_restore_connection_screen(self) -> None:

def watch_theme(self, old_theme: str, new_theme: str) -> None:
"""Save theme whenever it changes."""
settings = load_settings()
settings["theme"] = new_theme
save_settings(settings)
self._theme_manager.on_theme_changed(new_theme)

def _init_omarchy_theme(self, settings: dict) -> None:
"""Initialize theme on startup, with Omarchy matching if installed.
def get_custom_theme_names(self) -> set[str]:
return self._theme_manager.get_custom_theme_names()

Strategy:
1. If Omarchy is installed, try to match the Omarchy theme to a Textual theme
2. If a match is found, use it and start watching for changes
3. If no match or Omarchy not installed, use saved theme or default
"""
saved_theme = settings.get("theme")
def add_custom_theme(self, theme_name: str) -> str:
return self._theme_manager.add_custom_theme(theme_name)

# Check if Omarchy is installed
if not is_omarchy_installed():
# No Omarchy, use saved theme or default
self._apply_theme_safe(saved_theme or DEFAULT_THEME)
return
def open_custom_theme_in_editor(self, theme_name: str) -> None:
self._theme_manager.open_custom_theme_in_editor(theme_name)

# Omarchy is installed - match theme and start watcher
matched_theme = get_matching_textual_theme(self.available_themes)
self._omarchy_last_theme_name = get_current_theme_name()
self._apply_theme_safe(matched_theme)
self._start_omarchy_watcher()
def get_custom_theme_path(self, theme_name: str) -> Path:
return self._theme_manager.get_custom_theme_path(theme_name)

def _apply_theme_safe(self, theme_name: str) -> None:
"""Apply a theme with fallback to default on error."""
Expand All @@ -790,33 +742,3 @@ def _apply_theme_safe(self, theme_name: str) -> None:
self.theme = DEFAULT_THEME
except Exception:
self.theme = "sqlit"

def _start_omarchy_watcher(self) -> None:
"""Start watching for Omarchy theme changes."""
if self._omarchy_theme_watcher is not None:
return # Already watching

# Check for theme changes every 2 seconds
self._omarchy_theme_watcher = self.set_interval(2.0, self._check_omarchy_theme_change)

def _stop_omarchy_watcher(self) -> None:
"""Stop watching for Omarchy theme changes."""
if self._omarchy_theme_watcher is not None:
self._omarchy_theme_watcher.stop()
self._omarchy_theme_watcher = None

def _check_omarchy_theme_change(self) -> None:
"""Check if the Omarchy theme has changed and apply if so."""
current_name = get_current_theme_name()
if current_name is None:
return

# Check if theme name changed
if current_name != self._omarchy_last_theme_name:
self._omarchy_last_theme_name = current_name
self._apply_omarchy_theme()

def _apply_omarchy_theme(self) -> None:
"""Match and apply the current Omarchy theme."""
matched_theme = get_matching_textual_theme(self.available_themes)
self._apply_theme_safe(matched_theme)
7 changes: 7 additions & 0 deletions sqlit/omarchy.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@
"catppuccin": "catppuccin-mocha",
# Flexoki light variant
"flexoki-light": "flexoki",
# Handle space/underscore variations in omarchy theme names
"matte black": "matte-black",
"matte_black": "matte-black",
"osaka jade": "osaka-jade",
"osaka_jade": "osaka-jade",
# Ethereal doesn't have an exact match, use rose-pine as closest
"ethereal": "rose-pine",
}


Expand Down
Loading