Feed the ML Assistant plan strip from update_plan (#2521)
* Feed the ML Assistant plan strip from update_plan
The strip's setPlan seam gets its plan source: every streamed Plan update
maps onto MlPlanStep via planStepsToMlSteps (full step text as the
tooltip, a short cut as the dot label, statuses mapped onto the strip's
lifecycle), wired through a new onPlan callback in consumeMessageUpdates
so the send and reattach paths behave identically. Reopened mode
conversations render mid-plan from the conversation's persisted plan
snapshot, now carried on the conversation payload; seeding happens only
when syncConversation actually reset, so live streamed steps are never
clobbered.
* Let the model name each plan step for the progress dots
A word-boundary cut of the step text made a poor dot label in three ways
the strip's design cares about: the dot's accessible name became a
fragment ("Read the — running"), the aria-live row announced the same
fragment on every transition, and the tooltip's bold lead was a literal
prefix of the description beside it. update_plan now asks for a
one-or-two-word label per step (advertised required so models supply it;
validated leniently so plans from before the field, or an update that
omits it, still parse) and the strip mapping prefers it, keeping the cut
only as the fallback.
* Connect the plan dots and give the strip room to breathe
A muted stepper line now joins the dots (running behind them, ending
under the outer dots' centers; dark-mode pending dots get the strip's
band color so the line cannot show through), step spacing doubles to
10px, and the progress row sits a little further from the ML Assistant
label.
* Give completed plan dots a proper tick
Done dots trade their number for a LucideCheck (already the ML strip
family's check, in ThinkingEffortChip), thickened past lucide's default
stroke so it stays legible at dot size. Also updates the strip tests to
the doubled 10px dot rhythm from the previous commit.
* Keep skipped dots solid, legible, and in step with the band
Skipped dots lose their element opacity — which composited the connector
line straight through the dot face — for pre-blended solid fills, with
the border and number a notch above the band so a skipped step reads
muted rather than missing. The strip's band color becomes a custom
property (--ml-strip-band) that the dark pending dot consumes, so the
two stay matched if the band is ever retinted.
* Mark skipped plan dots with a slash
A skipped step now reads as skipped at a glance: the dot trades its
numeral for a GitHub-Actions-style diagonal slash (lucide's, like the
check), run smaller and heavier than the tick so the two sit at the same
visual weight, with the skipped palette eased back under pending's so the
most receded state is also the least prominent. Documents the aria-label
as the accessible carrier of step state — no dot glyph clears 4.5:1 by
design, so the label is load-bearing, not a nicety.
* Put the dot icons' stroke-width where it actually wins
The icon component inlines stroke-width="2" as a presentation attribute
on each path, which beats anything inherited from CSS on the svg root —
so the tick and slash had been rendering at lucide's default all along.
Targeting the paths makes the declared weights real for the first time.
* Recede the skipped slash a step further
Chunky enough to read as a slash, quiet enough to read as skipped: with
the stroke weight now real, the colour drops to #aeafbb (dark #6a6e7d)
so the shape carries the meaning and the state stays the most receded.
* Rebrand the mode strip: ML Intern, in orange
The strip's visible name becomes ML Intern (label, switch aria-label,
effort-chip title — matching the project whose prompt surfaces the mode
is adopting) and the accent moves from blue to a vibrant orange: #ea580c
for surfaces (switch track, running dot, pulse, focus rings), #c2410c /
dark #fdba74 for text on the new warm band (#fff4ea / #2b1c0e), with the
connector, example chips, composer border, PlanCard in-progress icon and
the skipped pre-blends retinted to match. Identifiers, file names and the
ML_ASSISTANT_MODE flag are unchanged — the persona rename inside the
preprompt belongs to the prompt-migration effort.
* Note the strip label's contrast headroom where it will be retinted
#c2410c on #fff4ea clears AA at 4.78:1 with little to spare, and it is
the one string on the strip that must — the label and the aria-live
status. Measured by review; recorded so a future retint moves band and
text together.