feat(terminal): add guided goals, tasks, and confirmed deletion

This commit is contained in:
Codex committed 2026-09-28 14:13:21 +08:00
1 parent 8ffeb2897f
commit 78c1ffe1fd
7 files changed
+175 -10

No files matched your search

+3 -3
View File
@@ -46,7 +46,7 @@
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 => { const data = board(); data.tasks = data.tasks.filter(item => item.id !== id); persist(data); toast('tasks.taskRemoved'); };
window.deleteTask = id => { window.LifeOSActions.deleteTask(id); toast('tasks.taskRemoved'); };
window.openGoalForm = () => {
document.querySelector('#modal').innerHTML = `<div class="modal" onclick="if(event.target===this) closeModal()"><form class="modalbox goal-form" onsubmit="saveGoal(event)" role="dialog" aria-modal="true" aria-labelledby="goal-form-title"><button type="button" class="modal-close" onclick="closeModal()" aria-label="${LifeI18n.t('common.close')}">×</button><div class="eyebrow">${t('eyebrow')}</div><h2 id="goal-form-title">${t('goalTitle')}</h2><label>${t('goalPlaceholder')}<input id="goal-new-title" maxlength="100" required autofocus></label><label>${t('goalDescription')}<textarea id="goal-new-description" maxlength="240" rows="3"></textarea></label><label>${t('target')}<input id="goal-new-date" type="date" oninput="document.querySelector('#goal-new-desktop').disabled=!this.value"></label><label class="goal-pin-option"><input id="goal-new-desktop" type="checkbox" disabled>${t('pinOnCreate')}</label><div class="actions"><button class="primary" type="submit">${t('save')}</button><button class="secondary" type="button" onclick="closeModal()">${t('cancel')}</button></div></form></div>`;
setTimeout(() => document.querySelector('#goal-new-title')?.focus(), 0);
@@ -59,11 +59,11 @@
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 = `<div class="desktop-goal-heading">${t('countdown')}</div>${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 `<button class="desktop-goal-card" onclick="openApp('tasks')"><span>${esc(goal.title)}</span><strong>${label}</strong><small>${dateLabel(goal.targetDate)}</small></button>`; }).join('')}`; };
setInterval(window.renderDesktopGoalCountdowns, 60000);
window.setGoalDone = (id, done) => { const data = board(), goal = data.goals.find(item => item.id === id); if (!goal) return; goal.done = done; goal.completedAt = done ? new Date().toISOString() : null; persist(data); };
window.setGoalDone = (id, done) => { window.LifeOSActions.setGoalDone(id, done); };
window.deleteGoal = id => {
document.querySelector('#modal').innerHTML = `<div class="modal" onclick="if(event.target===this) closeModal()"><div class="modalbox" role="dialog" aria-modal="true"><button class="modal-close" onclick="closeModal()" aria-label="${LifeI18n.t('common.close')}">×</button><div class="eyebrow">${t('eyebrow')}</div><h2>${t('confirmGoalDelete')}</h2><p class="sub">${t('confirmGoalDeleteBody')}</p><div class="actions"><button class="danger" onclick="confirmDeleteGoal('${esc(id)}')">${t('delete')}</button><button class="secondary" onclick="closeModal()">${t('cancel')}</button></div></div></div>`;
};
window.confirmDeleteGoal = id => { const data = board(); data.goals = data.goals.filter(goal => goal.id !== id); data.tasks.forEach(task => { if (task.goalId === id) task.goalId = ''; }); document.querySelector('#modal').innerHTML = ''; persist(data); toast('tasks.goalRemoved'); };
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{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{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-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:gr Line truncated