Files
led-controller/docs/mockups/presets.html
Jimmy 9e2409430c Add documentation and utility modules
- Add API specification documentation
- Add system specification document
- Add UI mockups and documentation
- Add utility modules (wifi)
2026-01-11 21:34:18 +13:00

969 lines
35 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LED Driver - Presets</title>
<link rel="stylesheet" href="color-picker.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
color: #333;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
.header {
background: white;
border-radius: 12px;
padding: 24px;
margin-bottom: 24px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
display: flex;
justify-content: space-between;
align-items: center;
}
.header h1 {
color: #667eea;
margin-bottom: 8px;
}
.header p {
color: #666;
}
.controls {
background: white;
border-radius: 12px;
padding: 20px;
margin-bottom: 24px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
display: flex;
gap: 16px;
flex-wrap: wrap;
align-items: center;
}
.search-box {
flex: 1;
min-width: 200px;
padding: 12px;
border: 1px solid #e0e0e0;
border-radius: 8px;
font-size: 1rem;
}
.search-box:focus {
outline: none;
border-color: #667eea;
}
.filter-group {
display: flex;
gap: 12px;
align-items: center;
}
.filter-select {
padding: 12px;
border: 1px solid #e0e0e0;
border-radius: 8px;
font-size: 1rem;
background: white;
cursor: pointer;
}
.filter-select:focus {
outline: none;
border-color: #667eea;
}
.view-toggle {
display: flex;
gap: 8px;
border: 1px solid #e0e0e0;
border-radius: 8px;
overflow: hidden;
}
.view-btn {
padding: 8px 16px;
border: none;
background: white;
cursor: pointer;
transition: all 0.2s;
}
.view-btn.active {
background: #667eea;
color: white;
}
.presets-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 24px;
margin-bottom: 24px;
}
.preset-card {
background: white;
border-radius: 12px;
padding: 24px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
transition: all 0.3s;
cursor: pointer;
border: 3px solid transparent;
}
.preset-card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
border-color: #667eea;
}
.preset-card.selected {
border-color: #667eea;
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%);
}
.preset-header {
display: flex;
justify-content: space-between;
align-items: start;
margin-bottom: 16px;
}
.preset-name {
font-size: 1.5rem;
font-weight: 600;
color: #333;
margin-bottom: 8px;
}
.pattern-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 12px;
font-size: 0.75rem;
font-weight: 600;
background: #667eea;
color: white;
margin-bottom: 12px;
}
.pattern-badge.on { background: #4caf50; }
.pattern-badge.off { background: #757575; }
.pattern-badge.blink { background: #ff9800; }
.pattern-badge.chase { background: #f44336; }
.pattern-badge.circle { background: #00bcd4; }
.pattern-badge.pulse { background: #e91e63; }
.pattern-badge.rainbow { background: linear-gradient(90deg, #ff0000, #ff7f00, #ffff00, #00ff00, #0000ff, #4b0082, #9400d3); }
.pattern-badge.transition { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
.color-preview {
display: flex;
gap: 8px;
margin-bottom: 16px;
flex-wrap: wrap;
}
.color-swatch {
width: 40px;
height: 40px;
border-radius: 8px;
border: 2px solid #e0e0e0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.preset-info {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin-bottom: 16px;
font-size: 0.875rem;
color: #666;
}
.info-item {
display: flex;
align-items: center;
gap: 8px;
}
.info-label {
font-weight: 500;
}
.info-value {
color: #667eea;
font-weight: 600;
}
.preset-actions {
display: flex;
gap: 8px;
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid #e0e0e0;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 8px;
font-size: 0.875rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
flex: 1;
}
.btn-primary {
background: #667eea;
color: white;
}
.btn-primary:hover {
background: #5568d3;
}
.btn-secondary {
background: #e0e0e0;
color: #333;
}
.btn-secondary:hover {
background: #d0d0d0;
}
.btn-danger {
background: #f44336;
color: white;
}
.btn-danger:hover {
background: #d32f2f;
}
.btn-icon {
padding: 8px;
min-width: 40px;
}
.btn-large {
padding: 16px 32px;
font-size: 1rem;
}
/* Modal Styles */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
align-items: center;
justify-content: center;
padding: 20px;
}
.modal.active {
display: flex;
}
.modal-content {
background: white;
border-radius: 12px;
padding: 32px;
max-width: 600px;
width: 100%;
max-height: 90vh;
overflow-y: auto;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}
.modal-header {
margin-bottom: 24px;
}
.modal-header h2 {
color: #667eea;
margin-bottom: 8px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: #333;
}
.form-group input,
.form-group select {
width: 100%;
padding: 12px;
border: 1px solid #e0e0e0;
border-radius: 8px;
font-size: 1rem;
}
.form-group input:focus,
.form-group select:focus {
outline: none;
border-color: #667eea;
}
.form-group small {
display: block;
margin-top: 4px;
color: #666;
font-size: 0.875rem;
}
.color-inputs {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.color-input-wrapper {
display: flex;
flex-direction: column;
gap: 8px;
}
.color-input-wrapper {
display: inline-block;
}
.params-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
}
.n-value-input {
width: 100%;
}
.n-value-input:focus {
border-color: #667eea;
outline: none;
}
.param-input {
display: flex;
flex-direction: column;
}
.param-input label {
font-size: 0.75rem;
margin-bottom: 4px;
}
.param-input input {
padding: 8px;
font-size: 0.875rem;
}
.modal-actions {
display: flex;
gap: 12px;
margin-top: 24px;
padding-top: 24px;
border-top: 1px solid #e0e0e0;
}
.empty-state {
background: white;
border-radius: 12px;
padding: 60px 40px;
text-align: center;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.empty-state-icon {
font-size: 4rem;
margin-bottom: 16px;
}
.empty-state h2 {
color: #667eea;
margin-bottom: 8px;
}
.empty-state p {
color: #666;
margin-bottom: 24px;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<div>
<h1>Preset Management</h1>
<p>Save and manage your favorite LED pattern configurations</p>
</div>
<div style="display: flex; gap: 12px; align-items: center;">
<button class="btn btn-secondary btn-large" onclick="syncPresets()" title="Sync all presets to all devices">🔄 Sync Presets to All Devices</button>
<button class="btn btn-primary btn-large" onclick="showCreateModal()">+ Create Preset</button>
</div>
</div>
<div class="controls">
<input type="text" class="search-box" placeholder="Search presets..." id="search-input">
<div class="filter-group">
<select class="filter-select" id="pattern-filter">
<option value="">All Patterns</option>
<option value="on">On</option>
<option value="off">Off</option>
<option value="blink">Blink</option>
<option value="chase">Chase</option>
<option value="circle">Circle</option>
<option value="pulse">Pulse</option>
<option value="rainbow">Rainbow</option>
<option value="transition">Transition</option>
</select>
<select class="filter-select" id="sort-select">
<option value="name">Sort by Name</option>
<option value="recent">Recently Used</option>
<option value="created">Recently Created</option>
</select>
<div class="view-toggle">
<button class="view-btn active" onclick="setView('grid')" id="view-grid">Grid</button>
<button class="view-btn" onclick="setView('list')" id="view-list">List</button>
</div>
</div>
</div>
<div class="presets-grid" id="presets-container">
<!-- Preset Card 1 -->
<div class="preset-card" data-pattern="rainbow" data-name="Fast Rainbow">
<div class="preset-header">
<div>
<div class="preset-name">Fast Rainbow</div>
<span class="pattern-badge rainbow">Rainbow</span>
</div>
</div>
<div class="color-preview">
<div class="color-swatch" style="background: #FF0000;"></div>
<div class="color-swatch" style="background: #00FF00;"></div>
<div class="color-swatch" style="background: #0000FF;"></div>
</div>
<div class="preset-info">
<div class="info-item">
<span class="info-label">Delay:</span>
<span class="info-value">30ms</span>
</div>
<div class="info-item">
<span class="info-label">N1:</span>
<span class="info-value">10</span>
</div>
</div>
<div class="preset-actions">
<button class="btn btn-primary" onclick="applyPreset('Fast Rainbow')">Apply</button>
<button class="btn btn-secondary btn-icon" onclick="editPreset('Fast Rainbow')" title="Edit">✏️</button>
<button class="btn btn-danger btn-icon" onclick="deletePreset('Fast Rainbow')" title="Delete">🗑️</button>
</div>
</div>
<!-- Preset Card 2 -->
<div class="preset-card" data-pattern="pulse" data-name="Slow Pulse">
<div class="preset-header">
<div>
<div class="preset-name">Slow Pulse</div>
<span class="pattern-badge pulse">Pulse</span>
</div>
</div>
<div class="color-preview">
<div class="color-swatch" style="background: #FF0000;"></div>
<div class="color-swatch" style="background: #0000FF;"></div>
</div>
<div class="preset-info">
<div class="info-item">
<span class="info-label">Delay:</span>
<span class="info-value">200ms</span>
</div>
<div class="info-item">
<span class="info-label">N1:</span>
<span class="info-value">500</span>
</div>
</div>
<div class="preset-actions">
<button class="btn btn-primary" onclick="applyPreset('Slow Pulse')">Apply</button>
<button class="btn btn-secondary btn-icon" onclick="editPreset('Slow Pulse')" title="Edit">✏️</button>
<button class="btn btn-danger btn-icon" onclick="deletePreset('Slow Pulse')" title="Delete">🗑️</button>
</div>
</div>
<!-- Preset Card 3 -->
<div class="preset-card" data-pattern="chase" data-name="Red Blue Chase">
<div class="preset-header">
<div>
<div class="preset-name">Red Blue Chase</div>
<span class="pattern-badge chase">Chase</span>
</div>
</div>
<div class="color-preview">
<div class="color-swatch" style="background: #FF0000;"></div>
<div class="color-swatch" style="background: #0000FF;"></div>
</div>
<div class="preset-info">
<div class="info-item">
<span class="info-label">Delay:</span>
<span class="info-value">100ms</span>
</div>
<div class="info-item">
<span class="info-label">N1:</span>
<span class="info-value">5</span>
</div>
</div>
<div class="preset-actions">
<button class="btn btn-primary" onclick="applyPreset('Red Blue Chase')">Apply</button>
<button class="btn btn-secondary btn-icon" onclick="editPreset('Red Blue Chase')" title="Edit">✏️</button>
<button class="btn btn-danger btn-icon" onclick="deletePreset('Red Blue Chase')" title="Delete">🗑️</button>
</div>
</div>
<!-- Preset Card 4 -->
<div class="preset-card" data-pattern="circle" data-name="Loading Circle">
<div class="preset-header">
<div>
<div class="preset-name">Loading Circle</div>
<span class="pattern-badge circle">Circle</span>
</div>
</div>
<div class="color-preview">
<div class="color-swatch" style="background: #00FF00;"></div>
</div>
<div class="preset-info">
<div class="info-item">
<span class="info-label">Delay:</span>
<span class="info-value">50ms</span>
</div>
<div class="info-item">
<span class="info-label">N1:</span>
<span class="info-value">50</span>
</div>
</div>
<div class="preset-actions">
<button class="btn btn-primary" onclick="applyPreset('Loading Circle')">Apply</button>
<button class="btn btn-secondary btn-icon" onclick="editPreset('Loading Circle')" title="Edit">✏️</button>
<button class="btn btn-danger btn-icon" onclick="deletePreset('Loading Circle')" title="Delete">🗑️</button>
</div>
</div>
<!-- Preset Card 5 -->
<div class="preset-card" data-pattern="blink" data-name="Party Blink">
<div class="preset-header">
<div>
<div class="preset-name">Party Blink</div>
<span class="pattern-badge blink">Blink</span>
</div>
</div>
<div class="color-preview">
<div class="color-swatch" style="background: #FF00FF;"></div>
<div class="color-swatch" style="background: #00FFFF;"></div>
<div class="color-swatch" style="background: #FFFF00;"></div>
</div>
<div class="preset-info">
<div class="info-item">
<span class="info-label">Delay:</span>
<span class="info-value">150ms</span>
</div>
<div class="info-item">
<span class="info-label">N1:</span>
<span class="info-value">0</span>
</div>
</div>
<div class="preset-actions">
<button class="btn btn-primary" onclick="applyPreset('Party Blink')">Apply</button>
<button class="btn btn-secondary btn-icon" onclick="editPreset('Party Blink')" title="Edit">✏️</button>
<button class="btn btn-danger btn-icon" onclick="deletePreset('Party Blink')" title="Delete">🗑️</button>
</div>
</div>
<!-- Preset Card 6 -->
<div class="preset-card" data-pattern="transition" data-name="Smooth Transition">
<div class="preset-header">
<div>
<div class="preset-name">Smooth Transition</div>
<span class="pattern-badge transition">Transition</span>
</div>
</div>
<div class="color-preview">
<div class="color-swatch" style="background: #FF0000;"></div>
<div class="color-swatch" style="background: #00FF00;"></div>
<div class="color-swatch" style="background: #0000FF;"></div>
<div class="color-swatch" style="background: #FFFF00;"></div>
</div>
<div class="preset-info">
<div class="info-item">
<span class="info-label">Delay:</span>
<span class="info-value">100ms</span>
</div>
<div class="info-item">
<span class="info-label">N1:</span>
<span class="info-value">0</span>
</div>
</div>
<div class="preset-actions">
<button class="btn btn-primary" onclick="applyPreset('Smooth Transition')">Apply</button>
<button class="btn btn-secondary btn-icon" onclick="editPreset('Smooth Transition')" title="Edit">✏️</button>
<button class="btn btn-danger btn-icon" onclick="deletePreset('Smooth Transition')" title="Delete">🗑️</button>
</div>
</div>
</div>
</div>
<!-- Create/Edit Preset Modal -->
<div class="modal" id="preset-modal">
<div class="modal-content">
<div class="modal-header">
<h2 id="modal-title">Create Preset</h2>
<p>Configure your preset settings</p>
</div>
<form id="preset-form">
<div class="form-group">
<label for="preset-name">Preset Name *</label>
<input type="text" id="preset-name" required placeholder="Enter preset name">
<small>Unique identifier for this preset</small>
</div>
<div class="form-group">
<label for="preset-pattern">Pattern *</label>
<select id="preset-pattern" required>
<option value="on">On</option>
<option value="off">Off</option>
<option value="blink">Blink</option>
<option value="chase">Chase</option>
<option value="circle">Circle</option>
<option value="pulse">Pulse</option>
<option value="rainbow">Rainbow</option>
<option value="transition">Transition</option>
</select>
</div>
<div class="form-group">
<label>Colors *</label>
<div class="color-inputs" id="color-inputs">
<!-- Color pickers will be added here -->
</div>
<button type="button" class="btn btn-secondary" onclick="addColorPicker()" style="margin-top: 8px;">+ Add Color</button>
<small>Minimum 2 colors required</small>
</div>
<div class="form-group">
<label for="preset-delay">
Delay (ms) *
<span id="delay-value-display" style="margin-left: 12px; color: #667eea; font-weight: 600;">100</span>
</label>
<input type="range" id="preset-delay" min="10" max="1000" value="100" step="10" required>
<small>Animation speed (10-1000 milliseconds)</small>
</div>
<div class="form-group">
<label for="step-offset">Step Offset</label>
<input type="number" id="step-offset" value="0" min="-1000" max="1000">
<small>Step offset for group synchronization. Applied per device when preset is used in a group.</small>
</div>
<div class="form-group">
<label for="step-increment">Step Increment</label>
<input type="number" id="step-increment" value="1" min="1" max="255">
<small>Amount step counter increments per cycle. Controls pattern advancement speed.</small>
</div>
<div class="form-group">
<label>Pattern Parameters (N1-N8)</label>
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
<small style="margin: 0;">Pattern-specific parameters (0-255, varies by pattern)</small>
<button type="button" class="btn btn-secondary" onclick="setAllNValues(0)" style="padding: 6px 12px; font-size: 0.75rem;">Reset All to 0</button>
</div>
<div class="params-grid">
<div class="param-input">
<label>N1</label>
<input type="number" id="n1" min="0" max="255" value="0" class="n-value-input">
</div>
<div class="param-input">
<label>N2</label>
<input type="number" id="n2" min="0" max="255" value="0" class="n-value-input">
</div>
<div class="param-input">
<label>N3</label>
<input type="number" id="n3" min="0" max="255" value="0" class="n-value-input">
</div>
<div class="param-input">
<label>N4</label>
<input type="number" id="n4" min="0" max="255" value="0" class="n-value-input">
</div>
<div class="param-input">
<label>N5</label>
<input type="number" id="n5" min="0" max="255" value="0" class="n-value-input">
</div>
<div class="param-input">
<label>N6</label>
<input type="number" id="n6" min="0" max="255" value="0" class="n-value-input">
</div>
<div class="param-input">
<label>N7</label>
<input type="number" id="n7" min="0" max="255" value="0" class="n-value-input">
</div>
<div class="param-input">
<label>N8</label>
<input type="number" id="n8" min="0" max="255" value="0" class="n-value-input">
</div>
</div>
<div style="margin-top: 12px; display: flex; gap: 8px; flex-wrap: wrap;">
<button type="button" class="btn btn-secondary" onclick="setAllNValues(0)" style="padding: 8px 16px; font-size: 0.875rem;">Set All to 0</button>
<button type="button" class="btn btn-secondary" onclick="copyNValuesFromCurrent()" style="padding: 8px 16px; font-size: 0.875rem;">Copy from Current Settings</button>
<button type="button" class="btn btn-secondary" onclick="showNValueHelp()" style="padding: 8px 16px; font-size: 0.875rem;"> Parameter Help</button>
</div>
</div>
<div class="modal-actions">
<button type="button" class="btn btn-secondary" onclick="closeModal()">Cancel</button>
<button type="submit" class="btn btn-primary">Save Preset</button>
</div>
</form>
</div>
</div>
<script>
let currentView = 'grid';
let editingPreset = null;
// Delay slider update
document.getElementById('preset-delay').addEventListener('input', function(e) {
document.getElementById('delay-value-display').textContent = e.target.value;
});
// Search functionality
document.getElementById('search-input').addEventListener('input', function(e) {
filterPresets();
});
// Filter functionality
document.getElementById('pattern-filter').addEventListener('change', function(e) {
filterPresets();
});
// Sort functionality
document.getElementById('sort-select').addEventListener('change', function(e) {
sortPresets(e.target.value);
});
function filterPresets() {
const search = document.getElementById('search-input').value.toLowerCase();
const patternFilter = document.getElementById('pattern-filter').value;
const cards = document.querySelectorAll('.preset-card');
cards.forEach(card => {
const name = card.dataset.name.toLowerCase();
const pattern = card.dataset.pattern;
const matchesSearch = name.includes(search);
const matchesPattern = !patternFilter || pattern === patternFilter;
if (matchesSearch && matchesPattern) {
card.style.display = '';
} else {
card.style.display = 'none';
}
});
}
function sortPresets(sortBy) {
const container = document.getElementById('presets-container');
const cards = Array.from(container.querySelectorAll('.preset-card'));
cards.sort((a, b) => {
if (sortBy === 'name') {
return a.dataset.name.localeCompare(b.dataset.name);
} else if (sortBy === 'recent') {
// In real implementation, would use actual usage data
return 0;
} else if (sortBy === 'created') {
// In real implementation, would use creation timestamps
return 0;
}
return 0;
});
cards.forEach(card => container.appendChild(card));
}
function setView(view) {
currentView = view;
const container = document.getElementById('presets-container');
const gridBtn = document.getElementById('view-grid');
const listBtn = document.getElementById('view-list');
if (view === 'grid') {
container.style.gridTemplateColumns = 'repeat(auto-fill, minmax(300px, 1fr))';
gridBtn.classList.add('active');
listBtn.classList.remove('active');
} else {
container.style.gridTemplateColumns = '1fr';
gridBtn.classList.remove('active');
listBtn.classList.add('active');
}
}
function showCreateModal() {
editingPreset = null;
document.getElementById('modal-title').textContent = 'Create Preset';
document.getElementById('preset-form').reset();
document.getElementById('preset-delay').value = 100;
document.getElementById('delay-value-display').textContent = '100';
// Reset to 2 colors
initializeColorPickers();
document.getElementById('preset-modal').classList.add('active');
}
// Initialize color pickers function (defined before showCreateModal)
const presetColorPickers = [];
function initializeColorPickers() {
const colorInputs = document.getElementById('color-inputs');
colorInputs.innerHTML = '';
presetColorPickers.length = 0;
addColorPicker('#FF0000');
addColorPicker('#0000FF');
}
function addColorPicker(color = '#00FF00') {
const colorInputs = document.getElementById('color-inputs');
const wrapper = document.createElement('div');
wrapper.className = 'color-input-wrapper';
colorInputs.appendChild(wrapper);
const picker = new ColorPicker(wrapper, {
initialColor: color,
onColorChange: (newColor) => {
console.log('Preset color changed:', newColor);
}
});
presetColorPickers.push(picker);
return picker;
}
function editPreset(name) {
editingPreset = name;
document.getElementById('modal-title').textContent = 'Edit Preset';
// In real implementation, would load preset data
document.getElementById('preset-name').value = name;
document.getElementById('preset-modal').classList.add('active');
}
function closeModal() {
document.getElementById('preset-modal').classList.remove('active');
editingPreset = null;
}
function applyPreset(name) {
alert(`Applying preset: ${name}\n(In real implementation, this would send preset configuration to device(s))`);
}
function deletePreset(name) {
if (confirm(`Delete preset "${name}"?`)) {
alert(`Preset "${name}" deleted\n(In real implementation, this would remove the preset from storage)`);
}
}
function syncPresets() {
if (confirm('Sync all presets to all devices?\nThis will send all presets from master to all devices via ESPNow.')) {
alert('Syncing presets to all devices...\n(In real implementation, this would send all presets via ESPNow to all devices)');
}
}
function setAllNValues(value) {
for (let i = 1; i <= 8; i++) {
document.getElementById(`n${i}`).value = value;
}
}
function copyNValuesFromCurrent() {
// In real implementation, this would copy from current device settings
alert('Copying N values from current device settings...\n(In real implementation, this would load current N1-N8 values from the active device)');
// Example: would set values like this:
// document.getElementById('n1').value = currentSettings.n1;
// ... etc
}
function showNValueHelp() {
const helpText = `
Pattern Parameter Guide:
Rainbow:
N1: Step increment (1-255, default: 1)
Pulse:
N1: Attack time in ms (0-255)
N2: Hold time in ms (0-255)
N3: Decay time in ms (0-255)
Chase:
N1: LEDs of color 0 (1-255)
N2: LEDs of color 1 (1-255)
N3: Step movement on odd steps (can be negative)
N4: Step movement on even steps (can be negative)
Circle:
N1: Head moves per second (1-255)
N2: Max length in LEDs (1-255)
N3: Tail moves per second (1-255)
N4: Min length in LEDs (0-255)
Other patterns:
N1-N8: Reserved for future pattern enhancements
All values range from 0-255 unless otherwise specified.
`;
alert(helpText);
}
// Form submission
document.getElementById('preset-form').addEventListener('submit', function(e) {
e.preventDefault();
const name = document.getElementById('preset-name').value;
const action = editingPreset ? 'updated' : 'created';
alert(`Preset "${name}" ${action}!\n(In real implementation, this would save the preset to storage)`);
closeModal();
});
// Close modal on outside click
document.getElementById('preset-modal').addEventListener('click', function(e) {
if (e.target === this) {
closeModal();
}
});
</script>
<script src="color-picker.js"></script>
</body>
</html>