// The three islands: ListsIsland, TasksIsland, DetailsIsland const { useState, useEffect, useRef, useMemo } = React; // ---------- Helpers ---------- const fmtDate = (iso) => { if (!iso) return null; const d = new Date(iso); const now = new Date(); const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); const target = new Date(d.getFullYear(), d.getMonth(), d.getDate()); const diff = Math.round((target - today) / 86400000); if (diff === 0) return 'Today'; if (diff === 1) return 'Tomorrow'; if (diff === -1) return 'Yesterday'; if (diff < 0) return `${Math.abs(diff)}d overdue`; if (diff < 7) return d.toLocaleDateString(undefined, { weekday: 'short' }); return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' }); }; const isToday = (iso) => { if (!iso) return false; const d = new Date(iso); const n = new Date(); return d.getFullYear() === n.getFullYear() && d.getMonth() === n.getMonth() && d.getDate() === n.getDate(); }; const isOverdue = (iso) => { if (!iso) return false; const d = new Date(iso); const n = new Date(); return d < new Date(n.getFullYear(), n.getMonth(), n.getDate()); }; const STATUS_LABEL = { idle: 'Idle', queued: 'Queued', running: 'Running', review: 'Review', done: 'Done', error: 'Error', }; const relTime = (iso) => { if (!iso) return ''; const diff = Math.max(0, Date.now() - new Date(iso).getTime()); const s = Math.floor(diff / 1000); if (s < 60) return s + 's ago'; const m = Math.floor(s / 60); if (m < 60) return m + 'm ago'; const h = Math.floor(m / 60); if (h < 24) return h + 'h ago'; return Math.floor(h / 24) + 'd ago'; }; const logTime = (iso) => { const d = new Date(iso); return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' }); }; // ---------- Checkbox ---------- const Checkbox = ({ done, onToggle, size }) => (
{ e.stopPropagation(); onToggle(); }} role="checkbox" aria-checked={done} >
); // ---------- Lists Island ---------- const ListsIsland = ({ activeList, setActiveList, counts, search, setSearch }) => { return (
Navigator

Lists

setSearch(e.target.value)} /> ⌘K
Smart lists
{SEED_LISTS.map((l) => (
setActiveList(l.id)} >
{l.name}
{counts[l.id] ?? ''}
))}
My lists
{SEED_USER_LISTS.map((l) => (
setActiveList(l.id)} >
{l.name}
{counts[l.id] ?? ''}
))}
AK
Aoife Kelly
rider.island / local
); }; // ---------- Tasks Island ---------- const TaskRow = ({ task, selected, onSelect, onToggle, onStar, leaving, entering }) => { const [starPulse, setStarPulse] = useState(false); const handleStar = (e) => { e.stopPropagation(); setStarPulse(true); setTimeout(() => setStarPulse(false), 400); onStar(); }; const list = SEED_USER_LISTS.find((l) => l.id === task.list); const overdue = isOverdue(task.due) && !task.done; const today = isToday(task.due); return (
{task.title}
{task.agent && ( {STATUS_LABEL[task.agent.status]} )} {list && ( {list.name} )} {task.agent?.branch && ( {task.agent.branch.replace('agent/', '')} )} {task.agent?.diff && task.agent.diff.files > 0 && ( +{task.agent.diff.additions} −{task.agent.diff.deletions} )} {task.due && !task.agent && ( {fmtDate(task.due)} )} {task.subtasks && task.subtasks.length > 0 && ( {task.subtasks.filter((s) => s.done).length}/{task.subtasks.length} steps )} {task.tags && task.tags.map((t) => {t})}
{task.agent && task.agent.status === 'running' && task.agent.log && task.agent.log.length > 0 && (() => { const last = task.agent.log[task.agent.log.length - 1]; return (
{last.m}
); })()}
); }; const TasksIsland = ({ tasks, selectedId, setSelected, onToggle, onStar, onAdd, leavingIds, enteringIds, activeList, showCompleted, setShowCompleted, }) => { const [newTitle, setNewTitle] = useState(''); const inputRef = useRef(null); const now = new Date(); const dateLine = now.toLocaleDateString(undefined, { weekday: 'long', month: 'long', day: 'numeric' }); const activeTasks = tasks.filter((t) => !t.done); const doneTasks = tasks.filter((t) => t.done); const overdueTasks = activeTasks.filter((t) => isOverdue(t.due)); const todayTasks = activeTasks.filter((t) => !isOverdue(t.due)); const listMeta = SEED_LISTS.find((l) => l.id === activeList) || SEED_USER_LISTS.find((l) => l.id === activeList); const title = activeList === 'myday' ? 'My Day' : (listMeta?.name || 'Tasks'); const eyebrow = activeList === 'myday' ? dateLine : `${activeTasks.length} open · ${doneTasks.length} done`; const handleSubmit = (e) => { e.preventDefault(); if (newTitle.trim()) { onAdd(newTitle.trim()); setNewTitle(''); } }; return (
{activeList === 'myday' ? 'My Day' : 'List'}

{title}

{activeList === 'myday' ? dateLine : eyebrow} · {activeTasks.length} open
+
setNewTitle(e.target.value)} /> ENTER
{overdueTasks.length > 0 && ( <>
Overdue
{overdueTasks.map((t) => ( setSelected(t.id)} onToggle={() => onToggle(t.id)} onStar={() => onStar(t.id)} leaving={leavingIds.includes(t.id)} entering={enteringIds.includes(t.id)} /> ))} )} {todayTasks.length > 0 && ( <> {overdueTasks.length > 0 &&
Tasks
} {todayTasks.map((t) => ( setSelected(t.id)} onToggle={() => onToggle(t.id)} onStar={() => onStar(t.id)} leaving={leavingIds.includes(t.id)} entering={enteringIds.includes(t.id)} /> ))} )} {activeTasks.length === 0 && (
All clear
The harbor is calm. Add a task above.
)} {showCompleted && doneTasks.length > 0 && ( <>
Completed · {doneTasks.length}
{doneTasks.map((t) => ( setSelected(t.id)} onToggle={() => onToggle(t.id)} onStar={() => onStar(t.id)} leaving={leavingIds.includes(t.id)} entering={enteringIds.includes(t.id)} /> ))} )}
); }; // ---------- Worktree + Terminal sub-components ---------- const WorktreeCard = ({ agent, onOpenDiff, onOpenWorktree }) => { if (!agent) return null; return (
Worktree {agent.worktree}
Branch {agent.branch} ← {agent.baseBranch}
Diff {agent.diff.files > 0 ? ( {agent.diff.files} files +{agent.diff.additions} −{agent.diff.deletions} {Array.from({ length: 5 }).map((_, i) => { const total = agent.diff.additions + agent.diff.deletions || 1; const addShare = Math.round((agent.diff.additions / total) * 5); return ; })} ) : No changes yet}
{agent.commits > 0 && (
Commits {agent.commits} on branch
)}
); }; const SessionTerminal = ({ agent, onInput }) => { const bodyRef = useRef(null); const [draft, setDraft] = useState(''); useEffect(() => { if (bodyRef.current) bodyRef.current.scrollTop = bodyRef.current.scrollHeight; }, [agent?.log?.length]); if (!agent) return null; const running = agent.status === 'running'; const statusLabel = running ? 'LIVE' : STATUS_LABEL[agent.status]; return (
claude-session · {agent.branch} {running ? LIVE : {statusLabel}}
{(agent.log || []).map((l, i) => (
{logTime(l.t)} {l.k === 'msg' ? 'claude' : l.k === 'tool' ? 'tool' : l.k === 'sys' ? 'sys' : l.k === 'stdout' ? 'out' : l.k === 'stderr' ? 'err' : l.k} {l.m}
))} {running && (
{logTime(new Date().toISOString())} claude
)}
setDraft(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter' && draft.trim()) { onInput(draft); setDraft(''); } }} disabled={!running} style={{ flex: 1, fontFamily: 'var(--mono)', fontSize: 11, color: 'var(--text)' }} />
); }; // ---------- Details Island ---------- const DetailsIsland = ({ task, onUpdate, onDelete, onToggle, onStar, onAgentAction, onOpenDiff, onOpenWorktree, onAgentInput }) => { if (!task) { return (
No task selected
Pick a task from the middle
to see its details here.
); } const list = SEED_USER_LISTS.find((l) => l.id === task.list); const created = task.created ? new Date(task.created).toLocaleDateString(undefined, { month: 'short', day: 'numeric' }) : '—'; const due = task.due ? new Date(task.due).toLocaleDateString(undefined, { weekday: 'short', month: 'short', day: 'numeric' }) : 'None'; const toggleSub = (sid) => { const next = task.subtasks.map((s) => s.id === sid ? { ...s, done: !s.done } : s); onUpdate({ ...task, subtasks: next }); }; const addSub = (title) => { if (!title.trim()) return; const next = [...(task.subtasks || []), { id: 's' + Date.now(), title: title.trim(), done: false }]; onUpdate({ ...task, subtasks: next }); }; const [subDraft, setSubDraft] = useState(''); return (
Logbook #{task.id}

{task.agent ? 'Agent task' : 'Task details'}

{task.agent && (
{STATUS_LABEL[task.agent.status]}
{task.agent.model} · {task.agent.turns} turns · {(task.agent.tokens / 1000).toFixed(1)}k tok {task.agent.startedAt && <>·{relTime(task.agent.startedAt)}}
{task.agent.status === 'running' ? ( ) : task.agent.status === 'idle' || task.agent.status === 'error' || task.agent.status === 'queued' ? ( ) : null}
)}
onToggle(task.id)} />