--- name: add-section description: Create a new HUD section (tab) with lifecycle, persistent state, and registry registration aliases: [new-section, create-section] --- # Add Section Skill ## Usage ``` /add-section ``` --- ## Step 1: Ask Questions ### 1. Section Purpose ``` Brief description (1 sentence): What does this section display/manage? ``` ### 2. Persistent State ``` What state needs to persist across sessions? (Must be JSON-serializable: strings, numbers, booleans, arrays, plain objects) Examples: - Selected tab/filter - Sort order - Collapsed/expanded states - User preferences ``` ### 3. Game Data ``` Does this section need game data? A) Static data → MGData (plants, items, pets, etc.) B) Real-time state → Globals (myInventory, myGarden, etc.) C) Both D) None ``` ### 4. Sprites ``` Does it display game sprites? → MGSprite.toCanvas() ``` ### 5. Child Components ``` Does it need existing UI components? → REUSE from src/ui/components/ [ ] ArcadeButton, GeminiIconButton → Buttons [ ] Modal → Dialogs [ ] ProgressBar → Progress [ ] SegmentedControl → Tab-like selection [ ] Tab → Tabs [ ] SoundPicker → Audio [ ] Other: ___ ``` ### 6. Complexity ``` A) Simple - Single file section B) Complex - Split into parts/ folder ``` ### 7. Tab Info ``` - Label (displayed in tab bar): ___________ - Icon (emoji): ___________ ``` --- ## Step 2: Create Structure ### Simple Section ``` src/ui/sections// ├── index.ts # Public exports ├── section.ts # build() / destroy() lifecycle ├── state.ts # Persistent state (createSectionStore) └── styles.css.ts # Scoped CSS (optional) ``` ### Complex Section (with parts) ``` src/ui/sections// ├── index.ts ├── section.ts # Assembles parts ├── state.ts ├── styles.css.ts └── parts/ ├── header.ts # Search, filters ├── content.ts # Main content └── footer.ts # Actions ``` **Read existing sections for templates:** `src/ui/sections/*/` --- ## Step 3: File Responsibilities | File | Purpose | |------|---------| | `index.ts` | Export `Section` and types only | | `section.ts` | `build(container)`, `destroy()`, `cleanups[]` | | `state.ts` | `createSectionStore()` with stable ID `tab-` | | `styles.css.ts` | CSS string with scoped classes | | `parts/*.ts` | Sub-components with own `destroy()` | ### Key Patterns #### state.ts ```typescript import { createSectionStore } from '../core/state'; export interface State { // JSON-serializable only! selectedTab: string; sortOrder: 'asc' | 'desc'; } const DEFAULT_STATE: State = { selectedTab: 'all', sortOrder: 'asc', }; // ID must be stable forever (storage key) export const state = createSectionStore<State>('tab-', { version: 1, // Increment when state shape changes defaults: DEFAULT_STATE, }); ``` #### section.ts ```typescript export interface SectionDefinition { build(container: HTMLElement): void; destroy(): void; } let root: HTMLElement | null = null; const cleanups: (() => void)[] = []; function build(container: HTMLElement): void { if (root) return; // Prevent double-build root = document.createElement('div'); root.className = '-section'; // Inject styles const style = document.createElement('style'); style.textContent = Css; root.appendChild(style); // Subscribe to state const unsub = state.subscribe((s) => { /* update UI */ }); cleanups.push(unsub); // Add child components (reuse existing!) // const button = createArcadeButton({ ... }); // cleanups.push(() => button.destroy()); container.appendChild(root); } function destroy(): void { cleanups.forEach(fn => fn()); cleanups.length = 0; root?.remove(); root = null; } export const Section: SectionDefinition = { build, destroy }; ``` --- ## Step 4: Register ### Registry → `src/ui/sections/registry.ts` ```typescript import { Section } from './'; export const sectionRegistry: Record = { // ... existing 'tab-': { id: 'tab-', label: '