From 8b5121c69dae6cb707a626c2a5953fb280fad87c Mon Sep 17 00:00:00 2001 From: Felix Martin Date: Sun, 31 May 2026 22:09:38 -0400 Subject: [PATCH] Show today's tasks as seed chips on the planning screen The planning view renders Marvin's today list as clickable chips; clicking one fills the intent field, feeding the existing coach flow. idle/error/empty render nothing, pending shows a quiet loading line. Co-Authored-By: Claude Opus 4.8 --- internal/web/static/app.css | 9 +++++++++ internal/web/static/app.js | 24 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/internal/web/static/app.css b/internal/web/static/app.css index 0fefa1d..e7d9c95 100644 --- a/internal/web/static/app.css +++ b/internal/web/static/app.css @@ -107,3 +107,12 @@ input:focus { outline: 0; border-color: var(--accent); } font-variant-numeric: tabular-nums; color: var(--ink-dim); } .summary-row span:last-child { color: var(--ink); font-family: ui-monospace, monospace; } + +/* Planning: today's tasks as clickable seed chips */ +.tasklist { display: flex; flex-wrap: wrap; gap: 8px; } +.task-chip { + padding: 6px 10px; border: 1px solid var(--line); border-radius: 999px; + background: var(--bg); color: var(--ink); font: inherit; font-size: 13px; + cursor: pointer; text-align: left; +} +.task-chip:hover { border-color: var(--accent); color: var(--accent); } diff --git a/internal/web/static/app.js b/internal/web/static/app.js index b766f3e..001df47 100644 --- a/internal/web/static/app.js +++ b/internal/web/static/app.js @@ -136,11 +136,33 @@ function updatePlanningCoach(coach) { } } +// updatePlanningTasks renders today's Marvin tasks as clickable seed chips. +// Clicking a chip fills the intent field; the coach pipeline is unchanged. +// idle/error/empty render nothing; pending shows a quiet loading line. +function updatePlanningTasks(tasks) { + const el = document.getElementById('tasksBand'); + if (!el) return; + if (!tasks || tasks.status === 'idle' || tasks.status === 'error') { el.innerHTML = ''; return; } + if (tasks.status === 'pending') { el.innerHTML = `
loading tasks…
`; return; } + const list = tasks.tasks || []; + if (!list.length) { el.innerHTML = ''; return; } + const chips = list.map((t, i) => + ``).join(''); + el.innerHTML = `
${chips}
`; + el.querySelectorAll('.task-chip').forEach(btn => { + btn.onclick = () => { + const intent = document.getElementById('intent'); + if (intent) { intent.value = list[+btn.dataset.i].title; intent.focus(); } + }; + }); +} + function render(state) { setStateAttr(state); const rs = state.runtime_state; if (rs === 'planning' && renderedState === 'planning') { updatePlanningCoach(state.coach); + updatePlanningTasks(state.tasks); return; } if (rs === 'active' && renderedState === 'active') { @@ -166,6 +188,7 @@ function render(state) {
+
@@ -190,6 +213,7 @@ function render(state) { if (intent) post('/coach', { intent }); }; updatePlanningCoach(state.coach); + updatePlanningTasks(state.tasks); } else if (rs === 'active') { const c = state.commitment || {};