feat: manual darkman switching
This commit is contained in:
@@ -75,6 +75,23 @@
|
|||||||
default = false;
|
default = false;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
darkman = {
|
||||||
|
enable = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Add a light/dark mode toggle to the control center and the
|
||||||
|
`theme-toggle` / `theme-set-light` / `theme-set-dark` actions
|
||||||
|
(`sims-cli theme toggle|light|dark`). Switches darkman via its
|
||||||
|
control socket.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
socket_path = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
description = "darkman control socket; null means \${XDG_RUNTIME_DIR}/darkman/control.sock";
|
||||||
|
};
|
||||||
|
};
|
||||||
battery = {
|
battery = {
|
||||||
enable = lib.mkOption {
|
enable = lib.mkOption {
|
||||||
type = lib.types.bool;
|
type = lib.types.bool;
|
||||||
@@ -314,6 +331,7 @@
|
|||||||
};
|
};
|
||||||
default = {
|
default = {
|
||||||
vinyl.enable = false;
|
vinyl.enable = false;
|
||||||
|
darkman.enable = false;
|
||||||
battery.enable = false;
|
battery.enable = false;
|
||||||
buddy.enable = false;
|
buddy.enable = false;
|
||||||
height = 40;
|
height = 40;
|
||||||
|
|||||||
+19
@@ -93,6 +93,15 @@ def _cmd_corners(ns: argparse.Namespace) -> None:
|
|||||||
invoke_action(mapping[ns.corners_cmd])
|
invoke_action(mapping[ns.corners_cmd])
|
||||||
|
|
||||||
|
|
||||||
|
def _cmd_theme(ns: argparse.Namespace) -> None:
|
||||||
|
mapping = {
|
||||||
|
"toggle": "theme-toggle",
|
||||||
|
"light": "theme-set-light",
|
||||||
|
"dark": "theme-set-dark",
|
||||||
|
}
|
||||||
|
invoke_action(mapping[ns.theme_cmd])
|
||||||
|
|
||||||
|
|
||||||
def _cmd_list(ns: argparse.Namespace) -> None:
|
def _cmd_list(ns: argparse.Namespace) -> None:
|
||||||
actions = list_actions()
|
actions = list_actions()
|
||||||
if ns.json:
|
if ns.json:
|
||||||
@@ -205,6 +214,16 @@ def build_parser() -> argparse.ArgumentParser:
|
|||||||
corners_sub.add_parser(sub_name, help=sub_help)
|
corners_sub.add_parser(sub_name, help=sub_help)
|
||||||
corners.set_defaults(func=_cmd_corners)
|
corners.set_defaults(func=_cmd_corners)
|
||||||
|
|
||||||
|
theme = sub.add_parser("theme", help="light/dark mode (via darkman)")
|
||||||
|
theme_sub = theme.add_subparsers(dest="theme_cmd", required=True, metavar="MODE")
|
||||||
|
for sub_name, sub_help in [
|
||||||
|
("toggle", "flip between light and dark mode"),
|
||||||
|
("light", "switch to light mode"),
|
||||||
|
("dark", "switch to dark mode"),
|
||||||
|
]:
|
||||||
|
theme_sub.add_parser(sub_name, help=sub_help)
|
||||||
|
theme.set_defaults(func=_cmd_theme)
|
||||||
|
|
||||||
lst = sub.add_parser("list", help="list registered actions")
|
lst = sub.add_parser("list", help="list registered actions")
|
||||||
lst.add_argument("--json", action="store_true", help="emit JSON array")
|
lst.add_argument("--json", action="store_true", help="emit JSON array")
|
||||||
lst.set_defaults(func=_cmd_list)
|
lst.set_defaults(func=_cmd_list)
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ if app_config is None:
|
|||||||
raise Exception("Config file missing")
|
raise Exception("Config file missing")
|
||||||
|
|
||||||
VINYL = app_config.get("vinyl", {"enable": False})
|
VINYL = app_config.get("vinyl", {"enable": False})
|
||||||
|
DARKMAN = app_config.get("darkman", {"enable": False})
|
||||||
BATTERY = app_config.get("battery", {"enable": False})
|
BATTERY = app_config.get("battery", {"enable": False})
|
||||||
WINDOW_TITLE = app_config.get("window_title", {"enable": True})
|
WINDOW_TITLE = app_config.get("window_title", {"enable": True})
|
||||||
STYLIX = app_config.get("stylix", {"enable": False})
|
STYLIX = app_config.get("stylix", {"enable": False})
|
||||||
|
|||||||
+25
-1
@@ -32,9 +32,11 @@ from .modules.control_center import ControlCenter
|
|||||||
from .modules.notifications import NotificationToasts
|
from .modules.notifications import NotificationToasts
|
||||||
from .modules.org import OrgDropdown
|
from .modules.org import OrgDropdown
|
||||||
from .modules.stylix import generate_stylix_css
|
from .modules.stylix import generate_stylix_css
|
||||||
|
from .modules.theme_toggle import ThemeToggleButton
|
||||||
from .modules.vinyl import VinylButton
|
from .modules.vinyl import VinylButton
|
||||||
from .services.org import OrgService
|
from .services.org import OrgService
|
||||||
from .config import CALENDAR, NOTIFICATIONS, ORG, POWER, SCREENREC, STYLIX, VINYL
|
from .config import CALENDAR, DARKMAN, NOTIFICATIONS, ORG, POWER, SCREENREC, STYLIX, VINYL
|
||||||
|
from .services.darkman import get_darkman_service
|
||||||
from .services.fenster import get_i3_connection
|
from .services.fenster import get_i3_connection
|
||||||
from .services.capture import get_capture_service
|
from .services.capture import get_capture_service
|
||||||
from .services.notification_history import NotificationHistoryService
|
from .services.notification_history import NotificationHistoryService
|
||||||
@@ -89,6 +91,9 @@ if NOTIFICATIONS.get("enable", False):
|
|||||||
)
|
)
|
||||||
|
|
||||||
vinyl_button: VinylButton | None = VinylButton() if VINYL.get("enable", False) else None
|
vinyl_button: VinylButton | None = VinylButton() if VINYL.get("enable", False) else None
|
||||||
|
theme_button: ThemeToggleButton | None = (
|
||||||
|
ThemeToggleButton(get_darkman_service()) if DARKMAN.get("enable", False) else None
|
||||||
|
)
|
||||||
calendar_service: CalendarService | None = (
|
calendar_service: CalendarService | None = (
|
||||||
CalendarService(update_interval=120000) if CALENDAR.get("enable", True) else None
|
CalendarService(update_interval=120000) if CALENDAR.get("enable", True) else None
|
||||||
)
|
)
|
||||||
@@ -99,6 +104,7 @@ if notification_history is not None:
|
|||||||
history=notification_history,
|
history=notification_history,
|
||||||
calendar_service=calendar_service,
|
calendar_service=calendar_service,
|
||||||
vinyl_button=vinyl_button,
|
vinyl_button=vinyl_button,
|
||||||
|
theme_button=theme_button,
|
||||||
monitor=0,
|
monitor=0,
|
||||||
width=NOTIFICATIONS.get("center_width", 380),
|
width=NOTIFICATIONS.get("center_width", 380),
|
||||||
)
|
)
|
||||||
@@ -198,6 +204,24 @@ def toggle_control_center():
|
|||||||
control_center.toggle()
|
control_center.toggle()
|
||||||
|
|
||||||
|
|
||||||
|
@Application.action()
|
||||||
|
def theme_toggle():
|
||||||
|
if DARKMAN.get("enable", False):
|
||||||
|
get_darkman_service().toggle()
|
||||||
|
|
||||||
|
|
||||||
|
@Application.action()
|
||||||
|
def theme_set_light():
|
||||||
|
if DARKMAN.get("enable", False):
|
||||||
|
get_darkman_service().set_mode("light")
|
||||||
|
|
||||||
|
|
||||||
|
@Application.action()
|
||||||
|
def theme_set_dark():
|
||||||
|
if DARKMAN.get("enable", False):
|
||||||
|
get_darkman_service().set_mode("dark")
|
||||||
|
|
||||||
|
|
||||||
def _set_all_bars_rounded(rounded: bool):
|
def _set_all_bars_rounded(rounded: bool):
|
||||||
for bar in bar_windows:
|
for bar in bar_windows:
|
||||||
bar.set_corners_rounded(rounded)
|
bar.set_corners_rounded(rounded)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from fabric.widgets.wayland import WaylandWindow as Window
|
|||||||
from gi.repository import Gdk
|
from gi.repository import Gdk
|
||||||
|
|
||||||
from sims.modules.calendar import CalendarService
|
from sims.modules.calendar import CalendarService
|
||||||
|
from sims.modules.theme_toggle import ThemeToggleButton
|
||||||
from sims.modules.vinyl import VinylButton
|
from sims.modules.vinyl import VinylButton
|
||||||
from sims.services.notification_history import NotificationHistoryService
|
from sims.services.notification_history import NotificationHistoryService
|
||||||
from sims.widgets.notification_history_entry import NotificationHistoryEntryWidget
|
from sims.widgets.notification_history_entry import NotificationHistoryEntryWidget
|
||||||
@@ -20,6 +21,7 @@ class ControlCenter(Window):
|
|||||||
history: NotificationHistoryService,
|
history: NotificationHistoryService,
|
||||||
calendar_service: CalendarService | None = None,
|
calendar_service: CalendarService | None = None,
|
||||||
vinyl_button: VinylButton | None = None,
|
vinyl_button: VinylButton | None = None,
|
||||||
|
theme_button: ThemeToggleButton | None = None,
|
||||||
monitor: int = 0,
|
monitor: int = 0,
|
||||||
width: int = 380,
|
width: int = 380,
|
||||||
):
|
):
|
||||||
@@ -35,6 +37,7 @@ class ControlCenter(Window):
|
|||||||
self._history = history
|
self._history = history
|
||||||
self._calendar_service = calendar_service
|
self._calendar_service = calendar_service
|
||||||
self._vinyl_button = vinyl_button
|
self._vinyl_button = vinyl_button
|
||||||
|
self._theme_button = theme_button
|
||||||
self._width = width
|
self._width = width
|
||||||
self._visibility_listeners: list = []
|
self._visibility_listeners: list = []
|
||||||
|
|
||||||
@@ -58,8 +61,16 @@ class ControlCenter(Window):
|
|||||||
|
|
||||||
sections: list = []
|
sections: list = []
|
||||||
|
|
||||||
if vinyl_button is not None:
|
settings_rows = [
|
||||||
sections.append(self._build_settings_section())
|
(label, widget)
|
||||||
|
for label, widget in (
|
||||||
|
("Dark Mode", theme_button),
|
||||||
|
("Vinyl Passthrough", vinyl_button),
|
||||||
|
)
|
||||||
|
if widget is not None
|
||||||
|
]
|
||||||
|
if settings_rows:
|
||||||
|
sections.append(self._build_settings_section(settings_rows))
|
||||||
|
|
||||||
if calendar_service is not None:
|
if calendar_service is not None:
|
||||||
sections.append(self._build_calendar_section())
|
sections.append(self._build_calendar_section())
|
||||||
@@ -123,26 +134,26 @@ class ControlCenter(Window):
|
|||||||
)
|
)
|
||||||
return section
|
return section
|
||||||
|
|
||||||
def _build_settings_section(self) -> Box:
|
def _build_settings_section(self, rows: list) -> Box:
|
||||||
section = self._build_section("control-center-settings", "Settings")
|
section = self._build_section("control-center-settings", "Settings")
|
||||||
|
for label, widget in rows:
|
||||||
row = Box(
|
row = Box(
|
||||||
name="control-center-settings-row",
|
name="control-center-settings-row",
|
||||||
orientation="h",
|
orientation="h",
|
||||||
spacing=8,
|
spacing=8,
|
||||||
)
|
)
|
||||||
row.pack_start(
|
row.pack_start(
|
||||||
Label(
|
Label(
|
||||||
name="control-center-settings-label",
|
name="control-center-settings-label",
|
||||||
label="Vinyl Passthrough",
|
label=label,
|
||||||
h_align="start",
|
h_align="start",
|
||||||
),
|
),
|
||||||
True,
|
True,
|
||||||
True,
|
True,
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
row.pack_end(self._vinyl_button, False, False, 0)
|
row.pack_end(widget, False, False, 0)
|
||||||
section.add(row)
|
section.add(row)
|
||||||
return section
|
return section
|
||||||
|
|
||||||
def _build_calendar_section(self) -> Box:
|
def _build_calendar_section(self) -> Box:
|
||||||
|
|||||||
@@ -430,6 +430,33 @@ tooltip>* {{
|
|||||||
color: inherit;
|
color: inherit;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
/* Theme (darkman light/dark) toggle */
|
||||||
|
#theme-toggle-button {{
|
||||||
|
background-color: transparent;
|
||||||
|
color: #{colors["base05"]};
|
||||||
|
border: none;
|
||||||
|
padding: 4px;
|
||||||
|
margin: 0px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}}
|
||||||
|
|
||||||
|
#theme-toggle-button:hover {{
|
||||||
|
background-color: #{colors["base02"]};
|
||||||
|
}}
|
||||||
|
|
||||||
|
#theme-toggle-button.active {{
|
||||||
|
background-color: #{colors["base0D"]};
|
||||||
|
color: #{colors["base00"]};
|
||||||
|
}}
|
||||||
|
|
||||||
|
#theme-toggle-button:disabled {{
|
||||||
|
opacity: 0.4;
|
||||||
|
}}
|
||||||
|
|
||||||
|
#theme-toggle-icon {{
|
||||||
|
color: inherit;
|
||||||
|
}}
|
||||||
|
|
||||||
/* Toggle switch styling for quick menu */
|
/* Toggle switch styling for quick menu */
|
||||||
.toggle-active {{
|
.toggle-active {{
|
||||||
background-color: #{colors["base0B"]};
|
background-color: #{colors["base0B"]};
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
from fabric.widgets.button import Button
|
||||||
|
from fabric.widgets.image import Image
|
||||||
|
|
||||||
|
from sims.services.darkman import DarkmanService
|
||||||
|
|
||||||
|
|
||||||
|
class ThemeToggleButton(Button):
|
||||||
|
"""Flips darkman between light and dark mode.
|
||||||
|
|
||||||
|
Shows the active mode (moon + `active` class in dark mode) and is
|
||||||
|
insensitive while darkman is unreachable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, service: DarkmanService, **kwargs):
|
||||||
|
self._service = service
|
||||||
|
self.icon = Image(icon_size=16, name="theme-toggle-icon")
|
||||||
|
super().__init__(
|
||||||
|
name="theme-toggle-button",
|
||||||
|
child=self.icon,
|
||||||
|
on_clicked=lambda *_: self._service.toggle(),
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
self._service.connect("changed", lambda _svc, mode: self._update(mode))
|
||||||
|
self._update(self._service.mode)
|
||||||
|
|
||||||
|
def _update(self, mode: str | None):
|
||||||
|
dark = mode == "dark"
|
||||||
|
self.icon.set_from_icon_name(
|
||||||
|
"weather-clear-night-symbolic" if dark else "weather-clear-symbolic", 16
|
||||||
|
)
|
||||||
|
if dark:
|
||||||
|
self.add_style_class("active")
|
||||||
|
else:
|
||||||
|
self.remove_style_class("active")
|
||||||
|
self.set_sensitive(mode is not None)
|
||||||
|
if mode is None:
|
||||||
|
self.set_tooltip_text("darkman is not running")
|
||||||
|
else:
|
||||||
|
self.set_tooltip_text(f"Switch to {'light' if dark else 'dark'} mode")
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
"""darkman control service.
|
||||||
|
|
||||||
|
Talks to darkman's control socket (`$XDG_RUNTIME_DIR/darkman/control.sock`,
|
||||||
|
a newline-delimited text protocol: `get`, `set light|dark`, `toggle`,
|
||||||
|
`watch`) so sims can show and switch the current light/dark mode.
|
||||||
|
|
||||||
|
A long-lived `watch` connection tracks the mode; darkman prints the current
|
||||||
|
mode immediately and then every change. Switching opens a short-lived
|
||||||
|
connection and sends `set <mode>`. If darkman is not running (yet), or
|
||||||
|
restarts, the watch connection is retried in the background.
|
||||||
|
|
||||||
|
This only *controls* darkman. Re-theming sims itself still happens through
|
||||||
|
the portal color-scheme (see `sims.services.theme`), which darkman updates
|
||||||
|
as part of every switch.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from fabric.core.service import Service, Signal
|
||||||
|
from gi.repository import Gio, GLib
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
from sims.config import DARKMAN
|
||||||
|
|
||||||
|
MODES = ("light", "dark")
|
||||||
|
|
||||||
|
_RETRY_MIN_S = 2
|
||||||
|
_RETRY_MAX_S = 30
|
||||||
|
|
||||||
|
_service: "DarkmanService | None" = None
|
||||||
|
|
||||||
|
|
||||||
|
def _default_socket_path() -> str:
|
||||||
|
runtime_dir = os.environ.get("XDG_RUNTIME_DIR") or f"/run/user/{os.getuid()}"
|
||||||
|
return os.path.join(runtime_dir, "darkman", "control.sock")
|
||||||
|
|
||||||
|
|
||||||
|
class DarkmanService(Service):
|
||||||
|
@Signal
|
||||||
|
def changed(self, mode: object) -> None:
|
||||||
|
"""Emitted with the new mode ("light", "dark"), or None while darkman
|
||||||
|
is unreachable."""
|
||||||
|
|
||||||
|
def __init__(self, socket_path: str | None = None, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self._socket_path = os.path.expandvars(
|
||||||
|
os.path.expanduser(socket_path or _default_socket_path())
|
||||||
|
)
|
||||||
|
self._mode: str | None = None
|
||||||
|
self._client = Gio.SocketClient.new()
|
||||||
|
self._watch_conn: Gio.SocketConnection | None = None
|
||||||
|
self._watch_stream: Gio.DataInputStream | None = None
|
||||||
|
self._retry_s = _RETRY_MIN_S
|
||||||
|
self._retry_source: int | None = None
|
||||||
|
self._connect_watch()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mode(self) -> str | None:
|
||||||
|
return self._mode
|
||||||
|
|
||||||
|
@property
|
||||||
|
def available(self) -> bool:
|
||||||
|
return self._mode is not None
|
||||||
|
|
||||||
|
# -- control ---------------------------------------------------------
|
||||||
|
|
||||||
|
def set_mode(self, mode: str) -> None:
|
||||||
|
if mode not in MODES:
|
||||||
|
raise ValueError(f"invalid darkman mode: {mode!r}")
|
||||||
|
self._send(f"set {mode}")
|
||||||
|
|
||||||
|
def toggle(self) -> None:
|
||||||
|
if self._mode is None:
|
||||||
|
logger.warning("[Darkman] not connected, cannot toggle mode")
|
||||||
|
return
|
||||||
|
self.set_mode("light" if self._mode == "dark" else "dark")
|
||||||
|
|
||||||
|
def _send(self, command: str) -> None:
|
||||||
|
self._client.connect_async(
|
||||||
|
Gio.UnixSocketAddress.new(self._socket_path),
|
||||||
|
None,
|
||||||
|
self._on_command_connected,
|
||||||
|
command,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _on_command_connected(self, client: Gio.SocketClient, result, command: str):
|
||||||
|
try:
|
||||||
|
conn = client.connect_finish(result)
|
||||||
|
except GLib.Error as e:
|
||||||
|
logger.warning(f"[Darkman] could not send {command!r}: {e.message}")
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
# `set` gets no reply; a few bytes into a local socket never block.
|
||||||
|
conn.get_output_stream().write_all(f"{command}\n".encode(), None)
|
||||||
|
except GLib.Error as e:
|
||||||
|
logger.warning(f"[Darkman] could not send {command!r}: {e.message}")
|
||||||
|
finally:
|
||||||
|
conn.close(None)
|
||||||
|
|
||||||
|
# -- watch -----------------------------------------------------------
|
||||||
|
|
||||||
|
def _connect_watch(self) -> bool:
|
||||||
|
self._retry_source = None
|
||||||
|
self._client.connect_async(
|
||||||
|
Gio.UnixSocketAddress.new(self._socket_path),
|
||||||
|
None,
|
||||||
|
self._on_watch_connected,
|
||||||
|
)
|
||||||
|
return GLib.SOURCE_REMOVE
|
||||||
|
|
||||||
|
def _on_watch_connected(self, client: Gio.SocketClient, result):
|
||||||
|
try:
|
||||||
|
conn = client.connect_finish(result)
|
||||||
|
conn.get_output_stream().write_all(b"watch\n", None)
|
||||||
|
except GLib.Error as e:
|
||||||
|
logger.debug(f"[Darkman] socket {self._socket_path} unavailable: {e.message}")
|
||||||
|
self._schedule_retry()
|
||||||
|
return
|
||||||
|
logger.info(f"[Darkman] watching {self._socket_path}")
|
||||||
|
self._retry_s = _RETRY_MIN_S
|
||||||
|
self._watch_conn = conn
|
||||||
|
self._watch_stream = Gio.DataInputStream.new(conn.get_input_stream())
|
||||||
|
self._read_next()
|
||||||
|
|
||||||
|
def _read_next(self):
|
||||||
|
assert self._watch_stream is not None
|
||||||
|
self._watch_stream.read_line_async(
|
||||||
|
GLib.PRIORITY_DEFAULT, None, self._on_line
|
||||||
|
)
|
||||||
|
|
||||||
|
def _on_line(self, stream: Gio.DataInputStream, result):
|
||||||
|
try:
|
||||||
|
line, _length = stream.read_line_finish_utf8(result)
|
||||||
|
except GLib.Error as e:
|
||||||
|
logger.warning(f"[Darkman] watch connection failed: {e.message}")
|
||||||
|
line = None
|
||||||
|
if line is None:
|
||||||
|
# EOF: darkman stopped or restarted.
|
||||||
|
self._drop_watch()
|
||||||
|
self._schedule_retry()
|
||||||
|
return
|
||||||
|
mode = line.strip()
|
||||||
|
self._set_mode(mode if mode in MODES else None)
|
||||||
|
self._read_next()
|
||||||
|
|
||||||
|
def _drop_watch(self):
|
||||||
|
if self._watch_conn is not None:
|
||||||
|
self._watch_conn.close(None)
|
||||||
|
self._watch_conn = None
|
||||||
|
self._watch_stream = None
|
||||||
|
self._set_mode(None)
|
||||||
|
|
||||||
|
def _schedule_retry(self):
|
||||||
|
if self._retry_source is not None:
|
||||||
|
return
|
||||||
|
self._retry_source = GLib.timeout_add_seconds(self._retry_s, self._connect_watch)
|
||||||
|
self._retry_s = min(self._retry_s * 2, _RETRY_MAX_S)
|
||||||
|
|
||||||
|
def _set_mode(self, mode: str | None):
|
||||||
|
if mode == self._mode:
|
||||||
|
return
|
||||||
|
logger.info(f"[Darkman] mode={mode}")
|
||||||
|
self._mode = mode
|
||||||
|
self.changed(mode)
|
||||||
|
|
||||||
|
|
||||||
|
def get_darkman_service() -> DarkmanService:
|
||||||
|
global _service
|
||||||
|
if _service is None:
|
||||||
|
_service = DarkmanService(socket_path=DARKMAN.get("socket_path"))
|
||||||
|
return _service
|
||||||
@@ -27,3 +27,35 @@
|
|||||||
#vinyl-button.active #vinyl-icon {
|
#vinyl-button.active #vinyl-icon {
|
||||||
color: var(--background);
|
color: var(--background);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Theme (darkman light/dark) toggle -- same look as the vinyl button */
|
||||||
|
#theme-toggle-button {
|
||||||
|
background-color: var(--module-bg);
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 12px;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#theme-toggle-button:hover {
|
||||||
|
background-color: var(--light-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#theme-toggle-button.active {
|
||||||
|
background-color: var(--pink);
|
||||||
|
}
|
||||||
|
|
||||||
|
#theme-toggle-button.active:hover {
|
||||||
|
background-color: var(--turquoise);
|
||||||
|
}
|
||||||
|
|
||||||
|
#theme-toggle-button:disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#theme-toggle-icon {
|
||||||
|
color: var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
#theme-toggle-button.active #theme-toggle-icon {
|
||||||
|
color: var(--background);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user