feat(desktop): the side panel opens on Luke's first board drawing and first code, once per plan

Merged
DA
11d ago
+354-5

What

When Luke first draws on a plan's whiteboard, or first puts code on screen, during a call about the plan that is open, the side panel opens on that tab if it was closed. After that it doesn't open by itself again for that plan and kind. Later redraws, later calls' code, a closed panel, a relaunch, or a trip to another plan and back don't reopen it.

  • Trigger (live arrival only): per visit to a plan, the first read of each kind is a baseline. Luke drawing on a board he had not drawn on, or code coming on screen where there was none, is an arrival. Only Luke's drawing (board.drawing) counts; the developer's own strokes never trigger anything. A plan opened with Luke's drawing already on its board, or with code already on screen, counts as already seen, so plans drawn on before this shipped never auto-open. Leaving a plan ends its visit, so a plan reopened is baselined afresh.
  • Once per plan per kind: kept in the renderer's storage under luke.sidePanelArrivals as { [planId]: ("board" | "code")[] }, a nested record and not a concatenated key, read and written at the moment of an arrival.
  • Closed panel: opens on the arrival's tab through the panel's existing onChoose, the same state path as a press, so whatever open animation main has applies. It never enters full screen and never moves focus.
  • Open panel: left as it is: tab, full screen and width are unchanged. The arrival's tab gets the existing .tab-note dot until that tab is shown. The dot belongs to the plan it arrived on and doesn't follow you to another plan.
  • Fixture runs stay staged: their plans have nothing arriving, and nothing is written.

New module planning/use-panel-arrivals.ts; use-side-panel.ts's "Nothing opens it on its own" comment is updated.

Judgment calls

  • Code is call-only and never stored, so "a plan that had code before this shipped" can't be known. Such a plan's first code after this ships opens the panel once.
  • Deleted plans' entries aren't pruned. Each is a few bytes, nothing reads it again, and pruning has no observable behaviour to test.

Tests

use-plans-tab.test.ts, through usePlansTab:

  • Luke's first board drawing opens the panel on Board, not full screen; a later drawing after closing doesn't reopen it.
  • The first code on a call opens the panel on Code; a later call's code after closing doesn't reopen it.
  • A plan opened with its board already drawn opens nothing, then or later.
  • A plan left and reopened is baselined afresh, so a drawing made while it was away opens nothing.
  • The developer's own strokes open nothing, and Luke's first drawing after them still does.
  • Once per plan holds across a relaunch (a fresh mount over the same storage) and a trip to another plan.
  • An arrival while the panel is open on another tab, full screen, keeps the tab and full screen, and dots the arrival's tab until it is shown.

desktop-plans.test.ts: the unread tab draws a dot and the shown tab draws none.

Mac verification

CI builds nothing for the Mac and could not verify this UI change. ./scripts/verify.sh was not run: this was built on a Linux cloud VM. LUKE-159 is the manual Mac pass. ./scripts/check.sh exits 0 locally. The tab dot was rendered from the real components and stylesheet in headless Chrome to check how it looks.

šŸ¤– Generated with Claude Code