From 50db790f8615a1d53b8e2a362ba6a9ac28055cc4 Mon Sep 17 00:00:00 2001 From: Croissant Le Doux Date: Fri, 10 Jul 2026 15:57:30 -0400 Subject: [PATCH 1/2] Fix blank Focus + calm-standup nag chrome (found dogfooding live) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Poking at the live app (a real repo with no open issues) surfaced two empty-state gaps: - Focus (Morning service) rendered a bare header — no cards, no empty state — because scheduleFocus returns a FocusView with empty now/next/later rather than undefined, so the `if (!focus)` guard never fired. Guard on 'no now/next/later' too → shows the 'Nothing to pour' EmptyState. - Standup's calm sentinel nag (id 0) still rendered '#0 · steeping 0d · blocks' chrome and was a click target (would try to open issue #0). Render the calm case as a plain reassurance pill, no chrome, not clickable. Both verified against the live christian/commitea repo (all issues closed → no open work). desktop tsc clean. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/components/screens/focus-screen.tsx | 11 ++- .../src/components/screens/standup-screen.tsx | 84 ++++++++++++------- 2 files changed, 59 insertions(+), 36 deletions(-) diff --git a/apps/desktop/src/renderer/src/components/screens/focus-screen.tsx b/apps/desktop/src/renderer/src/components/screens/focus-screen.tsx index 6798486..0d35f05 100644 --- a/apps/desktop/src/renderer/src/components/screens/focus-screen.tsx +++ b/apps/desktop/src/renderer/src/components/screens/focus-screen.tsx @@ -7,9 +7,10 @@ import { EmptyState } from '../shell/states.js' import { Badge, Button, Card, IconButton, Tag } from '../ui/index.js' /** - * Morning service — the Now/Next/Later focus cards + the burn-up cone. - * `focus` (scheduler) and `forecast` (Monte Carlo) override the demo fixtures - * when gitea is configured; both fall back to the handoff demo otherwise. + * Morning service — the Now/Next/Later focus cards + the burn-up cone, from the + * scheduler (`focus`) and Monte Carlo forecast (`forecast`). Both come from the + * reconciled backlog; with no open work the scheduler has nothing to pour, so we + * show the empty state. */ export function FocusScreen({ onOpenIssue, @@ -66,7 +67,9 @@ export function FocusScreen({ ) - if (!focus) { + // The scheduler returns a FocusView even when the backlog has no open work — + // treat "no now/next/later" as empty, not just an absent `focus`. + if (!focus || (!focus.now && !focus.next && !focus.later)) { return (
-
onOpenIssue(NAG_REF)} - onKeyDown={(e) => { - if (e.key === 'Enter') onOpenIssue(NAG_REF) - }} - style={{ - display: 'flex', - gap: 10, - alignItems: 'flex-start', - cursor: 'pointer', - background: 'var(--warn-tint)', - borderRadius: 'var(--radius-2)', - padding: '12px 14px', - }} - > - - - -
-
- #{s.nag.id} - - steeping {s.nag.days} - - - blocks {s.nag.blocks.join(', ')} - -
-

{s.nag.text}

+ {s.nag.id === 0 ? ( + // Calm sentinel — nothing is over-steeping; render just the reassurance, + // no issue chrome, no click target (there's no #0 to open). +
+ + + +

{s.nag.text}

-
+ ) : ( +
onOpenIssue(NAG_REF)} + onKeyDown={(e) => { + if (e.key === 'Enter') onOpenIssue(NAG_REF) + }} + style={{ + display: 'flex', + gap: 10, + alignItems: 'flex-start', + cursor: 'pointer', + background: 'var(--warn-tint)', + borderRadius: 'var(--radius-2)', + padding: '12px 14px', + }} + > + + + +
+
+ #{s.nag.id} + + steeping {s.nag.days} + + + blocks {s.nag.blocks.join(', ')} + +
+

{s.nag.text}

+
+
+ )}