// Wrap-up ritual — fires when a burst finishes or a task is marked done.
// Forces the user to organize "open loops" (random ideas, follow-ups,
// new thoughts) into their proper homes BEFORE moving on.
//
// Routes per loop:
// • Zip → Brain zips (kind=dump)
// • Idea → Brain zips (kind=idea)
// • →P → sub-task on a chosen priority
// • Discard
//
// Plus a win to log + checkbox to mark the source task as done.
function Wrap() {
const { state, addLoop, routeLoop, setWrapWin, completeWrap, dismissWrap } = useStore();
const w = state.wrap;
if (!w.active) return null;
const [draft, setDraft] = React.useState('');
const [markDone, setMarkDone] = React.useState(true);
const inputRef = React.useRef(null);
React.useEffect(() => {
// focus the loop input on open
setTimeout(() => inputRef.current?.focus(), 250);
}, []);
const submitLoop = (e) => {
e?.preventDefault();
if (!draft.trim()) return;
addLoop(draft.trim());
setDraft('');
inputRef.current?.focus();
};
const stats = {
total: w.loops.length,
routed: w.loops.filter((l) => l.routed).length,
pending: w.loops.filter((l) => !l.routed).length,
};
const close = () => {
completeWrap({ markTaskDone: markDone });
};
return (
{ if (e.target === e.currentTarget) dismissWrap(); }}>
Burst complete · close the loops
Nice. You worked on "{w.taskLabel}".
60 seconds to sort what came up, before you walk off.
{/* Task-done toggle, only if there's a task */}
{w.taskId && (
)}
{/* Loops capture */}
Open loops
Random stuff that came up. Type, ↵, then route each one.
{w.loops.map((l) => (
!p.done)}
onRoute={(dest) => routeLoop(l.id, dest)}/>
))}
{w.loops.length === 0 && (
Nothing came up? Skip ahead.
)}
{/* Win to log */}
{stats.routed}/{stats.total} loops sorted
{stats.pending > 0 && (
· {stats.pending} still pending
)}
wrap streak {state.wrapStreak || 0}
);
}
function LoopRow({ loop, priorities, onRoute }) {
const [menuOpen, setMenuOpen] = React.useState(false);
const btnRef = React.useRef(null);
if (loop.routed) {
const lbl = loop.routed === 'zip' ? 'in zips'
: loop.routed === 'idea' ? 'in ideas'
: loop.routed === 'discard' ? 'discarded'
: loop.routed.startsWith('priority:')
? `→ ${priorities.find((p) => p.id === loop.routed.slice(9))?.title?.slice(0, 28) || 'priority'}…`
: loop.routed;
return (
{loop.text}
{lbl}
);
}
return (
{loop.text}
{menuOpen && (
setMenuOpen(false)}>
Add as sub-task to…
{priorities.map((p, i) => (
))}
{priorities.length === 0 && (
No active priorities
)}
)}
);
}
Object.assign(window, { Wrap });