refactor: remove inline gradient editor from CSS modal, use entity picker
Some checks failed
Lint & Test / test (push) Failing after 30s
Some checks failed
Lint & Test / test (push) Failing after 30s
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.
This commit is contained in:
@@ -398,10 +398,7 @@ function _ensureEffectTypeIconSelect() {
|
|||||||
function _ensureEffectPaletteIconSelect() {
|
function _ensureEffectPaletteIconSelect() {
|
||||||
const sel = document.getElementById('css-editor-effect-palette') as HTMLSelectElement | null;
|
const sel = document.getElementById('css-editor-effect-palette') as HTMLSelectElement | null;
|
||||||
if (!sel) return;
|
if (!sel) return;
|
||||||
const gradients = _getGradients();
|
const items = _buildGradientEntityItems();
|
||||||
const items = gradients.map(g => ({
|
|
||||||
value: g.id, icon: _gradientEntityStripHTML(g.stops), label: g.name,
|
|
||||||
}));
|
|
||||||
if (_effectPaletteIconSelect) { _effectPaletteIconSelect.updateItems(items); return; }
|
if (_effectPaletteIconSelect) { _effectPaletteIconSelect.updateItems(items); return; }
|
||||||
_effectPaletteIconSelect = new IconSelect({ target: sel, items, columns: 2 });
|
_effectPaletteIconSelect = new IconSelect({ target: sel, items, columns: 2 });
|
||||||
}
|
}
|
||||||
@@ -435,10 +432,7 @@ function _ensureCandleTypeIconSelect() {
|
|||||||
function _ensureAudioPaletteIconSelect() {
|
function _ensureAudioPaletteIconSelect() {
|
||||||
const sel = document.getElementById('css-editor-audio-palette') as HTMLSelectElement | null;
|
const sel = document.getElementById('css-editor-audio-palette') as HTMLSelectElement | null;
|
||||||
if (!sel) return;
|
if (!sel) return;
|
||||||
const gradients = _getGradients();
|
const items = _buildGradientEntityItems();
|
||||||
const items = gradients.map(g => ({
|
|
||||||
value: g.id, icon: _gradientEntityStripHTML(g.stops), label: g.name,
|
|
||||||
}));
|
|
||||||
if (_audioPaletteIconSelect) { _audioPaletteIconSelect.updateItems(items); return; }
|
if (_audioPaletteIconSelect) { _audioPaletteIconSelect.updateItems(items); return; }
|
||||||
_audioPaletteIconSelect = new IconSelect({ target: sel, items, columns: 2 });
|
_audioPaletteIconSelect = new IconSelect({ target: sel, items, columns: 2 });
|
||||||
}
|
}
|
||||||
@@ -455,37 +449,27 @@ function _ensureAudioVizIconSelect() {
|
|||||||
_audioVizIconSelect = new IconSelect({ target: sel, items, columns: 3 });
|
_audioVizIconSelect = new IconSelect({ target: sel, items, columns: 3 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function _buildGradientPresetItems() {
|
function _buildGradientEntityItems() {
|
||||||
const gradients = _getGradients();
|
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,
|
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() {
|
function _ensureGradientPresetIconSelect() {
|
||||||
const sel = document.getElementById('css-editor-gradient-preset') as HTMLSelectElement | null;
|
const sel = document.getElementById('css-editor-gradient-preset') as HTMLSelectElement | null;
|
||||||
if (!sel) return;
|
if (!sel) return;
|
||||||
const items = _buildGradientPresetItems();
|
const items = _buildGradientEntityItems();
|
||||||
if (_gradientPresetIconSelect) { _gradientPresetIconSelect.updateItems(items); return; }
|
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() {
|
export function refreshGradientPresetPicker() {
|
||||||
if (_gradientPresetIconSelect) {
|
const items = _buildGradientEntityItems();
|
||||||
_gradientPresetIconSelect.updateItems(_buildGradientPresetItems());
|
if (_gradientPresetIconSelect) _gradientPresetIconSelect.updateItems(items);
|
||||||
_gradientPresetIconSelect.setValue('');
|
if (_effectPaletteIconSelect) _effectPaletteIconSelect.updateItems(items);
|
||||||
}
|
if (_audioPaletteIconSelect) _audioPaletteIconSelect.updateItems(items);
|
||||||
_renderCustomPresetList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Render the user-created gradient list below the save button. */
|
/** Render the user-created gradient list below the save button. */
|
||||||
@@ -1209,49 +1193,34 @@ const _typeHandlers: Record<string, { load: (...args: any[]) => any; reset: (...
|
|||||||
},
|
},
|
||||||
gradient: {
|
gradient: {
|
||||||
load(css) {
|
load(css) {
|
||||||
const presetId = css.gradient_id || '';
|
const gradientId = css.gradient_id || '';
|
||||||
(document.getElementById('css-editor-gradient-preset') as HTMLInputElement).value = presetId;
|
_ensureGradientPresetIconSelect();
|
||||||
if (_gradientPresetIconSelect) _gradientPresetIconSelect.setValue(presetId);
|
(document.getElementById('css-editor-gradient-preset') as HTMLInputElement).value = gradientId;
|
||||||
// If gradient_id is set, load stops from the gradient entity
|
if (_gradientPresetIconSelect) _gradientPresetIconSelect.setValue(gradientId);
|
||||||
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);
|
|
||||||
_loadAnimationState(css.animation);
|
_loadAnimationState(css.animation);
|
||||||
(document.getElementById('css-editor-gradient-easing') as HTMLSelectElement).value = css.easing || 'linear';
|
(document.getElementById('css-editor-gradient-easing') as HTMLSelectElement).value = css.easing || 'linear';
|
||||||
if (_gradientEasingIconSelect) _gradientEasingIconSelect.setValue(css.easing || 'linear');
|
if (_gradientEasingIconSelect) _gradientEasingIconSelect.setValue(css.easing || 'linear');
|
||||||
},
|
},
|
||||||
reset() {
|
reset() {
|
||||||
(document.getElementById('css-editor-gradient-preset') as HTMLInputElement).value = '';
|
_ensureGradientPresetIconSelect();
|
||||||
if (_gradientPresetIconSelect) _gradientPresetIconSelect.setValue('');
|
// Default to first gradient
|
||||||
gradientInit([
|
const gradients = _getGradients();
|
||||||
{ position: 0.0, color: [255, 0, 0] },
|
const defaultId = gradients.length > 0 ? gradients[0].id : '';
|
||||||
{ position: 1.0, color: [0, 0, 255] },
|
(document.getElementById('css-editor-gradient-preset') as HTMLInputElement).value = defaultId;
|
||||||
]);
|
if (_gradientPresetIconSelect) _gradientPresetIconSelect.setValue(defaultId);
|
||||||
_loadAnimationState(null);
|
_loadAnimationState(null);
|
||||||
(document.getElementById('css-editor-gradient-easing') as HTMLSelectElement).value = 'linear';
|
(document.getElementById('css-editor-gradient-easing') as HTMLSelectElement).value = 'linear';
|
||||||
if (_gradientEasingIconSelect) _gradientEasingIconSelect.setValue('linear');
|
if (_gradientEasingIconSelect) _gradientEasingIconSelect.setValue('linear');
|
||||||
},
|
},
|
||||||
getPayload(name) {
|
getPayload(name) {
|
||||||
const gStops = getGradientStops();
|
const gradientId = (document.getElementById('css-editor-gradient-preset') as HTMLInputElement).value;
|
||||||
if (gStops.length < 2) {
|
if (!gradientId) {
|
||||||
cssEditorModal.showError(t('color_strip.gradient.min_stops'));
|
cssEditorModal.showError(t('color_strip.gradient.error.no_gradient'));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const gradientId = (document.getElementById('css-editor-gradient-preset') as HTMLInputElement).value || null;
|
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
gradient_id: gradientId,
|
gradient_id: gradientId,
|
||||||
stops: gStops.map(s => ({
|
|
||||||
position: s.position,
|
|
||||||
color: s.color,
|
|
||||||
...(s.colorRight ? { color_right: s.colorRight } : {}),
|
|
||||||
})),
|
|
||||||
animation: _getAnimationPayload(),
|
animation: _getAnimationPayload(),
|
||||||
easing: (document.getElementById('css-editor-gradient-easing') as HTMLSelectElement).value,
|
easing: (document.getElementById('css-editor-gradient-easing') as HTMLSelectElement).value,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -437,7 +437,7 @@
|
|||||||
"common.none": "None",
|
"common.none": "None",
|
||||||
"common.none_no_cspt": "None (no processing template)",
|
"common.none_no_cspt": "None (no processing template)",
|
||||||
"common.none_no_input": "None (no input source)",
|
"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",
|
"common.undo": "Undo",
|
||||||
"validation.required": "This field is required",
|
"validation.required": "This field is required",
|
||||||
"bulk.processing": "Processing…",
|
"bulk.processing": "Processing…",
|
||||||
@@ -980,6 +980,9 @@
|
|||||||
"color_strip.gradient.position": "Position (0.0–1.0)",
|
"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.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.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": "Preset:",
|
||||||
"color_strip.gradient.preset.hint": "Load a predefined gradient palette. Selecting a preset replaces the current stops.",
|
"color_strip.gradient.preset.hint": "Load a predefined gradient palette. Selecting a preset replaces the current stops.",
|
||||||
"color_strip.gradient.preset.custom": "— Custom —",
|
"color_strip.gradient.preset.custom": "— Custom —",
|
||||||
|
|||||||
@@ -105,52 +105,12 @@
|
|||||||
<div id="css-editor-gradient-section" style="display:none">
|
<div id="css-editor-gradient-section" style="display:none">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="label-row">
|
<div class="label-row">
|
||||||
<label for="css-editor-gradient-preset" data-i18n="color_strip.gradient.preset">Preset:</label>
|
<label for="css-editor-gradient-preset" data-i18n="color_strip.gradient.select">Gradient:</label>
|
||||||
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?" data-i18n-aria-label="aria.hint">?</button>
|
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?" data-i18n-aria-label="aria.hint">?</button>
|
||||||
</div>
|
</div>
|
||||||
<small class="input-hint" style="display:none" data-i18n="color_strip.gradient.preset.hint">Load a predefined gradient palette. Selecting a preset replaces the current stops.</small>
|
<small class="input-hint" style="display:none" data-i18n="color_strip.gradient.select.hint">Select a gradient from the library. Create and edit gradients in the Gradients tab.</small>
|
||||||
<select id="css-editor-gradient-preset" onchange="onGradientPresetChange(this.value)">
|
<select id="css-editor-gradient-preset">
|
||||||
<option value="" data-i18n="color_strip.gradient.preset.custom">— Custom —</option>
|
|
||||||
<option value="rainbow" data-i18n="color_strip.gradient.preset.rainbow">Rainbow</option>
|
|
||||||
<option value="sunset" data-i18n="color_strip.gradient.preset.sunset">Sunset</option>
|
|
||||||
<option value="ocean" data-i18n="color_strip.gradient.preset.ocean">Ocean</option>
|
|
||||||
<option value="forest" data-i18n="color_strip.gradient.preset.forest">Forest</option>
|
|
||||||
<option value="fire" data-i18n="color_strip.gradient.preset.fire">Fire</option>
|
|
||||||
<option value="lava" data-i18n="color_strip.gradient.preset.lava">Lava</option>
|
|
||||||
<option value="aurora" data-i18n="color_strip.gradient.preset.aurora">Aurora</option>
|
|
||||||
<option value="ice" data-i18n="color_strip.gradient.preset.ice">Ice</option>
|
|
||||||
<option value="warm" data-i18n="color_strip.gradient.preset.warm">Warm</option>
|
|
||||||
<option value="cool" data-i18n="color_strip.gradient.preset.cool">Cool</option>
|
|
||||||
<option value="neon" data-i18n="color_strip.gradient.preset.neon">Neon</option>
|
|
||||||
<option value="pastel" data-i18n="color_strip.gradient.preset.pastel">Pastel</option>
|
|
||||||
</select>
|
</select>
|
||||||
<div style="margin-top:6px;">
|
|
||||||
<button type="button" class="btn btn-secondary btn-sm"
|
|
||||||
onclick="promptAndSaveGradientPreset()"
|
|
||||||
data-i18n="color_strip.gradient.preset.save_button">Save as preset…</button>
|
|
||||||
</div>
|
|
||||||
<div id="css-editor-custom-presets-list" class="custom-presets-list"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="label-row">
|
|
||||||
<label data-i18n="color_strip.gradient.preview">Gradient:</label>
|
|
||||||
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?" data-i18n-aria-label="aria.hint">?</button>
|
|
||||||
</div>
|
|
||||||
<small class="input-hint" style="display:none" data-i18n="color_strip.gradient.preview.hint">Visual preview. Click the marker track below to add a stop. Drag markers to reposition.</small>
|
|
||||||
<div class="gradient-editor">
|
|
||||||
<canvas id="gradient-canvas" height="44"></canvas>
|
|
||||||
<div id="gradient-markers-track" class="gradient-markers-track"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="label-row">
|
|
||||||
<label data-i18n="color_strip.gradient.stops">Color Stops:</label>
|
|
||||||
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?" data-i18n-aria-label="aria.hint">?</button>
|
|
||||||
</div>
|
|
||||||
<small class="input-hint" style="display:none" data-i18n="color_strip.gradient.stops.hint">Each stop defines a color at a relative position (0.0 = start, 1.0 = end). The ↔ button adds a right-side color to create a hard edge at that stop.</small>
|
|
||||||
<div id="gradient-stops-list"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@@ -199,16 +159,7 @@
|
|||||||
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?" data-i18n-aria-label="aria.hint">?</button>
|
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?" data-i18n-aria-label="aria.hint">?</button>
|
||||||
</div>
|
</div>
|
||||||
<small class="input-hint" style="display:none" data-i18n="color_strip.effect.palette.hint">Color palette used by the effect.</small>
|
<small class="input-hint" style="display:none" data-i18n="color_strip.effect.palette.hint">Color palette used by the effect.</small>
|
||||||
<select id="css-editor-effect-palette" onchange="onEffectPaletteChange()">
|
<select id="css-editor-effect-palette">
|
||||||
<option value="fire" data-i18n="color_strip.palette.fire">Fire</option>
|
|
||||||
<option value="ocean" data-i18n="color_strip.palette.ocean">Ocean</option>
|
|
||||||
<option value="lava" data-i18n="color_strip.palette.lava">Lava</option>
|
|
||||||
<option value="forest" data-i18n="color_strip.palette.forest">Forest</option>
|
|
||||||
<option value="rainbow" data-i18n="color_strip.palette.rainbow">Rainbow</option>
|
|
||||||
<option value="aurora" data-i18n="color_strip.palette.aurora">Aurora</option>
|
|
||||||
<option value="sunset" data-i18n="color_strip.palette.sunset">Sunset</option>
|
|
||||||
<option value="ice" data-i18n="color_strip.palette.ice">Ice</option>
|
|
||||||
<option value="custom" data-i18n="color_strip.palette.custom">Custom</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user