refactor: split audio sources into 3 separate navtree subtabs
Some checks failed
Lint & Test / test (push) Failing after 31s

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.
This commit is contained in:
2026-03-24 19:43:33 +03:00
parent ae0a5cb160
commit c1940dadb7
4 changed files with 31 additions and 13 deletions

View File

@@ -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],
});
});

View File

@@ -1031,8 +1031,9 @@ const CSS_CARD_RENDERERS: Record<string, CardPropsRenderer> = {
${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 `<span class="stream-card-prop${as ? ' stream-card-link' : ''}" title="${t('color_strip.audio.source')}"${as ? ` onclick="event.stopPropagation(); navigateToCard('streams','audio','${asSection}','data-id','${source.audio_source_id}')"` : ''}>${ICON_AUDIO_LOOPBACK} ${escapeHtml(asName)}</span>`;
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 `<span class="stream-card-prop${as ? ' stream-card-link' : ''}" title="${t('color_strip.audio.source')}"${as ? ` onclick="event.stopPropagation(); navigateToCard('streams','${asTab}','${asSection}','data-id','${source.audio_source_id}')"` : ''}>${ICON_AUDIO_LOOPBACK} ${escapeHtml(asName)}</span>`;
})() : ''}
${source.mirror ? `<span class="stream-card-prop">🪞</span>` : ''}
`;

View File

@@ -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
? `<span class="stream-card-prop stream-card-link" title="${escapeHtml(t('audio_source.parent'))}" onclick="event.stopPropagation(); navigateToCard('streams','audio','audio-multi','data-id','${src.audio_source_id}')">${ICON_AUDIO_LOOPBACK} ${escapeHtml(parentName)}</span>`
? `<span class="stream-card-prop stream-card-link" title="${escapeHtml(t('audio_source.parent'))}" onclick="event.stopPropagation(); navigateToCard('streams','audio_multi','audio-multi','data-id','${src.audio_source_id}')">${ICON_AUDIO_LOOPBACK} ${escapeHtml(parentName)}</span>`
: `<span class="stream-card-prop" title="${escapeHtml(t('audio_source.parent'))}">${ICON_AUDIO_LOOPBACK} ${escapeHtml(parentName)}</span>`;
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
? `<span class="stream-card-prop stream-card-link" title="${escapeHtml(t('audio_source.band_parent'))}" onclick="event.stopPropagation(); navigateToCard('streams','audio','${parentSection}','data-id','${src.audio_source_id}')">${getAudioSourceIcon(parent.source_type)} ${escapeHtml(parentName)}</span>`
? `<span class="stream-card-prop stream-card-link" title="${escapeHtml(t('audio_source.band_parent'))}" onclick="event.stopPropagation(); navigateToCard('streams','${parentTab}','${parentSection}','data-id','${src.audio_source_id}')">${getAudioSourceIcon(parent.source_type)} ${escapeHtml(parentName)}</span>`
: `<span class="stream-card-prop" title="${escapeHtml(t('audio_source.band_parent'))}">${ICON_ACTIVITY} ${escapeHtml(parentName)}</span>`;
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',

View File

@@ -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
? `<span class="stream-card-prop stream-card-link" title="${escapeHtml(t('value_source.audio_source'))}" onclick="event.stopPropagation(); navigateToCard('streams','audio','${audioSection}','data-id','${src.audio_source_id}')">${ICON_MUSIC} ${escapeHtml(audioName)}</span>`
? `<span class="stream-card-prop stream-card-link" title="${escapeHtml(t('value_source.audio_source'))}" onclick="event.stopPropagation(); navigateToCard('streams','${audioTab}','${audioSection}','data-id','${src.audio_source_id}')">${ICON_MUSIC} ${escapeHtml(audioName)}</span>`
: `<span class="stream-card-prop" title="${escapeHtml(t('value_source.audio_source'))}">${ICON_MUSIC} ${escapeHtml(audioName)}</span>`;
propsHtml = `
${audioBadge}