Recording now creates an offscreen <img> (browser-native MJPEG
decoding) and captures frames via canvas.toBlob() at 100ms intervals.
This replaces the broken fetch-based multipart parser.
- useRecorder: img+canvas capture instead of fetch+ReadableStream
- RecorderControls: removed Cancel button, only Record/Stop remain
- App.vue: removed cancel wiring
Live view now uses <img :src='streamUrl'> (browser-native MJPEG
support, same as the working test HTML). The fetch-based multipart
parser is kept only for recording frame capture. Also fixed
multipart boundary parsing to correctly handle boundary values
that already include leading dashes.
- LiveView receives streamUrl string, passes it directly to <img>
- useMjpegStream simplified to URL + state management
- useRecorder manages its own fetch stream independently
- RecorderControls shows recorder-specific errors
- Add MJPEG stream parsing via Fetch ReadableStream (multipart/x-mixed-replace)
- Live view display with configurable stream URL (persisted to localStorage)
- Frontend recording: capture frames to memory, save to IndexedDB
- Playback panel: list saved recordings, replay with original frame timing
- Dark-themed single-page UI with Live/Playback tabs