/** * Live Captions - Recordings Panel * Handles viewing and managing saved recordings */ const Recordings = { // Current state recordings: [], currentRecording: null, // DOM elements elements: {}, /** * Initialize the recordings panel */ init() { this.cacheElements(); this.bindEvents(); }, /** * Cache DOM element references */ cacheElements() { this.elements = { btnRecordings: document.getElementById('btn-recordings'), btnClose: document.getElementById('btn-close-recordings'), btnBackToList: document.getElementById('btn-back-to-list'), btnDelete: document.getElementById('btn-delete-recording'), panel: document.getElementById('recordings-panel'), overlay: document.getElementById('overlay'), recordingsList: document.getElementById('recordings-list'), recordingViewer: document.getElementById('recording-viewer'), viewerFilename: document.getElementById('viewer-filename'), viewerContent: document.getElementById('viewer-content'), }; }, /** * Bind event listeners */ bindEvents() { this.elements.btnRecordings.addEventListener('click', () => this.openPanel()); this.elements.btnClose.addEventListener('click', () => this.closePanel()); this.elements.btnBackToList.addEventListener('click', () => this.showList()); this.elements.btnDelete.addEventListener('click', () => this.deleteCurrentRecording()); // Close on overlay click (but check if it's not settings panel) this.elements.overlay.addEventListener('click', () => { if (!this.elements.panel.classList.contains('hidden')) { this.closePanel(); } }); }, /** * Open the recordings panel */ openPanel() { this.elements.panel.classList.remove('hidden'); this.elements.overlay.classList.remove('hidden'); this.showList(); this.loadRecordings(); }, /** * Close the recordings panel */ closePanel() { this.elements.panel.classList.add('hidden'); this.elements.overlay.classList.add('hidden'); this.currentRecording = null; }, /** * Show the recordings list view */ showList() { this.elements.recordingsList.classList.remove('hidden'); this.elements.recordingViewer.classList.add('hidden'); }, /** * Show the recording viewer */ showViewer() { this.elements.recordingsList.classList.add('hidden'); this.elements.recordingViewer.classList.remove('hidden'); }, /** * Load recordings from the API */ async loadRecordings() { this.elements.recordingsList.innerHTML = '

Loading recordings...

'; try { const response = await fetch('/api/recordings'); if (!response.ok) throw new Error('Failed to load recordings'); this.recordings = await response.json(); this.renderRecordingsList(); } catch (error) { console.error('Error loading recordings:', error); this.elements.recordingsList.innerHTML = '

Failed to load recordings

'; } }, /** * Render the recordings list */ renderRecordingsList() { if (this.recordings.length === 0) { this.elements.recordingsList.innerHTML = '

No recordings yet.
Enable auto-save and record some captions!

'; return; } const html = this.recordings.map(recording => `
${recording.date} ${this.formatFileSize(recording.size)}
`).join(''); this.elements.recordingsList.innerHTML = html; // Bind click events to items this.elements.recordingsList.querySelectorAll('.recording-item').forEach(item => { item.addEventListener('click', () => { const filename = item.dataset.filename; this.viewRecording(filename); }); }); }, /** * Format file size in human-readable format */ formatFileSize(bytes) { if (bytes < 1024) return bytes + ' B'; if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'; return (bytes / (1024 * 1024)).toFixed(1) + ' MB'; }, /** * View a specific recording */ async viewRecording(filename) { try { const response = await fetch(`/api/recordings/${encodeURIComponent(filename)}`); if (!response.ok) throw new Error('Failed to load recording'); const data = await response.json(); this.currentRecording = filename; this.elements.viewerFilename.textContent = filename; this.elements.viewerContent.textContent = data.content; this.showViewer(); } catch (error) { console.error('Error loading recording:', error); alert('Failed to load recording'); } }, /** * Delete the currently viewed recording */ async deleteCurrentRecording() { if (!this.currentRecording) return; if (!confirm('Are you sure you want to delete this recording?')) { return; } try { const response = await fetch(`/api/recordings/${encodeURIComponent(this.currentRecording)}`, { method: 'DELETE' }); if (!response.ok) throw new Error('Failed to delete recording'); // Remove from local list this.recordings = this.recordings.filter(r => r.filename !== this.currentRecording); this.currentRecording = null; // Go back to list this.showList(); this.renderRecordingsList(); } catch (error) { console.error('Error deleting recording:', error); alert('Failed to delete recording'); } } }; // Initialize when DOM is ready document.addEventListener('DOMContentLoaded', () => { Recordings.init(); });