/* Goals and tasks are stored inside the existing synced Life OS system record. */ (() => { const t = (key, values) => LifeI18n.t(`tasks.${key}`, values); let filter = 'all'; const esc = value => String(value || '').replace(/[&<>"']/g, char => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[char])); const board = () => { const state = sys(); if (!state.tasksBoard || !Array.isArray(state.tasksBoard.goals) || !Array.isArray(state.tasksBoard.tasks)) state.tasksBoard = { goals: [], tasks: [] }; return state.tasksBoard; }; const persist = data => { const state = sys(); state.tasksBoard = data; save('lifeos_system', state); renderAll(); }; const dateLabel = value => value ? new Intl.DateTimeFormat(LifeI18n.locale(), { month: 'short', day: 'numeric', year: 'numeric' }).format(new Date(`${value}T12:00:00`)) : t('noDate'); const isOverdue = task => !task.done && task.dueDate && task.dueDate < day(); function renderTasks(container) { const data = board(), open = data.tasks.filter(task => !task.done).length; const todayTasks = data.tasks.filter(task => !task.done && task.dueDate === day()).length; const activeGoals = data.goals.filter(goal => !goal.done), finishedGoals = data.goals.filter(goal => goal.done); const visible = data.tasks.filter(task => { if (filter === 'today') return !task.done && (!task.dueDate || task.dueDate <= day()); if (filter === 'upcoming') return !task.done && task.dueDate > day(); if (filter === 'done') return task.done; return !task.done; }).sort((a, b) => Number(a.done) - Number(b.done) || (a.dueDate || '9999').localeCompare(b.dueDate || '9999') || b.createdAt.localeCompare(a.createdAt)); const goalOptions = data.goals.filter(goal => !goal.done).map(goal => ``).join(''); container.innerHTML = `
${t('eyebrow')}

${t('title')}

${t('subtitle')}

${t('todayHeading')}
${todayTasks}${t('openCount', { count: open })}
${todayTasks ? '↗' : '✓'}
${visible.length ? visible.map(task => { const goal = data.goals.find(item => item.id === task.goalId); return `
${esc(task.title)}
${t(`priority.${task.priority}`)}${goal ? `${esc(goal.title)}` : ''}${isOverdue(task) ? `${t('overdue')} · ` : ''}${task.dueDate ? t('due', { date: dateLabel(task.dueDate) }) : t('noDate')}
`; }).join('') : `
○

${t('noTasks')}

`}
${t('activeGoals')}
${activeGoals.length}
${activeGoals.length ? `
${activeGoals.map(goal => { const linked = data.tasks.filter(task => task.goalId === goal.id), completed = linked.filter(task => task.done).length, progress = linked.length ? Math.round(completed / linked.length * 100) : 0; return `
◎
${goal.targetDate ? `` : ''}

${esc(goal.title)}

${goal.description ? `

${esc(goal.description)}

` : ''}
${t('progress', { done: completed, total: linked.length })}${progress}%
${goal.targetDate ? `
${t('target')} · ${dateLabel(goal.targetDate)}
` : ''}
`; }).join('')}
` : `
${t('noGoals')}
`}${finishedGoals.length ? `
${t('finishedGoals')} · ${finishedGoals.length}
${finishedGoals.map(goal => `
✓

${esc(goal.title)}

`).join('')}
` : ''}
`; } window.tasksApp = renderTasks; window.setTaskFilter = value => { filter = value; renderAll(); }; window.addTask = event => { event.preventDefault(); const form = event.currentTarget, input = form.querySelector('#task-new-title'), title = input.value.trim(); if (!title) return; window.LifeOSActions.createTask({ title, dueDate: form.querySelector('#task-new-date').value || '', goalId: form.querySelector('#task-new-goal').value || '', priority: form.querySelector('#task-new-priority').value || 'normal' }); toast('tasks.taskAdded'); }; window.toggleTask = id => { const task = board().tasks.find(item => item.id === id); if (task) window.LifeOSActions.setTaskDone(id, !task.done); }; window.deleteTask = id => { window.LifeOSActions.deleteTask(id); toast('tasks.taskRemoved'); }; window.openTaskEdit = id => { const task = board().tasks.find(item => item.id === id); if (!task) return; const goals = board().goals.map(goal => ``).join(''); document.querySelector('#modal').innerHTML = ``; setTimeout(() => document.querySelector('#task-edit-title-input')?.focus(), 0); }; window.saveTaskEdit = event => { event.preventDefault(); const form = event.currentTarget, id = form.dataset.id; window.LifeOSActions.updateTask(id, { title: form.querySelector('#task-edit-title-input').value, dueDate: form.querySelector('#task-edit-date').value, goalId: form.querySelector('#task-edit-goal').value, priority: form.querySelector('#task-edit-priority').value }); closeModal(); toast('tasks.taskUpdated'); }; window.openGoalForm = (id = '') => { const goal = id ? board().goals.find(item => item.id === id) : null; if (id && !goal) return; document.querySelector('#modal').innerHTML = ``; setTimeout(() => document.querySelector('#goal-new-title')?.focus(), 0); }; window.saveGoal = event => { event.preventDefault(); const form = event.currentTarget, title = form.querySelector('#goal-new-title').value.trim(); if (!title) return; const values = { title, description: form.querySelector('#goal-new-description').value, targetDate: form.querySelector('#goal-new-date').value, showOnDesktop: form.querySelector('#goal-new-desktop').checked }; if (form.dataset.id) window.LifeOSActions.updateGoal(form.dataset.id, values); else window.LifeOSActions.createGoal(values); closeModal(); toast(form.dataset.id ? 'tasks.goalUpdated' : 'tasks.goalCreated'); }; window.toggleGoalDesktop = id => { const data = board(), goal = data.goals.find(item => item.id === id); if (!goal || !goal.targetDate) return; goal.showOnDesktop = !goal.showOnDesktop; persist(data); }; window.renderDesktopGoalCountdowns = () => { const container = document.querySelector('#desktop-goal-countdowns'); if (!container) return; const goals = board().goals.filter(goal => !goal.done && goal.targetDate && goal.showOnDesktop); if (!goals.length) { container.innerHTML = ''; container.classList.add('hidden'); return; } container.classList.remove('hidden'); const now = new Date(), todayUtc = Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()); container.innerHTML = `
${t('countdown')}
${goals.map(goal => { const [year, month, date] = goal.targetDate.split('-').map(Number), delta = Math.round((Date.UTC(year, month - 1, date) - todayUtc) / 864e5), count = Math.abs(delta), label = delta === 0 ? t('todayTarget') : delta > 0 ? t('daysLeft', { count }) : t('daysOverdue', { count }); return ``; }).join('')}`; }; setInterval(window.renderDesktopGoalCountdowns, 60000); window.setGoalDone = (id, done) => { window.LifeOSActions.setGoalDone(id, done); }; window.deleteGoal = id => { document.querySelector('#modal').innerHTML = ``; }; window.confirmDeleteGoal = id => { window.LifeOSActions.deleteGoal(id); document.querySelector('#modal').innerHTML = ''; toast('tasks.goalRemoved'); }; const style = document.createElement('style'); style.textContent = `.task-head{display:flex;align-items:flex-start;justify-content:space-between;gap:14px}.task-head h1{max-width:430px}.task-head .secondary{white-space:nowrap}.task-focus{display:flex;align-items:center;justify-content:space-between;margin:18px 0;padding:18px 20px;background:linear-gradient(115deg,color-mix(in srgb,var(--accent) 15%,var(--panel2)),var(--panel2));border:1px solid var(--line);border-radius:13px}.task-focus .eyebrow{margin-bottom:5px}.task-focus strong{font-size:30px;letter-spacing:-.06em}.task-focus span{margin-left:10px;color:var(--muted);font-size:12px}.task-focus-mark{font-size:26px;color:var(--accent)}.task-quick{display:grid;grid-template-columns:minmax(130px,1fr) 145px 120px 95px auto;gap:7px}.task-quick input,.task-quick select,.goal-form input,.goal-form textarea,.goal-form select{min-width:0;border:1px solid var(--line);background:var(--panel2);border-radius:8px;padding:10px 11px;outline:none}.task-quick input:focus,.task-quick select:focus,.goal-form input:focus,.goal-form textarea:focus,.goal-form select:focus{border-color:var(--accent)}.task-quick select{font-size:12px}.task-quick .primary{padding:10px 11px;white-space:nowrap}.task-filters{display:flex;gap:7px;flex-wrap:wrap;margin:19px 0 8px}.task-filters .chip{padding:7px 12px}.task-list{display:grid;gap:7px}.task-row{display:flex;align-items:center;gap:12px;padding:12px;background:color-mix(in srgb,var(--panel2) 55%,transparent);border:1px solid var(--line);border-radius:10px}.task-check{flex:none;width:21px;height:21px;border:1px solid var(--muted);border-radius:7px;background:transparent;color:var(--accent);font-weight:700}.task-check[aria-pressed=true]{background:var(--accent);border-color:var(--accent);color:var(--bg)}.task-copy{min-width:0;flex:1}.task-copy>strong{display:block;font-size:13px;font-weight:550;overflow-wrap:anywhere}.task-meta{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-top:5px;color:var(--muted);font-size:10px}.task-meta span{display:inline-flex;align-items:center;gap:4px}.task-meta b,.is-overdue .task-meta{color:#df8a70}.task-priority{font:9px 'DM Mono';letter-spacing:.05em}.task-priority.high{color:#df8a70}.task-priority.low{color:var(--muted)}.is-done .task-copy>strong{text-decoration:line-through;color:var(--muted)}.task-edit{border:1px solid var(--line);border-radius:7px;background:transparent;color:var(--muted);font:10px 'DM Mono';padding:6px 8px;white-space:nowrap}.task-edit:hover,.task-edit:focus-visible{color:var(--accent);border-color:var(--accent)}.task-delete{width:28px;height:28px;border:0;border-radius:7px;background:transparent;color:var(--muted);font-size:19px;line-height:1}.task-delete:hover{background:var(--panel2);color:var(--ink)}.task-empty,.goal-empty{padding:21px;text-align:center;color:var(--muted);font-size:13px}.task-empty span{font-size:23px;color:var(--accent)}.goal-section{margin-top:24px;padding-top:16px;border-top:1px solid var(--line)}.goal-section-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}.goal-section-head .eyebrow{margin:0}.goal-count{font:11px 'DM Mono';color:var(--muted)}.goal-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:9px}.goal-card{min-width:0;padding:14px;border:1px solid var(--line);border-radius:11px;background:color-mix(in srgb,var(--panel2) 55%,transparent)}.goal-card-top{display:flex;justify-content:space-between;align-items:center}.goal-actions{display:flex;align-items:center;gap:4px;flex-wrap:wrap;justify-content:flex-end}.goal-desktop-toggle{border:1px solid var(--line);border-radius:7px;background:transparent;color:var(--muted);font:9px 'DM Mono';padding:6px 8px;white-space:nowrap}.goal-desktop-toggle.selected{color:var(--accent);border-color:var(--accent)}.goal-form label.goal-pin-option{display:flex!important;grid-template-columns:18px 1fr;align-items:center;gap:8px!important}.goal-form label.goal-pin-option input{width:16px;height:16px;min-width:16px;padding:0;accent-color:var(--accent)}.goal-finish{border:0;background:transparent;color:var(--muted);font:9px 'DM Mono';letter-spacing:.04em;padding:5px}.goal-finish:hover{color:var(--accent)}.finished-goals{margin-top:14px}.finished-goals summary{cursor:pointer;color:var(--muted);font:10px 'DM Mono';letter-spacing:.08em;margin-bottom:9px}.is-goal-done{opacity:.72}.goal-mark{font-size:20px;color:var(--accent)}.goal-card h3{margin:8px 0 4px;font-size:15px;letter-spacing:-.025em;overflow-wrap:anywhere}.goal-card p{margin:0 0 12px;color:var(--muted);font-size:11px;line-height:1.5}.goal-progress-line{display:flex;justify-content:space-between;gap:8px;margin-top:11px;font:9px 'DM Mono';color:var(--muted)}.goal-progress{height:4px;background:var(--line);border-radius:5px;margin-top:5px;overflow:hidden}.goal-progress i{display:block;height:100%;background:var(--accent);border-radius:5px}.goal-date{margin-top:10px;color:var(--muted);font-size:10px}.goal-form{display:grid;gap:13px}.goal-form h2{font-size:21px;margin-bottom:0}.goal-form label{display:grid;gap:6px;color:var(--muted);font-size:12px}.goal-form textarea{resize:vertical}.goal-form .actions{margin:0}.desktop-goal-countdowns{position:absolute;z-index:1;top:24px;right:28px;width:min(310px,30vw);max-height:72vh;overflow:auto;display:grid;gap:8px}.desktop-goal-countdowns.hidden{display:none}.desktop-goal-heading{color:var(--muted);font:10px 'DM Mono';letter-spacing:.15em;text-align:right}.desktop-goal-card{display:grid;gap:5px;width:100%;padding:13px 15px;text-align:left;background:color-mix(in srgb,var(--panel) 88%,transparent);border:1px solid var(--line);border-radius:12px;box-shadow:var(--shadow);backdrop-filter:blur(12px)}.desktop-goal-card span{font-size:13px;font-weight:600;overflow-wrap:anywhere}.desktop-goal-card strong{color:var(--accent);font:500 13px 'DM Mono'}.desktop-goal-card small{color:var(--muted);font-size:11px}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}@media(max-width:650px){.desktop-goal-countdowns{position:relative;top:auto;right:auto;width:auto;max-width:none;max-height:30vh;margin:12px 16px 92px;overflow:auto;grid-template-columns:repeat(2,minmax(0,1fr));align-items:start}.desktop-goal-heading{grid-column:1/-1;text-align:left}.desktop-icons{padding:76px 14px 18px;display:flex;flex-wrap:wrap;justify-content:flex-start;gap:16px 4px}.app-icon{flex:0 0 calc(25% - 4px);min-width:0;justify-items:center;font-size:10px;letter-spacing:.04em}.desktop-goal-card{padding:11px 12px}.desktop-goal-card span{font-size:12px}}@media(min-width:651px){.desktop-icons{padding:72px 32px;display:grid;grid-template-columns:96px;justify-items:start;align-content:start;gap:14px}.app-icon{width:96px;justify-items:center;font-size:11px}.desktop-goal-countdowns{top:26px;right:30px;width:min(330px,28vw);max-height:calc(100vh - 110px)}}@media(max-width:700px){.task-quick{grid-template-columns:1fr 1fr}.task-quick input:first-of-type{grid-column:1/-1}.task-quick .primary{grid-column:1/-1}.task-head{align-items:flex-start}.task-head .secondary{font-size:9px;padding:9px 10px}.goal-grid{grid-template-columns:1fr}.task-meta{gap:6px}.task-focus{padding:14px}.task-focus span{display:block;margin:3px 0 0}}`; document.head.append(style); })();