191 lines
8.2 KiB
Markdown
191 lines
8.2 KiB
Markdown
# 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.
|