Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions docs/ui.md
Original file line number Diff line number Diff line change
Expand Up @@ -307,8 +307,11 @@ while the board enumerates tasks and was the only surface that showed it, with
the group header falling back to printing the raw project UUID at the user.

Swimlanes by agent (`task.cli`) are dividers INSIDE a column, sticky while
the column scrolls, shown only when more than one agent has live tasks; the
same rule hides project sub-headers on single-project groups. The Archived
the column scrolls, shown only when more than one agent has live tasks; a
task whose project left the profile keeps no lane alive either, since lanes
count visible cards. Project sub-headers are ALWAYS on (GH #318 feedback: on
a one-project board nothing else names the project, and the header is how a
card from another project reads at a glance when one appears). The Archived
column is agent-agnostic, read-only apart from being the drop-to-archive
target, and links to History in its footer.

Expand All @@ -334,12 +337,17 @@ then; each card subscribes to its own `selectTaskTabs` slice for its badge.
`selectorFanout.test.ts` pins all three counts.

Drags mean something or they do not happen. Hand-rolled pointer events, the
sidebar's pattern; no dnd-kit. Exactly two drags are wired: reorder within a
same-project group inside one cell (settle, one store write, `task_reorder`,
whose Rust contract is same-project ids) and drop on the Archived column
(shared `confirmAndArchive`, so the confirm dialog, delete-branch checkbox,
open-PR warning and spinner come with it). Every other drop snaps back with
no write. Restore stays in History; the Archived column links there.
sidebar's pattern; no dnd-kit. Four drags are wired: reorder within a
same-project group inside one cell (`task_reorder`, whose Rust contract is
same-project ids), drop on the Archived column (shared `confirmAndArchive`,
so the confirm dialog, delete-branch checkbox, open-PR warning and spinner
come with it), drop on Settled (`clearTaskWorkState`, the focus-clear write
on every terminal tab) and drop on In review (CreatePrDialog, the same entry
the command palette uses). Every other drop snaps back with no write, and so
does a wired one that would be a no-op: nothing to clear, or a main
checkout. The matrix lives in `boardDropCommand()` in
[src/lib/taskBoardState.ts](../src/lib/taskBoardState.ts). Restore stays in
History; the Archived column links there.

The Archived column renders a CAPPED slice, not the whole archive: the most
recent entries first (the same `archived_at ?? created` sort History uses),
Expand Down
15 changes: 13 additions & 2 deletions src/components/settings/TasksSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ export function TasksSection() {
const setBoardArchiveLimitMode = usePrefs(s => s.setBoardArchiveLimitMode);
const boardArchiveLimit = usePrefs(s => s.boardArchiveLimit);
const setBoardArchiveLimit = usePrefs(s => s.setBoardArchiveLimit);
// The custom limit input keeps its own draft: Number("") is 0, so writing
// the parse straight through turned "clear the field to retype" into
// "store 0" — the Archived column emptied mid-edit and a stuck "0" went
// back into the field. The store only ever sees a parseable number; the
// draft dies on blur and the stored value shows again.
const [limitDraft, setLimitDraft] = useState<string | null>(null);

const hydrated = useRef(false);
useEffect(() => {
Expand Down Expand Up @@ -425,8 +431,13 @@ export function TasksSection() {
{boardArchiveLimitMode === "custom" && (
<Input
type="number"
value={boardArchiveLimit}
onChange={(e) => setBoardArchiveLimit(Number(e.target.value))}
value={limitDraft ?? String(boardArchiveLimit)}
onChange={(e) => {
setLimitDraft(e.target.value);
const n = Number(e.target.value);
if (e.target.value.trim() !== "" && Number.isFinite(n)) setBoardArchiveLimit(n);
}}
onBlur={() => setLimitDraft(null)}
className="w-24 font-mono"
data-testid="board-archive-limit-input"
/>
Expand Down
Loading
Loading