Files
Modus/modules/launcher/plugins/emoji.py
T

189 lines
6.8 KiB
Python

import json
import os
import subprocess
import time
from collections import OrderedDict
from typing import Dict, List
from fabric.utils import get_relative_path
import config.data as data
from modules.launcher.plugin_base import PluginBase
from modules.launcher.result import Result
class EmojiPlugin(PluginBase):
"""
Plugin for searching and copying emojis.
"""
def __init__(self):
super().__init__()
self.name = "emoji"
self.display_name = "Emoji"
self.description = "Search and copy emojis"
self.emoji_data = {}
self.emoji_path = get_relative_path("../../../config/assets/emoji.json")
# Use cache directory for recent emojis (save directly in cache dir)
self.recent_emoji_path = os.path.join(data.CACHE_DIR, "recent_emoji.json")
self.recent_emojis = OrderedDict()
self.max_recent_emojis = 20 # Maximum number of recent emojis to track
def initialize(self):
"""Initialize the emoji plugin."""
self.set_triggers(["em"])
self._load_emoji_data()
self._load_recent_emojis()
def cleanup(self):
"""Cleanup the emoji plugin."""
pass
def _load_emoji_data(self):
"""Load emoji data from JSON file."""
try:
if os.path.exists(self.emoji_path):
with open(self.emoji_path, "r", encoding="utf-8") as f:
self.emoji_data = json.load(f)
else:
print(f"Emoji file not found: {self.emoji_path}")
except Exception as e:
print(f"Error loading emoji data: {e}")
def _load_recent_emojis(self):
"""Load recently used emojis from JSON file."""
try:
if os.path.exists(self.recent_emoji_path):
with open(self.recent_emoji_path, "r", encoding="utf-8") as f:
recent_data = json.load(f)
# Convert to OrderedDict to maintain order
self.recent_emojis = OrderedDict(recent_data)
else:
# Create empty recent emojis file
self.recent_emojis = OrderedDict()
self._save_recent_emojis()
except Exception as e:
print(f"Error loading recent emoji data: {e}")
self.recent_emojis = OrderedDict()
def _save_recent_emojis(self):
"""Save recently used emojis to JSON file."""
try:
# Ensure the cache directory exists
os.makedirs(data.CACHE_DIR, exist_ok=True)
with open(self.recent_emoji_path, "w", encoding="utf-8") as f:
json.dump(dict(self.recent_emojis), f, ensure_ascii=False, indent=2)
except Exception as e:
print(f"Error saving recent emoji data: {e}")
def _add_to_recent(self, emoji: str):
"""Add an emoji to the recent list."""
# Remove if already exists (to move it to front)
if emoji in self.recent_emojis:
del self.recent_emojis[emoji]
# Add to front with current timestamp
self.recent_emojis[emoji] = time.time()
# Keep only the most recent emojis
while len(self.recent_emojis) > self.max_recent_emojis:
# Remove the oldest item
self.recent_emojis.popitem(last=False)
# Save to file
self._save_recent_emojis()
def _copy_to_clipboard(self, emoji: str):
"""Copy emoji to clipboard and track usage."""
try:
# Try Wayland first
try:
subprocess.run(["wl-copy"], input=emoji.encode(), check=True)
except subprocess.SubprocessError:
# Fall back to X11
subprocess.run(
["xclip", "-selection", "clipboard"],
input=emoji.encode(),
check=True,
)
# Track this emoji as recently used
self._add_to_recent(emoji)
except Exception as e:
print(f"Failed to copy to clipboard: {e}")
def query(self, query_string: str) -> List[Result]:
"""Search emojis based on query."""
results = []
query = query_string.lower().strip()
# If no query, show recently used emojis
if not query:
if self.recent_emojis:
# Show recent emojis in reverse order (most recent first)
for emoji in reversed(list(self.recent_emojis.keys())):
if emoji in self.emoji_data:
emoji_info = self.emoji_data[emoji]
results.append(
self._create_emoji_result(emoji, emoji_info, 1.0)
)
else:
# If no recent emojis, show some popular ones as fallback
popular_emojis = ["😀", "👍", "❤️", "🎉", "🔥", "✨", "🚀", "🌈"]
for emoji in popular_emojis:
if emoji in self.emoji_data:
emoji_info = self.emoji_data[emoji]
results.append(
self._create_emoji_result(emoji, emoji_info, 1.0)
)
return results
# Search by name, group, or the emoji itself
for emoji, info in self.emoji_data.items():
relevance = 0
name = info.get("name", "").lower()
group = info.get("group", "").lower()
slug = info.get("slug", "").lower()
# Exact match with emoji
if query == emoji:
relevance = 1.0
# Name contains query
elif query in name:
relevance = 0.9
# Slug contains query
elif query in slug:
relevance = 0.8
# Group contains query
elif query in group:
relevance = 0.7
if relevance > 0:
results.append(self._create_emoji_result(emoji, info, relevance))
# Sort by relevance
results.sort(key=lambda x: x.relevance, reverse=True)
return results[:20] # Limit to 20 results
def _create_emoji_result(self, emoji: str, info: Dict, relevance: float) -> Result:
"""Create a Result object for an emoji."""
name = info.get("name", "")
group = info.get("group", "")
# Check if this is a recently used emoji
is_recent = emoji in self.recent_emojis
subtitle = f"{group}" + (" • Recently used" if is_recent else "")
return Result(
title=name, # Show only the name, not the emoji
subtitle=subtitle,
icon_markup=emoji, # Use the emoji itself as the icon
action=lambda e=emoji: self._copy_to_clipboard(e),
relevance=relevance,
plugin_name=self.display_name,
data={"emoji": emoji, "name": name, "group": group, "recent": is_recent},
)