# AI Builder UX Improvements Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** Three frontend-only UX improvements: always-available Publish button, "Generate All" branches in the AI wizard, and rotating activity messages during generation. **Architecture:** All changes are frontend-only. Feature 1 is a one-line fix in `TreeEditorPage.tsx`. Features 2 and 3 involve adding state to `aiFlowBuilderStore.ts` and updating `BranchDetailView.tsx` and `GeneratingAnimation.tsx`. No backend changes. **Tech Stack:** React 19, TypeScript, Zustand, Tailwind CSS v3, Lucide React icons. **Design doc:** `docs/plans/2026-02-24-ai-builder-ux-improvements.md` --- ## Task 1: Fix Publish button — remove `!isDirty` gate **Files:** - Modify: `frontend/src/pages/TreeEditorPage.tsx` (line ~654) **Step 1: Make the change** Find this line in `TreeEditorPage.tsx`: ```tsx disabled={isSaving || !isDirty || hasBlockingErrors} ``` Change to: ```tsx disabled={isSaving || hasBlockingErrors} ``` That's the only change needed. The button's `title` text references "Ctrl+S when no errors" which is still accurate — leave it. **Step 2: Verify build passes** ```bash cd frontend && npm run build 2>&1 | tail -20 ``` Expected: no errors. **Step 3: Commit** ```bash git add frontend/src/pages/TreeEditorPage.tsx git commit -m "fix: allow publishing clean drafts without requiring local edits" ``` --- ## Task 2: Add rotating activity messages to `GeneratingAnimation` **Files:** - Modify: `frontend/src/components/ai-builder/GeneratingAnimation.tsx` **Step 1: Read the current file** Read `frontend/src/components/ai-builder/GeneratingAnimation.tsx` to understand current structure before changing it. **Step 2: Rewrite with rotating messages** Replace the entire file content with: ```tsx import { useEffect, useState } from 'react' import { cn } from '@/lib/utils' const MESSAGES = [ 'Setting up your flow...', 'Building diagnostic paths...', 'Putting the pieces in place...', 'Almost there...', ] as const const MESSAGE_DURATIONS = [4000, 8000, 8000, Infinity] // ms each message shows interface GeneratingAnimationProps { branchContext?: { current: number; total: number } } export function GeneratingAnimation({ branchContext }: GeneratingAnimationProps) { const [messageIndex, setMessageIndex] = useState(0) // Reset and advance message on mount/remount useEffect(() => { setMessageIndex(0) let current = 0 const advance = () => { current += 1 if (current < MESSAGES.length - 1) { setMessageIndex(current) timer = setTimeout(advance, MESSAGE_DURATIONS[current]) } else { setMessageIndex(MESSAGES.length - 1) } } let timer = setTimeout(advance, MESSAGE_DURATIONS[0]) return () => clearTimeout(timer) }, []) return (
{/* Spinner */}
{/* Branch context (Generate All mode) */} {branchContext && (

Branch {branchContext.current} of {branchContext.total}

)} {/* Rotating message */}

{MESSAGES[messageIndex]}

) } ``` **Step 3: Verify build passes** ```bash cd frontend && npm run build 2>&1 | tail -20 ``` Expected: no errors. **Step 4: Commit** ```bash git add frontend/src/components/ai-builder/GeneratingAnimation.tsx git commit -m "feat: add rotating activity messages to generation loading state" ``` --- ## Task 3: Add `generateAllBranchDetails` and cancel to the store **Files:** - Modify: `frontend/src/store/aiFlowBuilderStore.ts` **Step 1: Read the current store** Read `frontend/src/store/aiFlowBuilderStore.ts` fully to understand current state shape before modifying. **Step 2: Add new state fields and actions** Add to the `AIFlowBuilderState` interface (after `isLoading: boolean`): ```tsx isGeneratingAll: boolean stopGeneratingAll: boolean generateAllBranchDetails: () => Promise cancelGenerateAll: () => void ``` Add to the initial state in `create()(...)` (after `isLoading: false`): ```tsx isGeneratingAll: false, stopGeneratingAll: false, ``` Add the two new actions after `assemble`: ```tsx generateAllBranchDetails: async () => { const { selectedBranches, generateBranchDetail } = get() const undetailed = selectedBranches.filter((b) => !b.steps) if (undetailed.length === 0) return set({ isGeneratingAll: true, stopGeneratingAll: false, error: null }) for (const branch of undetailed) { if (get().stopGeneratingAll) break // Set currentBranchIndex so tabs show the active branch const idx = get().selectedBranches.findIndex((b) => b.name === branch.name) if (idx !== -1) set({ currentBranchIndex: idx }) await generateBranchDetail(branch.name) // If generateBranchDetail set phase to 'error', stop if (get().phase === 'error') break } set({ isGeneratingAll: false }) }, cancelGenerateAll: () => { set({ stopGeneratingAll: true }) }, ``` Also add `isGeneratingAll: false, stopGeneratingAll: false` to the `reset()` action's `set({...})` call. **Step 3: Verify TypeScript compiles** ```bash cd frontend && npm run build 2>&1 | tail -20 ``` Expected: no errors. **Step 4: Commit** ```bash git add frontend/src/store/aiFlowBuilderStore.ts git commit -m "feat: add generateAllBranchDetails and cancelGenerateAll to AI builder store" ``` --- ## Task 4: Update `BranchDetailView` with Generate All UI **Files:** - Modify: `frontend/src/components/ai-builder/BranchDetailView.tsx` **Step 1: Read the current file** Read `frontend/src/components/ai-builder/BranchDetailView.tsx` fully. **Step 2: Add imports and pull new store state** Add `Zap, Square` to the lucide-react import (Zap = lightning bolt for "Generate All", Square = stop). Pull new state from store in the component: ```tsx const { // existing... isGeneratingAll, generateAllBranchDetails, cancelGenerateAll, } = useAIFlowBuilderStore() ``` **Step 3: Add Generate All / Stop button above branch tabs** After the opening `
` and before the branch tabs div, add: ```tsx {/* Generate All / Stop control */} {(() => { const undetailedCount = selectedBranches.filter((b) => !b.steps).length if (undetailedCount === 0) return null return (
{undetailedCount} branch{undetailedCount !== 1 ? 'es' : ''} need detail {isGeneratingAll ? ( ) : ( )}
) })()} ``` **Step 4: Disable individual controls during `isGeneratingAll`** On the "Generate Detail" button (the primary one in the empty-state section): ```tsx disabled={isLoading || isGeneratingAll} ``` On the "Skip" button: ```tsx disabled={isGeneratingAll} // also add: className includes opacity-50 when disabled ``` On the "Regenerate" button: ```tsx disabled={isLoading || isGeneratingAll} ``` **Step 5: Pass `branchContext` to `GeneratingAnimation`** The `GeneratingAnimation` is rendered when `phase === 'generating' && isLoading`. Update that render: ```tsx if (phase === 'generating' && isLoading) { return ( b.steps).length + 1, total: selectedBranches.length, } : undefined } /> ) } ``` **Step 6: Verify build passes** ```bash cd frontend && npm run build 2>&1 | tail -20 ``` Expected: no errors. **Step 7: Commit** ```bash git add frontend/src/components/ai-builder/BranchDetailView.tsx git commit -m "feat: add Generate All button and per-branch progress to AI builder detail stage" ``` --- ## Task 5: Push and verify **Step 1: Push branch** ```bash git push ``` **Step 2: Verify CI passes** ```bash gh pr checks 88 2>&1 | head -20 ``` Expected: all checks passing (or wait for them to run). **Step 3: Manual smoke test checklist** - [ ] Open a fresh AI-generated draft in the tree editor → Publish button is enabled - [ ] Open AI Flow Builder, complete foundation → scaffold → select branches - [ ] On detail stage: "Generate All" button is visible - [ ] Click "Generate All" → branches generate one at a time, tabs show progress, "Branch X of Y" appears in animation - [ ] "Stop" button appears during run, clicking it halts after current branch - [ ] Activity messages cycle: "Setting up your flow..." → "Building diagnostic paths..." → "Putting the pieces in place..." → "Almost there..." - [ ] Single-branch generate still works as before