feat(patterns,api): pattern OTA, graceful shutdown, driver delivery updates
- Pattern controller/UI and presets patterns tab for OTA to Wi-Fi drivers - Device controller extensions; driver_delivery chunk handling - main: SIGINT/SIGTERM shutdown, TCP/UDP server close coordination - Submodule led-driver: Wi-Fi default transport, lazy espnow import, dynamic patterns Made-with: Cursor
This commit is contained in:
@@ -547,32 +547,26 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
presetPatternInput.style.backgroundColor = '';
|
||||
presetPatternInput.style.cursor = '';
|
||||
}
|
||||
|
||||
// Update labels and visibility based on pattern
|
||||
updatePresetNLabels(patternName);
|
||||
|
||||
|
||||
// Get pattern config to map descriptive names back to n keys
|
||||
const patternConfig = cachedPatterns && cachedPatterns[patternName];
|
||||
const nToLabel = {};
|
||||
if (patternConfig && typeof patternConfig === 'object') {
|
||||
// Now n keys are keys, labels are values
|
||||
Object.entries(patternConfig).forEach(([nKey, label]) => {
|
||||
if (typeof nKey === 'string' && nKey.startsWith('n') && typeof label === 'string') {
|
||||
nToLabel[nKey] = label;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Set n values, checking both n keys and descriptive names
|
||||
for (let i = 1; i <= 8; i++) {
|
||||
const nKey = `n${i}`;
|
||||
const inputEl = document.getElementById(`preset-${nKey}-input`);
|
||||
if (inputEl) {
|
||||
// First check if preset has n key directly
|
||||
if (preset[nKey] !== undefined) {
|
||||
inputEl.value = preset[nKey] || 0;
|
||||
} else {
|
||||
// Check if preset has descriptive name (from pattern.json mapping)
|
||||
const label = nToLabel[nKey];
|
||||
if (label && preset[label] !== undefined) {
|
||||
inputEl.value = preset[label] || 0;
|
||||
@@ -582,6 +576,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// After values: show only mapped n params with labels from pattern.json; clear hidden inputs
|
||||
updatePresetNLabels(patternName);
|
||||
updatePresetEditorTabActionsVisibility();
|
||||
};
|
||||
|
||||
@@ -774,44 +771,65 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
};
|
||||
|
||||
const updatePresetNLabels = (patternName) => {
|
||||
const rawPatternName = String(patternName || '').trim();
|
||||
const normalizedPatternName = rawPatternName.endsWith('.py')
|
||||
? rawPatternName.slice(0, -3)
|
||||
: rawPatternName;
|
||||
let patternConfig =
|
||||
(cachedPatterns && cachedPatterns[rawPatternName]) ||
|
||||
(cachedPatterns && cachedPatterns[normalizedPatternName]) ||
|
||||
null;
|
||||
if (!patternConfig && cachedPatterns && typeof cachedPatterns === 'object') {
|
||||
const lower = normalizedPatternName.toLowerCase();
|
||||
const matchedKey = Object.keys(cachedPatterns).find(
|
||||
(k) => String(k).toLowerCase() === lower,
|
||||
);
|
||||
if (matchedKey) {
|
||||
patternConfig = cachedPatterns[matchedKey];
|
||||
}
|
||||
}
|
||||
if (patternConfig && typeof patternConfig === 'object' && patternConfig.data && typeof patternConfig.data === 'object') {
|
||||
patternConfig = patternConfig.data;
|
||||
}
|
||||
if (patternConfig && typeof patternConfig === 'object' && patternConfig.parameter_mappings && typeof patternConfig.parameter_mappings === 'object') {
|
||||
patternConfig = patternConfig.parameter_mappings;
|
||||
}
|
||||
const labels = {};
|
||||
const visibleNKeys = new Set();
|
||||
|
||||
// Initialize all labels with default n1:, n2:, etc.
|
||||
for (let i = 1; i <= 8; i++) {
|
||||
labels[`n${i}`] = `n${i}:`;
|
||||
}
|
||||
|
||||
const patternConfig = cachedPatterns && cachedPatterns[patternName];
|
||||
|
||||
if (patternConfig && typeof patternConfig === 'object') {
|
||||
// Now n values are keys and descriptive names are values
|
||||
Object.entries(patternConfig).forEach(([key, label]) => {
|
||||
if (typeof key === 'string' && key.startsWith('n') && typeof label === 'string') {
|
||||
labels[key] = `${label}:`;
|
||||
visibleNKeys.add(key); // Mark this n key as visible
|
||||
const text = label.trim();
|
||||
if (text) {
|
||||
labels[key] = `${text}:`;
|
||||
visibleNKeys.add(key);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Update labels and show/hide input groups
|
||||
|
||||
for (let i = 1; i <= 8; i++) {
|
||||
const nKey = `n${i}`;
|
||||
const labelEl = document.getElementById(`preset-${nKey}-label`);
|
||||
const inputEl = document.getElementById(`preset-${nKey}-input`);
|
||||
const groupEl = labelEl ? labelEl.closest('.n-param-group') : null;
|
||||
|
||||
const show = visibleNKeys.has(nKey);
|
||||
const inputEl = document.getElementById(`preset-${nKey}-input`);
|
||||
|
||||
if (labelEl) {
|
||||
labelEl.textContent = labels[nKey];
|
||||
labelEl.textContent = show ? labels[nKey] : '';
|
||||
}
|
||||
|
||||
// Show or hide the entire group based on whether it has a mapping
|
||||
if (groupEl) {
|
||||
if (visibleNKeys.has(nKey)) {
|
||||
groupEl.style.display = ''; // Show
|
||||
} else {
|
||||
groupEl.style.display = 'none'; // Hide
|
||||
}
|
||||
groupEl.style.display = show ? '' : 'none';
|
||||
}
|
||||
if (inputEl && !show) {
|
||||
inputEl.value = '0';
|
||||
}
|
||||
}
|
||||
|
||||
const nGrid = presetEditorModal && presetEditorModal.querySelector('.n-params-grid');
|
||||
if (nGrid) {
|
||||
nGrid.style.display = visibleNKeys.size > 0 ? '' : 'none';
|
||||
}
|
||||
};
|
||||
|
||||
@@ -845,6 +863,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
editButton.addEventListener('click', async () => {
|
||||
currentEditId = presetId;
|
||||
currentEditTabId = null;
|
||||
await loadPatterns();
|
||||
const paletteColors = await getCurrentProfilePaletteColors();
|
||||
const presetForEditor = {
|
||||
...(preset || {}),
|
||||
|
||||
Reference in New Issue
Block a user