import gradio as gr import json import os from datetime import datetime from typing import List, Dict # File to persist todos TODO_FILE = "todos.json" def load_todos() -> List[Dict]: """Load todos from JSON file""" if os.path.exists(TODO_FILE): try: with open(TODO_FILE, 'r') as f: return json.load(f) except: return [] return [] def save_todos(todos: List[Dict]): """Save todos to JSON file""" try: with open(TODO_FILE, 'w') as f: json.dump(todos, f, indent=2) except Exception as e: print(f"Error saving todos: {e}") def add_todo(task: str, priority: int) -> tuple[List[Dict], str]: """Add a new todo item""" if not task.strip(): return load_todos(), "Task cannot be empty!" todos = load_todos() new_todo = { "id": len(todos) + 1, "task": task.strip(), "priority": priority, "completed": False, "created": datetime.now().isoformat() } todos.append(new_todo) save_todos(todos) return todos, f"Added: '{task}' (Priority: {priority})" def toggle_todo(todo_id: int) -> List[Dict]: """Toggle todo completion status""" todos = load_todos() for todo in todos: if todo["id"] == todo_id: todo["completed"] = not todo["completed"] break save_todos(todos) return todos def delete_todo(todo_id: int) -> List[Dict]: """Delete a todo item""" todos = load_todos() todos = [todo for todo in todos if todo["id"] != todo_id] # Update IDs to maintain sequential order for i, todo in enumerate(todos): todo["id"] = i + 1 save_todos(todos) return todos def get_todos_display(todos: List[Dict]) -> str: """Format todos for display""" if not todos: return "🎉 No todos! Great job!" lines = [] sorted_todos = sorted(todos, key=lambda x: (x["priority"], x["id"])) for todo in sorted_todos: status = "✅" if todo["completed"] else "⏳" priority_emoji = {1: "🔥", 2: "⚡", 3: "📋"}.get(todo["priority"], "📌") line = f"{status} [{priority_emoji} P{todo['priority']}] {todo['task']}" lines.append(line) return "\n".join(lines) def clear_completed() -> List[Dict]: """Remove all completed todos""" todos = load_todos() todos = [todo for todo in todos if not todo["completed"]] # Update IDs for i, todo in enumerate(todos): todo["id"] = i + 1 save_todos(todos) return todos # Initialize app with current todos initial_todos = load_todos() with gr.Blocks( title="🚀 Todo App", theme=gr.themes.Soft(), css=""" .todo-header { font-size: 2em; margin-bottom: 1em; } .priority-selector { display: flex; gap: 1em; align-items: center; } .todo-list { background: white; border-radius: 10px; padding: 1.5em; min-height: 200px; } .action-buttons { display: flex; gap: 1em; flex-wrap: wrap; } """ ) as demo: gr.HTML( """

🚀 My Todo App

Built with anycoder
""" ) with gr.Row(): with gr.Column(scale=1): task_input = gr.Textbox( label="New Task", placeholder="What needs to be done?", lines=2 ) priority_input = gr.Slider( minimum=1, maximum=3, value=2, step=1, label="Priority", info="1 = Urgent 🔥 | 2 = Important ⚡ | 3 = Normal 📋" ) add_btn = gr.Button("➕ Add Todo", variant="primary", size="lg") with gr.Column(scale=2): todos_display = gr.Markdown( get_todos_display(initial_todos), label="Your Todos", elem_classes=["todo-list"] ) with gr.Row(): clear_btn = gr.Button("🗑️ Clear Completed", variant="secondary") delete_btn = gr.Button("🗑️ Delete Selected", interactive=False, visible=False) # Status message status_msg = gr.Textbox( label="Status", interactive=False, visible=False ) # Event handlers add_btn.click( add_todo, inputs=[task_input, priority_input], outputs=[todos_display, status_msg] ).then( lambda: gr.update(visible=True), outputs=[status_msg] ).then( gr.update(value=""), outputs=[task_input] ) clear_btn.click( clear_completed, outputs=[todos_display] ) # Update display when todos change demo.load( fn=lambda: get_todos_display(load_todos()), outputs=todos_display ) if __name__ == "__main__": demo.launch()