Adds a new "procedural" tree type for linear step-by-step project workflows (domain controller setup, M365 onboarding, VPN config, etc). Includes intake form builder, two-panel step navigation, variable resolution, procedural exports, 3 seed templates, and UI rename from "Trees" to "Flows". Also archives 19 implemented plan docs and creates deferred features backlog. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
195 lines
8.6 KiB
Markdown
195 lines
8.6 KiB
Markdown
# 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).
|