✨ feat(launcher): improve launcher experience with mouse tracking, application search, and visual enhancements

This commit is contained in:
sankalp committed 2025-08-03 07:48:33 +05:45
1 parent ac067abd0e
commit db210e2e6c
19 files changed
+513 -339

No files matched your search

+163 -54
View File
@@ -1,15 +1,15 @@
from typing import List, Optional, Tuple
from gi.repository import Gdk, GLib
from fabric.core.service import Property
from fabric.widgets.box import Box
from fabric.widgets.entry import Entry
from fabric.widgets.scrolledwindow import ScrolledWindow
from gi.repository import Gdk, GLib
from modules.launcher.plugin_manager import PluginManager
from modules.launcher.result import Result
from modules.launcher.result_item import ResultItem
from modules.launcher.trigger_config import TriggerConfig
from widgets.smartscrolled import SmartScrolledBox
from widgets.wayland import WaylandWindow as Window
# Constants
@@ -44,26 +44,15 @@ class Launcher(Window):
keyboard_mode="exclusive",
**kwargs,
)
# Initialization flag to prevent callbacks during setup
self._initializing = True
# Flag to prevent recursion when automatically adding spaces
self._auto_adding_space = False
# Flag to prevent search change handler interference during backspace processing
self._processing_backspace = False
# Initialize plugin manager
self.plugin_manager = PluginManager()
# Initialize trigger configuration
self.trigger_config = TriggerConfig()
# Current results and selection
self.results: List[Result] = []
self.selected_index = 0
self.max_results = 8 # Spotlight typically shows fewer results
self.max_results = 5 # Show 4 applications by default instead of list
# Trigger system
self.triggered_plugin = None # Currently active triggered plugin
@@ -75,6 +64,16 @@ class Launcher(Window):
# Focus management
self.focus_mode = "search" # "search", "results"
# Mouse activity tracking
self._mouse_active = False
self._last_mouse_activity = 0
self._mouse_activity_timeout = 1000 # 1 second timeout
self._keyboard_used_recently = False
self._last_keyboard_activity = 0
self._keyboard_activity_timeout = 2000 # 2 second timeout
self._launcher_just_opened = False
self._mouse_interaction_delay = 500 # 500ms delay after opening
# Setup UI
main_box = Box(
name="launcher",
@@ -107,28 +106,29 @@ class Launcher(Window):
main_box.add(self.header_box)
# Results container with increased height for Spotlight-style
self.results_scroll = ScrolledWindow(
name="launcher-results-scroll",
h_scrollbar_policy="never",
min_content_size=(LAUNCHER_WIDTH, LAUNCHER_HEIGHT),
max_content_size=(LAUNCHER_WIDTH, LAUNCHER_HEIGHT),
propagate_width=False,
propagate_height=False,
)
self.results_scroll = SmartScrolledBox(max_height=LAUNCHER_HEIGHT)
self.results_box = Box(
name="launcher-results",
orientation="v",
spacing=0,
)
self.results_scroll.add(self.results_box)
self.results_scroll.append(self.results_box)
main_box.add(self.results_scroll)
# Initially hide the results container
self.results_scroll.hide()
# Keep the results container visible initially
self.connect("key-press-event", self._on_key_press)
# Connect mouse events to track activity
self.results_scroll.connect("button-press-event", self._on_mouse_activity)
self.results_scroll.connect("motion-notify-event", self._on_mouse_activity)
self.results_scroll.connect("scroll-event", self._on_mouse_activity)
# Also track mouse activity on the main window for better coverage
self.connect("button-press-event", self._on_mouse_activity)
self.connect("motion-notify-event", self._on_mouse_activity)
self.hide()
# Mark initialization as complete
@@ -144,6 +144,20 @@ class Launcher(Window):
trigger_keyword: Optional trigger keyword to activate immediately (e.g., "google", "calc", "app")
external: If True, execute the command without showing the launcher UI
"""
# Reset mouse activity tracking when launcher is shown
self._mouse_active = False
self._last_mouse_activity = 0
self._keyboard_used_recently = False
self._last_keyboard_activity = 0
self._launcher_just_opened = True
# Schedule to allow mouse interactions after a delay
def enable_mouse_interactions():
self._launcher_just_opened = False
return False
GLib.timeout_add(self._mouse_interaction_delay, enable_mouse_interactions)
if external and trigger_keyword:
# Execute command externally without showing launcher
return self._execute_external_command(trigger_keyword)
@@ -180,10 +194,11 @@ class Launcher(Window):
self.search_entry.set_text("")
self._clear_results()
else:
# Normal launcher opening - clear everything
# Normal launcher opening - show applications
self.opened_with_trigger = False
self.search_entry.set_text("")
self._clear_results()
# Trigger initial search to show applications immediately
self._perform_search("")
# Reset focus mode to search
self.focus_mode = "search"
@@ -305,8 +320,8 @@ class Launcher(Window):
# Debounce search to avoid too many queries
GLib.timeout_add(SEARCH_DEBOUNCE_MS, self._perform_search, query)
else:
# Hide results when query is empty
self._clear_results()
# Show applications when query is empty
self._perform_search("")
def _perform_search(self, query: str) -> bool:
"""Perform search across all plugins."""
@@ -315,10 +330,25 @@ class Launcher(Window):
return False
if not query:
# Empty query - reset trigger mode and hide results
# Empty query - show popular applications
self.triggered_plugin = None
self.active_trigger = ""
self._clear_results()
# Get applications plugin and show popular apps
applications_plugin = self._get_applications_plugin()
if applications_plugin:
try:
# Query with empty string to get popular/frequently used applications
all_results = applications_plugin.query("")
# Limit to max_results for empty query
self.results = all_results[: self.max_results]
self.selected_index = 0
self._update_results_display()
except Exception as e:
print(f"Error getting applications: {e}")
self._clear_results()
else:
self._clear_results()
return False
# Check if we're already in trigger mode
@@ -365,13 +395,24 @@ class Launcher(Window):
print(f"Error in triggered plugin {triggered_plugin.name}: {e}")
all_results = []
else:
# No trigger detected - only show trigger suggestions
# No trigger detected - search applications and show trigger suggestions
self.triggered_plugin = None
self.active_trigger = ""
# Show trigger suggestions if query matches trigger prefixes
all_results = []
# Search applications directly without trigger
applications_plugin = self._get_applications_plugin()
if applications_plugin:
try:
app_results = applications_plugin.query(query)
all_results.extend(app_results)
except Exception as e:
print(f"Error searching applications: {e}")
# Also show trigger suggestions if query matches trigger prefixes
trigger_suggestions = self._get_trigger_suggestions(query)
all_results = trigger_suggestions
all_results.extend(trigger_suggestions)
# Sort results by relevance score
all_results.sort(key=lambda r: r.relevance, reverse=True)
@@ -387,8 +428,19 @@ class Launcher(Window):
# In trigger mode - show all results from the triggered plugin
self.results = all_results
elif not has_bypass:
# Global search or trigger suggestions - apply max_results limit
self.results = all_results[: self.max_results]
# Global search - check if it's an application search
applications_plugin = self._get_applications_plugin()
is_application_search = applications_plugin and any(
hasattr(r, "plugin_name") and r.plugin_name == "Applications"
for r in all_results
)
if is_application_search:
# Don't limit application search results
self.results = all_results
else:
# Apply max_results limit for other global searches and trigger suggestions
self.results = all_results[: self.max_results]
else:
# Has bypass flag - show all results
self.results = all_results
@@ -462,6 +514,16 @@ class Launcher(Window):
return None, ""
def _get_applications_plugin(self):
"""Get the applications plugin instance."""
for plugin in self.plugin_manager.get_active_plugins():
if (
hasattr(plugin, "display_name")
and plugin.display_name == "Applications"
):
return plugin
return None
def _get_trigger_suggestions(self, query: str) -> List[Result]:
"""
Get trigger suggestions based on the current query.
@@ -515,7 +577,7 @@ class Launcher(Window):
return Result(
title=f"{trigger_clean}",
subtitle=f"{description} - {', '.join(examples[:max_examples])}",
icon_markup=icon_name,
icon_name=icon_name,
action=lambda t=trigger_clean: self._activate_trigger(t),
# Shorter triggers get higher relevance
relevance=100 - len(trigger_clean),
@@ -678,11 +740,11 @@ class Launcher(Window):
self.results_box.show_all()
# Show/hide scroll container based on results
if self.results:
self.results_scroll.show()
else:
self.results_scroll.hide()
# Force height constraints after adding results
self.results_scroll.force_height_constraint()
# Always show scroll container
self.results_scroll.show()
def _update_input_action_text(self):
"""Update the input field with action text (Spotlight-style)."""
@@ -716,8 +778,6 @@ class Launcher(Window):
# Not in trigger mode - show Spotlight-style help
if current_text == ":":
self.search_entry.set_placeholder_text("Available search triggers")
elif current_text:
self.search_entry.set_placeholder_text("Spotlight Search")
else:
self.search_entry.set_placeholder_text("Spotlight Search")
@@ -727,7 +787,7 @@ class Launcher(Window):
self.selected_index = 0
for child in self.results_box.get_children():
self.results_box.remove(child)
self.results_scroll.hide()
# Keep the results scroll visible even when empty
def _handle_escape_key(self) -> bool:
"""Handle escape key press."""
@@ -816,6 +876,13 @@ class Launcher(Window):
def _on_key_press(self, _widget, event):
"""Handle key press events."""
# Track keyboard activity
import time
current_time = int(time.time() * 1000)
self._last_keyboard_activity = current_time
self._keyboard_used_recently = True
keyval = event.keyval
# Escape - handle password entry, exit trigger mode, or hide launcher
@@ -1011,16 +1078,58 @@ class Launcher(Window):
def _on_result_clicked(self, _result_item, index):
"""Handle result item click."""
self.selected_index = index
self._activate_selected()
# Only allow clicks when mouse is active or keyboard hasn't been used recently
if self._should_allow_mouse_interaction():
self.selected_index = index
self._activate_selected()
def _on_result_hovered(self, index):
"""Handle result item hover."""
# Update selection when mouse hovers over a result (without scrolling)
if 0 <= index < len(self.results):
self.selected_index = index
self.focus_mode = "results" # Switch to results focus mode
self._update_selection_visual_only()
# Only allow hover selection when mouse is active and keyboard hasn't been used recently
if self._should_allow_mouse_interaction():
if 0 <= index < len(self.results):
self.selected_index = index
self.focus_mode = "results" # Switch to results focus mode
self._update_selection_visual_only()
def _on_mouse_activity(self, widget, event):
"""Track mouse activity to determine when mouse interactions should be enabled."""
import time
current_time = int(time.time() * 1000)
self._last_mouse_activity = current_time
self._mouse_active = True
# Schedule a check to disable mouse activity after timeout
def check_mouse_timeout():
if current_time - self._last_mouse_activity >= self._mouse_activity_timeout:
self._mouse_active = False
return False
GLib.timeout_add(self._mouse_activity_timeout, check_mouse_timeout)
return False
def _should_allow_mouse_interaction(self):
"""Determine if mouse interactions should be allowed."""
import time
current_time = int(time.time() * 1000)
# Don't allow mouse interactions immediately after launcher opens
if self._launcher_just_opened:
return False
# Allow mouse interaction if:
# 1. Mouse is currently active (recent activity)
# 2. OR keyboard hasn't been used recently (2 seconds)
mouse_recently_active = (
current_time - self._last_mouse_activity
) < self._mouse_activity_timeout
keyboard_not_recently_used = (
current_time - self._last_keyboard_activity
) > self._keyboard_activity_timeout
return mouse_recently_active or keyboard_not_recently_used
def _is_mouse_over_results(self):
"""Check if mouse is over the results area."""
+35 -7
View File
@@ -4,7 +4,6 @@ from typing import List
from fabric.utils import DesktopApp
from fabric.utils.helpers import get_desktop_applications, get_relative_path
from modules.dock import Dock
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
@@ -17,8 +16,7 @@ class ApplicationsPlugin(PluginBase):
self.description = "Search and launch desktop applications"
def initialize(self):
# Set up triggers for applications - both with and without spaces
self.set_triggers(["app"])
pass
def cleanup(self):
pass
@@ -71,9 +69,8 @@ class ApplicationsPlugin(PluginBase):
def query(self, query_string: str) -> List[Result]:
"""Search applications based on query."""
if not query_string.strip():
return []
return self._get_all_applications()
# Get fresh applications list each time (like examples/app-launcher)
try:
applications = get_desktop_applications(include_hidden=False)
except Exception as e:
@@ -86,9 +83,8 @@ class ApplicationsPlugin(PluginBase):
for app in applications:
relevance = self._calculate_relevance(app, query)
if relevance > 0:
# Truncate description to prevent overflow beyond 550px window
description = app.description or app.generic_name or ""
if len(description) > 80: # Limit to ~80 characters to fit in 550px
if len(description) > 80:
description = description[:70] + "..."
result = Result(
@@ -161,3 +157,35 @@ class ApplicationsPlugin(PluginBase):
def _launch_application(self, app: DesktopApp):
app.launch()
def _get_all_applications(self) -> List[Result]:
"""Get a list of all available applications."""
try:
applications = get_desktop_applications(include_hidden=False)
except Exception as e:
print(f"Failed to load applications: {e}")
return []
results = []
for app in applications:
# Truncate description
description = app.description or app.generic_name or ""
if len(description) > 80:
description = description[:70] + "..."
result = Result(
title=app.display_name or app.name,
subtitle=description,
icon=app.get_icon_pixbuf(size=48),
action=lambda a=app: self._launch_application(a),
relevance=0.5, # Default relevance for all apps
plugin_name=self.display_name,
data={
"app": app,
"pin_action": lambda a=app: self._pin_application(a),
},
)
results.append(result)
return results
+22 -20
View File
@@ -4,10 +4,8 @@ import threading
import time
from typing import Dict, List
from fabric.utils import exec_shell_command_async
import config.data as data
import utils.icons as icons
from fabric.utils import exec_shell_command_async
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
@@ -38,17 +36,17 @@ class BashScriptsPlugin(PluginBase):
"pm balanced": {
"description": "Switch to balanced power profile",
"profile": "balanced",
"icon": icons.power_balanced,
"icon": "power-balanced",
},
"pm performance": {
"description": "Switch to performance power profile",
"profile": "performance",
"icon": icons.power_performance,
"icon": "power-performance",
},
"pm saver": {
"description": "Switch to power saver profile",
"profile": "power-saver",
"icon": icons.power_saving,
"icon": "power-saving",
},
}
@@ -134,12 +132,16 @@ class BashScriptsPlugin(PluginBase):
if actual_profile:
self._battery_service._profile_proxy.ActiveProfile = actual_profile
print(
f"BashScriptsPlugin: Successfully set power profile to {actual_profile}"
f"BashScriptsPlugin: Successfully set power profile to {
actual_profile
}"
)
return True
else:
print(
f"BashScriptsPlugin: No matching profile found for '{profile}' in available profiles: {available_profiles}"
f"BashScriptsPlugin: No matching profile found for '{
profile
}' in available profiles: {available_profiles}"
)
return False
@@ -186,7 +188,6 @@ class BashScriptsPlugin(PluginBase):
current_time - self._last_cache_update > self._cache_update_interval
or not self._scripts_cache
):
if not self._cache_building:
self._cache_building = True
self._cache_thread = threading.Thread(
@@ -197,7 +198,6 @@ class BashScriptsPlugin(PluginBase):
def _build_scripts_cache_background(self):
"""Build scripts cache in background thread."""
try:
new_cache = {}
# Scan Modus scripts directory for discovered scripts
@@ -208,7 +208,9 @@ class BashScriptsPlugin(PluginBase):
self._scan_directory_for_scripts(self.modus_scripts_dir, new_cache)
except (PermissionError, FileNotFoundError, OSError) as e:
print(
f"BashScriptsPlugin: Error scanning Modus scripts directory: {e}"
f"BashScriptsPlugin: Error scanning Modus scripts directory: {
e
}"
)
# Update cache atomically
@@ -423,13 +425,12 @@ class BashScriptsPlugin(PluginBase):
or query_lower in cmd_lower
or query_lower in description_lower
):
relevance = 1.0 if query_lower == cmd_lower else 0.9
result = Result(
title=cmd,
subtitle=info["description"],
icon_markup=info["icon"],
icon_name=info["icon"],
action=self._create_power_action(info["profile"]),
relevance=relevance,
plugin_name=self.display_name,
@@ -451,7 +452,7 @@ class BashScriptsPlugin(PluginBase):
result = Result(
title=cmd,
subtitle=info["description"],
icon_markup=info["icon"],
icon_name=info["icon"],
action=self._create_power_action(info["profile"]),
relevance=0.8,
plugin_name=self.display_name,
@@ -473,9 +474,10 @@ class BashScriptsPlugin(PluginBase):
if success:
# Clear the search query after successful execution
try:
from fabric import Application
from gi.repository import GLib
from fabric import Application
app = Application.get_default()
if app and hasattr(app, "launcher"):
launcher = app.launcher
@@ -517,16 +519,16 @@ class BashScriptsPlugin(PluginBase):
# Choose icon based on script type and status
if not executable:
icon_markup = icons.file
icon_name = "gtk-file"
elif script_type == "custom":
icon_markup = icons.star
icon_name = "folder-script-symbolic"
else:
icon_markup = icons.terminal
icon_name = "terminalc"
return Result(
title=script_name,
subtitle=subtitle,
icon_markup=icon_markup,
icon_name=icon_name,
action=self._create_script_action(script_name, script_info),
relevance=relevance,
plugin_name=self.display_name,
@@ -556,7 +558,7 @@ class BashScriptsPlugin(PluginBase):
variant_result = Result(
title=f"{script_name} {arg}",
subtitle=f"{desc} | [scripts]",
icon_markup=icons.terminal,
icon_name="terminal-symbolic",
action=self._create_script_action_with_args(
script_name, script_info, [arg]
),
+19 -19
View File
@@ -6,10 +6,9 @@ from pathlib import Path
from typing import Dict, List, Optional
from urllib.parse import urlparse
from fabric.utils.helpers import get_relative_path
from thefuzz import fuzz
import utils.icons as icons
from fabric.utils.helpers import get_relative_path
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
@@ -268,7 +267,7 @@ class BookmarksPlugin(PluginBase):
Result(
title="No Bookmarks Found",
subtitle="Use 'add <title> <url>' to add first bookmark",
icon_markup=icons.info,
icon_name="info",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -298,7 +297,7 @@ class BookmarksPlugin(PluginBase):
Result(
title="No Bookmarks Yet",
subtitle="Use 'add <title> <url>' to add first bookmark",
icon_markup=icons.info,
icon_name="info",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -307,7 +306,7 @@ class BookmarksPlugin(PluginBase):
Result(
title="Example: Add Google",
subtitle="add Google https://google.com",
icon_markup=icons.bulb,
icon_name="info",
action=lambda: None,
relevance=0.9,
plugin_name=self.display_name,
@@ -334,7 +333,7 @@ class BookmarksPlugin(PluginBase):
Result(
title="Add Bookmark",
subtitle="Usage: add <title> <url> [description]",
icon_markup=icons.info,
icon_name="info",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -343,7 +342,7 @@ class BookmarksPlugin(PluginBase):
Result(
title="Example",
subtitle="add Google https://google.com Search engine",
icon_markup=icons.bulb,
icon_name="info",
action=lambda: None,
relevance=0.9,
plugin_name=self.display_name,
@@ -358,7 +357,7 @@ class BookmarksPlugin(PluginBase):
Result(
title="Invalid Format",
subtitle="Usage: add <title> <url> [description]",
icon_markup=icons.alert,
icon_name="alert",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -387,7 +386,7 @@ class BookmarksPlugin(PluginBase):
Result(
title="Bookmark Already Exists",
subtitle=f"URL '{display_url}' already exists",
icon_markup=icons.alert,
icon_name="alert",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -407,7 +406,7 @@ class BookmarksPlugin(PluginBase):
# Truncate description to prevent launcher resize
max_desc_len = 35 - len(domain) # Account for domain + separator
if len(description) > max_desc_len:
description = description[:max_desc_len-3] + "..."
description = description[: max_desc_len - 3] + "..."
subtitle += f" • {description}"
# Truncate title for display
@@ -419,7 +418,7 @@ class BookmarksPlugin(PluginBase):
Result(
title=f"Add bookmark '{display_title}'",
subtitle=subtitle,
icon_markup=icons.plus,
icon_name="plus",
action=lambda: self._add_bookmark_action(title, url, description),
relevance=1.0,
plugin_name=self.display_name,
@@ -451,7 +450,7 @@ class BookmarksPlugin(PluginBase):
Result(
title="Bookmark Not Found",
subtitle=f"No bookmark found matching '{identifier}'",
icon_markup=icons.alert,
icon_name="alert",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -477,7 +476,7 @@ class BookmarksPlugin(PluginBase):
Result(
title=f"Remove '{display_title}'?",
subtitle=f"Click to confirm: {display_domain}",
icon_markup=icons.trash,
icon_name="trash",
action=lambda: self._remove_bookmark_action(identifier),
relevance=1.0,
plugin_name=self.display_name,
@@ -492,7 +491,7 @@ class BookmarksPlugin(PluginBase):
Result(
title="Remove Bookmark",
subtitle="Usage: remove <title|url|domain>",
icon_markup=icons.info,
icon_name="info",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -506,7 +505,7 @@ class BookmarksPlugin(PluginBase):
Result(
title="Available Bookmarks:",
subtitle=f"{len(bookmarks)} bookmarks available to remove",
icon_markup=icons.bookmark,
icon_name="bookmarks-organize",
action=lambda: None,
relevance=0.9,
plugin_name=self.display_name,
@@ -532,7 +531,7 @@ class BookmarksPlugin(PluginBase):
Result(
title=f"remove {display_title}",
subtitle=f"Click to remove: {display_title} ({display_domain})",
icon_markup=icons.trash,
icon_name="trash",
action=lambda t=title: self._remove_bookmark_action(t),
relevance=0.8,
plugin_name=self.display_name,
@@ -643,9 +642,10 @@ class BookmarksPlugin(PluginBase):
if description:
# Truncate description to prevent launcher resize
max_desc_len = 50 - len(domain) # Account for domain + separator
# Account for domain + separator
max_desc_len = 50 - len(domain)
if len(description) > max_desc_len:
description = description[:max_desc_len-3] + "..."
description = description[: max_desc_len - 3] + "..."
subtitle = f"{domain} • {description}"
else:
subtitle = domain
@@ -657,7 +657,7 @@ class BookmarksPlugin(PluginBase):
return Result(
title=title,
subtitle=subtitle,
icon_markup=icons.bookmark,
icon_name="bookmark-organize",
action=lambda u=url: self._open_bookmark(u),
relevance=relevance,
plugin_name=self.display_name,
+4 -5
View File
@@ -3,11 +3,10 @@ from threading import Timer
from typing import List
import gi
from fabric.utils import get_relative_path
from fabric.utils.helpers import exec_shell_command_async
import config.data as data
import utils.icons as icons
from fabric.utils import get_relative_path
from fabric.utils.helpers import exec_shell_command_async
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
@@ -173,7 +172,7 @@ class CaffeinePlugin(PluginBase):
result = Result(
title=f"Custom: {query}",
subtitle="Set custom duration",
icon_markup=icons.coffee,
icon_name="caffeine",
action=self._create_inhibit_action(query),
relevance=1.0,
plugin_name=self.display_name,
@@ -194,7 +193,7 @@ class CaffeinePlugin(PluginBase):
result = Result(
title=description,
subtitle=subtitle,
icon_markup=icons.coffee,
icon_name="caffeine",
action=self._create_inhibit_action(duration),
relevance=0.9 if query == duration else 0.7,
plugin_name=self.display_name,
+3 -4
View File
@@ -4,7 +4,6 @@ import subprocess
import time
from typing import List
import utils.icons as icons
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
from utils.conversion import Conversion
@@ -99,7 +98,7 @@ class CalculatorPlugin(PluginBase):
Result(
title=f"{result:.6g} {to_unit}",
subtitle=subtitle,
icon_markup=icons.calculator,
icon_name="calculator-symbolic",
action=lambda r=f"{result:.6g}": self._copy_to_clipboard(r),
relevance=1.0,
plugin_name=self.display_name,
@@ -111,7 +110,7 @@ class CalculatorPlugin(PluginBase):
Result(
title="Invalid conversion",
subtitle=str(e),
icon_markup=icons.calculator,
icon_name="calculator-symbolic",
relevance=0.0,
plugin_name=self.display_name,
)
@@ -127,7 +126,7 @@ class CalculatorPlugin(PluginBase):
Result(
title=f"{result:.6g}",
subtitle=f"{query} = {result:.6g}",
icon_markup=icons.calculator,
icon_name="calculator-symbolic",
action=lambda r=f"{result:.6g}": self._copy_to_clipboard(r),
relevance=1.0,
plugin_name=self.display_name,
+1 -2
View File
@@ -5,9 +5,8 @@ import time
from collections import OrderedDict
from typing import Dict, List
from fabric.utils import get_relative_path
import config.data as data
from fabric.utils import get_relative_path
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
+27 -29
View File
@@ -6,8 +6,6 @@ from pathlib import Path
from typing import Dict, List, Optional
from fabric.utils import get_relative_path
import utils.icons as icons
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
from services.auth import (
@@ -354,7 +352,7 @@ class OTPPlugin(PluginBase):
Result(
title="Invalid format",
subtitle="Usage: add <account_name> <secret> or add <account_name>```<secret>```",
icon_markup=icons.question,
icon_name="info",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -373,7 +371,7 @@ class OTPPlugin(PluginBase):
Result(
title="Error adding account",
subtitle=f"Debug: {str(e)}",
icon_markup=icons.cancel,
icon_name="cancel",
action=lambda: None,
relevance=0.5,
plugin_name=self.display_name,
@@ -390,7 +388,7 @@ class OTPPlugin(PluginBase):
Result(
title="No OTP accounts configured",
subtitle="Use 'add <account> <secret>' to add your first account",
icon_markup=icons.key,
icon_name="gtk-authentication-symbolic",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -401,7 +399,7 @@ class OTPPlugin(PluginBase):
Result(
title="Available commands:",
subtitle="add <account> <secret> | remove <account> | qr <account>",
icon_markup=icons.info,
icon_name="info",
action=lambda: None,
relevance=0.9,
plugin_name=self.display_name,
@@ -425,7 +423,7 @@ class OTPPlugin(PluginBase):
subtitle_markup=f"{display_name} • {
time_display
} remaining • Shift+Enter: remove",
icon_markup=icons.key,
icon_name="gtk-authentication-symbolic",
action=lambda code=totp_code: self._copy_to_clipboard(code),
relevance=1.0,
plugin_name=self.display_name,
@@ -444,7 +442,7 @@ class OTPPlugin(PluginBase):
Result(
title=f"Error: {account_name}",
subtitle="Invalid secret or configuration",
icon_markup=icons.cancel,
icon_name="dialog-cancel-symbolic",
action=lambda: None,
relevance=0.5,
plugin_name=self.display_name,
@@ -485,7 +483,7 @@ class OTPPlugin(PluginBase):
subtitle_markup=f"{display_name} • {
time_display
} remaining • Shift+Enter: remove",
icon_markup=icons.key,
icon_name="gtk-authentication-symbolic",
action=lambda code=totp_code: self._copy_to_clipboard(code),
relevance=1.0,
plugin_name=self.display_name,
@@ -505,7 +503,7 @@ class OTPPlugin(PluginBase):
Result(
title=f"No accounts found for '{query}'",
subtitle="Use 'add <account> <secret>' to create new account",
icon_markup=icons.magnifier,
icon_name="edit-find-symbolic",
action=lambda: None,
relevance=0.5,
plugin_name=self.display_name,
@@ -516,7 +514,7 @@ class OTPPlugin(PluginBase):
Result(
title="Available commands:",
subtitle="add <account> <secret> | remove <account> | qr <account>",
icon_markup=icons.info,
icon_name="info",
action=lambda: None,
relevance=0.4,
plugin_name=self.display_name,
@@ -533,7 +531,7 @@ class OTPPlugin(PluginBase):
Result(
title="Enter account name",
subtitle="Usage: add <account_name> <secret>",
icon_markup=icons.question,
icon_name="dialog-question-symbolic",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -545,7 +543,7 @@ class OTPPlugin(PluginBase):
Result(
title=f"To add '{account_name}':",
subtitle=f"Type: add {account_name} <secret>",
icon_markup=icons.bulb,
icon_name="info",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -558,7 +556,7 @@ class OTPPlugin(PluginBase):
Result(
title="Base32 Secret Format:",
subtitle="Example: add gmail JBSWY3DPEHPK3PXP",
icon_markup=icons.info,
icon_name="info",
action=lambda: None,
relevance=0.9,
plugin_name=self.display_name,
@@ -567,7 +565,7 @@ class OTPPlugin(PluginBase):
Result(
title="otpauth URI Format:",
subtitle="Example: add github otpauth://totp/GitHub:user?secret=JBSWY3DPEHPK3PXP",
icon_markup=icons.info,
icon_name="info",
action=lambda: None,
relevance=0.9,
plugin_name=self.display_name,
@@ -576,7 +574,7 @@ class OTPPlugin(PluginBase):
Result(
title="Remove Account:",
subtitle="Example: remove gmail",
icon_markup=icons.trash,
icon_name="trash",
action=lambda: None,
relevance=0.8,
plugin_name=self.display_name,
@@ -591,7 +589,7 @@ class OTPPlugin(PluginBase):
Result(
title="Scan QR Code",
subtitle="Click to scan QR code from screen",
icon_markup=icons.connect,
icon_name="view-barcode-qr-symbolic",
action=lambda: self._scan_qr_and_add_account(""),
relevance=1.0,
plugin_name=self.display_name,
@@ -600,7 +598,7 @@ class OTPPlugin(PluginBase):
Result(
title="QR Scan Instructions:",
subtitle="Use 'qr <account_name>' to specify account name",
icon_markup=icons.info,
icon_name="info",
action=lambda: None,
relevance=0.9,
plugin_name=self.display_name,
@@ -612,7 +610,7 @@ class OTPPlugin(PluginBase):
Result(
title=f"Scan QR Code for '{account_name}'",
subtitle="Click to scan QR code from screen",
icon_markup=icons.connect,
icon_name="view-barcode-qr-symbolic",
action=lambda name=account_name: self._scan_qr_and_add_account(
name
),
@@ -658,7 +656,7 @@ class OTPPlugin(PluginBase):
Result(
title="No OTP accounts to remove",
subtitle="Use 'add <account> <secret>' to add accounts first",
icon_markup=icons.info,
icon_name="info",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -670,7 +668,7 @@ class OTPPlugin(PluginBase):
Result(
title="Select account to remove:",
subtitle="Type: remove <account_name> to remove an account",
icon_markup=icons.trash,
icon_name="user-trash-symbolic",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -697,7 +695,7 @@ class OTPPlugin(PluginBase):
subtitle_markup=f"Press Enter to remove • {
time_display
} remaining",
icon_markup=icons.trash,
icon_name="user-trash-symbolic",
action=lambda acc=acc_name: self._remove_account_and_refresh(
acc
),
@@ -716,7 +714,7 @@ class OTPPlugin(PluginBase):
Result(
title=f"Error: {acc_name}",
subtitle="Press Enter to remove (Invalid secret)",
icon_markup=icons.trash,
icon_name="user-trash-symbolic",
action=lambda acc=acc_name: self._remove_account_and_refresh(
acc
),
@@ -738,7 +736,7 @@ class OTPPlugin(PluginBase):
Result(
title=f"Account '{account_name}' not found",
subtitle="Use 'remove' to see all available accounts",
icon_markup=icons.cancel,
icon_name="dialog-cancel-symbolic",
action=lambda: None,
relevance=0.5,
plugin_name=self.display_name,
@@ -756,7 +754,7 @@ class OTPPlugin(PluginBase):
Result(
title=f"Remove '{display_name}'?",
subtitle="Press Enter to confirm removal",
icon_markup=icons.trash,
icon_name="user-trash-symbolic",
action=lambda acc=account_name: self._remove_account_and_refresh(acc),
relevance=1.0,
plugin_name=self.display_name,
@@ -777,7 +775,7 @@ class OTPPlugin(PluginBase):
Result(
title="Invalid Base32 secret",
subtitle=result["error"],
icon_markup=icons.cancel,
icon_name="dialog-cancel-symbolic",
action=lambda: None,
relevance=0.5,
plugin_name=self.display_name,
@@ -800,7 +798,7 @@ class OTPPlugin(PluginBase):
subtitle=f"OTP account added successfully (secret: {
result['secret'][:4]
}...)",
icon_markup=icons.check,
icon_name="emblem-ok-symbolic",
action=lambda: self._trigger_refresh(),
relevance=1.0,
plugin_name=self.display_name,
@@ -817,7 +815,7 @@ class OTPPlugin(PluginBase):
Result(
title="Error parsing otpauth URI",
subtitle=result["error"],
icon_markup=icons.cancel,
icon_name="dialog-cancel-symbolic",
action=lambda: None,
relevance=0.5,
plugin_name=self.display_name,
@@ -843,7 +841,7 @@ class OTPPlugin(PluginBase):
Result(
title=f"✓ Added '{display_name}'",
subtitle="OTP account added from URI",
icon_markup=icons.check,
icon_name="emblem-ok-symbolic",
action=lambda: self._trigger_refresh(),
relevance=1.0,
plugin_name=self.display_name,
+11 -13
View File
@@ -8,8 +8,6 @@ from pathlib import Path
from typing import Dict, List, Optional
from fabric.utils import get_relative_path
import utils.icons as icons
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
@@ -242,7 +240,7 @@ class PasswordPlugin(PluginBase):
Result(
title="No passwords stored",
subtitle="Use 'pass add <name> <password>' to add your first password",
icon_markup=icons.key,
icon_name="password-symbolic",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -253,7 +251,7 @@ class PasswordPlugin(PluginBase):
Result(
title="Available commands:",
subtitle="add <name> <password> | remove <name> | <name> (to search)",
icon_markup=icons.info,
icon_name="info",
action=lambda: None,
relevance=0.9,
plugin_name=self.display_name,
@@ -284,7 +282,7 @@ class PasswordPlugin(PluginBase):
Result(
title=title,
subtitle=subtitle,
icon_markup=icons.key,
icon_name="key",
action=lambda n=name: self._copy_password_to_clipboard(n),
relevance=1.0,
plugin_name=self.display_name,
@@ -311,7 +309,7 @@ class PasswordPlugin(PluginBase):
Result(
title="Add Password - Invalid format",
subtitle="Usage: add <name> <password> [description]",
icon_markup=icons.cancel,
icon_name="cancel",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -330,7 +328,7 @@ class PasswordPlugin(PluginBase):
Result(
title=f"Update password for '{name}'?",
subtitle="Password already exists. Click to update it.",
icon_markup=icons.key,
icon_name="key",
action=lambda: self._add_password_action(
name, password, description, update=True
),
@@ -345,7 +343,7 @@ class PasswordPlugin(PluginBase):
title=f"Add password for '{name}'",
subtitle="Click to save password"
+ (f" | {description}" if description else ""),
icon_markup=icons.plus,
icon_name="plus",
action=lambda: self._add_password_action(
name, password, description
),
@@ -367,7 +365,7 @@ class PasswordPlugin(PluginBase):
Result(
title="Remove Password - Invalid format",
subtitle="Usage: remove <name>",
icon_markup=icons.cancel,
icon_name="cancel",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -383,7 +381,7 @@ class PasswordPlugin(PluginBase):
Result(
title=f"Password '{name}' not found",
subtitle="Check the name and try again",
icon_markup=icons.cancel,
icon_name="cancel",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -395,7 +393,7 @@ class PasswordPlugin(PluginBase):
Result(
title=f"Remove password '{name}'?",
subtitle="Click to confirm deletion (this cannot be undone)",
icon_markup=icons.trash,
icon_name="trash",
action=lambda: self._remove_password_action(name),
relevance=1.0,
plugin_name=self.display_name,
@@ -420,7 +418,7 @@ class PasswordPlugin(PluginBase):
Result(
title=f"No passwords found matching '{query}'",
subtitle="Try a different search term or use 'pass' to see all passwords",
icon_markup=icons.magnifier,
icon_name="magnifier",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -462,7 +460,7 @@ class PasswordPlugin(PluginBase):
Result(
title=title,
subtitle=subtitle,
icon_markup=icons.key,
icon_name="key",
action=lambda n=name: self._copy_password_to_clipboard(n),
relevance=get_relevance(name),
plugin_name=self.display_name,
+7 -9
View File
@@ -1,8 +1,6 @@
from typing import List
from fabric.utils import exec_shell_command_async
import utils.icons as icons
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
@@ -19,27 +17,27 @@ class PowerPlugin(PluginBase):
self.commands = {
"shutdown": {
"description": "Shutdown the system",
"icon": icons.shutdown,
"icon": "system-shutdown-symbolic",
"action": self.shutdown,
},
"restart": {
"description": "Restart the system",
"icon": icons.reboot,
"icon": "system-reboot-symbolic",
"action": self.restart,
},
"lock": {
"description": "Lock the screen",
"icon": icons.lock,
"icon": "system-lock-screen-symbolic",
"action": self.lock,
},
"suspend": {
"description": "Suspend the system",
"icon": icons.suspend,
"icon": "system-suspend-symbolic",
"action": self.suspend,
},
"logout": {
"description": "Logout from current session",
"icon": icons.logout,
"icon": "system-log-out-symbolic",
"action": self.logout,
},
}
@@ -63,7 +61,7 @@ class PowerPlugin(PluginBase):
result = Result(
title=cmd.capitalize(),
subtitle=info["description"],
icon_markup=info["icon"],
icon_name=info["icon"],
action=info["action"],
relevance=1.0,
plugin_name=self.display_name,
@@ -77,7 +75,7 @@ class PowerPlugin(PluginBase):
result = Result(
title=cmd.capitalize(),
subtitle=info["description"],
icon_markup=info["icon"],
icon_name=info["icon"],
action=info["action"],
relevance=1.0 if query == cmd else 0.7,
plugin_name=self.display_name,
+11 -12
View File
@@ -4,7 +4,6 @@ import threading
from datetime import datetime, timedelta
from typing import Dict, List, Optional
import utils.icons as icons
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
@@ -258,7 +257,7 @@ class RemindersPlugin(PluginBase):
subtitle=f"Active reminders: {
active_count
} | Usage: remind 5m Take a break",
icon_markup=icons.timer_on,
icon_name="alarm-timer",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -279,7 +278,7 @@ class RemindersPlugin(PluginBase):
Result(
title=example,
subtitle=desc,
icon_markup=icons.timer_on,
icon_name="alarm-timer",
action=lambda: None,
relevance=0.8,
plugin_name=self.display_name,
@@ -296,7 +295,7 @@ class RemindersPlugin(PluginBase):
Result(
title="No Active Reminders",
subtitle="Use 'remind 5m message' to set a reminder",
icon_markup=icons.timer_off,
icon_name="timer-off",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -314,7 +313,7 @@ class RemindersPlugin(PluginBase):
Result(
title=f"#{reminder.id}: {reminder.message}",
subtitle=f"In {time_remaining} (at {target_time})",
icon_markup=icons.timer_on,
icon_name="alarm-timer",
action=lambda rid=reminder.id: self._cancel_reminder(rid),
relevance=1.0,
plugin_name=self.display_name,
@@ -334,7 +333,7 @@ class RemindersPlugin(PluginBase):
Result(
title=f"Cancelled {count} Reminders",
subtitle="All active reminders have been cancelled",
icon_markup=icons.timer_off,
icon_name="timer-off",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -351,7 +350,7 @@ class RemindersPlugin(PluginBase):
Result(
title=f"Cancelled Reminder #{reminder_id}",
subtitle="Reminder has been cancelled",
icon_markup=icons.timer_off,
icon_name="timer-off",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -363,7 +362,7 @@ class RemindersPlugin(PluginBase):
Result(
title="Reminder Not Found",
subtitle=f"No reminder with ID #{reminder_id}",
icon_markup=icons.alert,
icon_name="alert",
action=lambda: None,
relevance=0.5,
plugin_name=self.display_name,
@@ -375,7 +374,7 @@ class RemindersPlugin(PluginBase):
Result(
title="Invalid Reminder ID",
subtitle="Please provide a valid reminder ID number",
icon_markup=icons.alert,
icon_name="alert",
action=lambda: None,
relevance=0.5,
plugin_name=self.display_name,
@@ -407,7 +406,7 @@ class RemindersPlugin(PluginBase):
subtitle=f"Will remind in {time_remaining} (at {
target_time_str
})",
icon_markup=icons.timer_on,
icon_name="alarm-timer",
action=lambda ts=time_str, msg=message: self._create_and_confirm_reminder(
ts, msg
),
@@ -425,7 +424,7 @@ class RemindersPlugin(PluginBase):
Result(
title="Time is in the Past",
subtitle="Please specify a future time",
icon_markup=icons.alert,
icon_name="alert",
action=lambda: None,
relevance=0.5,
plugin_name=self.display_name,
@@ -438,7 +437,7 @@ class RemindersPlugin(PluginBase):
Result(
title="Invalid Time Format",
subtitle="Use formats like: 5m, 30s, 2h, 14:30, or '5 minutes'",
icon_markup=icons.alert,
icon_name="alert",
action=lambda: None,
relevance=0.5,
plugin_name=self.display_name,
+74 -73
View File
@@ -2,10 +2,8 @@ import subprocess
from typing import List
from fabric.utils import get_relative_path
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
from utils.icons import screenrecord, screenshots, ssfull, ssregion, stop
class ScreencapturePlugin(PluginBase):
@@ -17,7 +15,6 @@ class ScreencapturePlugin(PluginBase):
super().__init__()
self.display_name = "Screencapture"
self.description = "Take screenshots and screen recordings"
# Path to the screen-capture script
self.script_path = get_relative_path("../../../scripts/screen-capture.sh")
def initialize(self):
@@ -121,290 +118,290 @@ class ScreencapturePlugin(PluginBase):
"screenshot": (
"Take Screenshot (eDP-1)",
"Capture the main display",
ssfull,
"camera-photo-symbolic",
lambda: self._run_script("screenshot", "eDP-1"),
),
"ss": (
"Take Screenshot (eDP-1)",
"Capture the main display",
ssfull,
"camera-photo-symbolic",
lambda: self._run_script("screenshot", "eDP-1"),
),
"screenshot-region": (
"Take Region Screenshot",
"Capture a selected region",
ssregion,
"camera-photo-symbolic",
lambda: self._run_script("screenshot", "selection"),
),
"ss-region": (
"Take Region Screenshot",
"Capture a selected region",
ssregion,
"camera-photo-symbolic",
lambda: self._run_script("screenshot", "selection"),
),
"screenshot-both": (
"Take Screenshot (Both Displays)",
"Capture both displays combined",
screenshots,
"video-joined-displays-symbolic",
lambda: self._run_script("screenshot", "both"),
),
"ss-both": (
"Take Screenshot (Both Displays)",
"Capture both displays combined",
screenshots,
"video-joined-displays-symbolic",
lambda: self._run_script("screenshot", "both"),
),
"screenshot-hdmi": (
"Take Screenshot (HDMI-A-1)",
"Capture HDMI display",
ssfull,
"video-display-symbolic",
lambda: self._run_script("screenshot", "HDMI-A-1"),
),
"ss-hdmi": (
"Take Screenshot (HDMI-A-1)",
"Capture HDMI display",
ssfull,
"video-display-symbolic",
lambda: self._run_script("screenshot", "HDMI-A-1"),
),
# Recording commands (with audio)
"record": (
"Start Recording (eDP-1)",
"Record the main display with audio",
screenrecord,
"media-record-symbolic",
lambda: self._run_script("record", "eDP-1"),
),
"rec": (
"Start Recording (eDP-1)",
"Record the main display with audio",
screenrecord,
"media-record-symbolic",
lambda: self._run_script("record", "eDP-1"),
),
"record-region": (
"Start Region Recording",
"Record a selected region with audio",
screenrecord,
"media-record-symbolic",
lambda: self._run_script("record", "selection"),
),
"rec-region": (
"Start Region Recording",
"Record a selected region with audio",
screenrecord,
"media-record-symbolic",
lambda: self._run_script("record", "selection"),
),
"record-hdmi": (
"Start Recording (HDMI-A-1)",
"Record HDMI display with audio",
screenrecord,
"media-record-symbolic",
lambda: self._run_script("record", "HDMI-A-1"),
),
"rec-hdmi": (
"Start Recording (HDMI-A-1)",
"Record HDMI display with audio",
screenrecord,
"media-record-symbolic",
lambda: self._run_script("record", "HDMI-A-1"),
),
# Recording commands (no audio)
"record-noaudio": (
"Start Recording No Audio (eDP-1)",
"Record the main display without audio",
screenrecord,
"media-record-symbolic",
lambda: self._run_script("record-noaudio", "eDP-1"),
),
"rec-noaudio": (
"Start Recording No Audio (eDP-1)",
"Record the main display without audio",
screenrecord,
"media-record-symbolic",
lambda: self._run_script("record-noaudio", "eDP-1"),
),
"record-noaudio-region": (
"Start Region Recording No Audio",
"Record a selected region without audio",
screenrecord,
"media-record-symbolic",
lambda: self._run_script("record-noaudio", "selection"),
),
"rec-noaudio-region": (
"Start Region Recording No Audio",
"Record a selected region without audio",
screenrecord,
"media-record-symbolic",
lambda: self._run_script("record-noaudio", "selection"),
),
"record-noaudio-hdmi": (
"Start Recording No Audio (HDMI-A-1)",
"Record HDMI display without audio",
screenrecord,
"media-record-symbolic",
lambda: self._run_script("record-noaudio", "HDMI-A-1"),
),
"rec-noaudio-hdmi": (
"Start Recording No Audio (HDMI-A-1)",
"Record HDMI display without audio",
screenrecord,
"media-record-symbolic",
lambda: self._run_script("record-noaudio", "HDMI-A-1"),
),
# High-quality recording commands
"record-hq": (
"Start HQ Recording (eDP-1)",
"High-quality recording for YouTube",
screenrecord,
"media-record-symbolic",
lambda: self._run_script("record-hq", "eDP-1"),
),
"rec-hq": (
"Start HQ Recording (eDP-1)",
"High-quality recording for YouTube",
screenrecord,
"media-record-symbolic",
lambda: self._run_script("record-hq", "eDP-1"),
),
"record-hq-region": (
"Start HQ Region Recording",
"High-quality region recording",
screenrecord,
"media-record-symbolic",
lambda: self._run_script("record-hq", "selection"),
),
"rec-hq-region": (
"Start HQ Region Recording",
"High-quality region recording",
screenrecord,
"media-record-symbolic",
lambda: self._run_script("record-hq", "selection"),
),
"record-hq-hdmi": (
"Start HQ Recording (HDMI-A-1)",
"High-quality HDMI recording",
screenrecord,
"media-record-symbolic",
lambda: self._run_script("record-hq", "HDMI-A-1"),
),
"rec-hq-hdmi": (
"Start HQ Recording (HDMI-A-1)",
"High-quality HDMI recording",
screenrecord,
"media-record-symbolic",
lambda: self._run_script("record-hq", "HDMI-A-1"),
),
# GIF recording commands
"record-gif": (
"Start GIF Recording (eDP-1)",
"Record as optimized GIF",
screenrecord,
"media-record-symbolic",
lambda: self._run_script("record-gif", "eDP-1"),
),
"rec-gif": (
"Start GIF Recording (eDP-1)",
"Record as optimized GIF",
screenrecord,
"media-record-symbolic",
lambda: self._run_script("record-gif", "eDP-1"),
),
"record-gif-region": (
"Start GIF Region Recording",
"Record selected region as GIF",
screenrecord,
"media-record-symbolic",
lambda: self._run_script("record-gif", "selection"),
),
"rec-gif-region": (
"Start GIF Region Recording",
"Record selected region as GIF",
screenrecord,
"media-record-symbolic",
lambda: self._run_script("record-gif", "selection"),
),
# Control commands
"stop": (
"Stop Recording",
"Stop the current screen recording",
stop,
"media-playback-stop-symbolic",
lambda: self._run_script("record", "stop"),
),
# Conversion commands
"convert-webm": (
"Convert Latest to WebM",
"Convert latest MKV recording to WebM format",
screenrecord,
"video-x-generic-symbolic",
lambda: self._run_script("convert", "webm"),
),
"conv-webm": (
"Convert Latest to WebM",
"Convert latest MKV recording to WebM format",
screenrecord,
"video-x-generic-symbolic",
lambda: self._run_script("convert", "webm"),
),
"convert-iphone": (
"Convert Latest for iPhone",
"Convert latest MKV recording for iPhone compatibility",
screenrecord,
"video-x-generic-symbolic",
lambda: self._run_script("convert", "iphone"),
),
"conv-iphone": (
"Convert Latest for iPhone",
"Convert latest MKV recording for iPhone compatibility",
screenrecord,
"video-x-generic-symbolic",
lambda: self._run_script("convert", "iphone"),
),
"convert-youtube": (
"Convert Latest for YouTube",
"Convert latest recording for YouTube upload",
screenrecord,
"video-x-generic-symbolic",
lambda: self._run_script("convert", "youtube"),
),
"conv-youtube": (
"Convert Latest for YouTube",
"Convert latest recording for YouTube upload",
screenrecord,
"video-x-generic-symbolic",
lambda: self._run_script("convert", "youtube"),
),
"convert-gif": (
"Convert Latest to GIF",
"Convert latest recording to GIF format",
screenrecord,
"image-x-generic-symbolic",
lambda: self._run_script("convert", "gif"),
),
"conv-gif": (
"Convert Latest to GIF",
"Convert latest recording to GIF format",
screenrecord,
"image-x-generic-symbolic",
lambda: self._run_script("convert", "gif"),
),
# File-based conversion commands (these will be handled specially)
"convert-webm-file": (
"Convert File to WebM",
"Type filename after command (e.g., convert-webm-file video.mkv)",
screenrecord,
"video-x-generic-symbolic",
None, # Will be handled in query method
),
"conv-webm-file": (
"Convert File to WebM",
"Type filename after command (e.g., conv-webm-file video.mkv)",
screenrecord,
"video-x-generic-symbolic",
None, # Will be handled in query method
),
"convert-iphone-file": (
"Convert File for iPhone",
"Type filename after command (e.g., convert-iphone-file video.mkv)",
screenrecord,
"video-x-generic-symbolic",
None, # Will be handled in query method
),
"conv-iphone-file": (
"Convert File for iPhone",
"Type filename after command (e.g., conv-iphone-file video.mkv)",
screenrecord,
"video-x-generic-symbolic",
None, # Will be handled in query method
),
"convert-youtube-file": (
"Convert File for YouTube",
"Type filename after command (e.g., convert-youtube-file video.mkv)",
screenrecord,
"video-x-generic-symbolic",
None, # Will be handled in query method
),
"conv-youtube-file": (
"Convert File for YouTube",
"Type filename after command (e.g., conv-youtube-file video.mkv)",
screenrecord,
"video-x-generic-symbolic",
None, # Will be handled in query method
),
"convert-gif-file": (
"Convert File to GIF",
"Type filename after command (e.g., convert-gif-file video.mkv)",
screenrecord,
"image-x-generic-symbolic",
None, # Will be handled in query method
),
"conv-gif-file": (
"Convert File to GIF",
"Type filename after command (e.g., conv-gif-file video.mkv)",
screenrecord,
"image-x-generic-symbolic",
None, # Will be handled in query method
),
}
@@ -415,7 +412,7 @@ class ScreencapturePlugin(PluginBase):
return Result(
title=title,
subtitle=subtitle,
icon_markup=icon,
icon_name=icon,
action=action,
relevance=1.0,
plugin_name=self.display_name,
@@ -424,7 +421,7 @@ class ScreencapturePlugin(PluginBase):
return Result(
title=title,
subtitle=subtitle,
icon_markup=icon,
icon_name=icon,
action=lambda: None, # No action for instruction
relevance=1.0,
plugin_name=self.display_name,
@@ -465,7 +462,11 @@ class ScreencapturePlugin(PluginBase):
Result(
title=f"Convert {file_param} to {format_type.upper()}",
subtitle=f"Convert specified file to {format_type} format",
icon_markup=screenrecord,
icon_name=(
"video-x-generic-symbolic"
if format_type != "gif"
else "image-x-generic-symbolic"
),
action=lambda fp=file_param, ft=format_type: self._run_script_with_file(
ft, fp
),
@@ -488,7 +489,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Stop Recording",
subtitle="Stop the current screen recording",
icon_markup=stop,
icon_name="media-playback-stop-symbolic",
action=lambda: self._run_script("record", "stop"),
relevance=2.0, # Highest relevance to appear at top
plugin_name=self.display_name,
@@ -501,7 +502,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Take Screenshot",
subtitle="Capture the entire screen (eDP-1)",
icon_markup=ssfull,
icon_name="camera-photo-symbolic",
action=lambda: self._run_script("screenshot", "eDP-1"),
relevance=1.0,
plugin_name=self.display_name,
@@ -509,7 +510,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Take Region Screenshot",
subtitle="Capture a selected region",
icon_markup=ssregion,
icon_name="camera-photo-symbolic",
action=lambda: self._run_script("screenshot", "selection"),
relevance=0.9,
plugin_name=self.display_name,
@@ -517,7 +518,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Take Screenshot (Both Displays)",
subtitle="Capture both displays combined",
icon_markup=screenshots,
icon_name="video-joined-displays-symbolic",
action=lambda: self._run_script("screenshot", "both"),
relevance=0.8,
plugin_name=self.display_name,
@@ -525,7 +526,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Take Screenshot (HDMI-A-1)",
subtitle="Capture HDMI display",
icon_markup=ssfull,
icon_name="video-display-symbolic",
action=lambda: self._run_script("screenshot", "HDMI-A-1"),
relevance=0.7,
plugin_name=self.display_name,
@@ -539,7 +540,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Start Recording (eDP-1)",
subtitle="Record the main display with audio",
icon_markup=screenrecord,
icon_name="media-record-symbolic",
action=lambda: self._run_script("record", "eDP-1"),
relevance=0.7,
plugin_name=self.display_name,
@@ -547,7 +548,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Start Region Recording",
subtitle="Record a selected region",
icon_markup=screenrecord,
icon_name="media-record-symbolic",
action=lambda: self._run_script("record", "selection"),
relevance=0.6,
plugin_name=self.display_name,
@@ -555,7 +556,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Start Recording (HDMI-A-1)",
subtitle="Record HDMI display with audio",
icon_markup=screenrecord,
icon_name="media-record-symbolic",
action=lambda: self._run_script("record", "HDMI-A-1"),
relevance=0.5,
plugin_name=self.display_name,
@@ -569,7 +570,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Start Recording No Audio (eDP-1)",
subtitle="Record the main display without audio",
icon_markup=screenrecord,
icon_name="media-record-symbolic",
action=lambda: self._run_script("record-noaudio", "eDP-1"),
relevance=0.65,
plugin_name=self.display_name,
@@ -577,7 +578,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Start Region Recording No Audio",
subtitle="Record a selected region without audio",
icon_markup=screenrecord,
icon_name="media-record-symbolic",
action=lambda: self._run_script("record-noaudio", "selection"),
relevance=0.55,
plugin_name=self.display_name,
@@ -585,7 +586,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Start Recording No Audio (HDMI-A-1)",
subtitle="Record HDMI display without audio",
icon_markup=screenrecord,
icon_name="media-record-symbolic",
action=lambda: self._run_script("record-noaudio", "HDMI-A-1"),
relevance=0.45,
plugin_name=self.display_name,
@@ -599,7 +600,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Start HQ Recording (eDP-1)",
subtitle="High-quality recording for YouTube",
icon_markup=screenrecord,
icon_name="media-record-symbolic",
action=lambda: self._run_script("record-hq", "eDP-1"),
relevance=0.4,
plugin_name=self.display_name,
@@ -607,7 +608,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Start HQ Region Recording",
subtitle="High-quality region recording",
icon_markup=screenrecord,
icon_name="media-record-symbolic",
action=lambda: self._run_script("record-hq", "selection"),
relevance=0.3,
plugin_name=self.display_name,
@@ -615,7 +616,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Start HQ Recording (HDMI-A-1)",
subtitle="High-quality HDMI recording",
icon_markup=screenrecord,
icon_name="media-record-symbolic",
action=lambda: self._run_script("record-hq", "HDMI-A-1"),
relevance=0.2,
plugin_name=self.display_name,
@@ -629,7 +630,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Start GIF Recording (eDP-1)",
subtitle="Record as optimized GIF",
icon_markup=screenrecord,
icon_name="media-record-symbolic",
action=lambda: self._run_script("record-gif", "eDP-1"),
relevance=0.1,
plugin_name=self.display_name,
@@ -637,7 +638,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Start GIF Region Recording",
subtitle="Record selected region as GIF",
icon_markup=screenrecord,
icon_name="media-record-symbolic",
action=lambda: self._run_script("record-gif", "selection"),
relevance=0.05,
plugin_name=self.display_name,
@@ -651,7 +652,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Convert Latest to WebM",
subtitle="Convert latest MKV recording to WebM format",
icon_markup=screenrecord,
icon_name="video-x-generic-symbolic",
action=lambda: self._run_script("convert", "webm"),
relevance=0.01,
plugin_name=self.display_name,
@@ -659,7 +660,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Convert Latest for iPhone",
subtitle="Convert latest MKV recording for iPhone compatibility",
icon_markup=screenrecord,
icon_name="video-x-generic-symbolic",
action=lambda: self._run_script("convert", "iphone"),
relevance=0.01,
plugin_name=self.display_name,
@@ -667,7 +668,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Convert Latest for YouTube",
subtitle="Convert latest recording for YouTube upload",
icon_markup=screenrecord,
icon_name="video-x-generic-symbolic",
action=lambda: self._run_script("convert", "youtube"),
relevance=0.01,
plugin_name=self.display_name,
@@ -675,7 +676,7 @@ class ScreencapturePlugin(PluginBase):
Result(
title="Convert Latest to GIF",
subtitle="Convert latest recording to GIF format",
icon_markup=screenrecord,
icon_name="image-x-generic-symbolic",
action=lambda: self._run_script("convert", "gif"),
relevance=0.01,
plugin_name=self.display_name,
+2 -5
View File
@@ -5,10 +5,8 @@ import threading
import time
from typing import List, Set, Union
from fabric.utils import exec_shell_command_async
import config.data as data
import utils.icons as icons
from fabric.utils import exec_shell_command_async
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
@@ -91,7 +89,6 @@ class SystemPlugin(PluginBase):
current_time - self._last_bin_update > self._bin_update_interval
or not self._bin_cache
):
if not self._cache_building:
self._cache_building = True
self._cache_thread = threading.Thread(
@@ -213,7 +210,7 @@ class SystemPlugin(PluginBase):
result = Result(
title=display_command,
subtitle=f"Execute: {display_command}",
icon_markup=icons.terminal,
icon_name="terminal",
action=self._create_action(command_to_execute),
relevance=relevance,
plugin_name=self.display_name,
+13 -11
View File
@@ -3,10 +3,8 @@ import threading
import time
from typing import List
from fabric.utils import exec_shell_command_async
import config.data as data
import utils.icons as icons
from fabric.utils import exec_shell_command_async
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
@@ -24,13 +22,15 @@ class TmuxPlugin(PluginBase):
# Cache for sessions to avoid repeated subprocess calls
self._sessions_cache = []
self._last_cache_update = 0
self._cache_ttl = 10 # Cache sessions for 10 seconds (increased from 2)
# Cache sessions for 10 seconds (increased from 2)
self._cache_ttl = 10
# Threading for auto-refresh - only when actively used
self.refresh_thread = None
self.stop_refresh = threading.Event()
self._last_query_time = 0
self._active_refresh_timeout = 30 # Stop refreshing after 30 seconds of inactivity
# Stop refreshing after 30 seconds of inactivity
self._active_refresh_timeout = 30
def initialize(self):
"""Initialize the tmux plugin."""
@@ -67,7 +67,7 @@ class TmuxPlugin(PluginBase):
self._sessions_cache = self._get_tmux_sessions()
self._last_cache_update = current_time
self.stop_refresh.wait(5)
self.stop_refresh.wait(5)
except Exception as e:
print(f"TmuxPlugin: Error in refresh thread: {e}")
self.stop_refresh.wait(10) # Wait longer on error
@@ -166,7 +166,7 @@ class TmuxPlugin(PluginBase):
return Result(
title=f"Attach to '{session_name}'",
subtitle=f"Connect to tmux session: {session_name}",
icon_markup=icons.terminal,
icon_name="terminal",
action=lambda: self._attach_to_session(session_name),
relevance=0.9,
data={"type": "attach", "session": session_name},
@@ -177,8 +177,10 @@ class TmuxPlugin(PluginBase):
display_name = session_name if session_name else "new session"
return Result(
title=f"Create '{display_name}'",
subtitle=f"Create new tmux session{f': {session_name}' if session_name else ''}",
icon_markup=icons.plus,
subtitle=f"Create new tmux session{
f': {session_name}' if session_name else ''
}",
icon_name="plus",
action=lambda: self._create_session(session_name),
relevance=0.8,
data={"type": "create", "session": session_name},
@@ -189,7 +191,7 @@ class TmuxPlugin(PluginBase):
return Result(
title=f"Kill '{session_name}'",
subtitle=f"Terminate tmux session: {session_name}",
icon_markup=icons.trash,
icon_name="trash",
action=lambda: self._kill_session(session_name),
relevance=0.7,
data={"type": "kill", "session": session_name},
@@ -200,7 +202,7 @@ class TmuxPlugin(PluginBase):
return Result(
title=f"Rename '{old_name}' to '{new_name}'",
subtitle=f"Rename tmux session from {old_name} to {new_name}",
icon_markup=icons.config,
icon_name="config",
action=lambda: self._rename_session(old_name, new_name),
relevance=0.6,
data={"type": "rename", "old_session": old_name, "new_session": new_name},
+26 -54
View File
@@ -8,12 +8,11 @@ import threading
import time
from typing import Dict, List, Optional
from fabric.utils.helpers import exec_shell_command_async
from gi.repository import GdkPixbuf
from PIL import Image
import config.data as data
import utils.icons as icons
from fabric.utils.helpers import exec_shell_command_async
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
@@ -124,9 +123,10 @@ class WallpaperPlugin(PluginBase):
"""Clear the launcher search query and reset to trigger."""
try:
# Try to access the launcher through the fabric Application
from fabric import Application
from gi.repository import GLib
from fabric import Application
app = Application.get_default()
if app and hasattr(app, "launcher"):
@@ -467,16 +467,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Random Wallpaper{indicator_text}",
subtitle=f"Set a random wallpaper • {status_text}",
icon_markup=random.choice(
[
icons.dice_1,
icons.dice_2,
icons.dice_3,
icons.dice_4,
icons.dice_5,
icons.dice_6,
]
),
icon_name="media-playlist-shuffle-symbolic",
action=lambda: self._set_random_wallpaper(),
relevance=1.0,
plugin_name=self.display_name,
@@ -493,16 +484,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Random Wallpaper{indicator_text}",
subtitle=f"Set a random wallpaper • {status_text}",
icon_markup=random.choice(
[
icons.dice_1,
icons.dice_2,
icons.dice_3,
icons.dice_4,
icons.dice_5,
icons.dice_6,
]
),
icon_name="media-playlist-shuffle-symbolic",
action=lambda: self._set_random_wallpaper(),
relevance=0.9,
plugin_name=self.display_name,
@@ -552,10 +534,9 @@ class WallpaperPlugin(PluginBase):
subtitle=f"Apply with {scheme_name} scheme • {
status_text
}",
icon_markup=icons.palette,
action=lambda c=hex_color, s=scheme: self._apply_hex_color_direct(
c, s
),
icon_name="color-picker-symbolic",
action=lambda c=hex_color,
s=scheme: self._apply_hex_color_direct(c, s),
relevance=1.0,
plugin_name=self.display_name,
data={
@@ -575,7 +556,7 @@ class WallpaperPlugin(PluginBase):
indicator_text
}",
subtitle="Matugen is enabled • Disable matugen to use hex colors",
icon_markup=icons.palette,
icon_name="color-picker-symbolic",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -594,7 +575,7 @@ class WallpaperPlugin(PluginBase):
subtitle=f"Apply with {scheme_name} scheme • {
status_text
}",
icon_markup=icons.palette,
icon_name="color-picker-symbolic",
action=lambda c=hex_color, s=scheme: (
self._apply_hex_color_direct(c, s)
if not matugen_enabled
@@ -617,7 +598,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Hex Color (incomplete): {hex_color}",
subtitle="Complete the 6-digit hex color to apply",
icon_markup=icons.palette,
icon_name="color-picker-symbolic",
action=lambda: None,
relevance=0.7,
plugin_name=self.display_name,
@@ -647,7 +628,7 @@ class WallpaperPlugin(PluginBase):
subtitle=f"Generate and apply random color with {
scheme_name
} scheme • {status_text}",
icon_markup=icons.palette,
icon_name="color-picker-symbolic",
action=lambda s=scheme: self._apply_hex_color_direct(
self._generate_random_hex_color(), s
),
@@ -667,7 +648,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Cannot Apply Random Color{indicator_text}",
subtitle="Matugen is enabled • Disable matugen to use hex colors",
icon_markup=icons.palette,
icon_name="color-picker-symbolic",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -683,7 +664,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Random Hex Color{indicator_text}",
subtitle=f"Generate and apply random color • {status_text}",
icon_markup=icons.palette,
icon_name="color-picker-symbolic",
action=lambda s=scheme: (
self._apply_hex_color_direct(
self._generate_random_hex_color(), s
@@ -707,7 +688,7 @@ class WallpaperPlugin(PluginBase):
Result(
title="Hex Color Commands",
subtitle="Use: color #FF5733, hex #00FF00, color random",
icon_markup=icons.palette,
icon_name="color-picker-symbolic",
action=lambda: None,
relevance=0.8,
plugin_name=self.display_name,
@@ -761,7 +742,7 @@ class WallpaperPlugin(PluginBase):
if not matugen_enabled
else ""
),
icon_markup=icons.palette,
icon_name="color-management-symbolic",
action=lambda s=scheme_id: self._set_current_scheme(s),
relevance=1.0 if scheme_id == current_scheme else 0.8,
plugin_name=self.display_name,
@@ -785,7 +766,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Enable Matugen{indicator_text}",
subtitle=f"Turn on dynamic color generation • {status_text}",
icon_markup=icons.palette,
icon_name="color-management-symbolic",
action=lambda: self._set_matugen_state(True),
relevance=0.9,
plugin_name=self.display_name,
@@ -802,7 +783,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Disable Matugen{indicator_text}",
subtitle=f"Turn off dynamic color generation • {status_text}",
icon_markup=icons.palette,
icon_name="color-management-symbolic",
action=lambda: self._set_matugen_state(False),
relevance=0.9,
plugin_name=self.display_name,
@@ -824,7 +805,7 @@ class WallpaperPlugin(PluginBase):
subtitle=f"Switch matugen to {
'enabled' if new_state else 'disabled'
} • {status_text}",
icon_markup=icons.palette,
icon_name="color-management-symbolic",
action=lambda: self._set_matugen_state(new_state),
relevance=0.9,
plugin_name=self.display_name,
@@ -843,7 +824,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Enable Matugen{indicator_text}",
subtitle=f"Turn on dynamic color generation • {status_text}",
icon_markup=icons.palette,
icon_name="color-management-symbolic",
action=lambda: self._set_matugen_state(True),
relevance=0.8,
plugin_name=self.display_name,
@@ -862,7 +843,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Disable Matugen{indicator_text}",
subtitle=f"Turn off dynamic color generation • {status_text}",
icon_markup=icons.palette,
icon_name="color-management-symbolic",
action=lambda: self._set_matugen_state(False),
relevance=0.8,
plugin_name=self.display_name,
@@ -879,7 +860,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Toggle Matugen{indicator_text}",
subtitle=f"Switch matugen state • {status_text}",
icon_markup=icons.palette,
icon_name="color-management-symbolic",
action=lambda: self._set_matugen_state(not current_state),
relevance=0.8,
plugin_name=self.display_name,
@@ -898,7 +879,7 @@ class WallpaperPlugin(PluginBase):
indicator_text
}",
subtitle=f"Dynamic colors • {status_text}",
icon_markup=icons.palette,
icon_name="color-management-symbolic",
action=lambda: None,
relevance=0.8,
plugin_name=self.display_name,
@@ -912,7 +893,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Wallpaper System Status{indicator_text}",
subtitle=status_text,
icon_markup=icons.palette,
icon_name="color-management-symbolic",
action=lambda: None,
relevance=1.0,
plugin_name=self.display_name,
@@ -925,16 +906,7 @@ class WallpaperPlugin(PluginBase):
Result(
title=f"Random Wallpaper{indicator_text}",
subtitle=f"Set a random wallpaper • {status_text}",
icon_markup=random.choice(
[
icons.dice_1,
icons.dice_2,
icons.dice_3,
icons.dice_4,
icons.dice_5,
icons.dice_6,
]
),
icon_name="media-playlist-shuffle-symbolic",
action=lambda: self._set_random_wallpaper(),
relevance=0.9,
plugin_name=self.display_name,
@@ -981,7 +953,7 @@ class WallpaperPlugin(PluginBase):
' • ' + status_text if not query else ''
}",
icon=icon,
icon_markup=icons.wallpapers if not icon else None,
icon_name="image-x-generic-symbolic" if not icon else None,
action=lambda w=wallpaper: self._set_wallpaper(w),
relevance=relevance,
plugin_name=self.display_name,
+12 -13
View File
@@ -2,7 +2,6 @@ import subprocess
import urllib.parse
from typing import List
import utils.icons as icons
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
@@ -23,49 +22,49 @@ class WebSearchPlugin(PluginBase):
"google": {
"name": "Google",
"url": "https://www.google.com/search?q={}",
"icon": icons.google,
"icon": "google-symbolic",
"description": "Search with Google",
},
"duckduckgo": {
"name": "DuckDuckGo",
"url": "https://duckduckgo.com/?q={}",
"icon": icons.duckduckgo,
"icon": "preferences-desktop-search",
"description": "Search with DuckDuckGo (privacy-focused)",
},
"youtube": {
"name": "YouTube",
"url": "https://www.youtube.com/results?search_query={}",
"icon": icons.youtube,
"icon": "youtube-symbolic",
"description": "Search videos on YouTube",
},
"github": {
"name": "GitHub",
"url": "https://github.com/search?q={}",
"icon": icons.github,
"icon": "github-desktop-symbolic",
"description": "Search repositories on GitHub",
},
"stackoverflow": {
"name": "Stack Overflow",
"url": "https://stackoverflow.com/search?q={}",
"icon": icons.stackoverflow,
"icon": "stack",
"description": "Search programming questions on Stack Overflow",
},
"wikipedia": {
"name": "Wikipedia",
"url": "https://en.wikipedia.org/wiki/Special:Search?search={}",
"icon": icons.wikipedia,
"icon": "search-menus-symbolic",
"description": "Search articles on Wikipedia",
},
"reddit": {
"name": "Reddit",
"url": "https://www.reddit.com/search/?q={}",
"icon": icons.reddit,
"icon": "reddit-symbolic",
"description": "Search discussions on Reddit",
},
"linkedin": {
"name": "LinkedIn",
"url": "https://www.linkedin.com/search/results/all/?keywords={}",
"icon": icons.linkedin,
"icon": "link",
"description": "Search professionals and jobs on LinkedIn",
},
}
@@ -166,7 +165,7 @@ class WebSearchPlugin(PluginBase):
return Result(
title=f"Open {url}",
subtitle="Open this URL in your default browser",
icon_markup=icons.world,
icon_name="link",
action=lambda u=url: self._open_url(u),
relevance=1.0,
plugin_name=self.display_name,
@@ -190,7 +189,7 @@ class WebSearchPlugin(PluginBase):
result = Result(
title=engine_info["name"],
subtitle=engine_info["description"],
icon_markup=engine_info["icon"],
icon_name=engine_info["icon"],
action=lambda e=engine_id: self._show_engine_help(e),
relevance=1.0 if engine_id == self.default_engine else 0.8,
plugin_name=self.display_name,
@@ -207,7 +206,7 @@ class WebSearchPlugin(PluginBase):
return Result(
title=f"Search '{query}' on {engine_info['name']}",
subtitle=f"{engine_info['description']} - {query}",
icon_markup=engine_info["icon"],
icon_name=engine_info["icon"],
action=lambda e=engine_id, q=query: self._perform_search(e, q),
relevance=1.0 if engine_id == self.default_engine else 0.9,
plugin_name=self.display_name,
@@ -221,7 +220,7 @@ class WebSearchPlugin(PluginBase):
return Result(
title=f"{engine_info['name']} Search",
subtitle=f"{engine_info['description']} - Type your search query",
icon_markup=engine_info["icon"],
icon_name=engine_info["icon"],
action=lambda: None, # No action for info result
relevance=1.0,
plugin_name=self.display_name,
+3 -3
View File
@@ -62,7 +62,7 @@ class ResultItem(EventBox):
icon_widget = Image(pixbuf=self.result.icon, name="result-item-icon")
elif self.result.icon_name:
icon_widget = Image(
icon_name=self.result.icon_name, icon_size=32, name="result-item-icon"
icon_name=self.result.icon_name, icon_size=48, name="result-item-icon"
)
elif self.result.icon_markup:
icon_widget = Label(
@@ -71,8 +71,8 @@ class ResultItem(EventBox):
else:
# Default icon
icon_widget = Image(
icon_name="application-x-executable",
icon_size=32,
icon_name="application-default-icon",
icon_size=48,
name="result-item-icon",
)
+4 -6
View File
@@ -4,8 +4,6 @@ from typing import Any, Dict, List
from fabric.utils import get_relative_path
import utils.icons as icons
class TriggerConfig:
def __init__(self, config_path: str = None):
@@ -29,7 +27,7 @@ class TriggerConfig:
# Initialize settings with defaults
default_settings = {
"max_examples_shown": 2,
"default_icon": "apps",
"default_icon": "application-default-icon",
"fallback_example_template": "{trigger} <search>",
"config_version": "1.0",
}
@@ -45,10 +43,10 @@ class TriggerConfig:
return examples
def get_trigger_icon(self, trigger: str) -> str:
icon_name = self.launcher_config.get(trigger, {}).get(
"icon", self.settings.get("default_icon", "apps")
icon = self.launcher_config.get(trigger, {}).get(
"icon", self.settings.get("default_icon", "application-default-icon")
)
return getattr(icons, icon_name, icons.apps)
return icon
def get_trigger_description(self, trigger: str) -> str:
return self.launcher_config.get(trigger, {}).get(