- Audio value source cards link to the referenced audio source - Adaptive scene cards link to the referenced picture source - Fix SceneValueStream starting at 0.5 regardless of actual scene; first frame now skips smoothing to avoid artificial bias - Add crosslinks guidance to CLAUDE.md card appearance section Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
192 lines
7.7 KiB
Markdown
192 lines
7.7 KiB
Markdown
# 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
|
|
|
|
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
|
|
<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.
|
|
|
|
### 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: `<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:
|
|
|
|
```html
|
|
<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:
|
|
|
|
```html
|
|
<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.
|
|
|
|
## 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
|