From 178d115cc59badf3ec7e5990460c7e0b1cefbae1 Mon Sep 17 00:00:00 2001 From: "alexei.dolgolyov" Date: Tue, 24 Mar 2026 14:09:49 +0300 Subject: [PATCH] refactor: remove inline gradient editor from CSS modal, use entity picker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the gradient stop editor (canvas, markers, stop list) in the CSS editor gradient section with a simple gradient entity selector. Gradients are now created/edited exclusively in the Gradients tab. Fix effect and audio palette pickers to populate from gradient entities dynamically instead of hardcoded HTML options. Unify all gradient/palette pickers via _buildGradientEntityItems(). Also: rename "None (use own speed)" → "None (no sync)" for sync clocks. Add i18n keys for gradient selector and missing error messages. --- .../static/js/features/color-strips.ts | 79 ++++++------------- .../wled_controller/static/locales/en.json | 5 +- .../templates/modals/css-editor.html | 57 +------------ 3 files changed, 32 insertions(+), 109 deletions(-) diff --git a/server/src/wled_controller/static/js/features/color-strips.ts b/server/src/wled_controller/static/js/features/color-strips.ts index dcf80ac..8497e44 100644 --- a/server/src/wled_controller/static/js/features/color-strips.ts +++ b/server/src/wled_controller/static/js/features/color-strips.ts @@ -398,10 +398,7 @@ function _ensureEffectTypeIconSelect() { function _ensureEffectPaletteIconSelect() { const sel = document.getElementById('css-editor-effect-palette') as HTMLSelectElement | null; if (!sel) return; - const gradients = _getGradients(); - const items = gradients.map(g => ({ - value: g.id, icon: _gradientEntityStripHTML(g.stops), label: g.name, - })); + const items = _buildGradientEntityItems(); if (_effectPaletteIconSelect) { _effectPaletteIconSelect.updateItems(items); return; } _effectPaletteIconSelect = new IconSelect({ target: sel, items, columns: 2 }); } @@ -435,10 +432,7 @@ function _ensureCandleTypeIconSelect() { function _ensureAudioPaletteIconSelect() { const sel = document.getElementById('css-editor-audio-palette') as HTMLSelectElement | null; if (!sel) return; - const gradients = _getGradients(); - const items = gradients.map(g => ({ - value: g.id, icon: _gradientEntityStripHTML(g.stops), label: g.name, - })); + const items = _buildGradientEntityItems(); if (_audioPaletteIconSelect) { _audioPaletteIconSelect.updateItems(items); return; } _audioPaletteIconSelect = new IconSelect({ target: sel, items, columns: 2 }); } @@ -455,37 +449,27 @@ function _ensureAudioVizIconSelect() { _audioVizIconSelect = new IconSelect({ target: sel, items, columns: 3 }); } -function _buildGradientPresetItems() { +function _buildGradientEntityItems() { const gradients = _getGradients(); - const builtInItems = gradients.filter(g => g.is_builtin).map(g => ({ + return gradients.map(g => ({ value: g.id, icon: _gradientEntityStripHTML(g.stops), label: g.name, })); - const userItems = gradients.filter(g => !g.is_builtin).map(g => ({ - value: g.id, icon: _gradientEntityStripHTML(g.stops), label: g.name, - isCustom: true, - })); - return [ - { value: '', icon: _icon(P.palette), label: t('color_strip.gradient.preset.custom') }, - ...builtInItems, - ...userItems, - ]; } function _ensureGradientPresetIconSelect() { const sel = document.getElementById('css-editor-gradient-preset') as HTMLSelectElement | null; if (!sel) return; - const items = _buildGradientPresetItems(); + const items = _buildGradientEntityItems(); if (_gradientPresetIconSelect) { _gradientPresetIconSelect.updateItems(items); return; } - _gradientPresetIconSelect = new IconSelect({ target: sel, items, columns: 3, onChange: (v) => onGradientPresetChange(v) }); + _gradientPresetIconSelect = new IconSelect({ target: sel, items, columns: 3 }); } -/** Rebuild the preset picker after adding/removing custom presets. */ +/** Rebuild the gradient picker after entity changes. */ export function refreshGradientPresetPicker() { - if (_gradientPresetIconSelect) { - _gradientPresetIconSelect.updateItems(_buildGradientPresetItems()); - _gradientPresetIconSelect.setValue(''); - } - _renderCustomPresetList(); + const items = _buildGradientEntityItems(); + if (_gradientPresetIconSelect) _gradientPresetIconSelect.updateItems(items); + if (_effectPaletteIconSelect) _effectPaletteIconSelect.updateItems(items); + if (_audioPaletteIconSelect) _audioPaletteIconSelect.updateItems(items); } /** Render the user-created gradient list below the save button. */ @@ -1209,49 +1193,34 @@ const _typeHandlers: Record any; reset: (... }, gradient: { load(css) { - const presetId = css.gradient_id || ''; - (document.getElementById('css-editor-gradient-preset') as HTMLInputElement).value = presetId; - if (_gradientPresetIconSelect) _gradientPresetIconSelect.setValue(presetId); - // If gradient_id is set, load stops from the gradient entity - let stops = css.stops || [ - { position: 0.0, color: [255, 0, 0] }, - { position: 1.0, color: [0, 0, 255] }, - ]; - if (presetId) { - const g = _findGradient(presetId); - if (g) stops = g.stops; - } - gradientInit(stops); + const gradientId = css.gradient_id || ''; + _ensureGradientPresetIconSelect(); + (document.getElementById('css-editor-gradient-preset') as HTMLInputElement).value = gradientId; + if (_gradientPresetIconSelect) _gradientPresetIconSelect.setValue(gradientId); _loadAnimationState(css.animation); (document.getElementById('css-editor-gradient-easing') as HTMLSelectElement).value = css.easing || 'linear'; if (_gradientEasingIconSelect) _gradientEasingIconSelect.setValue(css.easing || 'linear'); }, reset() { - (document.getElementById('css-editor-gradient-preset') as HTMLInputElement).value = ''; - if (_gradientPresetIconSelect) _gradientPresetIconSelect.setValue(''); - gradientInit([ - { position: 0.0, color: [255, 0, 0] }, - { position: 1.0, color: [0, 0, 255] }, - ]); + _ensureGradientPresetIconSelect(); + // Default to first gradient + const gradients = _getGradients(); + const defaultId = gradients.length > 0 ? gradients[0].id : ''; + (document.getElementById('css-editor-gradient-preset') as HTMLInputElement).value = defaultId; + if (_gradientPresetIconSelect) _gradientPresetIconSelect.setValue(defaultId); _loadAnimationState(null); (document.getElementById('css-editor-gradient-easing') as HTMLSelectElement).value = 'linear'; if (_gradientEasingIconSelect) _gradientEasingIconSelect.setValue('linear'); }, getPayload(name) { - const gStops = getGradientStops(); - if (gStops.length < 2) { - cssEditorModal.showError(t('color_strip.gradient.min_stops')); + const gradientId = (document.getElementById('css-editor-gradient-preset') as HTMLInputElement).value; + if (!gradientId) { + cssEditorModal.showError(t('color_strip.gradient.error.no_gradient')); return null; } - const gradientId = (document.getElementById('css-editor-gradient-preset') as HTMLInputElement).value || null; return { name, gradient_id: gradientId, - stops: gStops.map(s => ({ - position: s.position, - color: s.color, - ...(s.colorRight ? { color_right: s.colorRight } : {}), - })), animation: _getAnimationPayload(), easing: (document.getElementById('css-editor-gradient-easing') as HTMLSelectElement).value, }; diff --git a/server/src/wled_controller/static/locales/en.json b/server/src/wled_controller/static/locales/en.json index 9b819f7..33fe47d 100644 --- a/server/src/wled_controller/static/locales/en.json +++ b/server/src/wled_controller/static/locales/en.json @@ -437,7 +437,7 @@ "common.none": "None", "common.none_no_cspt": "None (no processing template)", "common.none_no_input": "None (no input source)", - "common.none_own_speed": "None (use own speed)", + "common.none_own_speed": "None (no sync)", "common.undo": "Undo", "validation.required": "This field is required", "bulk.processing": "Processing…", @@ -980,6 +980,9 @@ "color_strip.gradient.position": "Position (0.0–1.0)", "color_strip.gradient.bidir.hint": "Add a second color on the right side of this stop to create a hard edge in the gradient.", "color_strip.gradient.min_stops": "Gradient must have at least 2 stops", + "color_strip.gradient.select": "Gradient:", + "color_strip.gradient.select.hint": "Select a gradient from the library. Create and edit gradients in the Gradients tab.", + "color_strip.gradient.error.no_gradient": "Please select a gradient", "color_strip.gradient.preset": "Preset:", "color_strip.gradient.preset.hint": "Load a predefined gradient palette. Selecting a preset replaces the current stops.", "color_strip.gradient.preset.custom": "— Custom —", diff --git a/server/src/wled_controller/templates/modals/css-editor.html b/server/src/wled_controller/templates/modals/css-editor.html index 31172e8..994157b 100644 --- a/server/src/wled_controller/templates/modals/css-editor.html +++ b/server/src/wled_controller/templates/modals/css-editor.html @@ -105,52 +105,12 @@