🔧 chore(dock/components): import cleanup in __init__ files, refactor SystemTrayItem monkey patching

This commit is contained in:
sankalp committed 2025-06-30 01:11:08 +05:45
1 parent 994b44f515
commit e5471eb771
9 files changed
+293 -499

No files matched your search

+10 -6
View File
@@ -1,6 +1,9 @@
from gi.repository import Gtk
from fabric.system_tray.widgets import SystemTrayItem
import json
import os
import gi
from fabric.hyprland.service import HyprlandEvent
from fabric.hyprland.widgets import Language, get_hyprland_connection
from fabric.system_tray.widgets import SystemTray
@@ -21,12 +24,9 @@ from .metrics import Metrics
from .music_player import MusicPlayer
from .workspaces import workspace
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
# Monkey patch SystemTray to use GTK theme icons
from fabric.system_tray.widgets import SystemTrayItem
def _patched_do_update_properties(self, *_):
"""Enhanced update method that prioritizes GTK theme icons"""
@@ -65,10 +65,13 @@ def _patched_do_update_properties(self, *_):
# Set tooltip (same as original)
tooltip = self._item.tooltip
self.set_tooltip_markup(
tooltip.description or tooltip.title or self._item.title.title() if self._item.title else "Unknown"
tooltip.description or tooltip.title or self._item.title.title()
if self._item.title
else "Unknown"
)
return
# Apply the patch
SystemTrayItem.do_update_properties = _patched_do_update_properties
@@ -142,7 +145,8 @@ class DockComponents(Box):
# Apply invert style for specific themes and positions
should_invert = data.DOCK_THEME in ["Dense", "Edge"] or (
data.DOCK_THEME == "Pills" and data.DOCK_POSITION in ["Left", "Right", "Top"]
data.DOCK_THEME == "Pills"
and data.DOCK_POSITION in ["Left", "Right", "Top"]
)
if should_invert:
+122 -42
View File
@@ -8,9 +8,9 @@ from gi.repository import Gdk
import config.data as data
import utils.icons as icons
from modules.dock.components.player import PlayerContainer
from services.mpris import PlayerManager, PlayerService
from utils.wayland import WaylandWindow as Window
from modules.dock.components.player import PlayerContainer
class MusicPlayerPopup(Window):
@@ -40,7 +40,9 @@ class MusicPlayerPopup(Window):
# Connect player container events to sync with dock
if self.dock_component:
self.player_container.connect("active-player-changed", self.on_active_player_changed)
self.player_container.connect(
"active-player-changed", self.on_active_player_changed
)
# Connect to escape key to close
self.connect("key-press-event", self.on_key_press)
@@ -152,15 +154,19 @@ class MusicPlayer(Box):
# Set up periodic check for active player
from gi.repository import GLib
self.active_player_check_timeout = GLib.timeout_add_seconds(2, self.check_active_player)
self.active_player_check_timeout = GLib.timeout_add_seconds(
2, self.check_active_player
)
# Connect to destroy signal for cleanup
self.connect("destroy", self.on_destroy)
def on_destroy(self, widget):
"""Clean up popup when component is destroyed"""
if hasattr(self, 'active_player_check_timeout'):
if hasattr(self, "active_player_check_timeout"):
from gi.repository import GLib
GLib.source_remove(self.active_player_check_timeout)
if self.popup:
self.popup.destroy()
@@ -175,8 +181,12 @@ class MusicPlayer(Box):
self.set_current_player(player)
else:
# If the new player is playing, switch to it
if (player.props.playback_status.value_name == "PLAYERCTL_PLAYBACK_STATUS_PLAYING" and
self.current_player.props.playback_status.value_name != "PLAYERCTL_PLAYBACK_STATUS_PLAYING"):
if (
player.props.playback_status.value_name
== "PLAYERCTL_PLAYBACK_STATUS_PLAYING"
and self.current_player.props.playback_status.value_name
!= "PLAYERCTL_PLAYBACK_STATUS_PLAYING"
):
self.set_current_player(player)
self.update_visibility()
@@ -194,26 +204,40 @@ class MusicPlayer(Box):
self.current_player_service = None
# Try to find another player with media content
if hasattr(self.manager, '_manager') and hasattr(self.manager._manager, 'props'):
if hasattr(self.manager, "_manager") and hasattr(
self.manager._manager, "props"
):
for player_name in self.manager._manager.props.player_names:
try:
from gi.repository import Playerctl
candidate_player = Playerctl.Player.new_from_name(player_name)
# Check if this player has media content
if (hasattr(candidate_player, 'props') and
hasattr(candidate_player.props, 'metadata')):
if hasattr(candidate_player, "props") and hasattr(
candidate_player.props, "metadata"
):
metadata = candidate_player.props.metadata
if metadata and len(metadata.keys()) > 0:
keys = metadata.keys()
if ("xesam:title" in keys or
"xesam:artist" in keys or
"mpris:length" in keys):
print(f"[DEBUG] Switching to player with media: {candidate_player.props.player_name}")
if (
"xesam:title" in keys
or "xesam:artist" in keys
or "mpris:length" in keys
):
print(
f"[DEBUG] Switching to player with media: {
candidate_player.props.player_name
}"
)
self.set_current_player(candidate_player)
break
except Exception as e:
print(f"[DEBUG] Error checking candidate player {player_name}: {e}")
print(
f"[DEBUG] Error checking candidate player {player_name}: {
e
}"
)
continue
self.update_visibility()
@@ -221,15 +245,25 @@ class MusicPlayer(Box):
def get_playing_player(self):
"""Find a currently playing player from the manager"""
try:
if hasattr(self.manager, '_manager') and hasattr(self.manager._manager, 'props'):
if hasattr(self.manager, "_manager") and hasattr(
self.manager._manager, "props"
):
for player_name in self.manager._manager.props.player_names:
try:
from gi.repository import Playerctl
player = Playerctl.Player.new_from_name(player_name)
if (hasattr(player, 'props') and
hasattr(player.props, 'playback_status') and
player.props.playback_status.value_name == "PLAYERCTL_PLAYBACK_STATUS_PLAYING"):
print(f"[DEBUG] Found playing player: {player.props.player_name}")
if (
hasattr(player, "props")
and hasattr(player.props, "playback_status")
and player.props.playback_status.value_name
== "PLAYERCTL_PLAYBACK_STATUS_PLAYING"
):
print(
f"[DEBUG] Found playing player: {
player.props.player_name
}"
)
return player
except Exception as e:
print(f"[DEBUG] Error checking player {player_name}: {e}")
@@ -246,22 +280,42 @@ class MusicPlayer(Box):
if playing_player:
# If we found a playing player and it's different from current, switch to it
if (not self.current_player or
self.current_player.props.player_name != playing_player.props.player_name):
print(f"[DEBUG] Switching to playing player: {playing_player.props.player_name}")
if (
not self.current_player
or self.current_player.props.player_name
!= playing_player.props.player_name
):
print(
f"[DEBUG] Switching to playing player: {
playing_player.props.player_name
}"
)
self.set_current_player(playing_player)
elif self.current_player:
# If no player is playing, check if current player is paused
if (self.current_player.props.playback_status.value_name != "PLAYERCTL_PLAYBACK_STATUS_PLAYING"):
if (
self.current_player.props.playback_status.value_name
!= "PLAYERCTL_PLAYBACK_STATUS_PLAYING"
):
# Current player is paused, see if there's another player available
if hasattr(self.manager, '_manager') and hasattr(self.manager._manager, 'props'):
if hasattr(self.manager, "_manager") and hasattr(
self.manager._manager, "props"
):
for player_name in self.manager._manager.props.player_names:
try:
from gi.repository import Playerctl
player = Playerctl.Player.new_from_name(player_name)
if player.props.player_name != self.current_player.props.player_name:
if (
player.props.player_name
!= self.current_player.props.player_name
):
# Switch to a different available player
print(f"[DEBUG] Switching to available player: {player.props.player_name}")
print(
f"[DEBUG] Switching to available player: {
player.props.player_name
}"
)
self.set_current_player(player)
break
except:
@@ -320,10 +374,12 @@ class MusicPlayer(Box):
pos_sec = int(pos % 60)
dur_min = int(dur // 60)
dur_sec = int(dur % 60)
timestamp_text = f"{pos_min:02d}:{pos_sec:02d} / {dur_min:02d}:{dur_sec:02d}"
timestamp_text = (
f"{pos_min:02d}:{pos_sec:02d} / {dur_min:02d}:{dur_sec:02d}"
)
# Get current track info if available
if hasattr(self, '_current_track_info'):
if hasattr(self, "_current_track_info"):
tooltip = f"{self._current_track_info}\n{timestamp_text}"
else:
tooltip = f"Music Player\n{timestamp_text}"
@@ -337,7 +393,7 @@ class MusicPlayer(Box):
self.music_label.set_markup(icons.music)
self.progress_bar.remove_style_class("paused")
# Update tooltip to show playing state
if hasattr(self, '_current_track_info'):
if hasattr(self, "_current_track_info"):
self.event_box.set_tooltip_text(f"{self._current_track_info} (Playing)")
# Trigger immediate check for active player when play state changes
@@ -351,7 +407,7 @@ class MusicPlayer(Box):
self.music_label.set_markup(icons.music)
self.progress_bar.add_style_class("paused")
# Update tooltip to show paused state
if hasattr(self, '_current_track_info'):
if hasattr(self, "_current_track_info"):
self.event_box.set_tooltip_text(f"{self._current_track_info} (Paused)")
# Trigger immediate check for active player when pause state changes
@@ -373,12 +429,18 @@ class MusicPlayer(Box):
self._current_track_info = f"{artist} - {title}"
# If we have position info, include timestamp
if hasattr(self, 'position') and hasattr(self, 'duration') and self.duration > 0:
if (
hasattr(self, "position")
and hasattr(self, "duration")
and self.duration > 0
):
pos_min = int(self.position // 60)
pos_sec = int(self.position % 60)
dur_min = int(self.duration // 60)
dur_sec = int(self.duration % 60)
timestamp_text = f"{pos_min:02d}:{pos_sec:02d} / {dur_min:02d}:{dur_sec:02d}"
timestamp_text = (
f"{pos_min:02d}:{pos_sec:02d} / {dur_min:02d}:{dur_sec:02d}"
)
tooltip = f"{self._current_track_info}\n{timestamp_text}"
else:
tooltip = self._current_track_info
@@ -438,29 +500,47 @@ class MusicPlayer(Box):
def has_running_players(self):
"""Check if there are any players that actually have media content running"""
try:
if hasattr(self.manager, '_manager') and hasattr(self.manager._manager, 'props'):
if hasattr(self.manager, "_manager") and hasattr(
self.manager._manager, "props"
):
for player_name in self.manager._manager.props.player_names:
try:
from gi.repository import Playerctl
player = Playerctl.Player.new_from_name(player_name)
# Check if player has metadata indicating actual media content
if hasattr(player, 'props') and hasattr(player.props, 'metadata'):
if hasattr(player, "props") and hasattr(
player.props, "metadata"
):
metadata = player.props.metadata
if metadata and len(metadata.keys()) > 0:
# Check for essential metadata that indicates real media
keys = metadata.keys()
if ("xesam:title" in keys or
"xesam:artist" in keys or
"mpris:length" in keys):
print(f"[DEBUG] Found player with media content: {player.props.player_name}")
if (
"xesam:title" in keys
or "xesam:artist" in keys
or "mpris:length" in keys
):
print(
f"[DEBUG] Found player with media content: {
player.props.player_name
}"
)
return True
# Also check if player is currently playing (even without full metadata)
if (hasattr(player, 'props') and
hasattr(player.props, 'playback_status') and
player.props.playback_status.value_name == "PLAYERCTL_PLAYBACK_STATUS_PLAYING"):
print(f"[DEBUG] Found playing player: {player.props.player_name}")
if (
hasattr(player, "props")
and hasattr(player.props, "playback_status")
and player.props.playback_status.value_name
== "PLAYERCTL_PLAYBACK_STATUS_PLAYING"
):
print(
f"[DEBUG] Found playing player: {
player.props.player_name
}"
)
return True
except Exception as e:
+18 -52
View File
@@ -7,33 +7,32 @@ from fabric.widgets.button import Button
from fabric.widgets.centerbox import CenterBox
from fabric.widgets.label import Label
from fabric.widgets.scrolledwindow import ScrolledWindow
from gi.repository import GLib, Gtk, Gdk
from gi.repository import Gdk, GLib, Gtk
import config.data as data
import utils.icons as icons
from modules.notification_popup import NotificationBox
from utils.custom_image import CustomImage
from utils.wayland import WaylandWindow as Window
from utils.notification_utils import (
CONFIG_FILE,
PERSISTENT_DIR,
PERSISTENT_HISTORY_FILE,
CONFIG_FILE,
load_scaled_pixbuf,
get_shared_notification_history,
cleanup_orphan_cached_images,
get_date_header,
schedule_midnight_update,
compute_time_label,
create_historical_notification_from_data,
save_persistent_history
get_date_header,
get_shared_notification_history,
load_scaled_pixbuf,
save_persistent_history,
schedule_midnight_update,
)
from utils.wayland import WaylandWindow as Window
class NotificationIndicator(Button):
def __init__(self, **kwargs):
super().__init__(name="button-bar-notifications", **kwargs)
self.notification_icon = Label(
name="notification-icon", markup=icons.notifications
)
@@ -46,7 +45,6 @@ class NotificationIndicator(Button):
self.popup_window = None
self._connect_to_dnd_state()
def on_clicked(self, _button):
@@ -64,14 +62,15 @@ class NotificationIndicator(Button):
def _connect_to_dnd_state(self):
try:
self.notification_history = get_shared_notification_history()
self.notification_history.connect(
"dnd-state-changed", self._on_dnd_state_changed
)
self.notification_history.connect('dnd-state-changed', self._on_dnd_state_changed)
self._update_icon_for_dnd_state(self.notification_history.do_not_disturb_enabled)
self._update_icon_for_dnd_state(
self.notification_history.do_not_disturb_enabled
)
except Exception as e:
print(f"Could not connect to DND state: {e}")
@@ -87,9 +86,6 @@ class NotificationIndicator(Button):
self.set_tooltip_text("Notifications")
class NotificationHistory(Box):
def __init__(self, **kwargs):
super().__init__(name="notification-history", orientation="v", **kwargs)
@@ -169,10 +165,8 @@ class NotificationHistory(Box):
cleanup_orphan_cached_images(self.persistent_notifications)
self.schedule_midnight_update()
self._connect_to_shared_history()
self._load_and_sync_dnd_state()
self.LIMITED_APPS_HISTORY = ["Spotify"]
@@ -180,7 +174,9 @@ class NotificationHistory(Box):
def _connect_to_shared_history(self):
try:
shared_history = get_shared_notification_history()
shared_history.connect('notification-added', self._on_shared_notification_added)
shared_history.connect(
"notification-added", self._on_shared_notification_added
)
except Exception as e:
print(f"Could not connect to shared notification history: {e}")
@@ -189,20 +185,17 @@ class NotificationHistory(Box):
def _refresh_history(self):
try:
for child in self.notifications_list.get_children()[:]:
self.notifications_list.remove(child)
if hasattr(child, 'destroy'):
if hasattr(child, "destroy"):
child.destroy()
self.containers.clear()
if os.path.exists(PERSISTENT_HISTORY_FILE):
with open(PERSISTENT_HISTORY_FILE, "r") as f:
self.persistent_notifications = json.load(f)
for note in reversed(self.persistent_notifications[-50:]):
self._add_historical_notification(note)
@@ -212,18 +205,15 @@ class NotificationHistory(Box):
def _load_and_sync_dnd_state(self):
try:
dnd_enabled = False
if os.path.exists(CONFIG_FILE):
with open(CONFIG_FILE, "r") as f:
config_data = json.load(f)
dnd_enabled = config_data.get("dnd_enabled", False)
shared_history = get_shared_notification_history()
shared_history.do_not_disturb_enabled = dnd_enabled
self.header_switch.set_active(dnd_enabled)
self.do_not_disturb_enabled = dnd_enabled
@@ -284,8 +274,6 @@ class NotificationHistory(Box):
def on_do_not_disturb_changed(self, switch, pspec):
self.do_not_disturb_enabled = switch.get_active()
try:
shared_history = get_shared_notification_history()
shared_history.set_do_not_disturb_enabled(self.do_not_disturb_enabled)
@@ -305,8 +293,6 @@ class NotificationHistory(Box):
self.notifications_list.remove(child)
child.destroy()
if os.path.exists(PERSISTENT_HISTORY_FILE):
try:
os.remove(PERSISTENT_HISTORY_FILE)
@@ -322,14 +308,11 @@ class NotificationHistory(Box):
if not os.path.exists(PERSISTENT_DIR):
os.makedirs(PERSISTENT_DIR, exist_ok=True)
if os.path.exists(PERSISTENT_HISTORY_FILE):
try:
with open(PERSISTENT_HISTORY_FILE, "r") as f:
self.persistent_notifications = json.load(f)
for note in reversed(self.persistent_notifications[-50:]):
self._add_historical_notification(note)
@@ -349,8 +332,6 @@ class NotificationHistory(Box):
self.no_notifications_box.set_visible(not has_notifications)
self.notifications_list.set_visible(has_notifications)
def _add_historical_notification(self, note):
hist_notif = create_historical_notification_from_data(note)
@@ -497,8 +478,6 @@ class NotificationHistory(Box):
if removed_from_list:
self.persistent_notifications = new_persistent_notifications
self._save_persistent_history()
container.destroy()
self.containers = [c for c in self.containers if c != container]
@@ -507,10 +486,8 @@ class NotificationHistory(Box):
class NotificationHistoryWindow(Window):
def __init__(self):
self.notification_history = NotificationHistory()
scrolled = ScrolledWindow(
name="notifications-scrolled",
child=self.notification_history,
@@ -533,11 +510,9 @@ class NotificationHistoryWindow(Window):
children=[scrolled],
)
dock_position = data.DOCK_POSITION
popup_anchor = self._get_popup_anchor(dock_position)
margin = self._get_popup_margin(dock_position)
super().__init__(
@@ -551,15 +526,12 @@ class NotificationHistoryWindow(Window):
all_visible=True,
)
self.connect("button-press-event", self.on_button_press)
self.connect("key-press-event", self.on_key_press)
self.set_can_focus(True)
self.grab_focus()
self.set_tooltip_text(
"Keyboard shortcuts:\n• Escape: Close popup\n• Ctrl+D: Toggle DND\n• Ctrl+A: Clear all notifications"
)
@@ -586,22 +558,16 @@ class NotificationHistoryWindow(Window):
pass
def on_key_press(self, _widget, event):
if event.keyval == Gdk.KEY_Escape:
self.set_visible(False)
return True
elif event.state & Gdk.ModifierType.CONTROL_MASK and event.keyval == Gdk.KEY_d:
current_dnd = self.notification_history.do_not_disturb_enabled
self.notification_history.header_switch.set_active(not current_dnd)
return True
elif event.state & Gdk.ModifierType.CONTROL_MASK and event.keyval == Gdk.KEY_a:
self.notification_history.clear_history()
return True
+40 -22
View File
@@ -1,25 +1,27 @@
from fabric.widgets.box import Box
from fabric.widgets.label import Label
from fabric.widgets.centerbox import CenterBox
from fabric.widgets.button import Button
from fabric.widgets.stack import Stack
from fabric.core.service import Signal
from fabric.widgets.box import Box
from fabric.widgets.button import Button
from fabric.widgets.centerbox import CenterBox
from fabric.widgets.label import Label
from fabric.widgets.stack import Stack
from loguru import logger
import config.data as data
import utils.icons as icons
from services.mpris import PlayerManager, PlayerService
from utils.wiggle_bar import WigglyWidget
import utils.icons as icons
import config.data as data
from loguru import logger
class Player(Box):
def __init__(self, player, **kwargs):
super().__init__(style_classes="player", orientation="v", **kwargs)
# Check if we're in vertical mode based on dock position
vertical_mode = data.DOCK_POSITION in ["Left", "Right"] if hasattr(data, 'DOCK_POSITION') else False
vertical_mode = (
data.DOCK_POSITION in ["Left", "Right"]
if hasattr(data, "DOCK_POSITION")
else False
)
if not vertical_mode:
# vertical class binding from unknown source
@@ -42,9 +44,7 @@ class Player(Box):
)
# Use a fallback background image
self.set_style(
f"background-image:url('{data.HOME_DIR}/.current.wall')"
)
self.set_style(f"background-image:url('{data.HOME_DIR}/.current.wall')")
self.song = Label(
name="song",
@@ -182,7 +182,11 @@ class Player(Box):
keys = metadata.keys()
if "xesam:artist" in keys and "xesam:title" in keys:
# Check if we're in vertical mode
vertical_mode = data.DOCK_POSITION in ["Left", "Right"] if hasattr(data, 'DOCK_POSITION') else False
vertical_mode = (
data.DOCK_POSITION in ["Left", "Right"]
if hasattr(data, "DOCK_POSITION")
else False
)
_max_chars = 43 if not vertical_mode else 30
song_title = metadata["xesam:title"]
@@ -201,16 +205,21 @@ class Player(Box):
elif "xesam:url" in keys and "youtube.com" in metadata["xesam:url"]:
# Simple YouTube thumbnail extraction for Firefox
import re
url = metadata["xesam:url"]
match = re.search(r'(?:v=|/)([a-zA-Z0-9_-]{11})', url)
match = re.search(r"(?:v=|/)([a-zA-Z0-9_-]{11})", url)
if match:
video_id = match.group(1)
thumbnail_url = f"https://img.youtube.com/vi/{video_id}/hqdefault.jpg"
thumbnail_url = (
f"https://img.youtube.com/vi/{video_id}/hqdefault.jpg"
)
self.set_style(f"background-image:url('{thumbnail_url}')")
print(f"Using YouTube thumbnail: {thumbnail_url}")
else:
# Fallback to default wallpaper
self.set_style(f"background-image:url('{data.HOME_DIR}/.current.wall')")
self.set_style(
f"background-image:url('{data.HOME_DIR}/.current.wall')"
)
else:
# Fallback to default wallpaper when no artwork is available
self.set_style(f"background-image:url('{data.HOME_DIR}/.current.wall')")
@@ -222,7 +231,9 @@ class Player(Box):
self.on_play(self._player)
if player.props.shuffle == True:
self.shuffle_button.get_child().set_markup(icons.shuffle) # Use same icon for now
self.shuffle_button.get_child().set_markup(
icons.shuffle
) # Use same icon for now
self.shuffle_button.get_child().set_name("disable-shuffle")
else:
self.shuffle_button.get_child().set_markup(icons.shuffle)
@@ -250,7 +261,9 @@ class Player(Box):
self.shuffle_button.get_child().set_markup(icons.shuffle)
self.shuffle_button.get_child().set_name("shuffle")
else:
self.shuffle_button.get_child().set_markup(icons.shuffle) # Use same icon for now
self.shuffle_button.get_child().set_markup(
icons.shuffle
) # Use same icon for now
self.shuffle_button.get_child().set_name("disable-shuffle")
self.shuffle_button.get_child().set_style("color: white")
@@ -324,7 +337,11 @@ class PlayerContainer(Box):
)
# Check if we're in vertical mode
vertical_mode = data.DOCK_POSITION in ["Left", "Right"] if hasattr(data, 'DOCK_POSITION') else False
vertical_mode = (
data.DOCK_POSITION in ["Left", "Right"]
if hasattr(data, "DOCK_POSITION")
else False
)
self.player_switch_container = CenterBox(
name="player-switch-container",
@@ -347,7 +364,8 @@ class PlayerContainer(Box):
new_player.gtk_wrapper.queue_draw()
new_player.set_name(player.props.player_name)
self.player.append(new_player)
self.player_objects[player.props.player_name] = player # Store the player object
# Store the player object
self.player_objects[player.props.player_name] = player
print("stacking player")
self.stack.add_named(new_player, player.props.player_name)
+35 -12
View File
@@ -13,9 +13,9 @@ from modules.launcher.result import Result
from services.auth import (
generate_totp,
get_time_remaining_with_blink,
validate_base32_secret,
parse_otpauth_uri,
scan_qr_and_add_account,
validate_base32_secret,
)
@@ -421,7 +421,9 @@ class OTPPlugin(PluginBase):
results.append(
Result(
title=f"{totp_code}",
subtitle_markup=f"{display_name} • {time_display} remaining • Shift+Enter: remove",
subtitle_markup=f"{display_name} • {
time_display
} remaining • Shift+Enter: remove",
icon_markup=icons.key,
action=lambda code=totp_code: self._copy_to_clipboard(code),
relevance=1.0,
@@ -430,7 +432,9 @@ class OTPPlugin(PluginBase):
"type": "totp",
"account": account_name,
"code": totp_code,
"alt_action": lambda acc=account_name: self._remove_account_and_refresh(acc),
"alt_action": lambda acc=account_name: self._remove_account_and_refresh(
acc
),
},
)
)
@@ -488,7 +492,9 @@ class OTPPlugin(PluginBase):
"type": "totp",
"account": account_name,
"code": totp_code,
"alt_action": lambda acc=account_name: self._remove_account_and_refresh(acc),
"alt_action": lambda acc=account_name: self._remove_account_and_refresh(
acc
),
},
)
)
@@ -606,7 +612,9 @@ class OTPPlugin(PluginBase):
title=f"Scan QR Code for '{account_name}'",
subtitle="Click to scan QR code from screen",
icon_markup=icons.connect,
action=lambda name=account_name: self._scan_qr_and_add_account(name),
action=lambda name=account_name: self._scan_qr_and_add_account(
name
),
relevance=1.0,
plugin_name=self.display_name,
data={"type": "qr_scan"},
@@ -620,6 +628,7 @@ class OTPPlugin(PluginBase):
# Run QR scanning asynchronously so launcher closes immediately
import threading
thread = threading.Thread(target=self._scan_qr_async, args=(account_name,))
thread.daemon = True
thread.start()
@@ -688,14 +697,16 @@ class OTPPlugin(PluginBase):
time_display
} remaining",
icon_markup=icons.trash,
action=lambda acc=acc_name: self._remove_account_and_refresh(acc),
action=lambda acc=acc_name: self._remove_account_and_refresh(
acc
),
relevance=0.9,
plugin_name=self.display_name,
data={
"type": "remove_instruction",
"account": acc_name,
"code": totp_code,
"keep_launcher_open": True
"keep_launcher_open": True,
},
)
)
@@ -703,9 +714,11 @@ class OTPPlugin(PluginBase):
results.append(
Result(
title=f"Error: {acc_name}",
subtitle=f"Press Enter to remove (Invalid secret)",
subtitle="Press Enter to remove (Invalid secret)",
icon_markup=icons.trash,
action=lambda acc=acc_name: self._remove_account_and_refresh(acc),
action=lambda acc=acc_name: self._remove_account_and_refresh(
acc
),
relevance=0.8,
plugin_name=self.display_name,
data={
@@ -746,7 +759,11 @@ class OTPPlugin(PluginBase):
action=lambda acc=account_name: self._remove_account_and_refresh(acc),
relevance=1.0,
plugin_name=self.display_name,
data={"type": "remove_confirm", "account": account_name,"keep_launcher_open": True,},
data={
"type": "remove_confirm",
"account": account_name,
"keep_launcher_open": True,
},
)
]
@@ -779,7 +796,9 @@ class OTPPlugin(PluginBase):
return [
Result(
title=f"✓ Added '{account_name}'",
subtitle=f"OTP account added successfully (secret: {result['secret'][:4]}...)",
subtitle=f"OTP account added successfully (secret: {
result['secret'][:4]
}...)",
icon_markup=icons.check,
action=lambda: self._trigger_refresh(),
relevance=1.0,
@@ -814,7 +833,11 @@ class OTPPlugin(PluginBase):
}
self._save_secrets()
display_name = f"{result['issuer']} - {result['account_name']}" if result['issuer'] else result['account_name']
display_name = (
f"{result['issuer']} - {result['account_name']}"
if result["issuer"]
else result["account_name"]
)
return [
Result(
title=f"✓ Added '{display_name}'",