PURPOSE

Left-side library pane for the Weapon Workbench screen. Renders a searchable two-column grid of seed forge components, lets the user select one (driving the rest of the workbench UI), and exposes a ”+ New” entry point that opens the new-component modal.

OWNS

  • Local React state modalOpen (controls NewComponentModal visibility).
  • Layout of the library pane: header row (label + ”+ New” button), search input, scrollable card grid, empty-state placeholder, and the mounted NewComponentModal.
  • Per-card visual treatment, including thumbnail placeholder text derived from c.kind / c.baked.shader and selected-state styling.
  • Client-side filter logic over SEED_COMPONENTS matching the lowercased librarySearch query against component id and name.

READS FROM

  • useWorkbenchStore (./state/workbench-store): selectedComponentId, librarySearch.
  • SEED_COMPONENTS from ./fixtures as the source list of components.

PUSHES TO

  • useWorkbenchStore actions: selectComponent(id) on card click and after a new component is created; setLibrarySearch(value) on every search input change.
  • NewComponentModal via open, onClose, and onCreated props; on onCreated(id) it closes the modal and selects the newly created component.

DOES NOT

  • Does not fetch, persist, or mutate any component data (only the seed fixture list is rendered; creation is handled inside NewComponentModal).
  • Does not render component details, baked output, GLSL editor, or any preview — those live in sibling workbench panes.
  • Does not manage workbench-wide layout, routing, or modal stacking beyond its own new-component modal.
  • Does not debounce or paginate search; the entire fixture list is filtered on every keystroke.
  • Does not handle keyboard navigation, drag-and-drop, or context menus on cards.

Signals

  • data-testid="new-component-btn" on the ”+ New” button.
  • data-testid="library-card-${c.id}" on each component card button.
  • Empty-state message differs depending on whether librarySearch is set (“No components match your search.”) or not (“No components yet. Click + New to create one.”).

Entry points

  • Default export-equivalent named export ForgeLibraryPane, used by the weapon-workbench screen as the left pane.

Pattern notes

  • Functional component using useState for local modal state and selector-style subscriptions to useWorkbenchStore (one selector per field) — minimizes re-renders to changes in those slices.
  • Search filter is case-insensitive via toLowerCase() on both query and candidate fields, with String(... ?? '') guards so missing id/name do not throw.
  • Components are typed loosely as any at the iteration boundary, accommodating heterogeneous shapes in SEED_COMPONENTS (e.g. kind === 'live_shader' vs baked components with baked.shader).
  • All styling is inline; the file uses two custom font families (Cal Sans for the LIBRARY label, Space Grotesk for controls) and a dark palette anchored on #0d0d18 background with #44ffcc accent for selection and the primary action.
  • Selection state is derived (c.id === selectedComponentId) rather than stored locally, keeping the store as the single source of truth.
  • NewComponentModal is always mounted and gated by the open prop, rather than conditionally rendered.