# AI Builder UX Improvements

> **Date:** 2026-02-24
> **Branch:** frontend-standardization (PR #88)
> **Status:** Approved for implementation

---

## Overview

Three focused UX improvements to the AI Flow Builder and tree editor:

1. **Publish always available** — remove unnecessary `!isDirty` gate on the Publish button
2. **Generate All branches** — one-click auto-generation of all branch details sequentially
3. **Honest activity messages** — replace spinner-only loading with rotating status messages

---

## Feature 1: Publish Button Always Available

### Problem
The Publish button in `TreeEditorPage.tsx` is disabled when `!isDirty`. This prevents publishing a freshly AI-generated draft that landed in the editor already saved — the tree is valid and ready, but the button is greyed out because nothing has been locally changed.

This same issue affects any future import/ingestion pathway where a draft arrives pre-saved.

### Solution
Remove `!isDirty` from the Publish button's `disabled` condition. Keep it disabled only for `isSaving || hasBlockingErrors`.

The Save Draft button retains its `!isDirty` guard (no reason to re-save an unchanged draft).

### Change
**File:** `frontend/src/pages/TreeEditorPage.tsx`
**Line:** ~654
Before: `disabled={isSaving || !isDirty || hasBlockingErrors}`
After: `disabled={isSaving || hasBlockingErrors}`

---

## Feature 2: Generate All Branches

### Problem
With 4–7 branches, users must click "Generate Detail" for each branch individually, wait for it to complete, then click the next. For 6 branches this is 6 separate interactions with wait time between each.

### Solution
Add a "Generate All" button that auto-generates all undetailed branches sequentially without user intervention.

### Store changes (`aiFlowBuilderStore.ts`)
Add:
- `isGeneratingAll: boolean` — true while auto-run is in progress
- `stopGeneratingAll: boolean` — flag set by user to cancel mid-run
- `generateAllBranchDetails(): Promise<void>` — sequential loop
- `cancelGenerateAll(): void` — sets stop flag

`generateAllBranchDetails()` logic:
1. Set `isGeneratingAll: true`, `stopGeneratingAll: false`
2. Loop through `selectedBranches` in order, skipping branches that already have `steps`
3. For each: set `currentBranchIndex` to the active branch (so tabs show live progress), call `generateBranchDetail(branch.name)`
4. After each call, check `stopGeneratingAll` — if true, break
5. On `generateBranchDetail` failure: stop loop, set `isGeneratingAll: false`, leave error state on that branch (existing error handling handles display)
6. On complete: set `isGeneratingAll: false`

### UI changes (`BranchDetailView.tsx`)
- Add "Generate All" button above branch tabs, visible when ≥1 branch has no `steps` and `!isGeneratingAll`
- During a run: button becomes "Stop" (calls `cancelGenerateAll`)
- Individual "Generate Detail" and "Skip" buttons disabled during `isGeneratingAll`
- Branch tabs show `currentBranchIndex` highlight during run so user can see which is active
- When `isGeneratingAll`, show "Branch X of Y" context in the generating animation

### Error handling
On failure mid-run: stop at the failed branch, show the existing error indicator on that branch tab (red instead of green check), display the error message. User can retry that branch individually or skip it. No global abort.

---

## Feature 3: Honest Activity Messages

### Problem
`GeneratingAnimation` shows a spinner with static text. For waits of 5–30+ seconds this gives no sense of progress.

### Solution
Replace the static text in `GeneratingAnimation` with rotating messages tied to elapsed time. Messages are always-true descriptions of what the system is doing at a high level.

### Message sequence
| Elapsed | Message |
|---------|---------|
| 0–4s | "Setting up your flow..." |
| 4–12s | "Building diagnostic paths..." |
| 12–20s | "Putting the pieces in place..." |
| 20s+ | "Almost there..." |

Messages advance on a timer that resets each time generation starts. They use `useEffect` with `setInterval` — no new dependencies needed.

### Generate All context
When `isGeneratingAll` is true, show "Branch X of Y" as a small label above the rotating message. This comes from the store's `currentBranchIndex` and `selectedBranches.length`.

### Change
**File:** `frontend/src/components/ai-builder/GeneratingAnimation.tsx`
Replace static text with a `useEffect`-driven message cycler. Accept optional `branchContext?: { current: number; total: number }` prop.

`BranchDetailView.tsx` passes `branchContext` when `isGeneratingAll` is true.

---

## Files Changed

| File | Change |
|------|--------|
| `frontend/src/pages/TreeEditorPage.tsx` | Remove `!isDirty` from Publish disabled condition |
| `frontend/src/store/aiFlowBuilderStore.ts` | Add `isGeneratingAll`, `stopGeneratingAll`, `generateAllBranchDetails`, `cancelGenerateAll` |
| `frontend/src/components/ai-builder/BranchDetailView.tsx` | Add Generate All / Stop button, disable controls during run, pass branch context |
| `frontend/src/components/ai-builder/GeneratingAnimation.tsx` | Add rotating activity messages with elapsed timer, accept branchContext prop |

No backend changes required.

---

## Non-Goals
- SSE/streaming status from backend (deferred, would be needed for accurate retry messaging)
- Parallel branch generation (rate limit risk, sequential is safer and shows progress)
- Persisting generate-all state across modal close (not needed, user can re-run)
