Fix #45: sidebar label picker + list labels #57

Merged
crueber merged 1 commit from fix/issue-45 into main 2026-09-04 21:23:20 +00:00
Owner

Root cause: UI-only gap. The PATCH …/issues/{num} labels key (triage-gated, one labels_changed event per toggle) and the label CRUD behind the Labels page both already worked — verified directly against the API. What was missing was the 08 §2 LabelPicker contract: specified but unbuilt. The issue sidebar LABELS card was static text ('label / milestone edits live on their pages (triage)') with no apply path, so a created label could never be attached. The issue list already rendered applied names from the index cards (02 §2 projection) — it just lacked colors.

What this builds (no new deps, no Go changes, no doc amendment — implements 02 §11 + 08 §2 as specified):

  • web/src/components/LabelPicker.jsx (new): LabelPicker — inline toggle rows over the page-owned labels:{o}/{r} cache, native <button>s with aria-pressed (keyboard free, nothing to trap/dismiss), triage+ gated via roleAtLeast (server authoritative, 403s surface in the tray); LabelChip — color dot + name, deleted labels render as the bare string (02 §3.1).
  • web/src/lib/labels.js (new): headless toggleLabel (sorted-unique, case-insensitive remove) + labelColorMap/labelColor.
  • Issue.jsx sidebar: picker for triage+, one PATCH with the full next set per toggle, optimistic patchCached paint + guarded invalidate reconcile (the reaction-chip bump pattern); per-label busy set stops double-fire (repeat PATCH would be idempotent anyway).
  • Issues.jsx cards: chips now carry their colors via the shared cached labels set — no list-endpoint change (cards already carried names).
  • web/test/unit/labels.test.js (new): toggle round-trip, case-insensitive remove, color fallback.

Verification:

  • node --test web/test/unit/*.test.js: 242 pass, 0 fail (4 new).
  • go vet ./... clean.
  • Real-browser drive (hub headless Chrome over CDP against a scratch server on this branch): created label bug (#d73a4a) + issue #1 → picker listed it → click applied (sidebar chip + pressed row, server thread labels:["bug"], empty error tray) → list card shows BUG chip with the red dot; both dark and light themes; zero console errors. Screenshots checked for both themes.
  • Dark + light share the chip classes (dot carries color, text stays theme foreground).
Root cause: UI-only gap. The `PATCH …/issues/{num}` `labels` key (triage-gated, one `labels_changed` event per toggle) and the label CRUD behind the Labels page both already worked — verified directly against the API. What was missing was the 08 §2 `LabelPicker` contract: specified but unbuilt. The issue sidebar LABELS card was static text ('label / milestone edits live on their pages (triage)') with no apply path, so a created label could never be attached. The issue list already rendered applied names from the index cards (02 §2 projection) — it just lacked colors. What this builds (no new deps, no Go changes, no doc amendment — implements 02 §11 + 08 §2 as specified): - `web/src/components/LabelPicker.jsx` (new): `LabelPicker` — inline toggle rows over the page-owned `labels:{o}/{r}` cache, native `<button>`s with `aria-pressed` (keyboard free, nothing to trap/dismiss), triage+ gated via `roleAtLeast` (server authoritative, 403s surface in the tray); `LabelChip` — color dot + name, deleted labels render as the bare string (02 §3.1). - `web/src/lib/labels.js` (new): headless `toggleLabel` (sorted-unique, case-insensitive remove) + `labelColorMap`/`labelColor`. - `Issue.jsx` sidebar: picker for triage+, one PATCH with the full next set per toggle, optimistic `patchCached` paint + guarded `invalidate` reconcile (the reaction-chip bump pattern); per-label busy set stops double-fire (repeat PATCH would be idempotent anyway). - `Issues.jsx` cards: chips now carry their colors via the shared cached labels set — no list-endpoint change (cards already carried names). - `web/test/unit/labels.test.js` (new): toggle round-trip, case-insensitive remove, color fallback. Verification: - `node --test web/test/unit/*.test.js`: 242 pass, 0 fail (4 new). - `go vet ./...` clean. - Real-browser drive (hub headless Chrome over CDP against a scratch server on this branch): created label `bug` (#d73a4a) + issue #1 → picker listed it → click applied (sidebar chip + pressed row, server thread `labels:["bug"]`, empty error tray) → list card shows BUG chip with the red dot; both dark and light themes; zero console errors. Screenshots checked for both themes. - Dark + light share the chip classes (dot carries color, text stays theme foreground).
The issue sidebar LABELS card was static text ('edits live on their
pages') with no way to apply a label; the PATCH …/issues/{num} labels
key and the labels CRUD existed and worked (verified against the API).
Root cause: UI-only gap — the 08 §2 LabelPicker contract was specified
but unbuilt.

- web/src/components/LabelPicker.jsx (new): LabelPicker (inline toggle
  rows, native buttons with aria-pressed, triage-gated by the page) +
  LabelChip (color dot, bare string for deleted labels per 02 §3.1).
- web/src/lib/labels.js (new): headless toggle/color helpers.
- Issue.jsx sidebar: picker for triage+ (roleAtLeast, server authoritative,
  403s to the tray), one PATCH per toggle, optimistic patchCached paint +
  guarded invalidate reconcile (reaction-chip bump pattern).
- Issues.jsx cards: index cards already carried label names (02 §2);
  chips now resolve colors via the cached labels set (no list-endpoint
  change needed).
- No doc amendment: implements 02 §11 + 08 §2 as specified.
Sign in to join this conversation.
No description provided.