123 lines
4.1 KiB
Python
123 lines
4.1 KiB
Python
import json
|
|
import os
|
|
import subprocess
|
|
from typing import List, Dict, Any
|
|
from . import LauncherPlugin
|
|
|
|
|
|
class EmojiPlugin(LauncherPlugin):
|
|
"""Plugin for emoji search and clipboard functionality"""
|
|
|
|
def __init__(self):
|
|
self._emojis = {}
|
|
self._load_emojis()
|
|
|
|
def _load_emojis(self):
|
|
"""Load emoji data from the JSON file"""
|
|
try:
|
|
emoji_file_path = os.path.expanduser("~/Modus/config/emoji.json")
|
|
with open(emoji_file_path, "r", encoding="utf-8") as f:
|
|
self._emojis = json.load(f)
|
|
except Exception as e:
|
|
print(f"Failed to load emoji data: {e}")
|
|
self._emojis = {}
|
|
|
|
@property
|
|
def name(self) -> str:
|
|
return "Emoji"
|
|
|
|
@property
|
|
def category(self) -> str:
|
|
return "Utilities"
|
|
|
|
@property
|
|
def icon_name(self) -> str:
|
|
return "face-smile-symbolic"
|
|
|
|
def search(self, query: str) -> List[Dict[str, Any]]:
|
|
if not query or len(query) < 2: # Require at least 2 characters
|
|
return []
|
|
|
|
results = []
|
|
query_lower = query.lower()
|
|
|
|
# Search through emojis
|
|
for emoji_char, emoji_data in self._emojis.items():
|
|
# Search in name, slug, and group
|
|
searchable_text = (
|
|
emoji_data.get("name", "").lower()
|
|
+ " "
|
|
+ emoji_data.get("slug", "").lower()
|
|
+ " "
|
|
+ emoji_data.get("group", "").lower()
|
|
)
|
|
|
|
if query_lower in searchable_text:
|
|
results.append(
|
|
{
|
|
"title": emoji_data.get('name', 'Unknown'),
|
|
"description": f"{emoji_data.get('group', 'Unknown')}",
|
|
"emoji_icon": emoji_char, # Store the emoji character for display
|
|
"action": lambda emoji=emoji_char: self.copy_emoji(emoji),
|
|
}
|
|
)
|
|
|
|
# Limit results to prevent overwhelming the UI
|
|
if len(results) >= 20:
|
|
break
|
|
|
|
# Sort results by relevance (exact matches first)
|
|
def sort_key(item):
|
|
name = item["title"].lower()
|
|
# Check if query matches the emoji name
|
|
if name.startswith(query_lower):
|
|
return 0 # Name starts with query
|
|
elif query_lower in name:
|
|
return 1 # Partial name match
|
|
else:
|
|
return 2 # Other matches
|
|
|
|
results.sort(key=sort_key)
|
|
return results
|
|
|
|
def copy_emoji(self, emoji: str):
|
|
"""Copy emoji to clipboard using wl-copy"""
|
|
# Use a fire-and-forget approach to avoid blocking
|
|
try:
|
|
# Start wl-copy in the background and don't wait for it
|
|
subprocess.Popen(
|
|
["wl-copy", emoji], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
|
|
)
|
|
print(f"Copied emoji to clipboard: {emoji}")
|
|
except FileNotFoundError:
|
|
try:
|
|
# Fallback to xclip
|
|
process = subprocess.Popen(
|
|
["xclip", "-selection", "clipboard"],
|
|
stdin=subprocess.PIPE,
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.DEVNULL,
|
|
text=True,
|
|
)
|
|
process.stdin.write(emoji)
|
|
process.stdin.close()
|
|
print(f"Copied emoji to clipboard (xclip): {emoji}")
|
|
except (FileNotFoundError, OSError):
|
|
print(f"Could not copy emoji to clipboard: {emoji}")
|
|
except OSError:
|
|
print(f"Could not copy emoji to clipboard: {emoji}")
|
|
|
|
def get_action_items(self, query: str) -> List[Dict[str, Any]]:
|
|
"""Get quick action items for emoji search"""
|
|
if not query or len(query) < 2:
|
|
return []
|
|
|
|
# Return a quick action to search for emojis
|
|
return [
|
|
{
|
|
"title": f'Search emojis for "{query}"',
|
|
"icon_name": "face-smile-symbolic",
|
|
"action": lambda: None, # No specific action needed
|
|
}
|
|
]
|