Files
resolutionflow/docs/superpowers/specs/2026-03-13-script-library-pane-takeover-design.md
2026-03-13 11:49:14 -04:00

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, 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.

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:

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.