mirror of https://github.com/kortix-ai/suna.git
545 lines
18 KiB
HTML
545 lines
18 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Fun Todo App</title>
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
font-family: 'Comic Sans MS', cursive, sans-serif;
|
||
}
|
||
|
||
body {
|
||
background: linear-gradient(135deg, #ff9a9e, #fad0c4);
|
||
color: #333;
|
||
display: flex;
|
||
justify-content: center;
|
||
padding: 40px 20px;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.container {
|
||
width: 100%;
|
||
max-width: 500px;
|
||
background-color: white;
|
||
border-radius: 16px;
|
||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
|
||
padding: 25px;
|
||
transform-style: preserve-3d;
|
||
perspective: 1000px;
|
||
}
|
||
|
||
h1 {
|
||
text-align: center;
|
||
margin-bottom: 20px;
|
||
color: #ff6b6b;
|
||
text-shadow: 2px 2px 0px #ffe66d;
|
||
font-size: 2.2em;
|
||
}
|
||
|
||
.input-group {
|
||
display: flex;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
#todo-input {
|
||
flex: 1;
|
||
padding: 12px;
|
||
border: 3px solid #ffe66d;
|
||
border-radius: 12px 0 0 12px;
|
||
font-size: 16px;
|
||
background-color: #fff9e6;
|
||
}
|
||
|
||
#add-button {
|
||
padding: 12px 20px;
|
||
background-color: #ff6b6b;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 0 12px 12px 0;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
font-weight: bold;
|
||
font-size: 16px;
|
||
}
|
||
|
||
#add-button:hover {
|
||
background-color: #ff8e8e;
|
||
transform: scale(1.05);
|
||
}
|
||
|
||
ul {
|
||
list-style-type: none;
|
||
}
|
||
|
||
li {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 15px;
|
||
border-bottom: 2px dashed #ffe66d;
|
||
animation: fadeIn 0.5s ease-out;
|
||
transition: all 0.3s;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
li:hover {
|
||
background-color: #fff9e6;
|
||
transform: translateY(-3px);
|
||
box-shadow: 0 5px 10px rgba(0,0,0,0.1);
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; transform: translateY(20px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
@keyframes celebrate {
|
||
0% { transform: scale(1); }
|
||
50% { transform: scale(1.2) rotate(5deg); }
|
||
100% { transform: scale(1); }
|
||
}
|
||
|
||
@keyframes confetti {
|
||
0% { transform: translateY(0) rotate(0deg); opacity: 1; }
|
||
100% { transform: translateY(100vh) rotate(720deg); opacity: 0; }
|
||
}
|
||
|
||
.todo-text {
|
||
flex: 1;
|
||
margin-left: 10px;
|
||
font-size: 18px;
|
||
}
|
||
|
||
.completed {
|
||
text-decoration: line-through;
|
||
color: #7f8c8d;
|
||
}
|
||
|
||
.completed::after {
|
||
content: " 🎉";
|
||
animation: celebrate 0.5s;
|
||
}
|
||
|
||
.delete-btn {
|
||
background-color: #ff6b6b;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 50%;
|
||
width: 30px;
|
||
height: 30px;
|
||
cursor: pointer;
|
||
margin-left: 10px;
|
||
transition: all 0.3s;
|
||
font-size: 16px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.delete-btn:hover {
|
||
transform: rotate(90deg);
|
||
background-color: #ff8e8e;
|
||
}
|
||
|
||
.empty-state {
|
||
text-align: center;
|
||
color: #7f8c8d;
|
||
padding: 30px;
|
||
font-size: 18px;
|
||
}
|
||
|
||
input[type="checkbox"] {
|
||
width: 20px;
|
||
height: 20px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.confetti {
|
||
position: fixed;
|
||
width: 10px;
|
||
height: 10px;
|
||
background-color: #f00;
|
||
pointer-events: none;
|
||
z-index: 999;
|
||
}
|
||
|
||
.stats {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-top: 20px;
|
||
padding: 10px;
|
||
background-color: #fff9e6;
|
||
border-radius: 10px;
|
||
border: 2px dashed #ffe66d;
|
||
}
|
||
|
||
.filter-buttons {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 10px;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.filter-btn {
|
||
padding: 8px 12px;
|
||
background-color: #fff9e6;
|
||
border: 2px solid #ffe66d;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.filter-btn.active {
|
||
background-color: #ffe66d;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.priority {
|
||
display: inline-block;
|
||
width: 12px;
|
||
height: 12px;
|
||
border-radius: 50%;
|
||
margin-right: 5px;
|
||
}
|
||
|
||
.priority-low {
|
||
background-color: #2ecc71;
|
||
}
|
||
|
||
.priority-medium {
|
||
background-color: #f39c12;
|
||
}
|
||
|
||
.priority-high {
|
||
background-color: #e74c3c;
|
||
}
|
||
|
||
.priority-selector {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-top: 10px;
|
||
gap: 10px;
|
||
}
|
||
|
||
.priority-option {
|
||
cursor: pointer;
|
||
padding: 5px 10px;
|
||
border-radius: 5px;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.priority-option:hover {
|
||
transform: scale(1.1);
|
||
}
|
||
|
||
.theme-toggle {
|
||
position: absolute;
|
||
top: 20px;
|
||
right: 20px;
|
||
background: none;
|
||
border: none;
|
||
font-size: 24px;
|
||
cursor: pointer;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<button class="theme-toggle" id="theme-toggle">🌙</button>
|
||
<div class="container">
|
||
<h1>✨ Fun Todo List ✨</h1>
|
||
|
||
<div class="input-group">
|
||
<input type="text" id="todo-input" placeholder="Add something fun to do...">
|
||
<button id="add-button">Add ✅</button>
|
||
</div>
|
||
|
||
<div class="priority-selector">
|
||
<span>Priority:</span>
|
||
<div class="priority-option" data-priority="low">
|
||
<span class="priority priority-low"></span> Low
|
||
</div>
|
||
<div class="priority-option" data-priority="medium">
|
||
<span class="priority priority-medium"></span> Medium
|
||
</div>
|
||
<div class="priority-option" data-priority="high">
|
||
<span class="priority priority-high"></span> High
|
||
</div>
|
||
</div>
|
||
|
||
<div class="filter-buttons">
|
||
<button class="filter-btn active" data-filter="all">All</button>
|
||
<button class="filter-btn" data-filter="active">Active</button>
|
||
<button class="filter-btn" data-filter="completed">Completed</button>
|
||
</div>
|
||
|
||
<ul id="todo-list">
|
||
<!-- Todo items will be added here -->
|
||
</ul>
|
||
|
||
<div id="empty-state" class="empty-state">
|
||
Your list is empty! 😮 Add something fun to do! 🚀
|
||
</div>
|
||
|
||
<div class="stats">
|
||
<div id="total-tasks">Total: 0</div>
|
||
<div id="completed-tasks">Completed: 0</div>
|
||
<div id="remaining-tasks">Remaining: 0</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
const todoInput = document.getElementById('todo-input');
|
||
const addButton = document.getElementById('add-button');
|
||
const todoList = document.getElementById('todo-list');
|
||
const emptyState = document.getElementById('empty-state');
|
||
const container = document.querySelector('.container');
|
||
const themeToggle = document.getElementById('theme-toggle');
|
||
const totalTasksEl = document.getElementById('total-tasks');
|
||
const completedTasksEl = document.getElementById('completed-tasks');
|
||
const remainingTasksEl = document.getElementById('remaining-tasks');
|
||
const filterButtons = document.querySelectorAll('.filter-btn');
|
||
const priorityOptions = document.querySelectorAll('.priority-option');
|
||
|
||
// Fun emojis to add randomly
|
||
const emojis = ['🚀', '⭐', '🎯', '🎨', '🎮', '🍕', '🏄', '🎸', '📚', '🎬'];
|
||
|
||
// Current filter and priority
|
||
let currentFilter = 'all';
|
||
let currentPriority = 'medium';
|
||
|
||
// Dark mode state
|
||
let isDarkMode = false;
|
||
|
||
// Load todos from localStorage
|
||
let todos = JSON.parse(localStorage.getItem('todos')) || [];
|
||
|
||
// Render initial todos
|
||
renderTodos();
|
||
updateStats();
|
||
|
||
// Add 3D effect on mouse move
|
||
document.addEventListener('mousemove', (e) => {
|
||
const xAxis = (window.innerWidth / 2 - e.pageX) / 25;
|
||
const yAxis = (window.innerHeight / 2 - e.pageY) / 25;
|
||
container.style.transform = `rotateY(${xAxis}deg) rotateX(${yAxis}deg)`;
|
||
});
|
||
|
||
// Reset transform when mouse leaves
|
||
document.addEventListener('mouseleave', () => {
|
||
container.style.transform = 'rotateY(0deg) rotateX(0deg)';
|
||
});
|
||
|
||
// Add new todo
|
||
addButton.addEventListener('click', addTodo);
|
||
todoInput.addEventListener('keypress', (e) => {
|
||
if (e.key === 'Enter') {
|
||
addTodo();
|
||
}
|
||
});
|
||
|
||
// Theme toggle
|
||
themeToggle.addEventListener('click', toggleTheme);
|
||
|
||
// Filter buttons
|
||
filterButtons.forEach(btn => {
|
||
btn.addEventListener('click', () => {
|
||
filterButtons.forEach(b => b.classList.remove('active'));
|
||
btn.classList.add('active');
|
||
currentFilter = btn.dataset.filter;
|
||
renderTodos();
|
||
});
|
||
});
|
||
|
||
// Priority selection
|
||
priorityOptions.forEach(option => {
|
||
option.addEventListener('click', () => {
|
||
currentPriority = option.dataset.priority;
|
||
priorityOptions.forEach(opt => opt.style.fontWeight = 'normal');
|
||
option.style.fontWeight = 'bold';
|
||
});
|
||
});
|
||
|
||
function addTodo() {
|
||
const todoText = todoInput.value.trim();
|
||
|
||
if (todoText) {
|
||
// Add a random emoji to the task
|
||
const randomEmoji = emojis[Math.floor(Math.random() * emojis.length)];
|
||
|
||
todos.push({
|
||
id: Date.now(),
|
||
text: `${todoText} ${randomEmoji}`,
|
||
completed: false,
|
||
priority: currentPriority,
|
||
createdAt: new Date().toISOString()
|
||
});
|
||
|
||
saveTodos();
|
||
renderTodos();
|
||
updateStats();
|
||
todoInput.value = '';
|
||
|
||
// Add a little bounce to the container
|
||
container.style.animation = 'celebrate 0.5s';
|
||
setTimeout(() => {
|
||
container.style.animation = '';
|
||
}, 500);
|
||
}
|
||
}
|
||
|
||
function toggleTodo(id) {
|
||
todos = todos.map(todo => {
|
||
if (todo.id === id) {
|
||
const newCompleted = !todo.completed;
|
||
if (newCompleted) {
|
||
createConfetti();
|
||
}
|
||
return { ...todo, completed: newCompleted };
|
||
}
|
||
return todo;
|
||
});
|
||
|
||
saveTodos();
|
||
renderTodos();
|
||
updateStats();
|
||
}
|
||
|
||
function deleteTodo(id) {
|
||
todos = todos.filter(todo => todo.id !== id);
|
||
saveTodos();
|
||
renderTodos();
|
||
updateStats();
|
||
}
|
||
|
||
function renderTodos() {
|
||
// Filter todos based on current filter
|
||
let filteredTodos = todos;
|
||
|
||
if (currentFilter === 'active') {
|
||
filteredTodos = todos.filter(todo => !todo.completed);
|
||
} else if (currentFilter === 'completed') {
|
||
filteredTodos = todos.filter(todo => todo.completed);
|
||
}
|
||
|
||
// Show/hide empty state
|
||
if (filteredTodos.length === 0) {
|
||
emptyState.style.display = 'block';
|
||
} else {
|
||
emptyState.style.display = 'none';
|
||
}
|
||
|
||
// Clear current list
|
||
todoList.innerHTML = '';
|
||
|
||
// Add todo items
|
||
filteredTodos.forEach(todo => {
|
||
const li = document.createElement('li');
|
||
|
||
const checkbox = document.createElement('input');
|
||
checkbox.type = 'checkbox';
|
||
checkbox.checked = todo.completed;
|
||
checkbox.addEventListener('change', () => toggleTodo(todo.id));
|
||
|
||
const priorityIndicator = document.createElement('span');
|
||
priorityIndicator.className = `priority priority-${todo.priority || 'medium'}`;
|
||
|
||
const span = document.createElement('span');
|
||
span.textContent = todo.text;
|
||
span.className = 'todo-text';
|
||
if (todo.completed) {
|
||
span.classList.add('completed');
|
||
}
|
||
|
||
const deleteBtn = document.createElement('button');
|
||
deleteBtn.innerHTML = '×';
|
||
deleteBtn.className = 'delete-btn';
|
||
deleteBtn.addEventListener('click', () => deleteTodo(todo.id));
|
||
|
||
li.appendChild(checkbox);
|
||
li.appendChild(priorityIndicator);
|
||
li.appendChild(span);
|
||
li.appendChild(deleteBtn);
|
||
|
||
todoList.appendChild(li);
|
||
});
|
||
}
|
||
|
||
function saveTodos() {
|
||
localStorage.setItem('todos', JSON.stringify(todos));
|
||
}
|
||
|
||
function updateStats() {
|
||
const total = todos.length;
|
||
const completed = todos.filter(todo => todo.completed).length;
|
||
const remaining = total - completed;
|
||
|
||
totalTasksEl.textContent = `Total: ${total}`;
|
||
completedTasksEl.textContent = `Completed: ${completed}`;
|
||
remainingTasksEl.textContent = `Remaining: ${remaining}`;
|
||
}
|
||
|
||
function createConfetti() {
|
||
for (let i = 0; i < 50; i++) {
|
||
const confetti = document.createElement('div');
|
||
confetti.className = 'confetti';
|
||
|
||
// Random position
|
||
const x = Math.random() * window.innerWidth;
|
||
const y = Math.random() * window.innerHeight;
|
||
|
||
// Random color
|
||
const colors = ['#ff6b6b', '#ffe66d', '#4ecdc4', '#a29bfe', '#ff9a9e'];
|
||
const color = colors[Math.floor(Math.random() * colors.length)];
|
||
|
||
// Random size
|
||
const size = Math.random() * 10 + 5;
|
||
|
||
// Set styles
|
||
confetti.style.left = `${x}px`;
|
||
confetti.style.top = `${y}px`;
|
||
confetti.style.backgroundColor = color;
|
||
confetti.style.width = `${size}px`;
|
||
confetti.style.height = `${size}px`;
|
||
confetti.style.borderRadius = Math.random() > 0.5 ? '50%' : '0';
|
||
|
||
// Set animation
|
||
confetti.style.animation = `confetti ${Math.random() * 3 + 2}s linear forwards`;
|
||
|
||
document.body.appendChild(confetti);
|
||
|
||
// Remove after animation
|
||
setTimeout(() => {
|
||
confetti.remove();
|
||
}, 5000);
|
||
}
|
||
}
|
||
|
||
function toggleTheme() {
|
||
isDarkMode = !isDarkMode;
|
||
|
||
if (isDarkMode) {
|
||
document.body.style.background = 'linear-gradient(135deg, #2c3e50, #4a69bd)';
|
||
container.style.backgroundColor = '#34495e';
|
||
container.style.color = '#ecf0f1';
|
||
todoInput.style.backgroundColor = '#2c3e50';
|
||
todoInput.style.color = '#ecf0f1';
|
||
todoInput.style.borderColor = '#3498db';
|
||
themeToggle.textContent = '☀️';
|
||
} else {
|
||
document.body.style.background = 'linear-gradient(135deg, #ff9a9e, #fad0c4)';
|
||
container.style.backgroundColor = 'white';
|
||
container.style.color = '#333';
|
||
todoInput.style.backgroundColor = '#fff9e6';
|
||
todoInput.style.color = '#333';
|
||
todoInput.style.borderColor = '#ffe66d';
|
||
themeToggle.textContent = '🌙';
|
||
}
|
||
}
|
||
});
|
||
</script>
|
||
</body>
|
||
</html> |