fix: audio tree structure, filter i18n, and IconSelect for filter options

Restructure audio tree nav into Capture (Sources + Engine Templates)
and Processed (Sources + Filter Templates) subgroups.
Add missing i18n description keys for all 11 audio filters.
Wrap plain select filter options with IconSelect grids.
This commit is contained in:
2026-04-02 13:37:50 +03:00
parent d04192ffb7
commit af2c89c8df
5 changed files with 62 additions and 5 deletions
@@ -202,9 +202,11 @@ export class FilterListManager {
const gridAttr = hasPaletteColors ? ` data-palette-grid="${escapeHtml(JSON.stringify(filteredChoices))}"` : '';
const isTemplateRef = opt.key === 'template_id';
const entityAttr = isTemplateRef ? ' data-entity-select="template"' : '';
const isPlainSelect = !hasPaletteColors && !isTemplateRef;
const iconGridAttr = isPlainSelect ? ' data-icon-grid="true"' : '';
html += `<div class="pp-filter-option">
<label for="${inputId}"><span>${escapeHtml(opt.label)}:</span></label>
<select id="${inputId}"${gridAttr}${entityAttr}
<select id="${inputId}"${gridAttr}${entityAttr}${iconGridAttr}
onchange="${updateFn}(${index}, '${opt.key}', this.value)">
${options}
</select>
@@ -235,6 +237,7 @@ export class FilterListManager {
});
container.innerHTML = html;
this._enhanceIconGridSelects(container);
if (this._initDrag) {
this._initDrag(this._containerId, filtersArr, () => this.render());
}
@@ -243,6 +246,33 @@ export class FilterListManager {
}
}
/**
* Wrap plain select-type filter options with IconSelect grids.
* Called after render() sets innerHTML.
*/
private _enhanceIconGridSelects(container: HTMLElement) {
const selects = container.querySelectorAll<HTMLSelectElement>('select[data-icon-grid]');
selects.forEach(sel => {
const items: { value: string; icon: string; label: string }[] = [];
for (const option of Array.from(sel.options)) {
if (!option.value) continue;
items.push({
value: option.value,
icon: `<svg class="icon" viewBox="0 0 24 24">${P.wrench}</svg>`,
label: option.textContent || option.value,
});
}
if (items.length > 0) {
new IconSelect({
target: sel,
items,
columns: Math.min(items.length, 3),
onChange: () => { sel.dispatchEvent(new Event('change')); },
});
}
});
}
/**
* Add a filter from the select element into the filters array.
*/
@@ -636,10 +636,20 @@ function renderPictureSourcesList(streams: any) {
{
key: 'audio_group', icon: getAudioSourceIcon('capture'), titleKey: 'tree.group.audio',
children: [
{ key: 'audio_capture', titleKey: 'audio_source.group.capture', icon: getAudioSourceIcon('capture'), count: captureSources.length },
{ key: 'audio_processed', titleKey: 'audio_source.group.processed', icon: getAudioSourceIcon('processed'), count: processedAudioSources.length },
{ key: 'audio_templates', titleKey: 'tree.leaf.templates', icon: ICON_AUDIO_TEMPLATE, count: _cachedAudioTemplates.length },
{ key: 'audio_processing', titleKey: 'tree.leaf.processing_templates', icon: ICON_AUDIO_TEMPLATE, count: audioProcessingTemplates.length },
{
key: 'audio_capture_group', icon: getAudioSourceIcon('capture'), titleKey: 'tree.group.audio_capture',
children: [
{ key: 'audio_capture', titleKey: 'tree.leaf.sources', icon: getAudioSourceIcon('capture'), count: captureSources.length },
{ key: 'audio_templates', titleKey: 'tree.leaf.engine_templates', icon: ICON_AUDIO_TEMPLATE, count: _cachedAudioTemplates.length },
]
},
{
key: 'audio_processed_group', icon: getAudioSourceIcon('processed'), titleKey: 'tree.group.audio_processed',
children: [
{ key: 'audio_processed', titleKey: 'tree.leaf.sources', icon: getAudioSourceIcon('processed'), count: processedAudioSources.length },
{ key: 'audio_processing', titleKey: 'tree.leaf.filter_templates', icon: ICON_AUDIO_TEMPLATE, count: audioProcessingTemplates.length },
]
},
]
},
{
@@ -550,6 +550,17 @@
"filters.contrast.desc": "Adjust image contrast around mid-gray",
"filters.temporal_blur": "Temporal Blur",
"filters.temporal_blur.desc": "Smooth color transitions over time",
"filters.audio_filter_template.desc": "Embed another audio processing template",
"filters.channel_extract.desc": "Select mono, left, or right channel",
"filters.band_extract.desc": "Filter to bass, mid, treble, or custom frequency range",
"filters.peak_hold.desc": "Retain peak values with configurable decay",
"filters.gain.desc": "Amplify or attenuate signal levels",
"filters.envelope_follower.desc": "Smooth amplitude with attack/release times",
"filters.spectral_smoothing.desc": "Extra smoothing on spectrum data over time",
"filters.compressor.desc": "Reduce dynamic range above threshold",
"filters.inverter.desc": "Invert all levels (1 minus value)",
"filters.beat_gate.desc": "Pass signal only around detected beats",
"filters.delay.desc": "Time-shift the audio analysis by a delay",
"postprocessing.description_label": "Description (optional):",
"postprocessing.description_placeholder": "Describe this template...",
"postprocessing.created": "Template created successfully",
@@ -1408,6 +1419,8 @@
"tree.group.processing": "Processed",
"tree.group.strip": "Color Strip",
"tree.group.audio": "Audio",
"tree.group.audio_capture": "Capture",
"tree.group.audio_processed": "Processed",
"tree.group.integrations": "Integrations",
"tree.group.utility": "Utility",
"tree.leaf.sources": "Sources",
@@ -1311,6 +1311,8 @@
"tree.group.processing": "Обработанные",
"tree.group.strip": "Цветовые полосы",
"tree.group.audio": "Аудио",
"tree.group.audio_capture": "Захват",
"tree.group.audio_processed": "Обработка",
"tree.group.utility": "Утилиты",
"tree.leaf.sources": "Источники",
"tree.leaf.engine_templates": "Шаблоны движка",
@@ -1311,6 +1311,8 @@
"tree.group.processing": "已处理",
"tree.group.strip": "色带",
"tree.group.audio": "音频",
"tree.group.audio_capture": "采集",
"tree.group.audio_processed": "处理",
"tree.group.utility": "工具",
"tree.leaf.sources": "源",
"tree.leaf.engine_templates": "引擎模板",