feat: use sexier audio waveform #265

Merged
talksik merged 2 commits from worktree-starry-gliding-hennessy into main 2026-06-11 17:07:26 +00:00
talksik commented 2026-06-11 16:55:49 +00:00 (Migrated from github.com)

Closes #261

Summary by CodeRabbit

  • New Features

    • Replaced old level-bar meter with a centered live waveform visualization across recording overlays, playback review, media particles, and audio settings for richer frequency detail and smoother animations.
  • Refactor

    • Unified audio visualizer usage throughout the app.
  • Chores

    • Removed the legacy recording-mode hook and the prior simple level-bars component.
Closes #261 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Replaced old level-bar meter with a centered live waveform visualization across recording overlays, playback review, media particles, and audio settings for richer frequency detail and smoother animations. * **Refactor** * Unified audio visualizer usage throughout the app. * **Chores** * Removed the legacy recording-mode hook and the prior simple level-bars component. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
coderabbitai[bot] commented 2026-06-11 16:56:03 +00:00 (Migrated from github.com)

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 70e916ef-44db-464c-9305-3122b1f98244

📥 Commits

Reviewing files that changed from the base of the PR and between 8ed8e2cccb and ba82729f27.

📒 Files selected for processing (1)
  • js/desktop/src/components/audio/centered-waveform.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • js/desktop/src/components/audio/centered-waveform.tsx

📝 Walkthrough

Walkthrough

This PR replaces the three-bar AudioLevelBars visualizer with a new canvas-based CenteredWaveform (frequency-domain, log-spaced bars, asymmetric smoothing, DPR-aware). It also removes the local useRecordingMode hook and imports RecordingMode from the media-settings store; UI consumers are updated to the new component.

Changes

Audio Waveform Visualization Replacement

Layer / File(s) Summary
CenteredWaveform component implementation
js/desktop/src/components/audio/centered-waveform.tsx
New canvas-based waveform visualizer with log-spaced symmetric frequency-bin mapping, analyser attachment via silent-gain routing, asymmetric attack/release smoothing with cosine envelope, cached currentColor rendering, and DPR-aware backing store updates.
RecordingMode state management migration
js/desktop/src/hooks/use-recording-mode.ts, js/desktop/src/features/compose/use-recorder.ts
RecordingMode type and useRecordingMode hook removed from use-recording-mode.ts; import source updated to @/stores/media-settings-store in use-recorder.ts.
Recording overlay component migration
js/desktop/src/features/compose/recording-overlay.tsx
Imports RecordingMode from media-settings store; replaces AudioLevelBars with CenteredWaveform in review playback and live recording paths. Refactors ReviewPlayback audio element from useRef to useState-backed setter pattern.
Other UI components migration
js/desktop/src/features/particles/media-particle-view.tsx, js/desktop/src/features/settings/audio-video-settings-page.tsx
Both files import and render CenteredWaveform instead of AudioLevelBars. Settings page reworks InlineLevelMeter to show a muted placeholder when no audio, or centered waveform with fixed dimensions when available; small comment tweak in particle view.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related issues

  • #261: Sexier audio waveform — This PR implements the new centered waveform visualization and replaces all instances of the old audio level bars, aligning with the requested UI enhancement.

Poem

🐇 I hopped in to trade bars for waves so fair,
Canvas brushed soft with frequency in air,
Log-spaced peaks wiggle, smoothing with a hum,
DPR-aware strokes make the small screens hum,
A rabbit cheers — new waveform, fresh and rare!

🚥 Pre-merge checks | 3 | 2

Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check Inconclusive The pull request addresses issue #261 by implementing a new audio waveform visualization component, but linked issue lacks detailed requirements to fully validate implementation completeness. Review the linked issue #261 to verify that all specified requirements for the audio waveform are met by the CenteredWaveform implementation.
Passed checks (3 passed)
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title 'use sexier audio waveform' clearly describes the main change: replacing the AudioLevelBars component with a new CenteredWaveform component for improved visual presentation.
Out of Scope Changes check Passed All changes are within scope: removing old AudioLevelBars component, adding new CenteredWaveform, updating imports, and moving RecordingMode type to media-settings-store.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-starry-gliding-hennessy

Comment @coderabbitai help to get the list of available commands and usage tips.

<!-- This is an auto-generated comment: summarize by coderabbit.ai --> <!-- review_stack_entry_start --> [![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/flowy-live/llink/pull/265?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> No actionable comments were generated in the recent review. 🎉 <details> <summary>ℹ️ Recent review info</summary> <details> <summary>⚙️ Run configuration</summary> **Configuration used**: defaults **Review profile**: CHILL **Plan**: Pro Plus **Run ID**: `70e916ef-44db-464c-9305-3122b1f98244` </details> <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between 8ed8e2cccb5eba45f0f23d0d00a93adbdb0d19b4 and ba82729f270039e04fe6124797b29131daffd201. </details> <details> <summary>📒 Files selected for processing (1)</summary> * `js/desktop/src/components/audio/centered-waveform.tsx` </details> <details> <summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary> * js/desktop/src/components/audio/centered-waveform.tsx </details> </details> --- <!-- walkthrough_start --> <details> <summary>📝 Walkthrough</summary> ## Walkthrough This PR replaces the three-bar AudioLevelBars visualizer with a new canvas-based CenteredWaveform (frequency-domain, log-spaced bars, asymmetric smoothing, DPR-aware). It also removes the local useRecordingMode hook and imports RecordingMode from the media-settings store; UI consumers are updated to the new component. ## Changes **Audio Waveform Visualization Replacement** |Layer / File(s)|Summary| |---|---| |**CenteredWaveform component implementation** <br> `js/desktop/src/components/audio/centered-waveform.tsx`|New canvas-based waveform visualizer with log-spaced symmetric frequency-bin mapping, analyser attachment via silent-gain routing, asymmetric attack/release smoothing with cosine envelope, cached `currentColor` rendering, and DPR-aware backing store updates.| |**RecordingMode state management migration** <br> `js/desktop/src/hooks/use-recording-mode.ts`, `js/desktop/src/features/compose/use-recorder.ts`|`RecordingMode` type and `useRecordingMode` hook removed from `use-recording-mode.ts`; import source updated to `@/stores/media-settings-store` in `use-recorder.ts`.| |**Recording overlay component migration** <br> `js/desktop/src/features/compose/recording-overlay.tsx`|Imports `RecordingMode` from media-settings store; replaces `AudioLevelBars` with `CenteredWaveform` in review playback and live recording paths. Refactors `ReviewPlayback` audio element from `useRef` to `useState`-backed setter pattern.| |**Other UI components migration** <br> `js/desktop/src/features/particles/media-particle-view.tsx`, `js/desktop/src/features/settings/audio-video-settings-page.tsx`|Both files import and render `CenteredWaveform` instead of `AudioLevelBars`. Settings page reworks `InlineLevelMeter` to show a muted placeholder when no audio, or centered waveform with fixed dimensions when available; small comment tweak in particle view.| ## Estimated code review effort 🎯 3 (Moderate) | ⏱️ ~22 minutes ## Possibly related issues - [`#261`: Sexier audio waveform](https://github.com/flowy-live/llink/issues/261) — This PR implements the new centered waveform visualization and replaces all instances of the old audio level bars, aligning with the requested UI enhancement. ## Poem > 🐇 I hopped in to trade bars for waves so fair, > Canvas brushed soft with frequency in air, > Log-spaced peaks wiggle, smoothing with a hum, > DPR-aware strokes make the small screens hum, > A rabbit cheers — new waveform, fresh and rare! </details> <!-- walkthrough_end --> <!-- pre_merge_checks_walkthrough_start --> <details> <summary>🚥 Pre-merge checks | ✅ 3 | ❌ 2</summary> ### ❌ Failed checks (1 warning, 1 inconclusive) | Check name | Status | Explanation | Resolution | | :-----------------: | :------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- | | Docstring Coverage | ⚠️ Warning | Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. | | Linked Issues check | ❓ Inconclusive | The pull request addresses issue `#261` by implementing a new audio waveform visualization component, but linked issue lacks detailed requirements to fully validate implementation completeness. | Review the linked issue `#261` to verify that all specified requirements for the audio waveform are met by the CenteredWaveform implementation. | <details> <summary>✅ Passed checks (3 passed)</summary> | Check name | Status | Explanation | | :------------------------: | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. | | Title check | ✅ Passed | The title 'use sexier audio waveform' clearly describes the main change: replacing the AudioLevelBars component with a new CenteredWaveform component for improved visual presentation. | | Out of Scope Changes check | ✅ Passed | All changes are within scope: removing old AudioLevelBars component, adding new CenteredWaveform, updating imports, and moving RecordingMode type to media-settings-store. | </details> <sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub> </details> <!-- pre_merge_checks_walkthrough_end --> <!-- finishing_touch_checkbox_start --> <details> <summary>✨ Finishing Touches</summary> <details> <summary>📝 Generate docstrings</summary> - [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> Create stacked PR - [ ] <!-- {"checkboxId": "3e1879ae-f29b-4d0d-8e06-d12b7ba33d98"} --> Commit on current branch </details> <details> <summary>🧪 Generate unit tests (beta)</summary> - [ ] <!-- {"checkboxId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} --> Create PR with unit tests - [ ] <!-- {"checkboxId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} --> Commit unit tests in branch `worktree-starry-gliding-hennessy` </details> </details> <!-- finishing_touch_checkbox_end --> <!-- tips_start --> --- <sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub> <!-- tips_end --> <!-- internal state start --> <!-- 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 --> <!-- internal state end -->
Sign in to join this conversation.