feat: "show events that haven't started" governs both views

It was the board's alone, sitting between two app-wide filters and
carrying a line of small print to explain why it was different. It is the
same events answering the same question in either view, so it is now one
switch: the board plots them, and the checklist keeps its "Not started
yet" section.

That means the section is off by default, which is a visible change for
every existing reader — deliberate, and the same argument the board makes.
This app answers what expires next; on fourteen lanes the queued patches
are more rows than the thing they came for.

`timelineUpcoming` becomes `showUpcoming`, because the old name would now
be false, and `adoptRenamed` carries a stored answer across on load.
Nothing would be lost by dropping it — `prefs` is one blob under one key,
not a key space — but a reader who had switched the future on would find
it off with no explanation, and they should not have to say a thing
twice. A stored new name always wins, so it cannot overwrite a fresher
answer with a stale one.

Gating the section alone would have opened a hole: nothing running and
everything held back rendered an empty column, because the "nothing to
show" line keyed off there being no rows at all rather than none listed.
It now counts what is held back and names the switch, as the board does.

The split pills stay the board's: the checklist gives these a section
with a heading either way, so there is nothing there to mix them into.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lucas WintherandClaude Opus 5 committed 2026-08-19 06:20:14 +02:00
1 parent d3066aefdc
commit 2cb4dd214c
6 files changed
+145 -53

No files matched your search

+13 -16
View File
@@ -34,7 +34,7 @@ const SPLITS: Array<{ split: boolean; label: string; hint: string }> = [
{
split: true,
label: "In their own group",
hint: "Each lane runs out, then a \u201cNot started yet\u201d heading and what is queued behind it.",
hint: "Each lane runs out, then a \u201cNot started yet\u201d heading and what is queued behind it \u2014 the shape the checklist has either way.",
},
{
split: false,
@@ -156,28 +156,24 @@ export function Controls({
Show events I've finished
</label>
{/* Sits with the other two "what am I allowed to look at" rows
because that is the question it answers — but unlike them it
is answered for the board only, and a row that did not say so
would read as a promise about the whole app. The checklist
keeps its own "Not started yet" section either way, which is
why switching this off costs a reader nothing they cannot
still go and read. */}
{/* One of the three "what am I allowed to look at" rows, and it
reaches both views: the checklist's "Not started yet" section
and the board's future bars are the same events answering the
same question. Off is the default because this app answers
*what expires next* — see PRD F1. */}
<label className="flex cursor-pointer select-none items-start gap-2 text-xs text-muted">
<input
type="checkbox"
checked={prefs.timelineUpcoming}
onChange={(e) =>
onUpdate({ timelineUpcoming: e.target.checked })
}
checked={prefs.showUpcoming}
onChange={(e) => onUpdate({ showUpcoming: e.target.checked })}
className="mt-px size-4 accent-[var(--color-near)]"
/>
<span>
Show events that haven't started
<span className="mt-0.5 block max-w-xs leading-relaxed text-faint">
On the timeline, which draws its span from what it plots —
so this stretches the board weeks past today. The checklist
lists them under "Not started yet" regardless.
Adds the checklist's "Not started yet" section, and plots
them on the timeline — which draws its span from what it
plots, so the board stretches weeks past today.
</span>
</span>
</label>
@@ -187,7 +183,7 @@ export function Controls({
are on it, and offering it anyway is a control that does
nothing — the stored answer is kept either way, so switching
the row above back on restores it rather than a default. */}
{prefs.timelineUpcoming && (
{prefs.showUpcoming && (
<div className="ml-6 flex flex-col gap-1.5">
<div className="flex gap-1.5">
{SPLITS.map((s) => (
@@ -209,6 +205,7 @@ export function Controls({
))}
</div>
<p className="max-w-xs text-xs leading-relaxed text-faint">
On the timeline.{" "}
{SPLITS.find((s) => s.split === prefs.timelineSplitUpcoming)
?.hint}
</p>
+5 -4
View File
@@ -113,10 +113,11 @@ export function Timeline({
/**
* Whether events that have not started yet are plotted.
*
* Off by default (`prefs.timelineUpcoming`). The board is asked "how does the
* time I am in lay out?", and every lane has a next patch queued behind it —
* plotting those unasked stretches the window weeks past today and squeezes
* the running bars the reader came for.
* Off by default (`prefs.showUpcoming`, which governs the checklist's own
* "Not started yet" section too). The board is asked "how does the time I am
* in lay out?", and every lane has a next patch queued behind it — plotting
* those unasked stretches the window weeks past today and squeezes the
* running bars the reader came for.
*
* Read-only here: the switch lives in settings with the other two answers to
* "what am I allowed to look at" (`Controls`), not in the board's own header