feat: add LiveWaveform component and mic/screen capture strategies#308
Open
abroa01 wants to merge 4 commits into
Open
feat: add LiveWaveform component and mic/screen capture strategies#308abroa01 wants to merge 4 commits into
abroa01 wants to merge 4 commits into
Conversation
Adds two reusable, brand-agnostic media primitives extracted from the Ozwell recorder feature, where they were built in-app because the library's self-contained AudioRecorder (mic-only, buffers a final Blob) could not support a streaming, navigation-surviving, mic-or-screen recorder. LiveWaveform: real-time volume visualizer that takes an existing MediaStream (unlike AudioRecorder's private AnalyserNode-based visualizer), so it can share a recorder's stream. Bar color defaults to the theme's --color-primary-500, adapting to any brand. Exported as a component + tree-shakeable subpath. media/capture: a pluggable RecordingCapture strategy (createCapture) with MicrophoneCapture (getUserMedia) and ScreenCapture (getDisplayMedia + system/mic audio mixing via AudioContext, with a video-only fallback). Pure TS, SSR-safe. Fills a real gap: the library previously had no screen capture at all. Also registers the browser globals (ResizeObserver, MediaStreamTrack, DOMException) these use in the ESLint allowlist. Minor/additive: bumps 0.6.1 -> 0.7.0.
Contributor
There was a problem hiding this comment.
Pull request overview
Adds new, reusable media primitives to @mieweb/ui: a React waveform visualizer for an external MediaStream, plus a TypeScript-only capture strategy layer for microphone and screen recording. This extends the library toward streaming/long-lived recording pipelines without modifying existing components.
Changes:
- Adds
LiveWaveformcomponent with Storybook stories for default/custom/idle rendering. - Introduces
media/capturestrategies (MicrophoneCapture,ScreenCapture) and helpers (createCapture, MIME picking, error normalization). - Wires new exports/entries and updates lint globals + package version.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tsup.config.ts | Adds a dedicated entrypoint for components/LiveWaveform builds. |
| src/media/capture/types.ts | Defines CaptureMode, CaptureResult, and RecordingCapture contract. |
| src/media/capture/ScreenCapture.ts | Implements display capture with optional mic capture and mixed audio via AudioContext. |
| src/media/capture/mime.ts | Adds MIME probing helper and capture error normalization. |
| src/media/capture/MicrophoneCapture.ts | Implements microphone-only capture strategy and MIME selection. |
| src/media/capture/index.ts | Exposes createCapture plus public capture exports. |
| src/index.ts | Re-exports LiveWaveform and media/capture from the package root. |
| src/components/LiveWaveform/LiveWaveform.tsx | Implements the canvas-based analyser waveform renderer with DPR + ResizeObserver. |
| src/components/LiveWaveform/LiveWaveform.stories.tsx | Adds Storybook stories including a synthetic oscillator-based demo stream. |
| src/components/LiveWaveform/index.ts | Provides component entry export for tree-shaking. |
| package.json | Bumps version 0.6.1 → 0.7.0. |
| eslint.config.js | Allow-lists additional browser globals used by the new code. |
Deploying ui with
|
| Latest commit: |
caf3796
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://b3a16bb6.ui-6d0.pages.dev |
| Branch Preview URL: | https://feat-live-waveform-and-captu.ui-6d0.pages.dev |
typeof DOMException !== 'undefined' before instanceof, so toCaptureError doesn't throw a ReferenceError in SSR/Node/test environments and mask the original error. Addresses PR review feedback.
…rror edge cases LiveWaveform: feature-detect ResizeObserver; when unavailable, fall back to the one-time resize() already performed instead of throwing (older browsers, some webviews/test runtimes). ScreenCapture: wrap audio mixing in try/catch — on failure, close the partial AudioContext and degrade to video-only instead of rejecting start() and leaving the already-granted screen share running. Also only report audioCaptured: true when the mixed destination actually yields audio tracks. Addresses PR review feedback.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds two reusable, brand-agnostic media primitives to
@mieweb/ui:LiveWaveform— a real-time volume visualizer for an existingMediaStream.media/capture— a pluggable capture-strategy layer:createCapture(mode)returningMicrophoneCaptureorScreenCapture, plus theRecordingCapture/CaptureMode/CaptureResulttypes.Minor, additive release: 0.6.1 → 0.7.0. No existing component is modified or removed.
Why
These were built in the Ozwell app because the library's existing
AudioRecorderis a self-contained component that owns the microphone, buffers the whole recording in memory, and only hands back a finishedBlobat the end. That model can't support Ozwell's recorder, which needs to:Rather than keep these capabilities app-local, we're upstreaming the genuinely reusable, framework-agnostic pieces so any consumer can build a real recorder on top of the library — and so the app can delete its duplicates.
Gaps this fills in the library
MediaStreamAnalyserNode-based visualizer insideAudioRecorder)LiveWaveformgetDisplayMedia)ScreenCapturecreateCapture(mode)Details
LiveWaveformtakes aMediaStreamyou already have (so it can share a recorder's stream without contending for the device), draws vertically-centered rounded bars, is DPR-aware, and uses aResizeObserver. Bar color defaults to the theme token--color-primary-500(fallback#27aae1), so it adapts to the active brand with no config.media/captureis pure TypeScript (no React), SSR-safe (MediaRecorder/navigatorare only touched inside methods; MIME probing guardstypeof MediaRecorder).ScreenCapturemixes display + mic audio into one track via anAudioContextand falls back to video-only when no audio track is granted.ResizeObserver,MediaStreamTrack,DOMException) in the ESLint allowlist.Storybook (UI to review)
New story: Components/Images & Media/LiveWaveform with
Default,CustomColor, andIdle. The demo builds a synthetic oscillator-backed stream behind a "Play tone" button, so the waveform animates in Storybook without requesting microphone permission. ExistingAudioRecorder/AudioPlayerstories are unchanged.Testing
pnpm typecheck✅pnpm lint(new files) ✅pnpm build✅ —distexportsLiveWaveform,createCapture,CaptureMode,MicrophoneCapture,ScreenCapturepnpm test✅ 249 tests pass (no regressions)Downstream
Consumed by the Ozwell app, which removes its local
LiveWaveformandservices/audio/capture/copies and imports both from@mieweb/ui.