diff --git a/config/assets/icons/todo/checkbox-check.svg b/config/assets/icons/todo/checkbox-check.svg new file mode 100644 index 0000000..198f1dd --- /dev/null +++ b/config/assets/icons/todo/checkbox-check.svg @@ -0,0 +1,58 @@ + + + + + + + + + + + + diff --git a/config/assets/icons/todo/checkbox-uncheck.svg b/config/assets/icons/todo/checkbox-uncheck.svg new file mode 100644 index 0000000..a0ee941 --- /dev/null +++ b/config/assets/icons/todo/checkbox-uncheck.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + diff --git a/config/assets/icons/todo/delete-symbolic.svg b/config/assets/icons/todo/delete-symbolic.svg new file mode 100644 index 0000000..180f034 --- /dev/null +++ b/config/assets/icons/todo/delete-symbolic.svg @@ -0,0 +1,43 @@ + + + + + + + + diff --git a/config/assets/icons/todo/edit.svg b/config/assets/icons/todo/edit.svg new file mode 100644 index 0000000..42b5282 --- /dev/null +++ b/config/assets/icons/todo/edit.svg @@ -0,0 +1,59 @@ + + + + + + + + image/svg+xml + + + + + + + + diff --git a/config/assets/icons/todo/plus-symbolic.svg b/config/assets/icons/todo/plus-symbolic.svg new file mode 100644 index 0000000..af065bd --- /dev/null +++ b/config/assets/icons/todo/plus-symbolic.svg @@ -0,0 +1,51 @@ + + + + + + + diff --git a/config/assets/icons/todo/to-do-app-symbolic.svg b/config/assets/icons/todo/to-do-app-symbolic.svg new file mode 100644 index 0000000..17cbcdf --- /dev/null +++ b/config/assets/icons/todo/to-do-app-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/main.css b/main.css index d4cc37b..738870f 100644 --- a/main.css +++ b/main.css @@ -14,6 +14,7 @@ @import url("./styles/tray.css"); @import url("./styles/battery-widget.css"); @import url("./styles/lock.css"); +@import url("./styles/todo.css"); * { all: unset; diff --git a/modules/panel/main.py b/modules/panel/main.py index b4707f2..5228e7c 100644 --- a/modules/panel/main.py +++ b/modules/panel/main.py @@ -17,6 +17,7 @@ from modules.panel.components.indicators import ( from modules.panel.components.menubar import MenuBar from modules.panel.components.recording_indicator import RecordingIndicator from modules.panel.components.workspace import WorkspaceIndicator +from modules.todo.todo_widget import TodoListCapture from utils.roam import modus_service from widgets.mousecapture import MouseCapture from widgets.wayland import WaylandWindow as Window @@ -113,6 +114,9 @@ class Panel(Window): layer="overlay", child_window=NotificationCenter() ) + # Todo List with MouseCapture + self.todo_list = TodoListCapture() + # Notification Center Icon self.notification_icon = Svg( size=22, @@ -127,10 +131,11 @@ class Panel(Window): on_clicked=self.on_notification_icon_clicked, ) - # Clickable DateTime for notification center + # Clickable DateTime for todo list self.datetime_button = Button( name="datetime-button", child=DateTime(name="date-time", formatters=["%a %-d %b %I:%M %P"]), + on_clicked=self.on_datetime_clicked, ) self.recording_indicator = RecordingIndicator() @@ -213,6 +218,10 @@ class Panel(Window): self.notification_center.toggle_mousecapture() # Do nothing if no notifications + def on_datetime_clicked(self, *args): + """Handle datetime button clicks - open todo list.""" + self.todo_list.toggle_mousecapture() + def update_notification_icon(self): """Update the notification icon based on count and DND state.""" count = notification_service.count diff --git a/modules/todo/__init__.py b/modules/todo/__init__.py new file mode 100644 index 0000000..30afe86 --- /dev/null +++ b/modules/todo/__init__.py @@ -0,0 +1 @@ +# Todo module \ No newline at end of file diff --git a/modules/todo/todo_widget.py b/modules/todo/todo_widget.py new file mode 100644 index 0000000..007b4cc --- /dev/null +++ b/modules/todo/todo_widget.py @@ -0,0 +1,456 @@ +# Standard library imports +from datetime import datetime + +# Fabric imports +from fabric.utils import get_relative_path +from fabric.widgets.box import Box +from fabric.widgets.button import Button +from fabric.widgets.centerbox import CenterBox +from fabric.widgets.entry import Entry +from fabric.widgets.label import Label +from fabric.widgets.scrolledwindow import ScrolledWindow +from fabric.widgets.svg import Svg +from gi.repository import GLib + +# Local imports +from services.todo import todo_service +from widgets.mousecapture import MouseCapture +from widgets.wayland import WaylandWindow as Window + + +class TodoItem(Box): + """Individual todo item widget""" + + def __init__(self, todo_data, todo_list_widget, **kwargs): + self.todo_data = todo_data + self.todo_list_widget = todo_list_widget + self.editing = False + + super().__init__( + name="todo-item", + orientation="h", + spacing=8, + style_classes=["menu"], + **kwargs, + ) + + self._build_ui() + + def _build_ui(self): + """Build the todo item UI""" + # Checkbox for completion - using SVG icons + checkbox_icon = ( + "checkbox-check.svg" + if self.todo_data["completed"] + else "checkbox-uncheck.svg" + ) + self.checkbox_icon = Svg( + name="todo-checkbox-icon", + size=24, + svg_file=get_relative_path( + "../../config/assets/icons/todo/" + checkbox_icon + ), + ) + self.checkbox = Button( + name="todo-checkbox", + child=self.checkbox_icon, + on_clicked=self._toggle_completion, + ) + + # Todo text (can be converted to entry for editing) + text_content = self.todo_data["text"] + if self.todo_data["completed"]: + text_content = f"{text_content}" + + self.text_label = Label( + markup=text_content, + name="todo-text", + h_align="start", + h_expand=True, + line_wrap="word-char", + style_classes=( + ["title-widget"] + if not self.todo_data["completed"] + else ["status-label"] + ), + ) + + # Date/time label + created_at = datetime.fromisoformat(self.todo_data["created_at"]) + date_text = created_at.strftime("%b %d, %Y at %I:%M %p") + + self.date_label = Label( + label=date_text, + name="todo-date", + h_align="start", + style_classes=["todo-date-text"], + ) + + self.text_entry = Entry( + name="todo-text-entry", + text=self.todo_data["text"], + h_expand=True, + visible=False, + ) + self.text_entry.connect("activate", self._save_edit) + + # Priority indicator + # priority_symbols = {"high": "🔴", "medium": "🟡", "low": "🟢"} + # + # self.priority_label = Label( + # label=priority_symbols.get(self.todo_data["priority"], "🟡"), + # name="todo-priority-label", + # ) + # self.priority_button = Button( + # name="todo-priority", + # size=(20, 20), + # child=self.priority_label, + # on_clicked=self._cycle_priority, + # ) + # + # Edit button - using SVG icon + self.edit_icon = Svg( + name="todo-edit-icon", + size=14, + svg_file=get_relative_path("../../config/assets/icons/todo/edit.svg"), + ) + self.edit_button = Button( + name="todo-edit", + child=self.edit_icon, + on_clicked=self._start_edit, + ) + + # Delete button - using SVG icon + self.delete_icon = Svg( + name="todo-delete-icon", + size=14, + svg_file=get_relative_path( + "../../config/assets/icons/todo/delete-symbolic.svg" + ), + ) + self.delete_button = Button( + name="todo-delete", + child=self.delete_icon, + on_clicked=self._delete_todo, + ) + + # Text container that switches between label and entry + self.text_container = Box( + orientation="v", + h_expand=True, + children=[ + Box(orientation="h", children=[self.text_label]), + self.date_label, + ], + ) + + self.children = [ + self.checkbox, + self.text_container, + # self.priority_button, + self.edit_button, + self.delete_button, + ] + + def _toggle_completion(self, *_): + """Toggle todo completion status""" + todo_service.toggle_todo(self.todo_data["id"]) + + def _cycle_priority(self, *_): + """Cycle through priority levels""" + priorities = ["low", "medium", "high"] + current_index = priorities.index(self.todo_data["priority"]) + new_priority = priorities[(current_index + 1) % len(priorities)] + todo_service.set_priority(self.todo_data["id"], new_priority) + + def _start_edit(self, *_): + """Start editing the todo text""" + if self.editing: + return + + self.editing = True + self.text_container.children = [ + Box(orientation="h", children=[self.text_entry]), + self.date_label, + ] + self.text_entry.set_visible(True) + self.text_entry.grab_focus() + self.text_entry.set_position(-1) # Move cursor to end + + def _save_edit(self, *_): + """Save the edited todo text""" + if not self.editing: + return + + new_text = self.text_entry.get_text().strip() + if new_text: + todo_service.edit_todo(self.todo_data["id"], new_text) + + self._cancel_edit() + + def _cancel_edit(self): + """Cancel editing and revert to label""" + self.editing = False + self.text_container.children = [ + Box(orientation="h", children=[self.text_label]), + self.date_label, + ] + self.text_entry.set_visible(False) + + def _delete_todo(self, *_): + """Delete this todo""" + todo_service.delete_todo(self.todo_data["id"]) + + def update_from_data(self, todo_data): + """Update the widget from new todo data""" + self.todo_data = todo_data + + # Update checkbox icon by recreating it + checkbox_icon = ( + "checkbox-check.svg" if todo_data["completed"] else "checkbox-uncheck.svg" + ) + new_checkbox_icon = Svg( + name="todo-checkbox-icon", + size=20, + svg_file=get_relative_path( + "../../config/assets/icons/todo/" + checkbox_icon + ), + ) + self.checkbox.set_child(new_checkbox_icon) + self.checkbox_icon = new_checkbox_icon + + # Update text and styling with markup + text_content = todo_data["text"] + if todo_data["completed"]: + text_content = f"{text_content}" + + self.text_label.set_markup(text_content) + self.text_label.style_classes = ( + ["title-widget"] if not todo_data["completed"] else ["status-label"] + ) + + # Update date/time + created_at = datetime.fromisoformat(todo_data["created_at"]) + date_text = created_at.strftime("%b %d, %Y at %I:%M %p") + self.date_label.set_label(date_text) + + +class TodoListWidget(Window): + """Main todo list widget window""" + + def __init__(self, **kwargs): + super().__init__( + title="modus-todo", + anchor="top right", + margin="2px 10px 0px 0px", + exclusivity="auto", + keyboard_mode="on-demand", + name="todo-list-window", + visible=False, # Back to hidden by default + **kwargs, + ) + + self.todo_items = {} # Maps todo IDs to TodoItem widgets + + # Register callback with todo service + todo_service.add_callback(self._on_todo_event) + + self._build_ui() + self._refresh_todos() + + # Add keybinding for escape + self.add_keybinding("Escape", self.hide_todo_list) + + def _build_ui(self): + """Build the main UI""" + # Header with title and stats + self.stats_label = Label( + label="", + name="todo-stats", + style_classes=["status-label"], + h_align="start", + ) + + self.header = Box( + name="todo-header", + orientation="v", + children=[ + Label( + label="Todo List", + name="todo-title", + style_classes=["title"], + h_align="start", + ), + self.stats_label, + ], + ) + + # Add new todo section + self.new_todo_entry = Entry( + name="new-todo-entry", + placeholder_text="Add a new task...", + h_expand=True, + ) + self.new_todo_entry.connect("activate", self._add_todo) + + # Add button - using SVG icon + self.add_icon = Svg( + name="add-todo-icon", + size=12, + svg_file=get_relative_path( + "../../config/assets/icons/todo/plus-symbolic.svg" + ), + ) + self.add_button = Button( + name="add-todo-button", + child=self.add_icon, + on_clicked=self._add_todo, + ) + + self.add_section = Box( + name="todo-add-section", + orientation="h", + spacing=8, + style_classes=["menu"], + children=[ + self.new_todo_entry, + self.add_button, + ], + ) + + # Todo items container + self.todos_container = Box( + name="todos-container", + orientation="v", + spacing=4, + ) + + # Scrolled window for todos + self.scrolled = ScrolledWindow( + name="todos-scrolled", + min_content_height=300, + max_content_height=500, + min_content_width=400, + child=self.todos_container, + policy="automatic", + v_expand=True, # Allow vertical expansion + ) + + # Clear completed button + self.clear_button = Button( + name="clear-completed-button", + label="Clear Completed", + style_classes=["status-label"], + on_clicked=self._clear_completed, + ) + + # Main container + self.main_container = Box( + name="todo-main-container", + orientation="v", + spacing=8, + style_classes=["menu"], + children=[ + self.header, + self.add_section, + self.scrolled, + self.clear_button, + ], + ) + + self.children = [self.main_container] + + def _add_todo(self, *_): + """Add a new todo""" + text = self.new_todo_entry.get_text().strip() + if text: + todo_service.add_todo(text) + self.new_todo_entry.set_text("") + + def _clear_completed(self, *_): + """Clear all completed todos""" + todo_service.clear_completed() + + def _on_todo_event(self, event_type, data=None): + """Handle todo service events via callback""" + if event_type == "todos-changed": + GLib.idle_add(self._refresh_todos) + elif event_type == "todo-added": + GLib.idle_add(self._refresh_todos) + elif event_type == "todo-deleted": + GLib.idle_add(self._refresh_todos) + elif event_type in ["todo-toggled", "todo-edited", "todo-priority-changed"]: + if data and data["id"] in self.todo_items: + GLib.idle_add( + lambda: self.todo_items[data["id"]].update_from_data(data) + ) + GLib.idle_add(self._update_stats) + + def _refresh_todos(self, *_): + """Refresh the entire todo list""" + # Clear existing items + self.todo_items.clear() + self.todos_container.children = [] + + # Get all todos + todos = todo_service.todos + + # Sort todos: incomplete first, then by priority, then by creation date + def sort_key(todo): + priority_order = {"high": 0, "medium": 1, "low": 2} + return ( + todo["completed"], # False (incomplete) comes before True (completed) + priority_order.get(todo["priority"], 1), + todo["created_at"], + ) + + sorted_todos = sorted(todos, key=sort_key) + + # Create todo item widgets + for todo in sorted_todos: + todo_item = TodoItem(todo, self) + self.todo_items[todo["id"]] = todo_item + + # Update container children + self.todos_container.children = list(self.todo_items.values()) + + # Update stats + self._update_stats() + + def _update_stats(self): + """Update the statistics display""" + stats = todo_service.get_stats() + stats_text = f"{stats['pending']} pending, {stats['completed']} completed" + self.stats_label.set_label(stats_text) + + def set_visible(self, visible): + """Override set_visible for debugging""" + super().set_visible(visible) + + def hide_todo_list(self, *_): + """Hide the todo list""" + if hasattr(self, "_mousecapture_parent"): + self._mousecapture_parent.toggle_mousecapture() + self.set_visible(False) + + def _init_mousecapture(self, mousecapture): + """Initialize mousecapture parent reference""" + self._mousecapture_parent = mousecapture + + def destroy(self): + """Clean up when destroyed""" + # Remove callback from todo service + todo_service.remove_callback(self._on_todo_event) + + super().destroy() + + +class TodoListCapture(MouseCapture): + """MouseCapture wrapper for the todo list""" + + def __init__(self, **kwargs): + super().__init__( + layer="top", + child_window=TodoListWidget(), + **kwargs, + ) diff --git a/services/todo.py b/services/todo.py new file mode 100644 index 0000000..e8a9992 --- /dev/null +++ b/services/todo.py @@ -0,0 +1,169 @@ +# Standard library imports +import json +import os +import uuid +from datetime import datetime +from pathlib import Path + +# Fabric imports +from fabric.core.service import Property, Service, Signal + +# Local imports +import config.data as data + + +class TodoService(Service): + """Service for managing persistent todo list with JSON storage""" + + def __init__(self): + super().__init__() + self._todos = [] + self._file_path = self._get_todos_file_path() + self._load_todos() + self._callbacks = [] + + def add_callback(self, callback): + """Add a callback function to be notified of changes""" + self._callbacks.append(callback) + + def remove_callback(self, callback): + """Remove a callback function""" + if callback in self._callbacks: + self._callbacks.remove(callback) + + def _notify_callbacks(self, event_type, data=None): + """Notify all registered callbacks of changes""" + for callback in self._callbacks: + try: + callback(event_type, data) + except Exception as e: + print(f"Error in todo callback: {e}") + + def _get_todos_file_path(self): + """Returns the path to the todos JSON file""" + cache_dir = Path(data.CACHE_DIR) / "todos" + cache_dir.mkdir(parents=True, exist_ok=True) + return cache_dir / "todos.json" + + def _load_todos(self): + """Load todos from JSON file""" + try: + if self._file_path.exists(): + with open(self._file_path, "r", encoding="utf-8") as f: + self._todos = json.load(f) + else: + self._todos = [] + except Exception as e: + print(f"Error loading todos: {e}") + self._todos = [] + + def _save_todos(self): + """Save todos to JSON file""" + try: + with open(self._file_path, "w", encoding="utf-8") as f: + json.dump(self._todos, f, indent=2, ensure_ascii=False) + except Exception as e: + print(f"Error saving todos: {e}") + + @Property(list, "readable") + def todos(self): + """Get all todos""" + return self._todos.copy() + + def add_todo(self, text: str, priority: str = "medium") -> dict: + """Add a new todo item""" + todo = { + "id": str(uuid.uuid4()), + "text": text, + "completed": False, + "priority": priority, # low, medium, high + "created_at": datetime.now().isoformat(), + "updated_at": datetime.now().isoformat() + } + self._todos.append(todo) + self._save_todos() + self._notify_callbacks("todo-added", todo) + self._notify_callbacks("todos-changed") + return todo + + def delete_todo(self, todo_id: str) -> bool: + """Delete a todo item by ID""" + for i, todo in enumerate(self._todos): + if todo["id"] == todo_id: + deleted_todo = self._todos.pop(i) + self._save_todos() + self._notify_callbacks("todo-deleted", deleted_todo) + self._notify_callbacks("todos-changed") + return True + return False + + def toggle_todo(self, todo_id: str) -> bool: + """Toggle completion status of a todo item""" + for todo in self._todos: + if todo["id"] == todo_id: + todo["completed"] = not todo["completed"] + todo["updated_at"] = datetime.now().isoformat() + self._save_todos() + self._notify_callbacks("todo-toggled", todo) + self._notify_callbacks("todos-changed") + return True + return False + + def edit_todo(self, todo_id: str, new_text: str) -> bool: + """Edit the text of a todo item""" + for todo in self._todos: + if todo["id"] == todo_id: + todo["text"] = new_text + todo["updated_at"] = datetime.now().isoformat() + self._save_todos() + self._notify_callbacks("todo-edited", todo) + self._notify_callbacks("todos-changed") + return True + return False + + def set_priority(self, todo_id: str, priority: str) -> bool: + """Set the priority of a todo item""" + if priority not in ["low", "medium", "high"]: + return False + + for todo in self._todos: + if todo["id"] == todo_id: + todo["priority"] = priority + todo["updated_at"] = datetime.now().isoformat() + self._save_todos() + self._notify_callbacks("todo-priority-changed", todo) + self._notify_callbacks("todos-changed") + return True + return False + + def get_todo(self, todo_id: str) -> dict | None: + """Get a specific todo by ID""" + for todo in self._todos: + if todo["id"] == todo_id: + return todo.copy() + return None + + def clear_completed(self): + """Remove all completed todos""" + initial_count = len(self._todos) + self._todos = [todo for todo in self._todos if not todo["completed"]] + if len(self._todos) < initial_count: + self._save_todos() + self._notify_callbacks("todos-changed") + + def get_stats(self) -> dict: + """Get todo statistics""" + total = len(self._todos) + completed = sum(1 for todo in self._todos if todo["completed"]) + pending = total - completed + + return { + "total": total, + "completed": completed, + "pending": pending, + "completion_rate": (completed / total * 100) if total > 0 else 0 + } + + +# Global service instance +todo_service = TodoService() \ No newline at end of file diff --git a/styles/todo.css b/styles/todo.css new file mode 100644 index 0000000..a65ddf4 --- /dev/null +++ b/styles/todo.css @@ -0,0 +1,258 @@ +/* Todo List Styles - matching control center design */ + +#todo-list-window { + /* background-color: alpha(#fff, 0.05); */ + border-radius: 12px; + box-shadow: none; + margin: 0; +} + +#todo-main-container { + box-shadow: inset 0 0 0 1px alpha(#aaa, 0.4); + border-radius: 12px; + padding: 12px; /* Increased padding */ + min-height: 500px; /* Ensure minimum height */ + min-width: 350px; /* Ensure minimum width */ +} + +#todo-header { + margin-bottom: 8px; +} + +#todo-title { + font-size: 16px; + font-family: "SF Pro Rounded"; + font-weight: bold; + color: #ffffff; +} + +#todo-stats { + font-size: 12px; + font-weight: 500; + color: #999; + margin-top: -2px; +} + +/* Add new todo section */ +#todo-add-section { + box-shadow: inset 0 0 0 0.5px alpha(#aaa, 0.4); + border: 1px solid alpha(#111, 0.4); + border-radius: 12px; + padding: 1rem; + margin-bottom: 8px; + min-height: 40px; /* Ensure minimum height */ +} + +#new-todo-entry { + background-color: rgba(255, 255, 255, 0.1); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 6px; + padding: 8px 12px; + color: #ffffff; + font-size: 13px; + font-family: "SF Pro Rounded"; +} + +#new-todo-entry:focus { + border-color: #007aff; + box-shadow: 0 0 0 2px rgba(0, 122, 255, 0.3); + background-color: rgba(255, 255, 255, 0.15); +} + +#add-todo-button { + /* background-color: #007aff; */ + /* border: 1px solid #007aff; */ + border-radius: 6px; + min-width: 20px; + min-height: 12px; + color: #ffffff; + font-size: 16px; +} + +#add-todo-button:hover { + /* background-color: #0056cc; */ + /* border-color: #0056cc; */ +} + +/* Todo items container */ +#todos-scrolled { + background-color: transparent; + min-height: 200px; /* Minimum height for content */ +} + +#todos-container { + padding: 4px; +} + +/* Individual todo items */ +#todo-item { + background-color: alpha(#000, 0.2); /* More visible background */ + border: 1px solid alpha(#111, 0.4); + border-radius: 12px; + padding: 12px; + margin: 4px 0; + min-height: 40px; /* Ensure minimum height */ + transition: background-color 0.2s ease; +} + +#todo-item:hover { + background-color: alpha(#fff, 0.12); +} + +/* Todo item controls */ +#todo-checkbox { + background-color: transparent; + border-radius: 50%; /* Circular appearance */ + min-width: 20px; + min-height: 20px; + margin-right: 8px; +} + +#todo-checkbox:hover { + border-color: #007aff; +} + +/* SVG icon styling for checkboxes and buttons */ +#todo-checkbox-icon { + color: #007aff; +} + +#todo-edit-icon, +#todo-delete-icon, +#add-todo-icon { + color: #ffffff; + opacity: 0.8; +} + +#todo-edit-icon:hover, +#todo-delete-icon:hover, +#add-todo-icon:hover { + opacity: 1; +} + +#todo-text { + font-size: 13px; + font-weight: 400; + color: #ffffff; + font-family: "SF Pro Rounded"; +} + +#todo-date { + font-size: 10px; + font-weight: 400; + color: #999999; + font-family: "SF Pro Rounded"; + margin-top: 2px; + opacity: 0.8; +} + +.todo-date-text { + color: #999999; + font-size: 10px; +} + +#todo-text-entry { + background-color: rgba(255, 255, 255, 0.1); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 4px; + padding: 4px 8px; + color: #ffffff; + font-size: 14px; + font-family: "SF Pro Rounded"; +} + +#todo-text-entry:focus { + border-color: #007aff; + box-shadow: 0 0 0 1px rgba(0, 122, 255, 0.3); + background-color: rgba(255, 255, 255, 0.15); +} + +/* Priority, edit, delete buttons */ +#todo-priority, +#todo-edit, +#todo-delete { + background-color: transparent; + border: none; + border-radius: 4px; + min-width: 20px; + min-height: 20px; + margin: 0 2px; + transition: background-color 0.2s ease; +} + +#todo-priority:hover, +#todo-edit:hover, +#todo-delete:hover { + background-color: alpha(#fff, 0.1); +} + +#todo-priority-label, +#todo-edit-label, +#todo-delete-label { + font-size: 12px; +} + +/* Clear completed button */ +#clear-completed-button { + background-color: alpha(#fff, 0.1); + border: 1px solid alpha(#999, 0.3); + border-radius: 8px; + padding: 8px 16px; + color: #999; + font-size: 12px; + font-weight: 500; + font-family: "SF Pro Rounded"; + margin-top: 8px; + transition: background-color 0.2s ease; +} + +#clear-completed-button:hover { + background-color: alpha(#fff, 0.15); + color: #ffffff; +} + +/* Completed todos styling */ +#todo-item.completed { + opacity: 0.6; +} + +#todo-item.completed #todo-text { + text-decoration: line-through; + color: #999; +} + +/* Priority styling */ +.priority-high #todo-priority-label { + color: #ff4444; +} + +.priority-medium #todo-priority-label { + color: #ffaa00; +} + +.priority-low #todo-priority-label { + color: #44ff44; +} + +/* Scrollbar styling to match control center */ +#todos-scrolled scrollbar { + background-color: transparent; + border-radius: 8px; + margin: 2px; + min-width: 8px; +} + +#todos-scrolled scrollbar slider { + background-color: alpha(#fff, 0.3); + border-radius: 4px; + min-width: 6px; + margin: 1px; +} + +#todos-scrolled scrollbar slider:hover { + background-color: alpha(#fff, 0.5); +} + +#todos-scrolled scrollbar slider:active { + background-color: alpha(#fff, 0.7); +}