timelord7000's picture
analyze fix upgrade, prioritize mvp top 3 e2e tested n functional simple but valuable human struggle time minimizers
3e519ec verified
class ToolTasks extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: 'open' });
// Load tasks from localStorage if available
const savedTasks = JSON.parse(localStorage.getItem('flowstate_tasks')) || [];
this.tasks = savedTasks;
this.shadowRoot.innerHTML = `
<style>
.wrapper {
background: #1e293b;
border-radius: 1rem;
padding: 2rem;
border: 1px solid #334155;
}
.input-group {
display: flex;
gap: 0.5rem;
margin-bottom: 2rem;
}
input[type="text"] {
flex-grow: 1;
background: #0f172a;
border: 1px solid #334155;
padding: 0.75rem 1rem;
border-radius: 0.5rem;
color: white;
font-size: 1rem;
outline: none;
transition: border-color 0.2s;
}
input[type="text"]:focus {
border-color: #10b981;
}
.add-btn {
background: #10b981;
color: white;
border: none;
padding: 0 1.5rem;
border-radius: 0.5rem;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
}
.add-btn:hover {
background: #059669;
}
.task-list {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: 0.75rem;
max-height: 60vh;
overflow-y: auto;
}
.task-item {
display: flex;
align-items: center;
background: #0f172a;
padding: 0.75rem 1rem;
border-radius: 0.5rem;
border: 1px solid #334155;
transition: all 0.2s;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.task-item:hover {
border-color: #475569;
transform: translateX(4px);
}
.task-item.completed {
opacity: 0.5;
background: #020617;
}
.task-item.completed span {
text-decoration: line-through;
color: #64748b;
}
.checkbox {
width: 20px;
height: 20px;
border: 2px solid #475569;
border-radius: 4px;
margin-right: 1rem;
cursor: pointer;
display: grid;
place-items: center;
flex-shrink: 0;
}
.task-item.completed .checkbox {
background: #10b981;
border-color: #10b981;
}
.task-text {
flex-grow: 1;
font-size: 1rem;
}
.delete-btn {
background: transparent;
border: none;
color: #ef4444;
opacity: 0;
cursor: pointer;
transition: opacity 0.2s;
}
.task-item:hover .delete-btn {
opacity: 1;
}
.empty-state {
text-align: center;
color: #64748b;
padding: 2rem;
font-style: italic;
}
/* Custom Scrollbar for list */
.task-list::-webkit-scrollbar {
width: 6px;
}
.task-list::-webkit-scrollbar-thumb {
background: #334155;
border-radius: 3px;
}
</style>
<div class="wrapper">
<div class="input-group">
<input type="text" id="taskInput" placeholder="What needs doing?">
<button class="add-btn" id="addBtn">
<i data-feather="plus"></i>
</button>
</div>
<ul class="task-list" id="taskList">
<!-- Tasks will be injected here -->
</ul>
</div>
`;
this.inputEl = this.shadowRoot.getElementById('taskInput');
this.listEl = this.shadowRoot.getElementById('taskList');
this.addBtn = this.shadowRoot.getElementById('addBtn');
this.addBtn.addEventListener('click', () => this.addTask());
this.inputEl.addEventListener('keypress', (e) => {
if (e.key === 'Enter') this.addTask();
});
this.renderTasks();
feather.replace();
}
renderTasks() {
this.listEl.innerHTML = '';
if (this.tasks.length === 0) {
this.listEl.innerHTML = '<li class="empty-state">All clear. Flow unblocked.</li>';
return;
}
this.tasks.forEach((task, index) => {
const li = document.createElement('li');
li.className = `task-item ${task.completed ? 'completed' : ''}`;
li.innerHTML = `
<div class="checkbox" onclick="this.getRootNode().host.toggleTask(${index})">
${task.completed ? '<i data-feather="check" color="white" width="14" height="14"></i>' : ''}
</div>
<span class="task-text">${this.escapeHtml(task.text)}</span>
<button class="delete-btn" onclick="this.getRootNode().host.deleteTask(${index})">
<i data-feather="trash-2" width="18" height="18"></i>
</button>
`;
this.listEl.appendChild(li);
});
feather.replace();
this.save();
}
addTask() {
const text = this.inputEl.value.trim();
if (text) {
this.tasks.unshift({ text, completed: false });
this.inputEl.value = '';
this.renderTasks();
}
}
toggleTask(index) {
this.tasks[index].completed = !this.tasks[index].completed;
this.renderTasks();
}
deleteTask(index) {
this.tasks.splice(index, 1);
this.renderTasks();
}
save() {
localStorage.setItem('flowstate_tasks', JSON.stringify(this.tasks));
}
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
}
customElements.define('tool-tasks', ToolTasks);