# UX Improvements — Implementation Plan (Merged)

## Context

Five frontend UX improvements for tree navigation, my-trees, and app layout. Changes are primarily frontend with minimal backend updates for session rewind tracking. Single branch: `feat/ux-improvements`.

## Locked Decisions

- Breadcrumb rewind **persists immediately** via API call.
- Rewound steps are **soft-abandoned** (history preserved via rewind markers, never deleted).
- Shortcuts modal is **global from AppLayout** and **route-aware**.
- Follow existing **monochrome design system** with subtle keycap/hint styling.
- `sessions.decisions` is JSONB — no DB migration needed for rewind marker fields.

---

## Step 1: Command Copy UX (Quick Win)

**Files:** `TreeNavigationPage.tsx`

### Changes

- Add a copy-to-clipboard button next to every command block:
  - Action node commands (`currentNode.commands`)
  - Custom step commands (`currentCustomStep.step_data.content.commands`)
  - Command output textarea (when content exists)
- Button styling: `opacity-0 group-hover:opacity-100` with `Clipboard` icon, positioned `absolute top-1.5 right-1.5` inside a `group relative` wrapper.
- On click: `navigator.clipboard.writeText(text)`, swap icon to `Check` for 2 seconds.
- Track copied state with `copiedCommand: string | null`.
- Show toast only on copy failure to avoid noisy UX on success.
- **Refactor** duplicate command block rendering into a single local render helper to keep behavior consistent across action/custom-step sections.

---

## Step 2: Keyboard Hints + Global Shortcuts Modal (Quick Win)

**Files:** `AppLayout.tsx`, `TreeNavigationPage.tsx`, new `shortcutCatalog.ts`

### In-Page Hints (TreeNavigationPage.tsx)

- Add keycap-style badges (`[1]`, `[2]`, etc.) next to decision option buttons. Style: `kbd` look, lower contrast, subtle.
- Add `[Esc]` hint next to the Back button label.

### Global Shortcuts Modal (AppLayout.tsx)

- Add `?` icon button in the app header (desktop + mobile drawer).
- Opens a modal (reuse existing `Modal` component).
- Modal is **route-aware** using `location.pathname` via a `shortcutCatalog.ts` file:
  - **Tree Navigation routes:** `1-9` Select option, `Esc` Go back, `Enter` Continue.
  - **Tree Editor routes:** `Ctrl+S` Save, `Ctrl+Z` Undo, `Ctrl+Shift+Z` Redo.
  - **Other routes:** Minimal generic navigation shortcuts.
- **Do NOT bind `?` as a keyboard shortcut** — it conflicts with text inputs. The modal is click-driven only.

### Keyboard Listener (TreeNavigationPage.tsx)

- Add `useEffect` keydown listener on `document`.
- Keys `1-9`: If current node is a decision node with options, call `handleSelectOption` for the corresponding option (index = key - 1).
- `Escape`: Trigger back/rewind navigation (same handler as breadcrumb rewind — see Step 3).
- Guards:
  - Only active when `currentNode?.type === 'decision'` and options exist.
  - Ignore if `selectingOption` is truthy (loading state).
  - Ignore if any modal is open.
- Cleanup: Return removal function for the listener.

---

## Step 3: Interactive Breadcrumbs with Soft-Abandon Rewind (Medium)

**Files:** `TreeNavigationPage.tsx`, `session.py`, `session_to_tree.py`, `export_service.py`, `SessionDetailPage.tsx`, `SessionHistoryPage.tsx`

### Public Interface Changes

Extend `DecisionRecord` in `session.py` and `session.ts` with optional fields:

```
decision_kind?: 'step' | 'rewind_marker'  (default 'step')
rewind_source?: 'breadcrumb' | 'back_button'
rewind_from_node_id?: string
rewind_to_node_id?: string
abandoned_path_segment?: string[]
```

No DB migration required — `sessions.decisions` is JSONB and stores these optional fields directly.

### Frontend Changes (TreeNavigationPage.tsx)

- Replace static breadcrumb `<span>` elements with `<button>` for all non-current crumbs.
- Styling: `text-white/40 hover:text-white/70 hover:underline cursor-pointer` for clickable items. Current item remains `font-medium text-white` as a `<span>`.
- Introduce a **unified rewind handler** used by both:
  - Breadcrumb click
  - Back button / `Esc` key
- **Rewind algorithm:**
  1. Compute `newPath = pathTaken.slice(0, targetIndex + 1)`.
  2. Append a `rewind_marker` decision with: `decision_kind`, `rewind_source`, `rewind_from_node_id`, `rewind_to_node_id`, `abandoned_path_segment` (nodes removed from active path), and `timestamp`.
  3. Persist immediately via `sessionsApi.update(session.id, { path_taken: newPath, decisions: newDecisions })`.
  4. On API failure: restore previous local state and show error toast.
- Clear any custom step state if active (`customStepFlow.setCurrentCustomStep(null)`).
- Session continues accumulating steps naturally from the jumped-to node.

### Backend/Downstream Compatibility

- `save_as_tree` conversion: **Ignore** records where `decision_kind === 'rewind_marker'`.
- Export generators (Markdown, Text, HTML): **Skip** rewind marker records in step numbering and output.
- Session detail/timeline pages: **Display** rewind markers as explicit timeline events (e.g., "Rewound from Step 6 to Step 3").
- Session "decision count" in `SessionHistoryPage.tsx`: **Exclude** rewind markers from the count.

---

## Step 4: Prominent "Create Tree" CTA (Quick Win)

**Files:** `MyTreesPage.tsx`

### Header Update

- Convert header to flex row with action button:
  - Left: Title + subtitle.
  - Right: "Create Tree" button with `Plus` icon, linking to `/trees/new`.
- Permission-aware: Only show for users with create permission (use existing role checks / `usePermissions` hook). Viewers cannot see it.

### Empty State Update

- Replace single "Browse Trees" link with two actions:
  - **Primary** (white bg, higher visual weight): "Browse Library" → links to `/trees`.
  - **Secondary** (outline/border style): "Create from Scratch" with `Plus` icon → links to `/trees/new`.
- "Create from Scratch" is also permission-gated.

---

## Step 5: Timer Visibility + Action Loading Feedback (Quick Win)

**Files:** `TreeNavigationPage.tsx`

### Timer Enhancement

- Change timer from plain `text-white/40` to a pill/badge style:
  ```
  rounded-full bg-white/10 px-2.5 py-0.5 text-sm text-white/60
  ```
- Slightly larger icon: `h-4 w-4` (from `h-3.5 w-3.5`).
- More visible but still secondary to tree name.

### Action Loading Feedback

- Add `selectingOption: string | null` state (stores option ID being selected).
- When an option is being processed:
  - The active option button shows an inline spinner replacing the number badge.
  - All other option buttons get `opacity-50 pointer-events-none`.
  - **Keyboard shortcuts are disabled** to prevent double-submits.
- Add similar feedback for "Continue" button with a "Continuing..." label.
- Preserve existing `isCompleting` behavior for completion flow.

---

## Files Modified Summary

| File | Steps |
|------|-------|
| `TreeNavigationPage.tsx` | 1, 2, 3, 5 |
| `MyTreesPage.tsx` | 4 |
| `AppLayout.tsx` | 2 (global shortcuts modal + `?` button) |
| `shortcutCatalog.ts` (new) | 2 (route → shortcut definitions) |
| `session.py` | 3 (accept rewind marker fields in decisions) |
| `session_to_tree.py` | 3 (ignore rewind markers) |
| `export_service.py` | 3 (skip rewind markers in export output) |
| `SessionDetailPage.tsx` | 3 (display rewind events in timeline) |
| `SessionHistoryPage.tsx` | 3 (exclude rewind markers from decision count) |

---

## Verification

### Build

```bash
cd frontend && npm run build    # clean build, no type errors
cd backend && pytest --override-ini="addopts="
```

### Manual Testing

- [ ] Copy button appears on hover over all command blocks; copies correctly; shows check feedback; failure shows toast.
- [ ] Keycap hints `[1]`, `[2]`, etc. visible next to decision options; `[Esc]` visible near back button.
- [ ] Number keys 1-9 select decision options; Esc triggers rewind; shortcuts disabled during loading.
- [ ] `?` icon in app header opens global shortcuts modal with route-aware content.
- [ ] `?` is NOT bound as a keyboard shortcut (no conflict with text inputs).
- [ ] Clicking a previous breadcrumb rewinds to that point, persists immediately, and adds a rewind marker to session decisions.
- [ ] Back button uses the same rewind marker flow as breadcrumbs.
- [ ] Session detail page shows rewind events in timeline; decision count excludes rewind markers.
- [ ] Exports (Markdown, Text, HTML) skip rewind markers and render only actual steps.
- [ ] `save_as_tree` ignores rewind markers.
- [ ] Timer displays as a visible pill badge.
- [ ] Option/continue buttons show spinner + disable siblings during loading.
- [ ] MyTreesPage header shows "Create Tree" button (hidden for viewers).
- [ ] Empty state shows both "Browse Library" (primary) and "Create from Scratch" (secondary, permission-gated).
