# PMOControl WebUI - Design Recommendations & Implementation Plan ## Executive Summary Based on my analysis of the existing codebase, I'm providing comprehensive recommendations for implementing a Vue.js WebUI for PMOControl. The system already has: - A complete REST API with OpenAPI documentation (`/api/control/*`) - SSE endpoints for real-time updates (`/api/control/events/*`) - Vue 3 + TypeScript + Vite setup - Existing components (GenericMusicPlayer, UpnpExplorer, Cache Managers, LogView) --- ## Design Decisions & Recommendations ### 1. State Management: **Use Pinia** **Recommendation: Pinia (Vue 3's official state management)** **Rationale:** - **Centralized real-time state**: Essential for managing SSE updates from multiple sources (renderers, media servers) - **Multi-client synchronization**: Single source of truth for renderer states, volumes, playback positions - **TypeScript native**: Better type inference than Vuex - **DevTools integration**: Built-in debugging for SSE event flows - **Composition API friendly**: Matches existing Vue 3 patterns in codebase - **Performance**: Lightweight (~1KB), modular stores - **Official Vue 3 recommendation**: Future-proof choice **Store Architecture:** ```typescript // stores/renderers.ts - Renderer state (SSE updates) // stores/mediaServers.ts - Media server state (SSE updates) // stores/playback.ts - Current playback session // stores/ui.ts - UI state (selected renderer, view preferences) ``` **Benefits for your use case:** - Handle 20+ concurrent clients with shared state - Real-time SSE event synchronization across all views - Easy to scale with multi-renderer, multi-server, multi-session architecture --- ### 2. UI Component Library: **Headless UI + Custom Components** **Recommendation: Hybrid approach - Headless UI components + custom styling** **Component Library: Shadcn-vue (Headless UI primitives)** **Rationale:** - **Lightweight & performant**: Only import what you need - **Full style control**: Match "carte uniforme, responsive, colorée selon statut" spec exactly - **TypeScript-first**: Perfect type safety - **Accessibility built-in**: ARIA compliance out of the box - **No theme lock-in**: Complete CSS freedom - **Composable primitives**: Card, Dialog, Dropdown, Slider components **Why NOT a full framework (Vuetify, Element Plus)?** - Heavy bundle size (100-500KB vs ~10KB for headless) - Theme customization overhead - Your spec requires custom status-based coloring - Performance critical with 20 concurrent clients **Alternative if you prefer pre-styled:** PrimeVue - Good performance - Customizable themes - Strong TypeScript support - But: 150KB+ bundle size **Custom Components to Build:** - `RendererCard` - Status-colored cards for each renderer - `TransportControls` - Play/Pause/Stop/Next buttons - `VolumeControl` - Slider with mute toggle - `QueueViewer` - Playlist display with drag-drop - `MediaServerBrowser` - Container navigation --- ### 3. Existing Components: **Reorganize into Debug Section** **Recommendation: Keep existing components, create new PMOControl home** **Structure:** ``` /app (root) → PMOControl Dashboard (NEW) /app/debug → Dropdown menu ├─ /logs → LogView ├─ /upnp → UpnpExplorer ├─ /covers-cache → CoverCacheManager ├─ /audio-cache → AudioCacheManager ├─ /api-dashboard → APIDashboard └─ /radio-paradise → RadioParadiseExplorer ``` **Rationale:** - Existing components are valuable for development/debugging - Don't break existing functionality - PMOControl becomes primary interface as specified - Debug tools remain accessible but not prominent - Matches current App.vue dropdown pattern **Home Screen (/) - PMOControl Dashboard:** - Grid of renderer cards (status-colored) - Active playback session viewer - Quick controls (play/pause/volume) - Media server browser panel --- ### 4. Responsive Design: **Mobile-first with 3 breakpoints** **Recommendation: Follow existing 768px pattern + add tablet/desktop** **Breakpoints:** ```css /* Mobile: < 768px (existing pattern) */ - Single column layout - Stacked renderer cards - Bottom-fixed playback controls - Collapsible media browser /* Tablet: 768px - 1024px */ - Two column layout - Grid of renderer cards (2 columns) - Side panel for media browser - Floating playback controls /* Desktop: > 1024px */ - Three column layout - Renderer cards grid (3-4 columns) - Persistent media browser sidebar - Always-visible playback controls ``` **Target Devices:** - **Primary**: Desktop browsers (control station) - **Secondary**: Tablets (remote control) - **Tertiary**: Mobile phones (quick controls) **Performance considerations:** - Virtualized lists for 20+ renderers (use vue-virtual-scroller) - Lazy load album art - Throttle SSE position updates (max 1/sec per renderer) --- ### 5. Icons: **Lucide Icons (SVG library)** **Recommendation: Lucide Icons (NOT emoji)** **Rationale:** - **Professional appearance**: Emojis inconsistent across platforms - **Customizable**: Size, color, stroke width - **Lightweight**: Tree-shakeable SVG imports (~1KB per icon) - **Status coloring**: Icons can match card status colors - **Accessibility**: Proper ARIA labels - **Vue components**: `lucide-vue-next` package **Icon mapping:** ```typescript Play → PlayCircle Pause → PauseCircle Stop → StopCircle Next → SkipForward Volume → Volume2 / VolumeX (muted) Renderer → Speaker / MonitorSpeaker Server → Server / Database Queue → ListMusic ``` **Alternative if you prefer minimal bundle:** Heroicons - Smaller set (fewer icons) - Tailwind CSS integration - But: less comprehensive for music player needs **Why NOT emoji:** - Platform inconsistencies (iOS ≠ Android ≠ Windows) - No color control - Accessibility issues - Unprofessional for production UI --- ## Technical Architecture ### Real-time SSE Integration **SSE Event Handling:** ```typescript // services/controlPointSSE.ts class ControlPointSSE { private eventSource: EventSource private renderersStore: ReturnType connect() { this.eventSource = new EventSource('/api/control/events') this.eventSource.addEventListener('control', (e) => { const event = JSON.parse(e.data) if (event.category === 'renderer') { this.handleRendererEvent(event) } else if (event.category === 'media_server') { this.handleServerEvent(event) } }) } handleRendererEvent(event: RendererEventPayload) { switch (event.type) { case 'state_changed': this.renderersStore.updateState(event.renderer_id, event.state) break case 'volume_changed': this.renderersStore.updateVolume(event.renderer_id, event.volume) break // ... handle all event types } } } ``` **Store Integration:** ```typescript // stores/renderers.ts export const useRenderersStore = defineStore('renderers', () => { const renderers = ref>(new Map()) // SSE updates function updateState(id: string, state: string) { const renderer = renderers.value.get(id) if (renderer) { renderer.transport_state = state } } // REST API calls async function play(id: string) { await fetch(`/api/control/renderers/${id}/play`, { method: 'POST' }) // SSE will update state automatically } return { renderers, updateState, play } }) ``` ### Performance Optimizations **For 20+ concurrent clients:** 1. **Throttle position updates**: ```typescript const throttledPositionUpdate = throttle((id, pos) => { store.updatePosition(id, pos) }, 1000) // Max 1 update/second ``` 2. **Virtual scrolling** for renderer lists: ```bash npm install vue-virtual-scroller ``` 3. **Lazy load album art**: ```vue ``` 4. **Debounce volume sliders**: ```typescript const debouncedVolumeChange = debounce((id, vol) => { api.setVolume(id, vol) }, 300) ``` 5. **Memoize computed properties**: ```typescript const activeRenderers = computed(() => renderers.value.filter(r => r.online) ) ``` --- ## Implementation Roadmap ### Phase 1: Core Infrastructure (Week 1) 1. Install Pinia + configure stores 2. Install Lucide Icons 3. Create SSE service layer 4. Setup store structure (renderers, servers, playback, ui) 5. Connect SSE events to stores ### Phase 2: UI Components (Week 2) 5. Build RendererCard component (status-colored) 6. Build TransportControls component 7. Build VolumeControl component 8. Build QueueViewer component 9. Create responsive grid layouts ### Phase 3: Dashboard Assembly (Week 3) 10. Create PMOControl home view 11. Integrate all components 12. Add media server browser panel 13. Implement responsive breakpoints 14. Add loading states & error handling ### Phase 4: Polish & Testing (Week 4) 15. Test with 20+ concurrent clients 16. Performance profiling & optimization 17. Accessibility audit (ARIA, keyboard nav) 18. Cross-browser testing 19. Mobile/tablet testing 20. Documentation --- ## Dependencies to Install ```json { "dependencies": { "pinia": "^2.2.8", "lucide-vue-next": "^0.470.0", "vue-virtual-scroller": "^2.0.0-beta.8" }, "devDependencies": { // Already installed: vue, vue-router, typescript, vite } } ``` **Total bundle size estimate:** +15KB gzipped (Pinia + Lucide + Virtual Scroller) --- ## Status-based Coloring Scheme Based on "carte uniforme, responsive, colorée selon statut" spec: ```css /* Renderer Card Status Colors */ .renderer-card.playing { border-color: #22c55e; /* green */ background: linear-gradient(135deg, #22c55e10, transparent); } .renderer-card.paused { border-color: #f59e0b; /* amber */ background: linear-gradient(135deg, #f59e0b10, transparent); } .renderer-card.stopped { border-color: #6b7280; /* gray */ background: linear-gradient(135deg, #6b728010, transparent); } .renderer-card.offline { border-color: #ef4444; /* red */ background: linear-gradient(135deg, #ef444410, transparent); opacity: 0.6; } .renderer-card.transitioning { border-color: #3b82f6; /* blue */ background: linear-gradient(135deg, #3b82f610, transparent); animation: pulse 2s infinite; } ``` --- ## Answers to Your Specific Questions ### 1. State Management? **Answer: Pinia** - Vue 3 official, perfect for SSE real-time updates, TypeScript native, lightweight ### 2. UI Component Library? **Answer: Headless UI (Shadcn-vue) + Custom Components** - Full control over status-based styling, lightweight, no theme lock-in ### 3. Keep existing components? **Answer: Yes, reorganize into Debug section** - Keep valuable dev tools, make PMOControl the new home screen ### 4. Responsive breakpoints? **Answer: Mobile-first with 3 breakpoints** - <768px (mobile), 768-1024px (tablet), >1024px (desktop) ### 5. Icons? **Answer: Lucide Icons (SVG library)** - Professional, customizable, status-colored, NOT emoji --- ## Risk Mitigation **Potential challenges:** 1. **SSE connection management across tabs** - Solution: Use BroadcastChannel API for cross-tab sync - Fallback: LocalStorage events 2. **20+ renderers performance** - Solution: Virtual scrolling + throttled updates - Monitor: Chrome DevTools Performance profiler 3. **Network reliability (SSE reconnection)** - Solution: Exponential backoff reconnection - UI indicator for connection status 4. **Album art loading (CORS, 404s)** - Solution: Proxy through backend - Fallback: Default placeholder image 5. **Browser compatibility (SSE support)** - Chrome/Edge: Native support ✅ - Firefox: Native support ✅ - Safari: Native support ✅ - IE11: Use EventSource polyfill --- ## Success Metrics **Performance targets:** - Initial load: <2s (FCP) - SSE event latency: <100ms - UI interaction: <16ms (60fps) - Memory usage: <50MB with 20 renderers - Bundle size: <250KB gzipped **Functionality checklist:** - [ ] Display all discovered renderers in real-time - [ ] Show accurate playback state (play/pause/stop) - [ ] Volume control works across all renderer types - [ ] Queue display syncs with server - [ ] Media server browsing functional - [ ] Playlist attachment working - [ ] Responsive on mobile/tablet/desktop - [ ] Accessible (WCAG AA compliance) - [ ] 20+ concurrent clients supported --- ## Next Steps 1. **Review & approve** this plan with stakeholders 2. **Clarify any ambiguities** in requirements 3. **Set up development environment** (install dependencies) 4. **Begin Phase 1** (Core Infrastructure) Would you like me to proceed with implementation, or do you have questions about any of these recommendations?