# Claude Instructions for WLED Screen Controller ## Code Search **If `ast-index` is available, use it as the PRIMARY code search tool.** It is significantly faster than grep and returns structured, accurate results. Fall back to grep/Glob only when ast-index is not installed, returns empty results, or when searching regex patterns/string literals/comments. **IMPORTANT for subagents:** When spawning Agent subagents (Plan, Explore, general-purpose, etc.), always instruct them to use `ast-index` via Bash for code search instead of grep/Glob. Example: include "Use `ast-index search`, `ast-index class`, `ast-index usages` etc. via Bash for code search" in the agent prompt. ```bash # Check if available: ast-index version # Rebuild index (first time or after major changes): ast-index rebuild # Common commands: ast-index search "Query" # Universal search across files, symbols, modules ast-index class "ClassName" # Find class/struct/interface definitions ast-index usages "SymbolName" # Find all places a symbol is used ast-index implementations "BaseClass" # Find all subclasses/implementations ast-index symbol "FunctionName" # Find any symbol (class, function, property) ast-index outline "path/to/File.cpp" # Show all symbols in a file ast-index hierarchy "ClassName" # Show inheritance tree ast-index callers "FunctionName" # Find all call sites ast-index changed --base master # Show symbols changed in current branch ast-index update # Incremental update after file changes ``` ## CRITICAL: Git Commit and Push Policy **🚨 NEVER CREATE COMMITS WITHOUT EXPLICIT USER APPROVAL 🚨** **🚨 NEVER PUSH TO REMOTE WITHOUT EXPLICIT USER APPROVAL 🚨** ### Strict Rules 1. **DO NOT** create commits automatically after making changes 2. **DO NOT** commit without being explicitly instructed by the user 3. **DO NOT** push to remote repository without explicit instruction 4. **ALWAYS WAIT** for the user to review changes and ask you to commit 5. **ALWAYS ASK** if you're unsure whether to commit ### Workflow 1. Make code changes as requested 2. **STOP** - Inform user that changes are complete 3. **WAIT** - User reviews the changes 4. **ONLY IF** user explicitly says "commit" or "create a commit": - Stage the files with `git add` - Create the commit with a descriptive message - **STOP** - Do NOT push 5. **ONLY IF** user explicitly says "push" or "commit and push": - Push to remote repository ### What Counts as Explicit Approval ✅ **YES - These mean you can commit:** - "commit" - "create a commit" - "commit these changes" - "git commit" ✅ **YES - These mean you can push:** - "push" - "commit and push" - "push to remote" - "git push" ❌ **NO - These do NOT mean you should commit:** - "that looks good" - "thanks" - "perfect" - User silence after you make changes - Completing a feature/fix ### Example Bad Behavior (DON'T DO THIS) ``` ❌ User: "Fix the MSS engine test issue" ❌ Claude: [fixes the issue] ❌ Claude: [automatically commits without asking] <-- WRONG! ``` ### Example Good Behavior (DO THIS) ``` ✅ User: "Fix the MSS engine test issue" ✅ Claude: [fixes the issue] ✅ Claude: "I've fixed the MSS engine test issue by adding auto-initialization..." ✅ [WAITS FOR USER] ✅ User: "Looks good, commit it" ✅ Claude: [now creates the commit] ``` ## IMPORTANT: Auto-Restart Server on Code Changes **Whenever server-side Python code is modified** (any file under `/server/src/` **excluding** `/server/src/wled_controller/static/`), **automatically restart the server** so the changes take effect immediately. Do NOT wait for the user to ask for a restart. **No restart needed for frontend-only changes.** Files under `/server/src/wled_controller/static/` (HTML, JS, CSS, JSON locale files) are served directly by FastAPI's static file handler — changes take effect on the next browser page refresh without restarting the server. ### Restart procedure Use the PowerShell restart script — it reliably stops only the server process and starts a new detached instance: ```bash powershell -ExecutionPolicy Bypass -File "c:\Users\Alexei\Documents\wled-screen-controller\server\restart.ps1" ``` **Do NOT use** `Stop-Process -Name python` (kills unrelated Python processes like VS Code extensions) or bash background `&` jobs (get killed when the shell session ends). ## Default Config & API Key The server configuration is in `/server/config/default_config.yaml`. The default API key for development is `development-key-change-in-production` (label: `dev`). The server runs on port **8080** by default. ## Project Structure This is a monorepo containing: - `/server` - Python FastAPI backend (see `server/CLAUDE.md` for detailed instructions) - `/client` - Future frontend client (if applicable) ## Working with Server For detailed server-specific instructions (restart policy, testing, etc.), see: - `server/CLAUDE.md` ## UI Conventions for Dialogs ### Hints Every form field in a modal should have a hint. Use the `.label-row` wrapper with a `?` toggle button: ```html
``` Add hint text to both `en.json` and `ru.json` locale files using a `.hint` suffix on the label key. ### Select dropdowns Do **not** add placeholder options like `-- Select something --`. Populate the `` dropdowns should be enhanced with visual selectors depending on the data type: - **Predefined options** (source types, effect types, palettes, waveforms, viz modes) → use `IconSelect` from `js/core/icon-select.js`. This replaces the `` with a searchable command-palette-style picker. See `_cssPictureSourceEntitySelect` in `color-strips.js` or `_lineSourceEntitySelect` in `advanced-calibration.js` for examples. Both widgets hide the native `` value, call `.refresh()` (EntitySelect) or `.setValue(val)` (IconSelect) to update the trigger display. Call `.destroy()` when the modal closes. ### Modal dirty check (discard unsaved changes) Every editor modal **must** have a dirty check so closing with unsaved changes shows a "Discard unsaved changes?" confirmation. Use the `Modal` base class pattern from `js/core/modal.js`: 1. **Subclass Modal** with `snapshotValues()` returning an object of all tracked field values: ```javascript class MyEditorModal extends Modal { constructor() { super('my-modal-id'); } snapshotValues() { return { name: document.getElementById('my-name').value, // ... all form fields }; } onForceClose() { // Optional: cleanup (reset flags, clear state, etc.) } } const myModal = new MyEditorModal(); ``` 2. **Call `modal.snapshot()`** after the form is fully populated (after `modal.open()`). 3. **Close/cancel button** calls `await modal.close()` — triggers dirty check + confirmation. 4. **Save function** calls `modal.forceClose()` after successful save — skips dirty check. 5. For complex/dynamic state (filter lists, schedule rows, conditions), serialize to JSON string in `snapshotValues()`. The base class handles: `isDirty()` comparison, confirmation dialog, backdrop click, ESC key, focus trapping, and body scroll lock. ### Card appearance When creating or modifying entity cards (devices, targets, CSS sources, streams, audio/value sources, templates), **always reference existing cards** of the same or similar type for visual consistency. Cards should have: - Clone (📋) and Edit (✏️) icon buttons in `.template-card-actions` - Delete (✕) button as `.card-remove-btn` - Property badges in `.stream-card-props` with emoji icons - **Crosslinks**: When a card references another entity (audio source, picture source, capture template, PP template, etc.), make the property badge a clickable link using the `stream-card-link` CSS class and an `onclick` handler calling `navigateToCard(tab, subTab, sectionKey, cardAttr, cardValue)`. Only add the link when the referenced entity is found (to avoid broken navigation). Example: `🎵 Name` ### Modal footer buttons Use **icon-only** buttons (✓ / ✕) matching the device settings modal pattern, **not** text buttons: ```html ``` ### Slider value display For range sliders, display the current value **inside the label** (not in a separate wrapper). This keeps the value visible next to the property name: ```html ... ``` Do **not** use a `range-with-value` wrapper div. ### Tutorials The app has an interactive tutorial system (`static/js/features/tutorials.js`) with a generic engine, spotlight overlay, tooltip positioning, and keyboard navigation. Tutorials exist for: - **Getting started** (header-level walkthrough of all tabs and controls) - **Per-tab tutorials** (Dashboard, Targets, Sources, Profiles) triggered by `?` buttons - **Device card tutorial** and **Calibration tutorial** (context-specific) When adding **new tabs, sections, or major UI elements**, update the corresponding tutorial step array in `tutorials.js` and add `tour.*` i18n keys to all 3 locale files (`en.json`, `ru.json`, `zh.json`). ## Localization (i18n) **Every user-facing string must be localized.** Never use hardcoded English strings in `showToast()`, `error.textContent`, modal messages, or any other UI-visible text. Always use `t('key')` from `../core/i18n.js` and add the corresponding key to **all three** locale files (`en.json`, `ru.json`, `zh.json`). - In JS modules: `import { t } from '../core/i18n.js';` then `showToast(t('my.key'), 'error')` - In inline `