Skip to content

Conversation

@sfc-gh-nbellante
Copy link
Contributor

@sfc-gh-nbellante sfc-gh-nbellante commented Oct 20, 2025

Describe your changes

This PR improves the audio recording component by adding proper TypeScript types and enhancing the WaveformController API. Key changes include:

  1. Added proper TypeScript interfaces for WaveSurfer and Record plugin mocks in tests
  2. Enhanced the WaveformController API with:
    • A new destroy() method for proper cleanup
    • A mountRef property to allow the component to manage its own container
    • Improved return types for the stop() method, now returning metadata along with the blob
  3. Fixed memory management by properly cleaning up resources
  4. Made the onApprove event handler support async operations
  5. Improved error handling and initialization logic

Testing Plan

  • Existing tests have been updated to accommodate the new API changes
  • Type safety has been improved throughout the codebase
  • The changes are well-covered by the existing test suite, which verifies the functionality of the audio recording component

Contribution License Agreement

By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license.

@snyk-io
Copy link
Contributor

snyk-io bot commented Oct 20, 2025

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Licenses 0 0 0 0 0 issues
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

Copy link
Contributor Author

sfc-gh-nbellante commented Oct 20, 2025

@github-actions
Copy link
Contributor

github-actions bot commented Oct 20, 2025

✅ PR preview is ready!

Name Link
📦 Wheel file https://core-previews.s3-us-west-2.amazonaws.com/pr-12833/streamlit-1.51.0-py3-none-any.whl
📦 @streamlit/component-v2-lib Download from artifacts
🕹️ Preview app pr-12833.streamlit.app (☁️ Deploy here if not accessible)

@github-actions
Copy link
Contributor

github-actions bot commented Oct 20, 2025

📉 Python coverage change detected

The Python unit test coverage has decreased by 0.0050%

  • Current PR: 92.7593% (20081 statements, 1454 missed)
  • Latest develop: 92.7643% (20081 statements, 1453 missed)

✅ Coverage change is within normal range.

Coverage by files
Name Stmts Miss Cover
streamlit/__init__.py 141 0 100%
streamlit/__main__.py 3 3 0%
streamlit/auth_util.py 100 25 75%
streamlit/cli_util.py 39 6 85%
streamlit/column_config.py 3 0 100%
streamlit/commands/__init__.py 0 0 100%
streamlit/commands/echo.py 54 11 80%
streamlit/commands/execution_control.py 59 10 83%
streamlit/commands/experimental_query_params.py 40 2 95%
streamlit/commands/logo.py 41 6 85%
streamlit/commands/navigation.py 106 2 98%
streamlit/commands/page_config.py 101 4 96%
streamlit/components/__init__.py 0 0 100%
streamlit/components/lib/__init__.py 0 0 100%
streamlit/components/lib/local_component_registry.py 35 2 94%
streamlit/components/types/__init__.py 0 0 100%
streamlit/components/types/base_component_registry.py 14 0 100%
streamlit/components/types/base_custom_component.py 49 6 88%
streamlit/components/v1/__init__.py 5 0 100%
streamlit/components/v1/component_arrow.py 33 8 76%
streamlit/components/v1/component_registry.py 41 3 93%
streamlit/components/v1/components.py 4 4 0%
streamlit/components/v1/custom_component.py 85 7 92%
streamlit/components/v2/__init__.py 24 0 100%
streamlit/components/v2/bidi_component/__init__.py 4 0 100%
streamlit/components/v2/bidi_component/constants.py 5 0 100%
streamlit/components/v2/bidi_component/main.py 109 12 89%
streamlit/components/v2/bidi_component/serialization.py 81 5 94%
streamlit/components/v2/bidi_component/state.py 13 0 100%
streamlit/components/v2/component_definition_resolver.py 30 0 100%
streamlit/components/v2/component_file_watcher.py 117 9 92%
streamlit/components/v2/component_manager.py 96 16 83%
streamlit/components/v2/component_manifest_handler.py 24 0 100%
streamlit/components/v2/component_path_utils.py 68 5 93%
streamlit/components/v2/component_registry.py 118 8 93%
streamlit/components/v2/get_bidi_component_manager.py 8 1 88%
streamlit/components/v2/manifest_scanner.py 224 25 89%
streamlit/components/v2/presentation.py 84 19 77%
streamlit/components/v2/types.py 8 8 0%
streamlit/config.py 412 12 97%
streamlit/config_option.py 79 3 96%
streamlit/config_util.py 288 7 98%
streamlit/connections/__init__.py 6 0 100%
streamlit/connections/base_connection.py 45 0 100%
streamlit/connections/snowflake_connection.py 60 13 78%
streamlit/connections/snowpark_connection.py 44 3 93%
streamlit/connections/sql_connection.py 56 6 89%
streamlit/connections/util.py 33 0 100%
streamlit/cursor.py 82 2 98%
streamlit/dataframe_util.py 501 45 91%
streamlit/delta_generator.py 205 6 97%
streamlit/delta_generator_singletons.py 76 8 89%
streamlit/deprecation_util.py 59 4 93%
streamlit/development.py 1 0 100%
streamlit/elements/__init__.py 0 0 100%
streamlit/elements/alert.py 60 0 100%
streamlit/elements/arrow.py 198 15 92%
streamlit/elements/balloons.py 10 0 100%
streamlit/elements/bokeh_chart.py 27 0 100%
streamlit/elements/code.py 20 1 95%
streamlit/elements/deck_gl_json_chart.py 104 10 90%
streamlit/elements/dialog_decorator.py 38 0 100%
streamlit/elements/doc_string.py 227 9 96%
streamlit/elements/empty.py 16 4 75%
streamlit/elements/exception.py 101 10 90%
streamlit/elements/form.py 54 2 96%
streamlit/elements/graphviz_chart.py 35 1 97%
streamlit/elements/heading.py 56 0 100%
streamlit/elements/html.py 48 0 100%
streamlit/elements/iframe.py 29 0 100%
streamlit/elements/image.py 32 0 100%
streamlit/elements/json.py 39 2 95%
streamlit/elements/layouts.py 140 3 98%
streamlit/elements/lib/__init__.py 0 0 100%
streamlit/elements/lib/built_in_chart_utils.py 387 26 93%
streamlit/elements/lib/color_util.py 100 4 96%
streamlit/elements/lib/column_config_utils.py 168 1 99%
streamlit/elements/lib/column_types.py 189 4 98%
streamlit/elements/lib/dialog.py 67 1 99%
streamlit/elements/lib/dicttools.py 39 2 95%
streamlit/elements/lib/file_uploader_utils.py 30 0 100%
streamlit/elements/lib/form_utils.py 26 0 100%
streamlit/elements/lib/image_utils.py 176 21 88%
streamlit/elements/lib/js_number.py 28 3 89%
streamlit/elements/lib/layout_utils.py 109 1 99%
streamlit/elements/lib/mutable_status_container.py 73 4 95%
streamlit/elements/lib/options_selector_utils.py 90 0 100%
streamlit/elements/lib/pandas_styler_utils.py 80 2 98%
streamlit/elements/lib/policies.py 56 1 98%
streamlit/elements/lib/streamlit_plotly_theme.py 49 0 100%
streamlit/elements/lib/subtitle_utils.py 76 13 83%
streamlit/elements/lib/utils.py 76 5 93%
streamlit/elements/map.py 110 1 99%
streamlit/elements/markdown.py 63 2 97%
streamlit/elements/media.py 181 8 96%
streamlit/elements/metric.py 91 0 100%
streamlit/elements/pdf.py 49 2 96%
streamlit/elements/plotly_chart.py 114 4 96%
streamlit/elements/progress.py 36 0 100%
streamlit/elements/pyplot.py 39 2 95%
streamlit/elements/snow.py 10 0 100%
streamlit/elements/space.py 12 0 100%
streamlit/elements/spinner.py 34 0 100%
streamlit/elements/text.py 16 0 100%
streamlit/elements/toast.py 26 0 100%
streamlit/elements/vega_charts.py 226 3 99%
streamlit/elements/widgets/__init__.py 0 0 100%
streamlit/elements/widgets/audio_input.py 68 10 85%
streamlit/elements/widgets/button.py 214 3 99%
streamlit/elements/widgets/button_group.py 161 1 99%
streamlit/elements/widgets/camera_input.py 62 10 84%
streamlit/elements/widgets/chat.py 193 54 72%
streamlit/elements/widgets/checkbox.py 52 0 100%
streamlit/elements/widgets/color_picker.py 56 2 96%
streamlit/elements/widgets/data_editor.py 239 14 94%
streamlit/elements/widgets/file_uploader.py 103 18 83%
streamlit/elements/widgets/multiselect.py 105 4 96%
streamlit/elements/widgets/number_input.py 143 5 97%
streamlit/elements/widgets/radio.py 83 5 94%
streamlit/elements/widgets/select_slider.py 97 0 100%
streamlit/elements/widgets/selectbox.py 91 2 98%
streamlit/elements/widgets/slider.py 241 8 97%
streamlit/elements/widgets/text_widgets.py 130 6 95%
streamlit/elements/widgets/time_widgets.py 248 15 94%
streamlit/elements/write.py 166 30 82%
streamlit/emojis.py 4 0 100%
streamlit/env_util.py 21 3 86%
streamlit/error_util.py 33 2 94%
streamlit/errors.py 188 25 87%
streamlit/external/__init__.py 0 0 100%
streamlit/external/langchain/__init__.py 2 0 100%
streamlit/external/langchain/streamlit_callback_handler.py 141 82 42%
streamlit/file_util.py 84 8 90%
streamlit/git_util.py 100 5 95%
streamlit/logger.py 54 0 100%
streamlit/material_icon_names.py 1 0 100%
streamlit/navigation/__init__.py 0 0 100%
streamlit/navigation/page.py 78 2 97%
streamlit/net_util.py 55 3 95%
streamlit/platform.py 10 1 90%
streamlit/runtime/__init__.py 8 0 100%
streamlit/runtime/app_session.py 447 94 79%
streamlit/runtime/caching/__init__.py 19 0 100%
streamlit/runtime/caching/cache_data_api.py 164 3 98%
streamlit/runtime/caching/cache_errors.py 45 4 91%
streamlit/runtime/caching/cache_resource_api.py 122 0 100%
streamlit/runtime/caching/cache_type.py 11 1 91%
streamlit/runtime/caching/cache_utils.py 165 9 95%
streamlit/runtime/caching/cached_message_replay.py 108 1 99%
streamlit/runtime/caching/hashing.py 311 25 92%
streamlit/runtime/caching/legacy_cache_api.py 14 0 100%
streamlit/runtime/caching/storage/__init__.py 2 0 100%
streamlit/runtime/caching/storage/cache_storage_protocol.py 31 2 94%
streamlit/runtime/caching/storage/dummy_cache_storage.py 21 0 100%
streamlit/runtime/caching/storage/in_memory_cache_storage_wrapper.py 60 0 100%
streamlit/runtime/caching/storage/local_disk_cache_storage.py 86 4 95%
streamlit/runtime/connection_factory.py 85 9 89%
streamlit/runtime/context.py 140 0 100%
streamlit/runtime/context_util.py 18 0 100%
streamlit/runtime/credentials.py 139 4 97%
streamlit/runtime/forward_msg_cache.py 23 2 91%
streamlit/runtime/forward_msg_queue.py 63 4 94%
streamlit/runtime/fragment.py 112 2 98%
streamlit/runtime/media_file_manager.py 69 7 90%
streamlit/runtime/media_file_storage.py 15 0 100%
streamlit/runtime/memory_media_file_storage.py 68 0 100%
streamlit/runtime/memory_session_storage.py 15 0 100%
streamlit/runtime/memory_uploaded_file_manager.py 41 1 98%
streamlit/runtime/metrics_util.py 193 12 94%
streamlit/runtime/pages_manager.py 59 2 97%
streamlit/runtime/runtime.py 248 18 93%
streamlit/runtime/runtime_util.py 30 1 97%
streamlit/runtime/script_data.py 16 0 100%
streamlit/runtime/scriptrunner/__init__.py 5 0 100%
streamlit/runtime/scriptrunner/exec_code.py 49 5 90%
streamlit/runtime/scriptrunner/magic.py 83 1 99%
streamlit/runtime/scriptrunner/magic_funcs.py 10 1 90%
streamlit/runtime/scriptrunner/script_cache.py 27 0 100%
streamlit/runtime/scriptrunner/script_runner.py 230 27 88%
streamlit/runtime/scriptrunner_utils/__init__.py 0 0 100%
streamlit/runtime/scriptrunner_utils/exceptions.py 11 1 91%
streamlit/runtime/scriptrunner_utils/script_requests.py 106 5 95%
streamlit/runtime/scriptrunner_utils/script_run_context.py 135 2 99%
streamlit/runtime/secrets.py 242 26 89%
streamlit/runtime/session_manager.py 60 1 98%
streamlit/runtime/state/__init__.py 7 0 100%
streamlit/runtime/state/common.py 52 2 96%
streamlit/runtime/state/presentation.py 19 4 79%
streamlit/runtime/state/query_params.py 110 3 97%
streamlit/runtime/state/query_params_proxy.py 71 0 100%
streamlit/runtime/state/safe_session_state.py 77 11 86%
streamlit/runtime/state/session_state.py 433 29 93%
streamlit/runtime/state/session_state_proxy.py 62 8 87%
streamlit/runtime/state/widgets.py 15 1 93%
streamlit/runtime/stats.py 42 0 100%
streamlit/runtime/theme_util.py 46 1 98%
streamlit/runtime/uploaded_file_manager.py 39 3 92%
streamlit/runtime/websocket_session_manager.py 66 0 100%
streamlit/source_util.py 36 1 97%
streamlit/string_util.py 88 7 92%
streamlit/temporary_directory.py 18 1 94%
streamlit/testing/__init__.py 0 0 100%
streamlit/testing/v1/__init__.py 2 0 100%
streamlit/testing/v1/app_test.py 239 6 97%
streamlit/testing/v1/element_tree.py 1327 87 93%
streamlit/testing/v1/local_script_runner.py 71 2 97%
streamlit/testing/v1/util.py 17 0 100%
streamlit/time_util.py 28 1 96%
streamlit/type_util.py 138 12 91%
streamlit/url_util.py 39 5 87%
streamlit/user_info.py 87 8 91%
streamlit/util.py 38 1 97%
streamlit/version.py 3 0 100%
streamlit/watcher/__init__.py 3 0 100%
streamlit/watcher/event_based_path_watcher.py 181 24 87%
streamlit/watcher/folder_black_list.py 14 1 93%
streamlit/watcher/local_sources_watcher.py 127 9 93%
streamlit/watcher/path_watcher.py 43 3 93%
streamlit/watcher/polling_path_watcher.py 55 2 96%
streamlit/watcher/util.py 49 1 98%
streamlit/web/__init__.py 0 0 100%
streamlit/web/bootstrap.py 138 18 87%
streamlit/web/cache_storage_manager_config.py 5 0 100%
streamlit/web/cli.py 186 17 91%
streamlit/web/server/__init__.py 5 0 100%
streamlit/web/server/app_static_file_handler.py 29 3 90%
streamlit/web/server/authlib_tornado_integration.py 18 1 94%
streamlit/web/server/bidi_component_request_handler.py 65 8 88%
streamlit/web/server/browser_websocket_handler.py 115 31 73%
streamlit/web/server/component_file_utils.py 24 0 100%
streamlit/web/server/component_request_handler.py 55 4 93%
streamlit/web/server/media_file_handler.py 65 9 86%
streamlit/web/server/oauth_authlib_routes.py 118 18 85%
streamlit/web/server/oidc_mixin.py 44 0 100%
streamlit/web/server/routes.py 90 7 92%
streamlit/web/server/server.py 188 11 94%
streamlit/web/server/server_util.py 67 5 93%
streamlit/web/server/stats_request_handler.py 53 4 92%
streamlit/web/server/upload_file_request_handler.py 53 9 83%
streamlit/web/server/websocket_headers.py 19 1 95%
TOTAL 20081 1454 93%

📊 View detailed coverage comparison

@sfc-gh-nbellante sfc-gh-nbellante added security-assessment-completed Security assessment has been completed for PR impact:internal PR changes only affect internal code change:feature PR contains new feature or enhancement implementation labels Oct 21, 2025
@sfc-gh-nbellante sfc-gh-nbellante force-pushed the nico/pr6-audio-infra branch 2 times, most recently from 356976d to 0646ce4 Compare October 21, 2025 14:54
@github-actions
Copy link
Contributor

github-actions bot commented Oct 21, 2025

📉 Frontend coverage change detected

The frontend unit test (vitest) coverage has decreased by 0.0300%

  • Current PR: 86.0100% (50057 lines, 6999 missed)
  • Latest develop: 86.0400% (50022 lines, 6980 missed)

💡 Consider adding more unit tests to maintain or improve coverage.

📊 View detailed coverage comparison

@sfc-gh-nbellante sfc-gh-nbellante force-pushed the nico/pr5-wire-audioinput-chatinput branch from 7d59987 to 22d8d70 Compare October 21, 2025 21:01
@sfc-gh-nbellante sfc-gh-nbellante force-pushed the nico/pr5-wire-audioinput-chatinput branch from 22d8d70 to 0cee413 Compare October 23, 2025 14:18
@sfc-gh-nbellante sfc-gh-nbellante force-pushed the nico/pr6-audio-infra branch 4 times, most recently from ce4fa86 to 53dba69 Compare October 23, 2025 21:36
@sfc-gh-nbellante sfc-gh-nbellante force-pushed the nico/pr5-wire-audioinput-chatinput branch from 0caad17 to 8ddc29b Compare October 24, 2025 17:30
@sfc-gh-nbellante sfc-gh-nbellante force-pushed the nico/pr6-audio-infra branch 2 times, most recently from 5bcbbef to dc6bbd0 Compare October 31, 2025 19:06
@sfc-gh-nbellante sfc-gh-nbellante force-pushed the nico/pr5-wire-audioinput-chatinput branch from 9acade9 to 6fbbc3e Compare October 31, 2025 19:06
@sfc-gh-nbellante sfc-gh-nbellante force-pushed the nico/pr5-wire-audioinput-chatinput branch from 6fbbc3e to 73aa243 Compare October 31, 2025 21:19
@sfc-gh-nbellante sfc-gh-nbellante force-pushed the nico/pr5-wire-audioinput-chatinput branch from 73aa243 to 11a99ff Compare October 31, 2025 22:01
@sfc-gh-nbellante sfc-gh-nbellante changed the base branch from nico/pr5-wire-audioinput-chatinput to graphite-base/12833 November 1, 2025 07:02
- Update WaveformControllerEvents type to allow Promise<void> return
- Await onApprove callback in useWaveformController
- Mark all onApprove callback invocations with void operator
- Ensures promises are explicitly handled per linting rules
Change the onApprove callback signature from `void | Promise<void>`
to always return `Promise<void>` for API consistency.

Changes:
- Update WaveformControllerEvents type signature
- Make AudioInput onApprove implementation async with await
- Update test type annotations

This provides a consistent async interface and prevents bugs from
assuming synchronous behavior when the callback may be async.
Remove the internalMountRef fallback and make containerRef required
in useWaveformController to maintain architectural consistency with
ChatInput.

Changes:
- Remove internalMountRef and fallback logic
- Make containerRef required in UseWaveformControllerParams
- Replace all mountRef references with containerRef
- Return containerRef as mountRef in the controller interface

This maintains API compatibility while enforcing consistent ref
management patterns across the codebase.
Add vertical padding to StyledWaveSurferDiv to restore spacing that
was previously baked into the height calculation when switching from
explicit height to 'auto'.

Changes:
- Add paddingTop and paddingBottom using theme.spacing.threeXS
- Replaces the previous approach where WAVEFORM_PADDING was included
  in the height calculation (height = WAVEFORM_HEIGHT + WAVEFORM_PADDING)

This properly separates concerns - WaveSurfer handles waveform
dimensions while CSS handles spacing.
Remove the throw and .catch() pattern that was causing errors to be
reported twice - once in initializeWaveSurfer's catch block and again
in the useEffect's catch handler.

Changes:
- Remove throw from initializeWaveSurfer catch block
- Remove .catch() from useEffect
- Handle errors once in initializeWaveSurfer where they occur

This creates a single, clear error handling point and prevents
duplicate error reporting to the onError callback.
All WaveformControllerEvents callbacks now consistently return Promise<void>
instead of mixing void and Promise<void> return types. This provides a more
predictable API and aligns with modern async/await best practices where
"always async" is preferred over "sometimes async".
The lastStopResultRef was caching stop results as a defensive fallback for:
1. Returning cached results when stop() called repeatedly
2. Providing fallback blob in approve() when state is cleared

This is overly defensive and hides bugs in state management. If stop() is
called when not recording, that's a bug. If approve() can't find the blob,
that's also a bug. Both should fail loudly rather than return stale data.

Simplified approach:
- stop() now throws immediately if not recording
- approve() uses blob parameter or currentBlob only
- Removed all lastStopResultRef cleanup code

This forces proper state management and makes bugs visible.
Replaced all `MockRecordPluginClass as any` assertions with
`MockRecordPluginClass as unknown as typeof RecordPlugin` to be more
explicit about the type we're casting to.

While still a type assertion, this is more specific and documents what
type the mock is pretending to be, rather than completely bypassing
type safety with 'as any'.

Also removed the eslint-disable-next-line comments that were suppressing
the no-explicit-any rule, since we're no longer using 'as any'.
Add missing RecordPlugin type import to WaveSurferRecordBackend.test.ts.
The type was referenced in type assertions (typeof RecordPlugin) but
never imported, causing CI type check failures.
Resolves 25 ESLint violations that were blocking CI:

**AudioInput.tsx (8 fixes):**
- Add return Promise.resolve() to async callbacks that don't await
- Satisfies @typescript-eslint/require-await rule
- Callbacks: onPermissionDenied, onError, onRecordStart, onRecordReady,
  onCancel, onProgressMs, onPlaybackPause, onPlaybackFinish
- Maintains Promise<void> interface contract while avoiding unnecessary
  async overhead

**AudioInput.test.tsx (17 fixes):**
- Add void operator to unhandled promise calls
- Satisfies @typescript-eslint/no-floating-promises rule
- Makes explicit that promises are intentionally not awaited
- Matches pattern used in useWaveformController implementation

These changes maintain the architectural decision (commit 56221c8d0) to
have all WaveformControllerEvents callbacks return Promise<void> for
API consistency, while satisfying linting requirements.

No behavior changes - callbacks already worked correctly.
Only onApprove is truly async (handles encoding/uploading), so it's the
only callback that should return Promise<void>. All other callbacks are
synchronous (just set React state) and now correctly return void.

This eliminates unnecessary async wrappers and Promise.resolve() calls.
@graphite-app graphite-app bot changed the base branch from graphite-base/12833 to develop November 1, 2025 07:04
- Replace useEffect with useExecuteWhenChanged in useWaveformController
  to follow React best practices for adjusting state on prop changes
- Update temporal comment wording in AudioInput styled-components
  to improve long-term code maintainability
@sfc-gh-nbellante sfc-gh-nbellante merged commit 34fbfe4 into develop Nov 1, 2025
37 checks passed
Copy link
Contributor Author

Merge activity

@sfc-gh-nbellante sfc-gh-nbellante deleted the nico/pr6-audio-infra branch November 1, 2025 07:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:feature PR contains new feature or enhancement implementation impact:internal PR changes only affect internal code security-assessment-completed Security assessment has been completed for PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants