From c1940dadb77438e5f8791c027d471d20c4b8479d Mon Sep 17 00:00:00 2001 From: "alexei.dolgolyov" Date: Tue, 24 Mar 2026 19:43:33 +0300 Subject: [PATCH] refactor: split audio sources into 3 separate navtree subtabs Multichannel, Mono, and Band Extract each get their own subtab and panel within the Audio group, replacing the single combined Audio Sources tab. Cross-links from CSS, value sources, and command palette updated to navigate to the correct subtab. --- .../static/js/core/command-palette.ts | 5 ++-- .../static/js/features/color-strips.ts | 5 ++-- .../static/js/features/streams.ts | 29 ++++++++++++++----- .../static/js/features/value-sources.ts | 5 ++-- 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/server/src/wled_controller/static/js/core/command-palette.ts b/server/src/wled_controller/static/js/core/command-palette.ts index 8ce4f5d..a595b06 100644 --- a/server/src/wled_controller/static/js/core/command-palette.ts +++ b/server/src/wled_controller/static/js/core/command-palette.ts @@ -126,10 +126,11 @@ function _buildItems(results: any[], states: any = {}) { })); _mapEntities(audioSrc, a => { - const section = a.source_type === 'mono' ? 'audio-mono' : 'audio-multi'; + const section = a.source_type === 'mono' ? 'audio-mono' : a.source_type === 'band_extract' ? 'audio-band-extract' : 'audio-multi'; + const tab = a.source_type === 'mono' ? 'audio_mono' : a.source_type === 'band_extract' ? 'audio_band_extract' : 'audio_multi'; items.push({ name: a.name, detail: a.source_type, group: 'audio', icon: getAudioSourceIcon(a.source_type), - nav: ['streams', 'audio', section, 'data-id', a.id], + nav: ['streams', tab, section, 'data-id', a.id], }); }); 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 6454163..f5f97b0 100644 --- a/server/src/wled_controller/static/js/features/color-strips.ts +++ b/server/src/wled_controller/static/js/features/color-strips.ts @@ -1031,8 +1031,9 @@ const CSS_CARD_RENDERERS: Record = { ${source.audio_source_id ? (() => { const as = audioSourceMap && audioSourceMap[source.audio_source_id]; const asName = as ? as.name : source.audio_source_id; - const asSection = as && as.source_type === 'mono' ? 'audio-mono' : 'audio-multi'; - return `${ICON_AUDIO_LOOPBACK} ${escapeHtml(asName)}`; + const asSection = as ? (as.source_type === 'mono' ? 'audio-mono' : as.source_type === 'band_extract' ? 'audio-band-extract' : 'audio-multi') : 'audio-multi'; + const asTab = as ? (as.source_type === 'mono' ? 'audio_mono' : as.source_type === 'band_extract' ? 'audio_band_extract' : 'audio_multi') : 'audio_multi'; + return `${ICON_AUDIO_LOOPBACK} ${escapeHtml(asName)}`; })() : ''} ${source.mirror ? `🪞` : ''} `; diff --git a/server/src/wled_controller/static/js/features/streams.ts b/server/src/wled_controller/static/js/features/streams.ts index 8fe73b8..32dbc82 100644 --- a/server/src/wled_controller/static/js/features/streams.ts +++ b/server/src/wled_controller/static/js/features/streams.ts @@ -276,7 +276,9 @@ const _streamSectionMap = { proc_templates: [csProcTemplates], css_processing: [csCSPTemplates], color_strip: [csColorStrips], - audio: [csAudioMulti, csAudioMono, csAudioBandExtract], + audio_multi: [csAudioMulti], + audio_mono: [csAudioMono], + audio_band_extract: [csAudioBandExtract], audio_templates: [csAudioTemplates], value: [csValueSources], sync: [csSyncClocks], @@ -487,7 +489,9 @@ function renderPictureSourcesList(streams: any) { { key: 'css_processing', icon: ICON_CSPT, titleKey: 'streams.group.css_processing', count: csptTemplates.length }, { key: 'color_strip', icon: getColorStripIcon('static'), titleKey: 'streams.group.color_strip', count: colorStrips.length }, { key: 'gradients', icon: ICON_PALETTE, titleKey: 'streams.group.gradients', count: gradients.length }, - { key: 'audio', icon: getAudioSourceIcon('multichannel'), titleKey: 'streams.group.audio', count: _cachedAudioSources.length }, + { key: 'audio_multi', icon: getAudioSourceIcon('multichannel'), titleKey: 'audio_source.group.multichannel', count: multichannelSources.length }, + { key: 'audio_mono', icon: getAudioSourceIcon('mono'), titleKey: 'audio_source.group.mono', count: monoSources.length }, + { key: 'audio_band_extract', icon: getAudioSourceIcon('band_extract'), titleKey: 'audio_source.group.band_extract', count: bandExtractSources.length }, { key: 'audio_templates', icon: ICON_AUDIO_TEMPLATE, titleKey: 'streams.group.audio_templates', count: _cachedAudioTemplates.length }, { key: 'value', icon: ICON_VALUE_SOURCE, titleKey: 'streams.group.value', count: _cachedValueSources.length }, { key: 'sync', icon: ICON_CLOCK, titleKey: 'streams.group.sync', count: _cachedSyncClocks.length }, @@ -533,7 +537,9 @@ function renderPictureSourcesList(streams: any) { { key: 'audio_group', icon: getAudioSourceIcon('multichannel'), titleKey: 'tree.group.audio', children: [ - { key: 'audio', titleKey: 'tree.leaf.sources', icon: getAudioSourceIcon('multichannel'), count: _cachedAudioSources.length }, + { key: 'audio_multi', titleKey: 'audio_source.group.multichannel', icon: getAudioSourceIcon('multichannel'), count: multichannelSources.length }, + { key: 'audio_mono', titleKey: 'audio_source.group.mono', icon: getAudioSourceIcon('mono'), count: monoSources.length }, + { key: 'audio_band_extract', titleKey: 'audio_source.group.band_extract', icon: getAudioSourceIcon('band_extract'), count: bandExtractSources.length }, { key: 'audio_templates', titleKey: 'tree.leaf.templates', icon: ICON_AUDIO_TEMPLATE, count: _cachedAudioTemplates.length }, ] }, @@ -555,6 +561,12 @@ function renderPictureSourcesList(streams: any) { return 'audio-band-extract'; }; + const _getTabForSource = (sourceType: string): string => { + if (sourceType === 'multichannel') return 'audio_multi'; + if (sourceType === 'mono') return 'audio_mono'; + return 'audio_band_extract'; + }; + const renderAudioSourceCard = (src: any) => { const icon = getAudioSourceIcon(src.source_type); @@ -564,7 +576,7 @@ function renderPictureSourcesList(streams: any) { const parentName = parent ? parent.name : src.audio_source_id; const chLabel = src.channel === 'left' ? 'L' : src.channel === 'right' ? 'R' : 'M'; const parentBadge = parent - ? `${ICON_AUDIO_LOOPBACK} ${escapeHtml(parentName)}` + ? `${ICON_AUDIO_LOOPBACK} ${escapeHtml(parentName)}` : `${ICON_AUDIO_LOOPBACK} ${escapeHtml(parentName)}`; propsHtml = ` ${parentBadge} @@ -574,8 +586,9 @@ function renderPictureSourcesList(streams: any) { const parent = _cachedAudioSources.find(s => s.id === src.audio_source_id); const parentName = parent ? parent.name : src.audio_source_id; const parentSection = parent ? _getSectionForSource(parent.source_type) : 'audio-multi'; + const parentTab = parent ? _getTabForSource(parent.source_type) : 'audio_multi'; const parentBadge = parent - ? `${getAudioSourceIcon(parent.source_type)} ${escapeHtml(parentName)}` + ? `${getAudioSourceIcon(parent.source_type)} ${escapeHtml(parentName)}` : `${ICON_ACTIVITY} ${escapeHtml(parentName)}`; const bandLabel = _bandLabels[src.band] || src.band; const freqRange = `${Math.round(src.freq_low)}–${Math.round(src.freq_high)} Hz`; @@ -743,7 +756,9 @@ function renderPictureSourcesList(streams: any) { else if (tab.key === 'css_processing') panelContent = csCSPTemplates.render(csptItems); else if (tab.key === 'color_strip') panelContent = csColorStrips.render(colorStripItems); else if (tab.key === 'gradients') panelContent = csGradients.render(gradientItems); - else if (tab.key === 'audio') panelContent = csAudioMulti.render(multiItems) + csAudioMono.render(monoItems) + csAudioBandExtract.render(bandExtractItems); + else if (tab.key === 'audio_multi') panelContent = csAudioMulti.render(multiItems); + else if (tab.key === 'audio_mono') panelContent = csAudioMono.render(monoItems); + else if (tab.key === 'audio_band_extract') panelContent = csAudioBandExtract.render(bandExtractItems); else if (tab.key === 'audio_templates') panelContent = csAudioTemplates.render(audioTemplateItems); else if (tab.key === 'value') panelContent = csValueSources.render(valueItems); else if (tab.key === 'sync') panelContent = csSyncClocks.render(syncClockItems); @@ -772,7 +787,7 @@ function renderPictureSourcesList(streams: any) { 'css-proc-templates': 'css_processing', 'color-strips': 'color_strip', 'gradients': 'gradients', - 'audio-multi': 'audio', 'audio-mono': 'audio', 'audio-band-extract': 'audio', + 'audio-multi': 'audio_multi', 'audio-mono': 'audio_mono', 'audio-band-extract': 'audio_band_extract', 'audio-templates': 'audio_templates', 'value-sources': 'value', 'sync-clocks': 'sync', diff --git a/server/src/wled_controller/static/js/features/value-sources.ts b/server/src/wled_controller/static/js/features/value-sources.ts index 29182b8..afba00a 100644 --- a/server/src/wled_controller/static/js/features/value-sources.ts +++ b/server/src/wled_controller/static/js/features/value-sources.ts @@ -760,10 +760,11 @@ export function createValueSourceCard(src: ValueSource) { } else if (src.source_type === 'audio') { const audioSrc = _cachedAudioSources.find(a => a.id === src.audio_source_id); const audioName = audioSrc ? audioSrc.name : (src.audio_source_id || '-'); - const audioSection = audioSrc ? (audioSrc.source_type === 'mono' ? 'audio-mono' : 'audio-multi') : 'audio-multi'; + const audioSection = audioSrc ? (audioSrc.source_type === 'mono' ? 'audio-mono' : audioSrc.source_type === 'band_extract' ? 'audio-band-extract' : 'audio-multi') : 'audio-multi'; + const audioTab = audioSrc ? (audioSrc.source_type === 'mono' ? 'audio_mono' : audioSrc.source_type === 'band_extract' ? 'audio_band_extract' : 'audio_multi') : 'audio_multi'; const modeLabel = src.mode || 'rms'; const audioBadge = audioSrc - ? `${ICON_MUSIC} ${escapeHtml(audioName)}` + ? `${ICON_MUSIC} ${escapeHtml(audioName)}` : `${ICON_MUSIC} ${escapeHtml(audioName)}`; propsHtml = ` ${audioBadge}