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 @@
+
+
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);
+}