# TaskLane Improvements Design

> **Date:** 2026-03-26
> **Status:** Approved
> **Scope:** `frontend/src/components/assistant/TaskLane.tsx`, `frontend/src/pages/AssistantChatPage.tsx`

---

## Context

The TaskLane is a right-side panel in the AI Assistant chat that renders structured questions and diagnostic actions from the AI. It launched with a working parse → render → submit pipeline, but has four UX issues and a missing submission flow design.

## Changes

### 1. Progressive Preview + Batch Submit

**Problem:** The "Send All Responses" button is disabled until every item is answered or skipped. Engineers often want to submit partial results — answer 2 of 3 questions, paste output from 1 of 4 commands, and let the AI work with what they have.

**Design:**

- **Submit enabled when ≥1 item is done or skipped.** Remaining pending items are simply omitted from the message (not auto-skipped — they're just not addressed).
- **Dynamic submit label:** `Send 2 of 6 Responses` when partial, `Send All Responses` when all handled.
- **Collapsible preview section** above the submit button:
  - Toggle: `▶ Preview (2/6 done)` — collapsed by default
  - Expands to show the formatted markdown message that will be sent to the AI
  - Updates in real-time as items are answered/skipped
  - Uses the same formatting logic as `handleTaskSubmit` (question answers in blockquotes, command output in code fences, skipped items noted)
  - Styled as a `bg-code` block with `font-mono text-xs`, max-height ~150px with overflow scroll
- **Done items are re-editable.** Clicking anywhere on a completed (green) card reopens it in `active` state for editing. Cards get `cursor-pointer` and a subtle hover state (`hover:border-success/40`) to signal editability. This uses the existing `updateTask(idx, { state: 'active' })` mechanism — no new logic needed, just making the done card itself a click target.
- **No change to the TaskLane header or pending/active card behavior.** Changes affect the footer area and done card interactivity.

**Submission logic changes in `TaskLane.tsx`:**
- `allHandled` check on submit button changes from "all done/skipped" to "at least 1 done/skipped"
- `handleSubmit` sends only items that have state `done` or `skipped`
- Items still in `pending` or `active` state are excluded from the submission payload

**Submission logic in `AssistantChatPage.tsx` (`handleTaskSubmit`):**
- No changes needed — it already formats based on `r.state === 'done'` and `r.state === 'skipped'`, ignoring pending items.

### 2. TaskLane Reset on New Chat

**Problem:** Starting a new chat via `handleNewChat` doesn't clear the TaskLane. The previous session's questions/actions persist visually.

**Fix:** Add three lines to `handleNewChat` in `AssistantChatPage.tsx`:
```typescript
setShowTaskLane(false)
setActiveQuestions([])
setActiveActions([])
```

Same pattern already exists in `handleTaskSubmit`. Also add to `selectChat` for switching between chats.

### 3. Conditional Chat Input Border

**Problem:** The chat input area has `border-t border-border` which creates a double-border effect alongside the TaskLane footer's `border-t border-default`.

**Fix:** Conditionally remove the chat input's top border when the TaskLane is open:
```tsx
<div className={cn("px-3 sm:px-6 py-3 shrink-0", !showTaskLane && "border-t border-border")}>
```

The TaskLane's own left border and footer border provide sufficient visual separation when the panel is open. When closed, the chat input border returns.

### 4. Resizable TaskLane with Grip Handle

**Design:**

- **Drag zone:** 6px wide hit target on the left edge of the TaskLane, absolutely positioned.
- **Grip indicator:** Centered vertically on the drag zone — a 2×3 grid of small dots (6 dots total, `w-1 h-1 rounded-full bg-current`). Subtle `text-muted/40` by default, `text-muted-foreground` on hover.
- **Resize behavior:**
  - `onMouseDown` on the grip starts tracking
  - `mousemove` on `document` updates the width
  - `mouseUp` on `document` stops tracking
  - Width clamped between **280px** min and **50vw** max
  - `cursor: col-resize` applied to the grip and to `document.body` during drag (prevents cursor flicker)
  - `user-select: none` on `document.body` during drag (prevents text selection)
- **Persistence:** Width saved to `localStorage` key `rf-tasklane-width`. Read on mount, written on drag end. Default: `340px`.
- **Implementation:** All in `TaskLane.tsx` — no external libraries. Uses `useRef` for drag state (avoids re-renders during drag), `useState` for the width value.

**CSS change:** Replace `w-[340px]` with `style={{ width: panelWidth }}` on the outer div. Keep `shrink-0` so the chat area flexes.

---

## Files Modified

| File | Change |
|------|--------|
| `frontend/src/components/assistant/TaskLane.tsx` | Preview section, submit logic, resize handle, width persistence |
| `frontend/src/pages/AssistantChatPage.tsx` | TaskLane reset in `handleNewChat`/`selectChat`, conditional border |

## Out of Scope

- TaskLane rendering when loading historical sessions (markers are stripped from stored messages — no TaskLane on reload)
- Mobile-specific TaskLane layout (current: hidden on mobile, which is acceptable for now)
- Keyboard accessibility for resize handle (future enhancement)
