- Portal IconSelect popups to document.body with position:fixed to prevent clipping by modal overflow-y:auto - Replace custom scene selectors in automation editor with EntitySelect command-palette pickers (main scene + fallback scene) - Add IconSelect grid for automation deactivation mode (none/revert/fallback) - Add IconSelect grid for automation condition type and match type - Replace mapped zone source dropdowns with EntitySelect pickers - Replace scene target selector with EntityPalette.pick() pattern - Remove effect palette preview bar from CSS editor - Remove sensitivity badge from audio color strip source cards - Clean up unused scene-selector CSS and scene-target-add-row CSS - Add locale keys for all new UI elements across en/ru/zh Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
10 KiB
Claude Instructions for WLED Screen Controller
CRITICAL: Git Commit and Push Policy
🚨 NEVER CREATE COMMITS WITHOUT EXPLICIT USER APPROVAL 🚨
🚨 NEVER PUSH TO REMOTE WITHOUT EXPLICIT USER APPROVAL 🚨
Strict Rules
- DO NOT create commits automatically after making changes
- DO NOT commit without being explicitly instructed by the user
- DO NOT push to remote repository without explicit instruction
- ALWAYS WAIT for the user to review changes and ask you to commit
- ALWAYS ASK if you're unsure whether to commit
Workflow
- Make code changes as requested
- STOP - Inform user that changes are complete
- WAIT - User reviews the changes
- 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
- Stage the files with
- 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:
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 (seeserver/CLAUDE.mdfor 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:
<div class="form-group">
<div class="label-row">
<label for="my-field" data-i18n="my.label">Label:</label>
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?">?</button>
</div>
<small class="input-hint" style="display:none" data-i18n="my.label.hint">Hint text</small>
<input type="text" id="my-field">
</div>
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 <select> with real options only and let the first one be selected by default.
Enhanced selectors (IconSelect & EntitySelect)
Plain <select> dropdowns should be enhanced with visual selectors depending on the data type:
-
Predefined options (source types, effect types, palettes, waveforms, viz modes) → use
IconSelectfromjs/core/icon-select.js. This replaces the<select>with a visual grid of icon+label+description cells. See_ensureCSSTypeIconSelect(),_ensureEffectTypeIconSelect(),_ensureInterpolationIconSelect()incolor-strips.jsfor examples. -
Entity references (picture sources, audio sources, devices, templates, clocks) → use
EntitySelectfromjs/core/entity-palette.js. This replaces the<select>with a searchable command-palette-style picker. See_cssPictureSourceEntitySelectincolor-strips.jsor_lineSourceEntitySelectinadvanced-calibration.jsfor examples.
Both widgets hide the native <select> but keep it in the DOM with its value in sync. After programmatically changing the <select> 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:
-
Subclass Modal with
snapshotValues()returning an object of all tracked field values: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(); -
Call
modal.snapshot()after the form is fully populated (aftermodal.open()). -
Close/cancel button calls
await modal.close()— triggers dirty check + confirmation. -
Save function calls
modal.forceClose()after successful save — skips dirty check. -
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-propswith 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-linkCSS class and anonclickhandler callingnavigateToCard(tab, subTab, sectionKey, cardAttr, cardValue). Only add the link when the referenced entity is found (to avoid broken navigation). Example:<span class="stream-card-prop stream-card-link" onclick="event.stopPropagation(); navigateToCard('streams','audio','audio-multi','data-id','${id}')">🎵 Name</span>
Modal footer buttons
Use icon-only buttons (✓ / ✕) matching the device settings modal pattern, not text buttons:
<div class="modal-footer">
<button class="btn btn-icon btn-secondary" onclick="closeMyModal()" title="Cancel" data-i18n-title="settings.button.cancel" data-i18n-aria-label="aria.cancel">✕</button>
<button class="btn btn-icon btn-primary" onclick="saveMyEntity()" title="Save" data-i18n-title="settings.button.save" data-i18n-aria-label="aria.save">✓</button>
</div>
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:
<label for="my-slider"><span data-i18n="my.label">Speed:</span> <span id="my-slider-display">1.0</span></label>
...
<input type="range" id="my-slider" min="0" max="10" step="0.1" value="1.0"
oninput="document.getElementById('my-slider-display').textContent = this.value">
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';thenshowToast(t('my.key'), 'error') - In inline
<script>blocks (wheret()may not be available yet): usewindow.t ? t('key') : 'fallback' - In HTML templates: use
data-i18n="key"for text content,data-i18n-title="key"for title attributes,data-i18n-aria-label="key"for aria-labels - Keys follow dotted namespace convention:
feature.context.description(e.g.device.error.brightness,calibration.saved)
General Guidelines
- Always test changes before marking as complete
- Follow existing code style and patterns
- Update documentation when changing behavior
- Write clear, descriptive commit messages when explicitly instructed
- Never make commits or pushes without explicit user approval