8.2 KiB
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,ScriptGeneratorPaneloutput 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):
- Filter bar (
ScriptFilterBar) — category pills + search input, same as current - Template list (
ScriptTemplateList) — scrollable, fills remaining height
TemplateCard changes:
- Remove the full-card
onClickthat callsselectTemplate() - 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
- Style:
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):
-
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 clearselectedTemplatein the store — the store selection is preserved; only the pane view changes
-
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 currentTemplateCard ShieldAlertelevation icon ifrequires_elevation- Separator:
border-t border-border mt-3 pt-3
- Name:
-
ScriptParameterForm(existing component,canGenerateprop unchanged) -
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
generateErroris set - Warnings callout above Generate if
generationWarnings.length > 0(same amber box as current)
- Generate button: full-width,
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.
const [paneMode, setPaneMode] = useState<'browse' | 'configure'>('browse')
Transitions:
'browse' → 'configure': triggered by "Configure →" button click (afterselectTemplate()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 →":
selectTemplate(id)is called (setsisLoadingDetail: truein store)setPaneMode('configure')is called immediately — the user sees the loading spinner in configure mode, not a flash of browse mode- When
isLoadingDetailbecomes false, the full configure view renders
Component Changes
ScriptLibraryPage.tsx — Modified
- Add
paneModestate ('browse' | 'configure') - Pass
onConfigurecallback toScriptTemplateList(called withtemplate.idon "Configure →" click)onConfigurecallsselectTemplate(id)thensetPaneMode('configure')
- Pass
onBackcallback to the configure paneonBackcallsclearOutput()thensetPaneMode('browse')
- Render left pane conditionally:
paneMode === 'browse':ScriptFilterBar+ScriptTemplateListpaneMode === 'configure': newScriptConfigurePanecomponent
TemplateCard.tsx — Modified
- Remove
onClickon 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) => voidprop - Pass it to each
TemplateCard
ScriptConfigurePane.tsx — New Component
Encapsulates the configure mode layout:
- Back button → calls
onBackprop - Template header (name, description, tags, complexity, elevation)
- Loading spinner when
isLoadingDetail ScriptParameterForm(withcanGenerate)- Warnings callout
- Action bar: Generate (full-width), Download .ps1 + Copy (side-by-side below)
- Error text
Props:
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.