docs: add script library pane takeover design spec
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
# Script Library — Left Pane Takeover Design Spec
|
||||
|
||||
> **Created:** 2026-03-13
|
||||
> **Feature:** Redesign Script Library left pane to have two distinct modes: Browse and Configure
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The Script Library left pane gains two distinct modes. In **Browse mode** the user sees the full template list with filter bar and a "Configure →" button on each card. Clicking "Configure →" transitions the entire left pane to **Configure mode**, which replaces the list with a full-height view of the selected template's header, parameter form, and action buttons. The right pane (preview/output) is always visible. "Back to library" returns to Browse mode with filter/search state preserved.
|
||||
|
||||
---
|
||||
|
||||
## Goals
|
||||
|
||||
- Make template selection intentional (no accidental click-to-configure)
|
||||
- Give the parameter form more vertical space
|
||||
- Keep the output preview always visible on the right
|
||||
- Preserve filter/search state across Browse ↔ Configure transitions
|
||||
|
||||
---
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- No changes to the right pane (`ScriptPreview`, `ScriptGeneratorPanel` output section)
|
||||
- No changes to the Zustand store shape or actions
|
||||
- No changes to the filter/search debounce logic
|
||||
- No changes to routing
|
||||
|
||||
---
|
||||
|
||||
## Left Pane — Two Modes
|
||||
|
||||
### Browse Mode
|
||||
|
||||
Rendered when `selectedTemplate === null` OR when the user has returned via "Back to library".
|
||||
|
||||
Layout (top to bottom, full pane height):
|
||||
|
||||
1. **Filter bar** (`ScriptFilterBar`) — category pills + search input, same as current
|
||||
2. **Template list** (`ScriptTemplateList`) — scrollable, fills remaining height
|
||||
|
||||
**TemplateCard changes:**
|
||||
- Remove the full-card `onClick` that calls `selectTemplate()`
|
||||
- Remove the active/selected visual state (`bg-primary/10 border-l-[3px]` etc.) — cards are never "selected" in browse mode, they are only "configured"
|
||||
- Add a **"Configure →"** button to each card (right-aligned, replaces the old click-anywhere behavior)
|
||||
- Style: `bg-primary/10 border border-primary/20 text-primary text-xs px-2.5 py-1 rounded-md hover:bg-primary/20 transition-colors`
|
||||
- On click: calls `selectTemplate(template.id)` then transitions pane to Configure mode
|
||||
- The rest of the card (name, description, tags, complexity badge) is non-interactive
|
||||
|
||||
### Configure Mode
|
||||
|
||||
Rendered when `selectedTemplate !== null` (or `isLoadingDetail === true`).
|
||||
|
||||
The entire left pane (including filter bar area) is replaced by the Configure view. No filter bar visible.
|
||||
|
||||
Layout (top to bottom, full pane height, `overflow-y-auto`):
|
||||
|
||||
1. **Back button** — top of pane
|
||||
- `← Back to library`
|
||||
- Style: `flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors mb-4`
|
||||
- On click: calls `store.clearOutput()` then sets pane mode back to Browse (see State section)
|
||||
- Does NOT call `selectTemplate(null)` or clear `selectedTemplate` in the store — the store selection is preserved; only the pane view changes
|
||||
|
||||
2. **Template header**
|
||||
- Name: `text-base font-semibold font-heading text-foreground`
|
||||
- Description (if present): `text-sm text-muted-foreground mt-0.5`
|
||||
- Tag row: complexity badge + category name + template tags (first 3, overflow as `+N`) — same badge styles as current `TemplateCard`
|
||||
- `ShieldAlert` elevation icon if `requires_elevation`
|
||||
- Separator: `border-t border-border mt-3 pt-3`
|
||||
|
||||
3. **`ScriptParameterForm`** (existing component, `canGenerate` prop unchanged)
|
||||
|
||||
4. **Action bar**
|
||||
- **Generate** button: full-width, `bg-gradient-brand`, same disabled/loading behavior as current
|
||||
- **Download .ps1** + **Copy** buttons: side by side below Generate, each `flex-1`
|
||||
- Error text below if `generateError` is set
|
||||
- Warnings callout above Generate if `generationWarnings.length > 0` (same amber box as current)
|
||||
|
||||
**Loading state:** When `isLoadingDetail === true`, the Configure pane shows a centered `<Loader2 size={28} className="text-primary animate-spin" />` instead of the template content. Same as current panel behavior.
|
||||
|
||||
---
|
||||
|
||||
## State — Pane Mode
|
||||
|
||||
Pane mode is **local React state** in `ScriptLibraryPage`, not the Zustand store.
|
||||
|
||||
```tsx
|
||||
const [paneMode, setPaneMode] = useState<'browse' | 'configure'>('browse')
|
||||
```
|
||||
|
||||
Transitions:
|
||||
- `'browse' → 'configure'`: triggered by "Configure →" button click (after `selectTemplate()` call)
|
||||
- `'configure' → 'browse'`: triggered by "Back to library" button click
|
||||
|
||||
**Filter/search preservation:** Filter bar state (`inputValue`, `setInputValue`, store's `activeCategoryId`, `searchQuery`) is untouched by pane mode transitions. The filter bar simply unmounts in configure mode and remounts with the same state in browse mode.
|
||||
|
||||
**`isLoadingDetail` drives configure pane content, not pane mode.** When the user clicks "Configure →":
|
||||
1. `selectTemplate(id)` is called (sets `isLoadingDetail: true` in store)
|
||||
2. `setPaneMode('configure')` is called immediately — the user sees the loading spinner in configure mode, not a flash of browse mode
|
||||
3. When `isLoadingDetail` becomes false, the full configure view renders
|
||||
|
||||
---
|
||||
|
||||
## Component Changes
|
||||
|
||||
### `ScriptLibraryPage.tsx` — Modified
|
||||
|
||||
- Add `paneMode` state (`'browse' | 'configure'`)
|
||||
- Pass `onConfigure` callback to `ScriptTemplateList` (called with `template.id` on "Configure →" click)
|
||||
- `onConfigure` calls `selectTemplate(id)` then `setPaneMode('configure')`
|
||||
- Pass `onBack` callback to the configure pane
|
||||
- `onBack` calls `clearOutput()` then `setPaneMode('browse')`
|
||||
- Render left pane conditionally:
|
||||
- `paneMode === 'browse'`: `ScriptFilterBar` + `ScriptTemplateList`
|
||||
- `paneMode === 'configure'`: new `ScriptConfigurePane` component
|
||||
|
||||
### `TemplateCard.tsx` — Modified
|
||||
|
||||
- Remove `onClick` on the card root button; change root element to `<div>`
|
||||
- Remove `isActive` / active border-l styling
|
||||
- Add "Configure →" button
|
||||
|
||||
### `ScriptTemplateList.tsx` — Modified
|
||||
|
||||
- Accept `onConfigure: (id: string) => void` prop
|
||||
- Pass it to each `TemplateCard`
|
||||
|
||||
### `ScriptConfigurePane.tsx` — New Component
|
||||
|
||||
Encapsulates the configure mode layout:
|
||||
- Back button → calls `onBack` prop
|
||||
- Template header (name, description, tags, complexity, elevation)
|
||||
- Loading spinner when `isLoadingDetail`
|
||||
- `ScriptParameterForm` (with `canGenerate`)
|
||||
- Warnings callout
|
||||
- Action bar: Generate (full-width), Download .ps1 + Copy (side-by-side below)
|
||||
- Error text
|
||||
|
||||
Props:
|
||||
```tsx
|
||||
interface Props {
|
||||
canGenerate: boolean
|
||||
onBack: () => void
|
||||
}
|
||||
```
|
||||
|
||||
All data read directly from Zustand store (`selectedTemplate`, `isLoadingDetail`, `generatedScript`, etc.).
|
||||
|
||||
### `ScriptGeneratorPanel.tsx` — Removed (or repurposed)
|
||||
|
||||
The current `ScriptGeneratorPanel` is superseded by `ScriptConfigurePane`. It should be deleted; its logic is absorbed into `ScriptConfigurePane`.
|
||||
|
||||
---
|
||||
|
||||
## Visual Spec
|
||||
|
||||
### Configure Pane — action bar
|
||||
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ [Generate Script] │ ← full-width, bg-gradient-brand
|
||||
│ [Download .ps1] [Copy] │ ← side-by-side, flex-1 each
|
||||
│ <error text if any> │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
### "Configure →" button on TemplateCard
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ Restart Windows Service [Beginner] 🛡 │
|
||||
│ Stops and restarts a named service │
|
||||
│ services windows +1 [Configure →]│
|
||||
└──────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Affected Files
|
||||
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `frontend/src/pages/ScriptLibraryPage.tsx` | Add `paneMode` state, `onConfigure`/`onBack` callbacks, conditional left-pane render |
|
||||
| `frontend/src/components/scripts/TemplateCard.tsx` | Remove full-card click, add "Configure →" button |
|
||||
| `frontend/src/components/scripts/ScriptTemplateList.tsx` | Accept + thread `onConfigure` prop |
|
||||
| `frontend/src/components/scripts/ScriptConfigurePane.tsx` | New — configure mode layout |
|
||||
| `frontend/src/components/scripts/ScriptGeneratorPanel.tsx` | Delete — superseded by `ScriptConfigurePane` |
|
||||
|
||||
No store changes. No API changes. No routing changes.
|
||||
Reference in New Issue
Block a user