feat(processed-audio-sources): phase 8 - frontend design consistency review

Fix audio source modal error class (modal-error), use Modal.showError(),
reorder audio source card description, remove redundant APT filter count
badge, clean up unused imports in audio-sources.ts.
This commit is contained in:
2026-03-31 23:11:17 +03:00
parent ce1f4847f3
commit 6b0e4e5539
6 changed files with 22 additions and 31 deletions
@@ -290,9 +290,6 @@ export function createAudioProcessingTemplateCard(tmpl: any): string {
<div class="template-name" title="${escapeHtml(tmpl.name)}">${ICON_AUDIO_TEMPLATE} ${escapeHtml(tmpl.name)}</div>
</div>
${tmpl.description ? `<div class="template-config" style="opacity:0.7;">${escapeHtml(tmpl.description)}</div>` : ''}
<div class="stream-card-props">
<span class="stream-card-prop" title="${t('audio_processing.filter_count')}">${ICON_AUDIO_TEMPLATE} ${tmpl.filters ? tmpl.filters.length : 0} ${t('audio_processing.filters_label')}</span>
</div>
${filterChainHtml}
${renderTagChips(tmpl.tags)}`,
actions: `
@@ -13,13 +13,11 @@
import { _cachedAudioSources, _cachedAudioTemplates, _cachedAudioProcessingTemplates, audioProcessingTemplatesCache, apiKey, audioSourcesCache } from '../core/state.ts';
import { API_BASE, fetchWithAuth, escapeHtml } from '../core/api.ts';
import { t } from '../core/i18n.ts';
import { showToast, showConfirm, lockBody, unlockBody } from '../core/ui.ts';
import { showToast, showConfirm } from '../core/ui.ts';
import { Modal } from '../core/modal.ts';
import { ICON_MUSIC, getAudioSourceIcon, ICON_AUDIO_TEMPLATE, ICON_AUDIO_INPUT, ICON_AUDIO_LOOPBACK } from '../core/icons.ts';
import { EntitySelect } from '../core/entity-palette.ts';
import { IconSelect } from '../core/icon-select.ts';
import { TagInput } from '../core/tag-input.ts';
import * as P from '../core/icon-paths.ts';
import { loadPictureSources } from './streams.ts';
let _audioSourceTagsInput: TagInput | null = null;
@@ -53,8 +51,6 @@ let _asDeviceEntitySelect: EntitySelect | null = null;
let _asParentEntitySelect: EntitySelect | null = null;
let _asProcessingTemplateEntitySelect: EntitySelect | null = null;
const _svg = (d: string): string => `<svg class="icon" viewBox="0 0 24 24">${d}</svg>`;
// ── Auto-name generation ──────────────────────────────────────
let _asNameManuallyEdited = false;
@@ -158,11 +154,9 @@ export async function saveAudioSource() {
const name = (document.getElementById('audio-source-name') as HTMLInputElement).value.trim();
const sourceType = (document.getElementById('audio-source-type') as HTMLSelectElement).value;
const description = (document.getElementById('audio-source-description') as HTMLInputElement).value.trim() || null;
const errorEl = document.getElementById('audio-source-error') as HTMLElement;
if (!name) {
errorEl.textContent = t('audio_source.error.name_required');
errorEl.style.display = '';
audioSourceModal.showError(t('audio_source.error.name_required'));
return;
}
@@ -196,8 +190,8 @@ export async function saveAudioSource() {
audioSourcesCache.invalidate();
await loadPictureSources();
} catch (e: any) {
errorEl.textContent = e.message;
errorEl.style.display = '';
if (e.isAuth) return;
audioSourceModal.showError(e.message);
}
}
@@ -711,9 +711,9 @@ function renderPictureSourcesList(streams: any) {
<div class="template-card-header">
<div class="template-name" title="${escapeHtml(src.name)}">${icon} ${escapeHtml(src.name)}</div>
</div>
${src.description ? `<div class="template-config" style="opacity:0.7;">${escapeHtml(src.description)}</div>` : ''}
<div class="stream-card-props">${propsHtml}</div>
${renderTagChips(src.tags)}
${src.description ? `<div class="template-config" style="opacity:0.7;">${escapeHtml(src.description)}</div>` : ''}`,
${renderTagChips(src.tags)}`,
actions: `
<button class="btn btn-icon btn-secondary" data-action="test-audio" title="${t('audio_source.test')}">${ICON_TEST}</button>
<button class="btn btn-icon btn-secondary" data-action="clone-audio" title="${t('common.clone')}">${ICON_CLONE}</button>