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:
@@ -33,7 +33,7 @@ Clean-slate approach: no data migration for old source types.
|
||||
- [ ] Phase 5: Frontend — Audio Processing Templates [domain: frontend] → [subplan](./phase-5-frontend-templates.md)
|
||||
- [ ] Phase 6: Frontend — Source Types [domain: frontend] → [subplan](./phase-6-frontend-source-types.md)
|
||||
- [ ] Phase 7: Testing & Polish [domain: backend] → [subplan](./phase-7-testing-polish.md)
|
||||
- [ ] Phase 8: Frontend Design Consistency Review [domain: frontend] → [subplan](./phase-8-frontend-design-review.md)
|
||||
- [x] Phase 8: Frontend Design Consistency Review [domain: frontend] → [subplan](./phase-8-frontend-design-review.md)
|
||||
|
||||
## Phase Progress Log
|
||||
|
||||
@@ -46,7 +46,7 @@ Clean-slate approach: no data migration for old source types.
|
||||
| Phase 5: Frontend — Audio Processing Templates | frontend | ✅ Done | ✅ | ⏭️ | ✅ |
|
||||
| Phase 6: Frontend — Source Types | frontend | ✅ Done | ✅ | ⏭️ | ✅ |
|
||||
| Phase 7: Testing & Polish | backend | ✅ Done | — | ✅ | ✅ |
|
||||
| Phase 8: Frontend Design Review | frontend | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
|
||||
| Phase 8: Frontend Design Review | frontend | ✅ Done | ✅ | ✅ | ⬜ |
|
||||
|
||||
## Final Review
|
||||
- [ ] Comprehensive code review
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Phase 8: Frontend Design Consistency Review
|
||||
|
||||
**Status:** ⬜ Not Started
|
||||
**Status:** ✅ Done
|
||||
**Parent plan:** [PLAN.md](./PLAN.md)
|
||||
**Domain:** frontend
|
||||
|
||||
@@ -9,31 +9,31 @@ Review all new frontend UI created in Phases 5-6 for visual consistency, design
|
||||
|
||||
## Tasks
|
||||
|
||||
- [ ] Task 1: Review Audio Processing Templates section for design consistency
|
||||
- [x] Task 1: Review Audio Processing Templates section for design consistency
|
||||
- Card layout, spacing, typography alignment with existing sections
|
||||
- Filter editor modal — controls alignment, visual hierarchy, grouping
|
||||
- Responsive behavior at different viewport widths
|
||||
- [ ] Task 2: Review Processed Audio Source cards for design consistency
|
||||
- [x] Task 2: Review Processed Audio Source cards for design consistency
|
||||
- Card style matches existing source cards (capture, picture, value sources)
|
||||
- EntitySelect pickers are visually consistent
|
||||
- Type badges/icons are clear and distinguishable
|
||||
- [ ] Task 3: Review Capture Audio Source card (relabeled)
|
||||
- [x] Task 3: Review Capture Audio Source card (relabeled)
|
||||
- Label/icon updates look correct
|
||||
- No visual regressions from the rename
|
||||
- [ ] Task 4: Review source type picker/creation flow
|
||||
- [x] Task 4: Review source type picker/creation flow
|
||||
- Type selector is clear and accessible
|
||||
- Transition between types is smooth
|
||||
- Empty states handled properly
|
||||
- [ ] Task 5: Review real-time audio preview UI
|
||||
- [x] Task 5: Review real-time audio preview UI
|
||||
- Spectrum visualization looks polished
|
||||
- Source picker and controls are well-placed
|
||||
- Loading/error states
|
||||
- [ ] Task 6: Fix all design issues found in Tasks 1-5
|
||||
- [x] Task 6: Fix all design issues found in Tasks 1-5
|
||||
- CSS adjustments for spacing, alignment, typography
|
||||
- Icon/color consistency
|
||||
- Dark mode compatibility (if applicable)
|
||||
- Hover/focus/active states on interactive elements
|
||||
- [ ] Task 7: Cross-browser spot-check (if applicable)
|
||||
- [x] Task 7: Cross-browser spot-check (if applicable)
|
||||
|
||||
## Files to Modify/Create
|
||||
- `static/css/dashboard.css` — **modify** — design fixes
|
||||
@@ -55,11 +55,11 @@ Review all new frontend UI created in Phases 5-6 for visual consistency, design
|
||||
- The project uses vanilla CSS (no framework) — fixes must use the existing stylesheet approach
|
||||
|
||||
## Review Checklist
|
||||
- [ ] All tasks completed
|
||||
- [ ] Code follows project conventions
|
||||
- [ ] No unintended side effects
|
||||
- [ ] Build passes
|
||||
- [ ] Tests pass (new + existing)
|
||||
- [x] All tasks completed
|
||||
- [x] Code follows project conventions
|
||||
- [x] No unintended side effects
|
||||
- [x] Build passes
|
||||
- [x] Tests pass (new + existing)
|
||||
|
||||
## Handoff to Next Phase
|
||||
<!-- N/A — final phase -->
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<form id="audio-source-form" onsubmit="return false;">
|
||||
<input type="hidden" id="audio-source-id">
|
||||
|
||||
<div id="audio-source-error" class="error-message" style="display: none;"></div>
|
||||
<div id="audio-source-error" class="modal-error" style="display: none;"></div>
|
||||
|
||||
<!-- Name -->
|
||||
<div class="form-group">
|
||||
|
||||
Reference in New Issue
Block a user