Claude-skill-registry cli-todo-ui
Build modern, interactive terminal-based todo applications with beautiful UI/UX using Python's Textual framework. Use when building CLI todo apps, task managers, or interactive terminal interfaces that require menu-driven flows, visual polish (colors, icons, tables), keyboard shortcuts, mouse support, and professional developer experience. Ideal for hackathons and rapid prototyping of terminal UIs.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/cli-todo-ui" ~/.claude/skills/majiayu000-claude-skill-registry-cli-todo-ui && rm -rf "$T"
manifest:
skills/data/cli-todo-ui/SKILL.mdsource content
CLI Todo UI Builder
Build professional, interactive terminal-based todo applications with modern aesthetics using Python's Textual framework and Rich library.
Quick Start
Option 1: Generate Complete App (Recommended)
Use the complete todo app template from
assets/todo-app-template/:
# Copy the template to your project cp -r assets/todo-app-template/* ./ # Install dependencies pip install -r requirements.txt # Run the app python app.py
The template includes:
- Full Textual application with menu-driven interface
- In-memory task storage (decoupled from UI)
- Color-coded status indicators (☐ Pending, ☑ Completed)
- Keyboard shortcuts and mouse support
- Professional styling with Textual CSS
Option 2: Install Dependencies Only
bash scripts/install_dependencies.sh
Core Stack
- Textual (≥0.63.0): Modern TUI framework with reactive components
- Rich (≥13.7.0): Beautiful terminal formatting
- Pydantic (≥2.0.0): Data validation (optional)
Key Features to Implement
Essential Features (Must-Have)
- Interactive DataTable: Arrow key navigation, row selection
- Color-coded statuses: Visual indicators (🔴 High, 🟡 Medium, 🟢 Low priority)
- Keyboard shortcuts: Visible in footer (
Add,a
Delete,d
Toggle,space
Quit)q - Confirmation dialogs: Modal confirmations for destructive actions
- Stats panel: Live counts (Total, Pending, Completed, %)
Enhanced Features (Nice-to-Have)
- Live search/filter: Type to filter tasks instantly
- Mouse support: Click to select, drag to reorder
- Progress bars: Visual completion percentage
- Split layout: Task list (left) + Details preview (right)
- Tabbed interface: Switch between "All", "Active", "Completed"
Advanced Features (Bonus)
- Undo/Redo: Revert last action with visual feedback
- Bulk operations: Multi-select for batch delete/complete
- Export: Pretty-print to markdown/JSON
- Theme toggle: Dark/light mode switching
- Animations: Smooth transitions and task completion effects
Architecture Pattern
In-Memory Task Storage (Decoupled)
from dataclasses import dataclass from typing import List from datetime import datetime @dataclass class Task: id: int title: str description: str = "" completed: bool = False created_at: datetime = None def __post_init__(self): if self.created_at is None: self.created_at = datetime.now() class TaskManager: """Business logic layer - decoupled from UI""" def __init__(self): self.tasks: List[Task] = [] self.next_id = 1 def add_task(self, title: str, description: str = "") -> Task: task = Task(id=self.next_id, title=title, description=description) self.tasks.append(task) self.next_id += 1 return task def get_task(self, task_id: int) -> Task | None: return next((t for t in self.tasks if t.id == task_id), None) def delete_task(self, task_id: int) -> bool: task = self.get_task(task_id) if task: self.tasks.remove(task) return True return False def toggle_task(self, task_id: int) -> bool: task = self.get_task(task_id) if task: task.completed = not task.completed return True return False def get_stats(self) -> dict: total = len(self.tasks) completed = sum(1 for t in self.tasks if t.completed) return { "total": total, "completed": completed, "pending": total - completed, "percentage": (completed / total * 100) if total > 0 else 0 }
Textual App Structure
from textual.app import App, ComposeResult from textual.containers import Container, Horizontal from textual.widgets import Header, Footer, DataTable, Button, Static from textual.binding import Binding class TodoApp(App): """Main Textual application""" CSS = """ DataTable { height: 1fr; border: solid $primary; } #stats { height: 3; background: $panel; border: solid $secondary; padding: 1; } """ BINDINGS = [ Binding("a", "add_task", "Add Task"), Binding("d", "delete_task", "Delete"), Binding("space", "toggle_task", "Toggle"), Binding("q", "quit", "Quit"), ] def __init__(self): super().__init__() self.task_manager = TaskManager() def compose(self) -> ComposeResult: yield Header(show_clock=True) yield Static(id="stats") yield DataTable(zebra_stripes=True) yield Footer() def on_mount(self) -> None: table = self.query_one(DataTable) table.add_columns("ID", "Status", "Title", "Description") self.refresh_table() def action_add_task(self) -> None: # Implement add task modal pass def action_delete_task(self) -> None: # Implement delete with confirmation pass def action_toggle_task(self) -> None: # Toggle selected task pass def refresh_table(self) -> None: # Update table with current tasks pass if __name__ == "__main__": app = TodoApp() app.run()
Reference Documentation
- Textual Patterns: See
for widgets, styling, and reactive patternsreferences/textual-patterns.md - UI Features: See
for comprehensive UI/UX enhancement examplesreferences/ui-features.md - Keyboard Shortcuts: See
for standard binding patternsreferences/keyboard-shortcuts.md
Common Patterns
Adding Confirmation Dialogs
from textual.screen import ModalScreen from textual.widgets import Label, Button class ConfirmDialog(ModalScreen): def __init__(self, message: str): super().__init__() self.message = message def compose(self) -> ComposeResult: yield Container( Label(self.message), Horizontal( Button("Confirm", variant="error", id="confirm"), Button("Cancel", variant="default", id="cancel") ) ) # Usage in app def action_delete_task(self) -> None: def handle_response(confirmed: bool) -> None: if confirmed: # Delete task pass self.push_screen(ConfirmDialog("Delete this task?"), handle_response)
Live Filtering
from textual.widgets import Input class TodoApp(App): def compose(self) -> ComposeResult: yield Header() yield Input(placeholder="Search tasks...", id="search") yield DataTable() yield Footer() def on_input_changed(self, event: Input.Changed) -> None: search_term = event.value.lower() filtered_tasks = [ t for t in self.task_manager.tasks if search_term in t.title.lower() or search_term in t.description.lower() ] self.refresh_table(filtered_tasks)
Status Indicators
Use these emoji/color patterns for visual feedback:
- Task Status: ☐ Pending (gray), ☑ Completed (green), ⏳ In Progress (yellow)
- Priority: 🔴 High, 🟡 Medium, 🟢 Low
- Actions: ✨ Add, 🗑️ Delete, ✓ Toggle, 🔍 Search
Testing
Test the script by running it:
python app.py
Expected behavior:
- App launches with empty task list
- Keyboard shortcuts appear in footer
- Can add, view, toggle, and delete tasks
- Stats update in real-time
- UI is visually polished with colors and borders
Troubleshooting
- Import errors: Ensure
andtextual
are installedrich - Terminal size: Textual requires minimum 80x24 terminal
- Colors not showing: Check terminal supports 256 colors
- Mouse not working: Enable mouse support in terminal emulator