streamlit 1.53.1__py3-none-any.whl → 1.54.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- streamlit/__init__.py +1 -31
- streamlit/auth_util.py +91 -2
- streamlit/cli_util.py +3 -2
- streamlit/commands/echo.py +2 -2
- streamlit/commands/execution_control.py +1 -1
- streamlit/commands/logo.py +76 -24
- streamlit/commands/navigation.py +1 -1
- streamlit/components/types/base_custom_component.py +0 -2
- streamlit/components/v1/custom_component.py +0 -2
- streamlit/components/v2/bidi_component/main.py +2 -2
- streamlit/components/v2/component_path_utils.py +17 -29
- streamlit/components/v2/manifest_scanner.py +8 -3
- streamlit/components/v2/presentation.py +1 -1
- streamlit/config.py +57 -13
- streamlit/config_util.py +5 -5
- streamlit/connections/snowflake_connection.py +5 -3
- streamlit/dataframe_util.py +10 -10
- streamlit/deprecation_util.py +19 -1
- streamlit/elements/arrow.py +18 -8
- streamlit/elements/deck_gl_json_chart.py +6 -2
- streamlit/elements/exception.py +4 -2
- streamlit/elements/form.py +1 -1
- streamlit/elements/layouts.py +1 -1
- streamlit/elements/lib/built_in_chart_utils.py +36 -13
- streamlit/elements/lib/color_util.py +21 -2
- streamlit/elements/lib/column_config_utils.py +9 -7
- streamlit/elements/lib/dialog.py +1 -1
- streamlit/elements/lib/image_utils.py +5 -5
- streamlit/elements/lib/layout_utils.py +1 -1
- streamlit/elements/lib/options_selector_utils.py +72 -22
- streamlit/elements/lib/policies.py +1 -1
- streamlit/elements/lib/streamlit_plotly_theme.py +9 -11
- streamlit/elements/lib/utils.py +1 -1
- streamlit/elements/map.py +6 -6
- streamlit/elements/plotly_chart.py +2 -2
- streamlit/elements/toast.py +1 -1
- streamlit/elements/vega_charts.py +30 -7
- streamlit/elements/widgets/button.py +3 -3
- streamlit/elements/widgets/button_group.py +3 -3
- streamlit/elements/widgets/chat.py +1 -1
- streamlit/elements/widgets/data_editor.py +6 -6
- streamlit/elements/widgets/multiselect.py +1 -1
- streamlit/elements/widgets/number_input.py +1 -1
- streamlit/elements/widgets/radio.py +91 -31
- streamlit/elements/widgets/select_slider.py +123 -37
- streamlit/elements/widgets/slider.py +5 -5
- streamlit/elements/widgets/time_widgets.py +150 -18
- streamlit/elements/write.py +2 -3
- streamlit/env_util.py +1 -1
- streamlit/errors.py +2 -14
- streamlit/external/langchain/streamlit_callback_handler.py +1 -1
- streamlit/hello/dataframe_demo.py +1 -1
- streamlit/hello/plotting_demo.py +19 -12
- streamlit/path_security.py +98 -0
- streamlit/proto/Alert_pb2.py +2 -3
- streamlit/proto/AppPage_pb2.py +2 -3
- streamlit/proto/ArrowData_pb2.py +2 -3
- streamlit/proto/ArrowNamedDataSet_pb2.py +2 -3
- streamlit/proto/ArrowVegaLiteChart_pb2.py +2 -3
- streamlit/proto/Arrow_pb2.py +2 -3
- streamlit/proto/AudioInput_pb2.py +2 -3
- streamlit/proto/Audio_pb2.py +2 -3
- streamlit/proto/AuthRedirect_pb2.py +2 -3
- streamlit/proto/AutoRerun_pb2.py +2 -3
- streamlit/proto/BackMsg_pb2.py +2 -3
- streamlit/proto/Balloons_pb2.py +2 -3
- streamlit/proto/BidiComponent_pb2.py +2 -3
- streamlit/proto/Block_pb2.py +2 -3
- streamlit/proto/BokehChart_pb2.py +2 -3
- streamlit/proto/ButtonGroup_pb2.py +2 -3
- streamlit/proto/ButtonLikeIconPosition_pb2.py +2 -3
- streamlit/proto/Button_pb2.py +2 -3
- streamlit/proto/CameraInput_pb2.py +2 -3
- streamlit/proto/ChatInput_pb2.py +2 -3
- streamlit/proto/Checkbox_pb2.py +2 -3
- streamlit/proto/ClientState_pb2.py +2 -3
- streamlit/proto/Code_pb2.py +2 -3
- streamlit/proto/ColorPicker_pb2.py +2 -3
- streamlit/proto/Common_pb2.py +2 -3
- streamlit/proto/Components_pb2.py +2 -3
- streamlit/proto/DataFrame_pb2.py +2 -3
- streamlit/proto/DateInput_pb2.py +2 -3
- streamlit/proto/DateTimeInput_pb2.py +2 -3
- streamlit/proto/DeckGlJsonChart_pb2.py +2 -3
- streamlit/proto/Delta_pb2.py +2 -3
- streamlit/proto/DocString_pb2.py +2 -3
- streamlit/proto/DownloadButton_pb2.py +2 -3
- streamlit/proto/Element_pb2.py +2 -3
- streamlit/proto/Empty_pb2.py +2 -3
- streamlit/proto/Exception_pb2.py +2 -3
- streamlit/proto/Favicon_pb2.py +2 -3
- streamlit/proto/FileUploader_pb2.py +2 -3
- streamlit/proto/ForwardMsg_pb2.py +2 -3
- streamlit/proto/GapSize_pb2.py +2 -3
- streamlit/proto/GitInfo_pb2.py +2 -3
- streamlit/proto/GraphVizChart_pb2.py +2 -3
- streamlit/proto/Heading_pb2.py +2 -3
- streamlit/proto/HeightConfig_pb2.py +2 -3
- streamlit/proto/Html_pb2.py +2 -3
- streamlit/proto/IFrame_pb2.py +2 -3
- streamlit/proto/Image_pb2.py +2 -3
- streamlit/proto/Json_pb2.py +2 -3
- streamlit/proto/LabelVisibilityMessage_pb2.py +2 -3
- streamlit/proto/LinkButton_pb2.py +2 -3
- streamlit/proto/Logo_pb2.py +6 -5
- streamlit/proto/Logo_pb2.pyi +25 -1
- streamlit/proto/Markdown_pb2.py +2 -3
- streamlit/proto/Metric_pb2.py +2 -3
- streamlit/proto/MetricsEvent_pb2.py +2 -3
- streamlit/proto/MultiSelect_pb2.py +2 -3
- streamlit/proto/NamedDataSet_pb2.py +2 -3
- streamlit/proto/Navigation_pb2.py +2 -3
- streamlit/proto/NewSession_pb2.py +25 -24
- streamlit/proto/NewSession_pb2.pyi +28 -2
- streamlit/proto/NumberInput_pb2.py +2 -3
- streamlit/proto/PageConfig_pb2.py +2 -3
- streamlit/proto/PageInfo_pb2.py +2 -3
- streamlit/proto/PageLink_pb2.py +2 -3
- streamlit/proto/PageNotFound_pb2.py +2 -3
- streamlit/proto/PageProfile_pb2.py +2 -3
- streamlit/proto/PagesChanged_pb2.py +2 -3
- streamlit/proto/ParentMessage_pb2.py +2 -3
- streamlit/proto/PlotlyChart_pb2.py +2 -3
- streamlit/proto/Progress_pb2.py +2 -3
- streamlit/proto/Radio_pb2.py +5 -4
- streamlit/proto/Radio_pb2.pyi +20 -3
- streamlit/proto/RootContainer_pb2.py +2 -3
- streamlit/proto/Selectbox_pb2.py +2 -3
- streamlit/proto/SessionEvent_pb2.py +2 -3
- streamlit/proto/SessionStatus_pb2.py +2 -3
- streamlit/proto/Skeleton_pb2.py +2 -3
- streamlit/proto/Slider_pb2.py +7 -8
- streamlit/proto/Slider_pb2.pyi +9 -1
- streamlit/proto/Snow_pb2.py +2 -3
- streamlit/proto/Space_pb2.py +2 -3
- streamlit/proto/Spinner_pb2.py +2 -3
- streamlit/proto/TextAlignmentConfig_pb2.py +2 -3
- streamlit/proto/TextArea_pb2.py +2 -3
- streamlit/proto/TextInput_pb2.py +2 -3
- streamlit/proto/Text_pb2.py +2 -3
- streamlit/proto/TimeInput_pb2.py +2 -3
- streamlit/proto/Toast_pb2.py +2 -3
- streamlit/proto/Transient_pb2.py +2 -3
- streamlit/proto/VegaLiteChart_pb2.py +2 -3
- streamlit/proto/Video_pb2.py +2 -3
- streamlit/proto/WidgetStates_pb2.py +2 -3
- streamlit/proto/WidthConfig_pb2.py +2 -3
- streamlit/proto/openmetrics_data_model_pb2.py +2 -3
- streamlit/runtime/app_session.py +106 -60
- streamlit/runtime/caching/cache_data_api.py +3 -3
- streamlit/runtime/caching/cache_errors.py +0 -2
- streamlit/runtime/caching/cache_resource_api.py +1 -1
- streamlit/runtime/caching/cache_utils.py +2 -2
- streamlit/runtime/caching/hashing.py +1 -3
- streamlit/runtime/caching/storage/cache_storage_protocol.py +0 -3
- streamlit/runtime/connection_factory.py +1 -1
- streamlit/runtime/credentials.py +2 -2
- streamlit/runtime/metrics_util.py +3 -3
- streamlit/runtime/runtime.py +6 -6
- streamlit/runtime/scriptrunner/script_runner.py +17 -0
- streamlit/runtime/scriptrunner_utils/exceptions.py +0 -4
- streamlit/runtime/scriptrunner_utils/script_run_context.py +13 -31
- streamlit/runtime/secrets.py +3 -4
- streamlit/runtime/state/__init__.py +7 -1
- streamlit/runtime/state/common.py +13 -0
- streamlit/runtime/state/query_params.py +493 -24
- streamlit/runtime/state/session_state.py +179 -4
- streamlit/runtime/state/widgets.py +26 -1
- streamlit/runtime/stats.py +1 -10
- streamlit/static/index.html +1 -1
- streamlit/static/manifest.json +304 -304
- streamlit/static/static/js/{ErrorOutline.esm.CScZvf44.js → ErrorOutline.esm.BWk6F-Tz.js} +1 -1
- streamlit/static/static/js/{FileDownload.esm.COCxTZxP.js → FileDownload.esm.AllYUuOW.js} +1 -1
- streamlit/static/static/js/{FileHelper.Bhs-iVRI.js → FileHelper.BvVTNdmy.js} +1 -1
- streamlit/static/static/js/{FormClearHelper.CA_5b-Ut.js → FormClearHelper.C__r5Llk.js} +1 -1
- streamlit/static/static/js/{InputInstructions.Bzb0MCfv.js → InputInstructions.DOtkdOMV.js} +1 -1
- streamlit/static/static/js/Particles.DCsqQZlE.js +1 -0
- streamlit/static/static/js/{ProgressBar.DyQNhVsJ.js → ProgressBar.DLCRvt4m.js} +2 -2
- streamlit/static/static/js/{StreamlitSyntaxHighlighter.BOkJThtV.js → StreamlitSyntaxHighlighter.CYFWoZHb.js} +1 -1
- streamlit/static/static/js/{TableChart.esm.a60nntBC.js → TableChart.esm.D6ydHcIm.js} +1 -1
- streamlit/static/static/js/Toolbar.BHDNzWBx.js +1 -0
- streamlit/static/static/js/{WidgetLabelHelpIconInline.BjIku2ic.js → WidgetLabelHelpIconInline.DEXBrVlc.js} +1 -1
- streamlit/static/static/js/{base-input.avGkArOc.js → base-input.TSQjctlq.js} +4 -4
- streamlit/static/static/js/{checkbox.Q8mCuqps.js → checkbox.BKgfzJZV.js} +1 -1
- streamlit/static/static/js/{createDownloadLinkElement.CfqHRpxo.js → createDownloadLinkElement.CG7nr2a4.js} +1 -1
- streamlit/static/static/js/{data-grid-overlay-editor.PuoMl3yV.js → data-grid-overlay-editor.ChXO__lP.js} +1 -1
- streamlit/static/static/js/{downloader.CjG2csSm.js → downloader.DJ3R_zWA.js} +1 -1
- streamlit/static/static/js/embed.u3PPfLkw.js +193 -0
- streamlit/static/static/js/{es6.CQD6uUK7.js → es6.C5Mfy8nd.js} +2 -2
- streamlit/static/static/js/{formatNumber.CtjUO-if.js → formatNumber.CMRgW9EJ.js} +1 -1
- streamlit/static/static/js/{iconPosition.7Qt6oUiI.js → iconPosition.B4EEXI3E.js} +1 -1
- streamlit/static/static/js/{iframeResizer.contentWindow._oj2Xh0v.js → iframeResizer.contentWindow.WSvOiTW0.js} +1 -1
- streamlit/static/static/js/index.-FOBV3nz.js +1 -0
- streamlit/static/static/js/{index.BuBkymZd.js → index.-NF8OSF5.js} +1 -1
- streamlit/static/static/js/{index.B-XrnnK6.js → index.4cBg8kn5.js} +1 -1
- streamlit/static/static/js/{index.B_ylV_tl.js → index.B0pzzCsH.js} +1 -1
- streamlit/static/static/js/{index.BhJwyXH6.js → index.BID6ND5j.js} +2 -2
- streamlit/static/static/js/index.BMp5bGjh.js +1 -0
- streamlit/static/static/js/{index.Cptu1tS-.js → index.BQcmlvas.js} +1 -1
- streamlit/static/static/js/{index.DXQ_Fvpt.js → index.BRcmclgI.js} +1 -1
- streamlit/static/static/js/index.BaUZR4IG.js +1 -0
- streamlit/static/static/js/{index.CMBgAPh6.js → index.BbMJj4PN.js} +1 -1
- streamlit/static/static/js/{index.CVRgrLT-.js → index.BdCTJtq3.js} +2 -2
- streamlit/static/static/js/index.BdETLMuI.js +1 -0
- streamlit/static/static/js/index.BnKMWhs1.js +1 -0
- streamlit/static/static/js/index.Br1kXwQW.js +2 -0
- streamlit/static/static/js/{index.XGft6-dq.js → index.Bt2olRE4.js} +1 -1
- streamlit/static/static/js/{index.B2fAYU1N.js → index.Bxwsv5T8.js} +1 -1
- streamlit/static/static/js/index.C4KskYz6.js +1 -0
- streamlit/static/static/js/{index.DZE_91Ym.js → index.C6bmbXk0.js} +1 -1
- streamlit/static/static/js/{index.Egabyb7u.js → index.CEfKfbta.js} +1 -1
- streamlit/static/static/js/index.CIuaA8q0.js +2 -0
- streamlit/static/static/js/{index.DVtfSohT.js → index.CV1sObFX.js} +1 -1
- streamlit/static/static/js/{index.BlJhnb4M.js → index.CbR6dgaV.js} +1 -1
- streamlit/static/static/js/index.Cq6szKqJ.js +1 -0
- streamlit/static/static/js/index.CyouXqCz.js +1 -0
- streamlit/static/static/js/{index.B5wmZkRW.js → index.D1NUgMFI.js} +1 -1
- streamlit/static/static/js/{index.euRMkmNi.js → index.D7SWG4Om.js} +1 -1
- streamlit/static/static/js/{index.Bg-9YNUa.js → index.DAYPEwLI.js} +1 -1
- streamlit/static/static/js/index.DKS75Vfg.js +11 -0
- streamlit/static/static/js/{index.CIizdLeb.js → index.DOXrMIxB.js} +1 -1
- streamlit/static/static/js/{index.BRegnbUa.js → index.DOzYX8yS.js} +3 -3
- streamlit/static/static/js/{index.BksGMsW0.js → index.DRFMYcC4.js} +4 -4
- streamlit/static/static/js/{index.B8PovXCX.js → index.Divl5FCY.js} +1 -1
- streamlit/static/static/js/{index.DxQuXlXH.js → index.DjAJ_CUa.js} +1 -1
- streamlit/static/static/js/{index.BrRuSP42.js → index.Dncue2pm.js} +33 -33
- streamlit/static/static/js/{index.DSTThs-t.js → index.Drusyo5m.js} +47 -47
- streamlit/static/static/js/{index.BOafPwIE.js → index.DuUyDGnP.js} +1 -1
- streamlit/static/static/js/{index.D1bkwsLT.js → index.DvgT2rB2.js} +223 -223
- streamlit/static/static/js/{index.BmDXWfgx.js → index.DzutABu5.js} +2 -2
- streamlit/static/static/js/index.Dzw2iPzi.js +3 -0
- streamlit/static/static/js/{index.DJsqD2Sc.js → index.FsTmxLbT.js} +1 -1
- streamlit/static/static/js/{index.BOTEMJfV.js → index.OIwPqGYN.js} +1 -1
- streamlit/static/static/js/{index.CBqST2Yj.js → index.RXLN7YFT.js} +2 -2
- streamlit/static/static/js/{index.Ft2Zxbhr.js → index.YYb2u0jk.js} +2 -2
- streamlit/static/static/js/{index.BWCFtBS4.js → index.h8ejt-W3.js} +1 -1
- streamlit/static/static/js/{index.KuLql7H0.js → index.lFMCi9am.js} +1 -1
- streamlit/static/static/js/{index.D8t7R4QQ.js → index.pOgf4cEj.js} +1 -1
- streamlit/static/static/js/{index.CsoN0h7K.js → index.s_E0s7LB.js} +51 -51
- streamlit/static/static/js/{index.BVX_bqnf.js → index.xLCbzoqj.js} +1 -1
- streamlit/static/static/js/{input.Cf97CQME.js → input.BLG7kWaj.js} +2 -2
- streamlit/static/static/js/{main.Ccuk53yQ.js → main.D_CmqChN.js} +1 -1
- streamlit/static/static/js/{memory.Bng6Ij0g.js → memory.T8u9KqIQ.js} +1 -1
- streamlit/static/static/js/{number-overlay-editor.CFLv-CWC.js → number-overlay-editor.BKBSXkAM.js} +2 -2
- streamlit/static/static/js/{pandasStylerUtils.C2hcAKiv.js → pandasStylerUtils.B4tLYMwS.js} +1 -1
- streamlit/static/static/js/{sandbox.BXdeD-wA.js → sandbox.jRlkcPem.js} +1 -1
- streamlit/static/static/js/{styled-components.Br04Ogac.js → styled-components.D2QhNwzd.js} +1 -1
- streamlit/static/static/js/{throttle.mI9ItGre.js → throttle.Cyw_V0Dq.js} +1 -1
- streamlit/static/static/js/{timepicker.poFdB0sd.js → timepicker.PzyuDDWl.js} +1 -1
- streamlit/static/static/js/{toConsumableArray.92-fANS-.js → toConsumableArray.gE9fMkLj.js} +1 -1
- streamlit/static/static/js/uniqueId.B1GeHnT1.js +1 -0
- streamlit/static/static/js/{useBasicWidgetState.DzKGLAv_.js → useBasicWidgetState.DFklfao0.js} +1 -1
- streamlit/static/static/js/{useIntlLocale.BMma2iiY.js → useIntlLocale.C3tUGWTU.js} +8 -8
- streamlit/static/static/js/{useTextInputAutoExpand.DQbIhdma.js → useTextInputAutoExpand.D9nU_y-e.js} +1 -1
- streamlit/static/static/js/useUpdateUiValue.ClTdrkJN.js +1 -0
- streamlit/static/static/js/{useWaveformController.AH0ggRyc.js → useWaveformController.lzTbjMW2.js} +1 -1
- streamlit/static/static/js/{withCalculatedWidth.G5xJ-MbS.js → withCalculatedWidth.Dxs9I5Oe.js} +1 -1
- streamlit/static/static/js/{withFullScreenWrapper.rdRu6zZ4.js → withFullScreenWrapper.DfpAcJxf.js} +1 -1
- streamlit/string_util.py +2 -2
- streamlit/testing/v1/app_test.py +1 -1
- streamlit/testing/v1/element_tree.py +33 -20
- streamlit/type_util.py +2 -2
- streamlit/url_util.py +2 -2
- streamlit/user_info.py +2 -41
- streamlit/util.py +1 -1
- streamlit/watcher/event_based_path_watcher.py +37 -7
- streamlit/watcher/path_watcher.py +61 -2
- streamlit/watcher/util.py +26 -10
- streamlit/web/bootstrap.py +16 -4
- streamlit/web/cli.py +1 -4
- streamlit/web/server/app_discovery.py +2 -1
- streamlit/web/server/app_static_file_handler.py +9 -0
- streamlit/web/server/bidi_component_request_handler.py +4 -4
- streamlit/web/server/component_file_utils.py +14 -6
- streamlit/web/server/component_request_handler.py +2 -2
- streamlit/web/server/oauth_authlib_routes.py +14 -42
- streamlit/web/server/server.py +1 -1
- streamlit/web/server/server_util.py +23 -1
- streamlit/web/server/starlette/starlette_app.py +7 -1
- streamlit/web/server/starlette/starlette_auth_routes.py +94 -16
- streamlit/web/server/starlette/starlette_path_security_middleware.py +97 -0
- streamlit/web/server/starlette/starlette_routes.py +16 -9
- streamlit/web/server/starlette/starlette_server.py +2 -2
- streamlit/web/server/starlette/starlette_static_routes.py +14 -4
- streamlit/web/server/stats_request_handler.py +1 -3
- {streamlit-1.53.1.dist-info → streamlit-1.54.0.dist-info}/METADATA +10 -25
- {streamlit-1.53.1.dist-info → streamlit-1.54.0.dist-info}/RECORD +290 -290
- {streamlit-1.53.1.dist-info → streamlit-1.54.0.dist-info}/WHEEL +1 -1
- streamlit/commands/experimental_query_params.py +0 -169
- streamlit/static/static/js/Particles.ix5_l22I.js +0 -1
- streamlit/static/static/js/Toolbar.CxkcuBQ8.js +0 -1
- streamlit/static/static/js/embed.DZ-CLCPz.js +0 -195
- streamlit/static/static/js/index.B6ZAXv47.js +0 -1
- streamlit/static/static/js/index.BDm-Ia27.js +0 -1
- streamlit/static/static/js/index.BeCZLkzg.js +0 -1
- streamlit/static/static/js/index.BuEBeckn.js +0 -11
- streamlit/static/static/js/index.CL2eCR01.js +0 -1
- streamlit/static/static/js/index.CdLlbsiN.js +0 -1
- streamlit/static/static/js/index.CwIIk90V.js +0 -1
- streamlit/static/static/js/index.DDk0U8rh.js +0 -2
- streamlit/static/static/js/index.DNB79dOd.js +0 -3
- streamlit/static/static/js/index.DNj5S4tY.js +0 -1
- streamlit/static/static/js/index.DOY0ZriT.js +0 -2
- streamlit/static/static/js/index.r0gCrMFP.js +0 -1
- streamlit/static/static/js/uniqueId.BUj-C6GA.js +0 -1
- streamlit/static/static/js/useUpdateUiValue.Bk5OIXup.js +0 -1
- streamlit-1.53.1.data/scripts/streamlit.cmd +0 -16
- {streamlit-1.53.1.dist-info → streamlit-1.54.0.dist-info}/entry_points.txt +0 -0
- {streamlit-1.53.1.dist-info → streamlit-1.54.0.dist-info}/top_level.txt +0 -0
streamlit/static/static/js/{useWaveformController.AH0ggRyc.js → useWaveformController.lzTbjMW2.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{l as Z,r as i,aR as M,ab as $,aS as O}from"./index.DSTThs-t.js";async function q(l,e=16e3){if(!l||l.size===0)throw new Error("Invalid or empty blob provided");if(!window.AudioContext)throw new Error("AudioContext not supported in this browser");const o=new AudioContext;try{const n=await l.arrayBuffer(),r=await o.decodeAudioData(n),d=e??r.sampleRate,s=await J(r,d);return K(s,d)}finally{o.close()}}async function J(l,e){const{duration:o,numberOfChannels:n,sampleRate:r}=l,d=Math.ceil(o*e);if(!window.OfflineAudioContext)throw new Error("OfflineAudioContext not supported");const s=new OfflineAudioContext(1,d,e),p=s.createBufferSource();if(p.buffer=l,n>1){const h=s.createChannelSplitter(n),m=s.createChannelMerger(1);p.connect(h);for(let c=0;c<n;c++){const u=s.createGain();u.gain.value=1/n,h.connect(u,c),u.connect(m,0,0)}m.connect(s.destination)}else p.connect(s.destination);p.start(0);try{return await s.startRendering()}catch(h){throw new Error(`Failed to resample audio from ${r}Hz to ${e}Hz: ${h instanceof Error?h.message:String(h)}`)}}function K(l,e){const n=l.length,r=n*2+44,d=new ArrayBuffer(r),s=new DataView(d),p=l.getChannelData(0),h=(c,u)=>{for(let y=0;y<u.length;y++)s.setUint8(c+y,u.charCodeAt(y))};h(0,"RIFF"),s.setUint32(4,r-8,!0),h(8,"WAVE"),h(12,"fmt "),s.setUint32(16,16,!0),s.setUint16(20,1,!0),s.setUint16(22,1,!0),s.setUint32(24,e,!0),s.setUint32(28,e*2,!0),s.setUint16(32,2,!0),s.setUint16(34,16,!0),h(36,"data"),s.setUint32(40,n*2,!0);let m=44;for(let c=0;c<n;c++){const u=Math.max(-1,Math.min(1,p[c]));s.setInt16(m,u*32767,!0),m+=2}return new Blob([d],{type:"audio/wav"})}class W{constructor(){this.wavesurfer=null,this.currentBlobUrl=null,this.events={},this.isPlaying=!1}initialize(e){this.wavesurfer=e,this.setupEventListeners()}setupEventListeners(){this.wavesurfer&&(this.teardownEventListeners(),this.handleTimeUpdate=e=>{this.events.onTimeUpdate?.(e*1e3)},this.handlePause=()=>{this.isPlaying=!1,this.events.onPause?.()},this.handlePlay=()=>{this.isPlaying=!0,this.events.onPlay?.()},this.handleFinish=()=>{this.isPlaying=!1,this.events.onFinish?.()},this.handleReady=()=>{this.events.onReady?.()},this.handleError=e=>{const o=e instanceof Error?e:new Error(String(e));this.events.onError?.(o)},this.wavesurfer.on("timeupdate",this.handleTimeUpdate),this.wavesurfer.on("pause",this.handlePause),this.wavesurfer.on("play",this.handlePlay),this.wavesurfer.on("finish",this.handleFinish),this.wavesurfer.on("ready",this.handleReady),this.wavesurfer.on("error",this.handleError))}setEventHandlers(e){this.events=e}async load(e){if(!this.wavesurfer)throw new Error("WaveSurfer not initialized");this.cleanupPreviousUrl();let o,n=null;try{if(e instanceof Blob)n=URL.createObjectURL(e),o=n;else if(e instanceof ArrayBuffer){const r=new Blob([e]);n=URL.createObjectURL(r),o=n}else o=e;this.currentBlobUrl=n,await this.wavesurfer.load(o)}catch(r){throw this.cleanupPreviousUrl(),r}}async play(){if(!this.wavesurfer)throw new Error("WaveSurfer not initialized");await this.wavesurfer.play()}pause(){this.wavesurfer&&this.wavesurfer.pause()}getDuration(){return this.wavesurfer?this.wavesurfer.getDuration()*1e3:0}getCurrentTime(){return this.wavesurfer?this.wavesurfer.getCurrentTime()*1e3:0}getIsPlaying(){return this.isPlaying}seekToStart(){this.wavesurfer&&this.wavesurfer.seekTo(0)}cleanupPreviousUrl(){this.currentBlobUrl&&(URL.revokeObjectURL(this.currentBlobUrl),this.currentBlobUrl=null)}destroy(){this.pause(),this.cleanupPreviousUrl(),this.wavesurfer&&(this.teardownEventListeners(),this.wavesurfer.empty(),this.wavesurfer=null),this.events={},this.isPlaying=!1,this.handleTimeUpdate=void 0,this.handlePause=void 0,this.handlePlay=void 0,this.handleFinish=void 0,this.handleReady=void 0,this.handleError=void 0}teardownEventListeners(){this.wavesurfer&&(this.handleTimeUpdate&&(this.wavesurfer.un("timeupdate",this.handleTimeUpdate),this.handleTimeUpdate=void 0),this.handlePause&&(this.wavesurfer.un("pause",this.handlePause),this.handlePause=void 0),this.handlePlay&&(this.wavesurfer.un("play",this.handlePlay),this.handlePlay=void 0),this.handleFinish&&(this.wavesurfer.un("finish",this.handleFinish),this.handleFinish=void 0),this.handleReady&&(this.wavesurfer.un("ready",this.handleReady),this.handleReady=void 0),this.handleError&&(this.wavesurfer.un("error",this.handleError),this.handleError=void 0))}}function I(l){return l.name==="NotAllowedError"||l.name==="PermissionDeniedError"||l.message?.toLowerCase().includes("permission denied")}class Q{constructor(e={}){this.wavesurfer=null,this.recordPlugin=null,this.isRecording=!1,this.recordEndResolve=null,this.recordEndReject=null,this.events={},this.options=e}initialize(e,o){this.wavesurfer=e;try{const n={renderRecordedAudio:!1,mimeType:"audio/webm"};this.recordPlugin=e.registerPlugin(o.create(n)),this.setupEventListeners()}catch(n){const r=n instanceof Error?n:new Error(String(n));throw I(r)?(this.events.onPermissionDenied?.(),new Error("Microphone permission denied")):(this.events.onError?.(r),r)}}setupEventListeners(){this.recordPlugin&&(this.recordPlugin.on("record-start",()=>{this.isRecording=!0,this.events.onRecordStart?.()}),this.recordPlugin.on("record-end",e=>{this.isRecording=!1,this.events.onRecordEnd?.(e),this.recordEndResolve&&e&&e.size>0?(this.recordEndResolve(e),this.recordEndResolve=null,this.recordEndReject=null):this.recordEndReject?(this.recordEndReject(new Error("Invalid or empty recording")),this.recordEndResolve=null,this.recordEndReject=null):(this.recordEndResolve=null,this.recordEndReject=null)}),this.recordPlugin.on("record-progress",e=>{this.events.onRecordProgress?.(e)}))}setEventHandlers(e){this.events=e}async startRecording(){if(!this.recordPlugin)throw new Error("Record plugin not initialized");if(this.isRecording)return;const e=typeof this.options.sampleRate=="number"?this.options.sampleRate:void 0,o={};e!==void 0&&(o.sampleRate={ideal:e}),await this.startRecordingWithConstraints(o,e!==void 0)}async startRecordingWithConstraints(e,o){if(!this.recordPlugin)throw new Error("Record plugin not initialized");try{const n=Object.keys(e).length?e:void 0;await this.recordPlugin.startRecording(n)}catch(n){const r=n instanceof Error?n:new Error(String(n));if(I(r))throw this.events.onPermissionDenied?.(),new Error("Microphone permission denied");if(o&&(r.name==="OverconstrainedError"||r.name==="NotReadableError")){this.options.sampleRate=void 0,await this.startRecordingWithConstraints({},!1);return}throw this.events.onError?.(r),r}}async stopRecording(){if(!this.recordPlugin||!this.isRecording)throw new Error("Not currently recording");try{return await new Promise((e,o)=>{this.recordEndResolve=e,this.recordEndReject=o,this.recordPlugin?.stopRecording()})}catch(e){const o=e instanceof Error?e:new Error(String(e));throw this.events.onError?.(o),o}}cancelRecording(){this.recordPlugin&&this.isRecording&&(this.recordPlugin.stopRecording(),this.isRecording=!1,this.recordEndResolve=null,this.recordEndReject=null)}destroy(){this.cancelRecording(),this.recordPlugin&&(this.recordPlugin.destroy(),this.recordPlugin=null),this.wavesurfer=null,this.events={}}}const X=4,Y=4,ee=8,re=0,te=16e3;function se({containerRef:l,sampleRate:e,events:o,waveformPadding:n=0}){const r=Z(),[d,s]=i.useState("idle"),[p,h]=i.useState(null),[m,c]=i.useState(!1),u=i.useRef(null),y=i.useRef(null),a=i.useRef(null),f=i.useRef(o),U=i.useRef(!1),D=i.useRef(!1),E=i.useRef(new Set),P=i.useRef(!1),b=e===void 0?te:e,A=i.useCallback(()=>{E.current.clear(),c(!1),y.current&&(y.current.destroy(),y.current=null),a.current&&(a.current.destroy(),a.current=null),u.current&&(u.current.destroy(),u.current=null),U.current=!1,P.current=!1,s("idle"),h(null)},[]),x=i.useCallback(()=>{const t=Array.from(E.current);E.current.clear(),t.forEach(w=>{w.resolve()})},[]),_=i.useCallback(t=>{c(!1);const w=Array.from(E.current);E.current.clear(),w.forEach(v=>{v.reject(t)})},[]),T=i.useCallback(t=>{const w={onPlay:()=>{c(!0),f.current.onPlaybackPlay?.()},onPause:()=>{c(!1),f.current.onPlaybackPause?.()},onFinish:()=>{c(!1),f.current.onPlaybackFinish?.()},onReady:()=>{x()},onError:v=>{c(!1),_(v),f.current.onError?.(v)}};t.setEventHandlers(w)},[x,_]);i.useEffect(()=>{f.current=o,a.current&&T(a.current)},[o,T]);const S=i.useCallback(async()=>{if(!(U.current||D.current||!l.current)){D.current=!0;try{const[t,w]=await Promise.all([M(()=>import("./wavesurfer.esm.D1Sty35j.js"),[],import.meta.url),M(()=>import("./record.DytFsBUt.js"),[],import.meta.url)]),v=t.default,B=w.default,k=v.create({container:l.current,waveColor:r.colors.primary,progressColor:r.colors.bodyText,height:n>0?$(r.sizes.largestElementHeight)-2*n:"auto",barWidth:X,barGap:Y,barRadius:ee,cursorWidth:re,interact:!0});u.current=k,P.current=!1;const C=new Q({sampleRate:b});C.initialize(k,B),C.setEventHandlers({onRecordProgress:R=>{f.current.onProgressMs?.(R)},onPermissionDenied:()=>{f.current.onPermissionDenied(),s("idle")},onError:R=>{f.current.onError(R),s("idle")}}),y.current=C;const g=new W;g.initialize(k),a.current=g,T(g),U.current=!0}catch(t){const w=t instanceof Error?t:new Error(String(t));f.current.onError?.(w)}finally{D.current=!1}}},[l,r,b,T,n]);i.useEffect(()=>(S(),()=>{A()}),[A,S]),i.useEffect(()=>{const t=u.current;if(t){if(d==="recording"){t.setOptions({waveColor:r.colors.primary,progressColor:r.colors.primary});return}if(P.current){t.setOptions({interact:!0,waveColor:O(r.colors.fadedText40,r.colors.secondaryBg),progressColor:r.colors.bodyText});return}t.setOptions({waveColor:r.colors.primary,progressColor:r.colors.bodyText})}},[d,r.colors.bodyText,r.colors.fadedText40,r.colors.primary,r.colors.secondaryBg]);const F=i.useCallback(async()=>{if(d!=="recording"){if(U.current||await S(),!y.current)throw new Error("Record backend not initialized");u.current&&u.current.setOptions({waveColor:r.colors.primary,progressColor:r.colors.primary}),P.current=!1,await y.current.startRecording(),s("recording"),h(null),c(!1),f.current.onRecordStart?.()}},[d,S,r.colors.primary]),L=i.useCallback(()=>{a.current&&u.current&&(a.current.destroy(),a.current=new W,a.current.initialize(u.current),E.current.clear(),c(!1),T(a.current)),P.current=!1},[T]),z=i.useCallback(()=>{a.current?.seekToStart(),c(!1),P.current=!0,u.current&&u.current.setOptions({interact:!0,waveColor:O(r.colors.fadedText40,r.colors.secondaryBg),progressColor:r.colors.bodyText})},[r.colors.bodyText,r.colors.fadedText40,r.colors.secondaryBg]),H=i.useCallback(async()=>{if(d!=="recording")throw new Error("Not currently recording");if(!y.current||!a.current)throw new Error("Backends not initialized");try{const t=await y.current.stopRecording();h(t),await new Promise((k,C)=>{if(!a.current){C(new Error("Player not initialized"));return}const g={resolve:()=>{E.current.delete(g),k()},reject:R=>{E.current.delete(g),C(R)}};E.current.add(g),a.current.load(t).catch(R=>{E.current.delete(g),C(R instanceof Error?R:new Error(String(R)))})}),s("idle"),c(!1),z();const v={durationMs:a.current?.getDuration()??0,sampleRate:typeof b=="number"?b:null,mimeType:t.type||"audio/webm",size:t.size},B={blob:t,meta:v};return f.current.onRecordReady?.(t),B}catch(t){const w=t instanceof Error?t:new Error(String(t));return c(!1),s("idle"),f.current.onError(w),{blob:new Blob,meta:{durationMs:0,sampleRate:null,mimeType:"audio/webm",size:0}}}},[d,z,b]),j=i.useCallback(async t=>{const w=t??p;if(!w){const v=new Error("No recorded audio to approve");f.current.onError(v);return}try{const v=await q(w,b);await f.current.onApprove?.(v),h(null),s("idle")}catch(v){const B=v instanceof Error?v:new Error(String(v));f.current.onError(B)}},[p,b]),N=i.useCallback(()=>{d==="recording"&&y.current?.cancelRecording(),L(),h(null),s("idle"),c(!1),P.current=!1,f.current.onCancel?.()},[d,L]),V=i.useMemo(()=>({isPlaying:()=>a.current?.getIsPlaying()??!1,play:async()=>{if(!a.current)throw new Error("Player not initialized");await a.current.play()},pause:()=>{a.current?.pause()},load:async t=>{if(U.current||await S(),!a.current)throw new Error("Player not initialized");await a.current.load(t),z()},getCurrentTimeMs:()=>a.current?.getCurrentTime()??0,getDurationMs:()=>a.current?.getDuration()??0}),[z,S]),G=i.useCallback(t=>{f.current=t},[]);return i.useEffect(()=>()=>{A()},[A]),{state:d,isPlaybackPlaying:m,mountRef:l,start:F,stop:H,approve:j,cancel:N,destroy:A,playback:V,setEventHandlers:G}}export{se as u};
|
|
1
|
+
import{l as Z,r as i,aP as M,aa as $,aQ as O}from"./index.Drusyo5m.js";async function q(l,e=16e3){if(!l||l.size===0)throw new Error("Invalid or empty blob provided");if(!window.AudioContext)throw new Error("AudioContext not supported in this browser");const o=new AudioContext;try{const n=await l.arrayBuffer(),r=await o.decodeAudioData(n),d=e??r.sampleRate,s=await Q(r,d);return J(s,d)}finally{o.close()}}async function Q(l,e){const{duration:o,numberOfChannels:n,sampleRate:r}=l,d=Math.ceil(o*e);if(!window.OfflineAudioContext)throw new Error("OfflineAudioContext not supported");const s=new OfflineAudioContext(1,d,e),p=s.createBufferSource();if(p.buffer=l,n>1){const h=s.createChannelSplitter(n),m=s.createChannelMerger(1);p.connect(h);for(let c=0;c<n;c++){const u=s.createGain();u.gain.value=1/n,h.connect(u,c),u.connect(m,0,0)}m.connect(s.destination)}else p.connect(s.destination);p.start(0);try{return await s.startRendering()}catch(h){throw new Error(`Failed to resample audio from ${r}Hz to ${e}Hz: ${h instanceof Error?h.message:String(h)}`)}}function J(l,e){const n=l.length,r=n*2+44,d=new ArrayBuffer(r),s=new DataView(d),p=l.getChannelData(0),h=(c,u)=>{for(let y=0;y<u.length;y++)s.setUint8(c+y,u.charCodeAt(y))};h(0,"RIFF"),s.setUint32(4,r-8,!0),h(8,"WAVE"),h(12,"fmt "),s.setUint32(16,16,!0),s.setUint16(20,1,!0),s.setUint16(22,1,!0),s.setUint32(24,e,!0),s.setUint32(28,e*2,!0),s.setUint16(32,2,!0),s.setUint16(34,16,!0),h(36,"data"),s.setUint32(40,n*2,!0);let m=44;for(let c=0;c<n;c++){const u=Math.max(-1,Math.min(1,p[c]));s.setInt16(m,u*32767,!0),m+=2}return new Blob([d],{type:"audio/wav"})}class W{constructor(){this.wavesurfer=null,this.currentBlobUrl=null,this.events={},this.isPlaying=!1}initialize(e){this.wavesurfer=e,this.setupEventListeners()}setupEventListeners(){this.wavesurfer&&(this.teardownEventListeners(),this.handleTimeUpdate=e=>{this.events.onTimeUpdate?.(e*1e3)},this.handlePause=()=>{this.isPlaying=!1,this.events.onPause?.()},this.handlePlay=()=>{this.isPlaying=!0,this.events.onPlay?.()},this.handleFinish=()=>{this.isPlaying=!1,this.events.onFinish?.()},this.handleReady=()=>{this.events.onReady?.()},this.handleError=e=>{const o=e instanceof Error?e:new Error(String(e));this.events.onError?.(o)},this.wavesurfer.on("timeupdate",this.handleTimeUpdate),this.wavesurfer.on("pause",this.handlePause),this.wavesurfer.on("play",this.handlePlay),this.wavesurfer.on("finish",this.handleFinish),this.wavesurfer.on("ready",this.handleReady),this.wavesurfer.on("error",this.handleError))}setEventHandlers(e){this.events=e}async load(e){if(!this.wavesurfer)throw new Error("WaveSurfer not initialized");this.cleanupPreviousUrl();let o,n=null;try{if(e instanceof Blob)n=URL.createObjectURL(e),o=n;else if(e instanceof ArrayBuffer){const r=new Blob([e]);n=URL.createObjectURL(r),o=n}else o=e;this.currentBlobUrl=n,await this.wavesurfer.load(o)}catch(r){throw this.cleanupPreviousUrl(),r}}async play(){if(!this.wavesurfer)throw new Error("WaveSurfer not initialized");await this.wavesurfer.play()}pause(){this.wavesurfer&&this.wavesurfer.pause()}getDuration(){return this.wavesurfer?this.wavesurfer.getDuration()*1e3:0}getCurrentTime(){return this.wavesurfer?this.wavesurfer.getCurrentTime()*1e3:0}getIsPlaying(){return this.isPlaying}seekToStart(){this.wavesurfer&&this.wavesurfer.seekTo(0)}cleanupPreviousUrl(){this.currentBlobUrl&&(URL.revokeObjectURL(this.currentBlobUrl),this.currentBlobUrl=null)}destroy(){this.pause(),this.cleanupPreviousUrl(),this.wavesurfer&&(this.teardownEventListeners(),this.wavesurfer.empty(),this.wavesurfer=null),this.events={},this.isPlaying=!1,this.handleTimeUpdate=void 0,this.handlePause=void 0,this.handlePlay=void 0,this.handleFinish=void 0,this.handleReady=void 0,this.handleError=void 0}teardownEventListeners(){this.wavesurfer&&(this.handleTimeUpdate&&(this.wavesurfer.un("timeupdate",this.handleTimeUpdate),this.handleTimeUpdate=void 0),this.handlePause&&(this.wavesurfer.un("pause",this.handlePause),this.handlePause=void 0),this.handlePlay&&(this.wavesurfer.un("play",this.handlePlay),this.handlePlay=void 0),this.handleFinish&&(this.wavesurfer.un("finish",this.handleFinish),this.handleFinish=void 0),this.handleReady&&(this.wavesurfer.un("ready",this.handleReady),this.handleReady=void 0),this.handleError&&(this.wavesurfer.un("error",this.handleError),this.handleError=void 0))}}function I(l){return l.name==="NotAllowedError"||l.name==="PermissionDeniedError"||l.message?.toLowerCase().includes("permission denied")}class K{constructor(e={}){this.wavesurfer=null,this.recordPlugin=null,this.isRecording=!1,this.recordEndResolve=null,this.recordEndReject=null,this.events={},this.options=e}initialize(e,o){this.wavesurfer=e;try{const n={renderRecordedAudio:!1,mimeType:"audio/webm"};this.recordPlugin=e.registerPlugin(o.create(n)),this.setupEventListeners()}catch(n){const r=n instanceof Error?n:new Error(String(n));throw I(r)?(this.events.onPermissionDenied?.(),new Error("Microphone permission denied")):(this.events.onError?.(r),r)}}setupEventListeners(){this.recordPlugin&&(this.recordPlugin.on("record-start",()=>{this.isRecording=!0,this.events.onRecordStart?.()}),this.recordPlugin.on("record-end",e=>{this.isRecording=!1,this.events.onRecordEnd?.(e),this.recordEndResolve&&e&&e.size>0?(this.recordEndResolve(e),this.recordEndResolve=null,this.recordEndReject=null):this.recordEndReject?(this.recordEndReject(new Error("Invalid or empty recording")),this.recordEndResolve=null,this.recordEndReject=null):(this.recordEndResolve=null,this.recordEndReject=null)}),this.recordPlugin.on("record-progress",e=>{this.events.onRecordProgress?.(e)}))}setEventHandlers(e){this.events=e}async startRecording(){if(!this.recordPlugin)throw new Error("Record plugin not initialized");if(this.isRecording)return;const e=typeof this.options.sampleRate=="number"?this.options.sampleRate:void 0,o={};e!==void 0&&(o.sampleRate={ideal:e}),await this.startRecordingWithConstraints(o,e!==void 0)}async startRecordingWithConstraints(e,o){if(!this.recordPlugin)throw new Error("Record plugin not initialized");try{const n=Object.keys(e).length?e:void 0;await this.recordPlugin.startRecording(n)}catch(n){const r=n instanceof Error?n:new Error(String(n));if(I(r))throw this.events.onPermissionDenied?.(),new Error("Microphone permission denied");if(o&&(r.name==="OverconstrainedError"||r.name==="NotReadableError")){this.options.sampleRate=void 0,await this.startRecordingWithConstraints({},!1);return}throw this.events.onError?.(r),r}}async stopRecording(){if(!this.recordPlugin||!this.isRecording)throw new Error("Not currently recording");try{return await new Promise((e,o)=>{this.recordEndResolve=e,this.recordEndReject=o,this.recordPlugin?.stopRecording()})}catch(e){const o=e instanceof Error?e:new Error(String(e));throw this.events.onError?.(o),o}}cancelRecording(){this.recordPlugin&&this.isRecording&&(this.recordPlugin.stopRecording(),this.isRecording=!1,this.recordEndResolve=null,this.recordEndReject=null)}destroy(){this.cancelRecording(),this.recordPlugin&&(this.recordPlugin.destroy(),this.recordPlugin=null),this.wavesurfer=null,this.events={}}}const X=4,Y=4,ee=8,re=0,te=16e3;function se({containerRef:l,sampleRate:e,events:o,waveformPadding:n=0}){const r=Z(),[d,s]=i.useState("idle"),[p,h]=i.useState(null),[m,c]=i.useState(!1),u=i.useRef(null),y=i.useRef(null),a=i.useRef(null),f=i.useRef(o),S=i.useRef(!1),D=i.useRef(!1),E=i.useRef(new Set),P=i.useRef(!1),b=e===void 0?te:e,A=i.useCallback(()=>{E.current.clear(),c(!1),y.current&&(y.current.destroy(),y.current=null),a.current&&(a.current.destroy(),a.current=null),u.current&&(u.current.destroy(),u.current=null),S.current=!1,P.current=!1,s("idle"),h(null)},[]),x=i.useCallback(()=>{const t=Array.from(E.current);E.current.clear(),t.forEach(w=>{w.resolve()})},[]),_=i.useCallback(t=>{c(!1);const w=Array.from(E.current);E.current.clear(),w.forEach(v=>{v.reject(t)})},[]),T=i.useCallback(t=>{const w={onPlay:()=>{c(!0),f.current.onPlaybackPlay?.()},onPause:()=>{c(!1),f.current.onPlaybackPause?.()},onFinish:()=>{c(!1),f.current.onPlaybackFinish?.()},onReady:()=>{x()},onError:v=>{c(!1),_(v),f.current.onError?.(v)}};t.setEventHandlers(w)},[x,_]);i.useEffect(()=>{f.current=o,a.current&&T(a.current)},[o,T]);const U=i.useCallback(async()=>{if(!(S.current||D.current||!l.current)){D.current=!0;try{const[t,w]=await Promise.all([M(()=>import("./wavesurfer.esm.D1Sty35j.js"),[],import.meta.url),M(()=>import("./record.DytFsBUt.js"),[],import.meta.url)]),v=t.default,B=w.default,k=v.create({container:l.current,waveColor:r.colors.primary,progressColor:r.colors.bodyText,height:n>0?$(r.sizes.largestElementHeight)-2*n:"auto",barWidth:X,barGap:Y,barRadius:ee,cursorWidth:re,interact:!0});u.current=k,P.current=!1;const C=new K({sampleRate:b});C.initialize(k,B),C.setEventHandlers({onRecordProgress:R=>{f.current.onProgressMs?.(R)},onPermissionDenied:()=>{f.current.onPermissionDenied(),s("idle")},onError:R=>{f.current.onError(R),s("idle")}}),y.current=C;const g=new W;g.initialize(k),a.current=g,T(g),S.current=!0}catch(t){const w=t instanceof Error?t:new Error(String(t));f.current.onError?.(w)}finally{D.current=!1}}},[l,r,b,T,n]);i.useEffect(()=>(U(),()=>{A()}),[A,U]),i.useEffect(()=>{const t=u.current;if(t){if(d==="recording"){t.setOptions({waveColor:r.colors.primary,progressColor:r.colors.primary});return}if(P.current){t.setOptions({interact:!0,waveColor:O(r.colors.fadedText40,r.colors.secondaryBg),progressColor:r.colors.bodyText});return}t.setOptions({waveColor:r.colors.primary,progressColor:r.colors.bodyText})}},[d,r.colors.bodyText,r.colors.fadedText40,r.colors.primary,r.colors.secondaryBg]);const F=i.useCallback(async()=>{if(d!=="recording"){if(S.current||await U(),!y.current)throw new Error("Record backend not initialized");u.current&&u.current.setOptions({waveColor:r.colors.primary,progressColor:r.colors.primary}),P.current=!1,await y.current.startRecording(),s("recording"),h(null),c(!1),f.current.onRecordStart?.()}},[d,U,r.colors.primary]),L=i.useCallback(()=>{a.current&&u.current&&(a.current.destroy(),a.current=new W,a.current.initialize(u.current),E.current.clear(),c(!1),T(a.current)),P.current=!1},[T]),z=i.useCallback(()=>{a.current?.seekToStart(),c(!1),P.current=!0,u.current&&u.current.setOptions({interact:!0,waveColor:O(r.colors.fadedText40,r.colors.secondaryBg),progressColor:r.colors.bodyText})},[r.colors.bodyText,r.colors.fadedText40,r.colors.secondaryBg]),H=i.useCallback(async()=>{if(d!=="recording")throw new Error("Not currently recording");if(!y.current||!a.current)throw new Error("Backends not initialized");try{const t=await y.current.stopRecording();h(t),await new Promise((k,C)=>{if(!a.current){C(new Error("Player not initialized"));return}const g={resolve:()=>{E.current.delete(g),k()},reject:R=>{E.current.delete(g),C(R)}};E.current.add(g),a.current.load(t).catch(R=>{E.current.delete(g),C(R instanceof Error?R:new Error(String(R)))})}),s("idle"),c(!1),z();const v={durationMs:a.current?.getDuration()??0,sampleRate:typeof b=="number"?b:null,mimeType:t.type||"audio/webm",size:t.size},B={blob:t,meta:v};return f.current.onRecordReady?.(t),B}catch(t){const w=t instanceof Error?t:new Error(String(t));return c(!1),s("idle"),f.current.onError(w),{blob:new Blob,meta:{durationMs:0,sampleRate:null,mimeType:"audio/webm",size:0}}}},[d,z,b]),j=i.useCallback(async t=>{const w=t??p;if(!w){const v=new Error("No recorded audio to approve");f.current.onError(v);return}try{const v=await q(w,b);await f.current.onApprove?.(v),h(null),s("idle")}catch(v){const B=v instanceof Error?v:new Error(String(v));f.current.onError(B)}},[p,b]),N=i.useCallback(()=>{d==="recording"&&y.current?.cancelRecording(),L(),h(null),s("idle"),c(!1),P.current=!1,f.current.onCancel?.()},[d,L]),V=i.useMemo(()=>({isPlaying:()=>a.current?.getIsPlaying()??!1,play:async()=>{if(!a.current)throw new Error("Player not initialized");await a.current.play()},pause:()=>{a.current?.pause()},load:async t=>{if(S.current||await U(),!a.current)throw new Error("Player not initialized");await a.current.load(t),z()},getCurrentTimeMs:()=>a.current?.getCurrentTime()??0,getDurationMs:()=>a.current?.getDuration()??0}),[z,U]),G=i.useCallback(t=>{f.current=t},[]);return i.useEffect(()=>()=>{A()},[A]),{state:d,isPlaybackPlaying:m,mountRef:l,start:F,stop:H,approve:j,cancel:N,destroy:A,playback:V,setEventHandlers:G}}export{se as u};
|
streamlit/static/static/js/{withCalculatedWidth.G5xJ-MbS.js → withCalculatedWidth.Dxs9I5Oe.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{h as l,k as h,j as s,d as n}from"./index.
|
|
1
|
+
import{h as l,k as h,j as s,d as n}from"./index.Drusyo5m.js";const o=t=>{const a=e=>{const{width:i,elementRef:c}=h();return s(n,{ref:c,children:s(t,{...e,width:i})})};return a.displayName=`withCalculatedWidth(${t.displayName||t.name})`,l(a,t)};export{o as w};
|
streamlit/static/static/js/{withFullScreenWrapper.rdRu6zZ4.js → withFullScreenWrapper.DfpAcJxf.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as n,
|
|
1
|
+
import{r as n,z as f,b5 as p,b6 as h,l as x,k as y,j as i,h as g}from"./index.Drusyo5m.js";const m=n.createContext(null);m.displayName="ElementFullscreenContext";const w=f("div",{target:"e5bcvgj0"})(({theme:e,isExpanded:t})=>({width:"100%",height:"100%",...t?{position:"fixed",top:0,left:0,bottom:0,right:0,background:e.colors.bgColor,zIndex:e.zIndices.fullscreenWrapper,padding:e.spacing.md,paddingTop:e.sizes.fullScreenHeaderHeight,overflow:"auto",display:"flex",alignItems:"center",justifyContent:"center"}:{}})),C=()=>{const{setFullScreen:e}=n.useContext(p),[t,s]=n.useState(!1),{fullHeight:a,fullWidth:c}=h(),l=n.useCallback(r=>{s(r),e(r)},[e]),u=n.useCallback(()=>{document.body.style.overflow="hidden",l(!0)},[l]),o=n.useCallback(()=>{document.body.style.overflow="unset",l(!1)},[l]),d=n.useCallback(r=>{r.keyCode===27&&t&&o()},[o,t]);return n.useEffect(()=>(document.addEventListener("keydown",d,!1),()=>{document.removeEventListener("keydown",d,!1)}),[d]),n.useMemo(()=>({expanded:t,zoomIn:u,zoomOut:o,fullHeight:a,fullWidth:c}),[t,u,o,a,c])},b=({children:e})=>{const t=x(),{expanded:s,fullHeight:a,fullWidth:c,zoomIn:l,zoomOut:u}=C(),{width:o,elementRef:d}=y(),r=n.useMemo(()=>({width:s?c:o,height:s?a:void 0,expanded:s,expand:l,collapse:u}),[s,a,c,o,l,u]);return i(m.Provider,{value:r,children:i(w,{ref:d,isExpanded:s,"data-testid":"stFullScreenFrame",theme:t,children:e})})};function S(e){const t=s=>i(b,{children:i(e,{...s})});return t.displayName=`withFullScreenWrapper(${e.displayName||e.name})`,g(t,e)}export{m as E,S as w};
|
streamlit/string_util.py
CHANGED
|
@@ -209,8 +209,8 @@ def to_snake_case(camel_case_str: str) -> str:
|
|
|
209
209
|
BazBang -> baz_bang
|
|
210
210
|
|
|
211
211
|
"""
|
|
212
|
-
s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", camel_case_str)
|
|
213
|
-
return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower()
|
|
212
|
+
s1 = re.sub(r"(.)([A-Z][a-z]+)", r"\1_\2", camel_case_str)
|
|
213
|
+
return re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", s1).lower()
|
|
214
214
|
|
|
215
215
|
|
|
216
216
|
AnyNumber: TypeAlias = Union[
|
streamlit/testing/v1/app_test.py
CHANGED
|
@@ -218,7 +218,7 @@ class AppTest:
|
|
|
218
218
|
|
|
219
219
|
path = Path(TMP_DIR.name, script_name)
|
|
220
220
|
aligned_script = textwrap.dedent(script)
|
|
221
|
-
path.write_text(aligned_script)
|
|
221
|
+
path.write_text(aligned_script, encoding="utf-8")
|
|
222
222
|
return AppTest(
|
|
223
223
|
str(path), default_timeout=default_timeout, args=args, kwargs=kwargs
|
|
224
224
|
)
|
|
@@ -90,8 +90,6 @@ T = TypeVar("T")
|
|
|
90
90
|
class InitialValue:
|
|
91
91
|
"""Used to represent the initial value of a widget."""
|
|
92
92
|
|
|
93
|
-
pass
|
|
94
|
-
|
|
95
93
|
|
|
96
94
|
# TODO: This class serves as a fallback option for elements that have not
|
|
97
95
|
# been implemented yet, as well as providing implementations of some
|
|
@@ -515,7 +513,7 @@ DateValue: TypeAlias = SingleDateValue | Sequence[SingleDateValue] | None
|
|
|
515
513
|
class DateInput(Widget):
|
|
516
514
|
"""A representation of ``st.date_input``."""
|
|
517
515
|
|
|
518
|
-
_value: DateValue |
|
|
516
|
+
_value: DateValue | InitialValue | None
|
|
519
517
|
proto: DateInputProto = field(repr=False)
|
|
520
518
|
label: str
|
|
521
519
|
min: date
|
|
@@ -884,7 +882,7 @@ Number: TypeAlias = int | float
|
|
|
884
882
|
class NumberInput(Widget):
|
|
885
883
|
"""A representation of ``st.number_input``."""
|
|
886
884
|
|
|
887
|
-
_value: Number |
|
|
885
|
+
_value: Number | InitialValue | None
|
|
888
886
|
proto: NumberInputProto = field(repr=False)
|
|
889
887
|
label: str
|
|
890
888
|
min: Number | None
|
|
@@ -945,7 +943,7 @@ class NumberInput(Widget):
|
|
|
945
943
|
class Radio(Widget, Generic[T]):
|
|
946
944
|
"""A representation of ``st.radio``."""
|
|
947
945
|
|
|
948
|
-
_value: T |
|
|
946
|
+
_value: T | InitialValue | None
|
|
949
947
|
|
|
950
948
|
proto: RadioProto = field(repr=False)
|
|
951
949
|
label: str
|
|
@@ -995,8 +993,8 @@ class Radio(Widget, Generic[T]):
|
|
|
995
993
|
"""
|
|
996
994
|
ws = WidgetState()
|
|
997
995
|
ws.id = self.id
|
|
998
|
-
if self.index is not None:
|
|
999
|
-
ws.
|
|
996
|
+
if self.index is not None and len(self.options) > 0:
|
|
997
|
+
ws.string_value = self.options[self.index]
|
|
1000
998
|
return ws
|
|
1001
999
|
|
|
1002
1000
|
|
|
@@ -1004,7 +1002,7 @@ class Radio(Widget, Generic[T]):
|
|
|
1004
1002
|
class Selectbox(Widget, Generic[T]):
|
|
1005
1003
|
"""A representation of ``st.selectbox``."""
|
|
1006
1004
|
|
|
1007
|
-
_value: T |
|
|
1005
|
+
_value: T | InitialValue | None
|
|
1008
1006
|
|
|
1009
1007
|
proto: SelectboxProto = field(repr=False)
|
|
1010
1008
|
label: str
|
|
@@ -1096,18 +1094,33 @@ class SelectSlider(Widget, Generic[T]):
|
|
|
1096
1094
|
|
|
1097
1095
|
@property
|
|
1098
1096
|
def _widget_state(self) -> WidgetState:
|
|
1099
|
-
|
|
1097
|
+
# Build formatted options mapping
|
|
1098
|
+
format_func = self.format_func
|
|
1099
|
+
formatted_option_to_index = {
|
|
1100
|
+
format_func(opt): idx for idx, opt in enumerate(self.options)
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
# Determine if this is a range value
|
|
1104
|
+
is_range = isinstance(self.value, (list, tuple)) and len(self.value) == 2
|
|
1105
|
+
|
|
1106
|
+
serde = SelectSliderSerde(
|
|
1107
|
+
self.options,
|
|
1108
|
+
formatted_option_to_index=formatted_option_to_index,
|
|
1109
|
+
default_indices=[0] if not is_range else [0, len(self.options) - 1],
|
|
1110
|
+
format_func=format_func,
|
|
1111
|
+
)
|
|
1112
|
+
|
|
1100
1113
|
try:
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
v = serde.serialize(
|
|
1105
|
-
|
|
1106
|
-
|
|
1114
|
+
if is_range:
|
|
1115
|
+
v = serde.serialize(tuple(self.value)) # type: ignore
|
|
1116
|
+
else:
|
|
1117
|
+
v = serde.serialize(self.value) # type: ignore
|
|
1118
|
+
except (ValueError, TypeError) as e:
|
|
1119
|
+
raise ValueError(f"Could not serialize value {self.value}") from e
|
|
1107
1120
|
|
|
1108
1121
|
ws = WidgetState()
|
|
1109
1122
|
ws.id = self.id
|
|
1110
|
-
ws.
|
|
1123
|
+
ws.string_array_value.data[:] = v
|
|
1111
1124
|
return ws
|
|
1112
1125
|
|
|
1113
1126
|
@property
|
|
@@ -1224,7 +1237,7 @@ class Text(Element):
|
|
|
1224
1237
|
class TextArea(Widget):
|
|
1225
1238
|
"""A representation of ``st.text_area``."""
|
|
1226
1239
|
|
|
1227
|
-
_value: str |
|
|
1240
|
+
_value: str | InitialValue | None
|
|
1228
1241
|
|
|
1229
1242
|
proto: TextAreaProto = field(repr=False)
|
|
1230
1243
|
label: str
|
|
@@ -1276,7 +1289,7 @@ class TextArea(Widget):
|
|
|
1276
1289
|
class TextInput(Widget):
|
|
1277
1290
|
"""A representation of ``st.text_input``."""
|
|
1278
1291
|
|
|
1279
|
-
_value: str |
|
|
1292
|
+
_value: str | InitialValue | None
|
|
1280
1293
|
proto: TextInputProto = field(repr=False)
|
|
1281
1294
|
label: str
|
|
1282
1295
|
max_chars: int
|
|
@@ -1332,7 +1345,7 @@ DateTimeWidgetValue: TypeAlias = datetime
|
|
|
1332
1345
|
class TimeInput(Widget):
|
|
1333
1346
|
"""A representation of ``st.time_input``."""
|
|
1334
1347
|
|
|
1335
|
-
_value: TimeValue |
|
|
1348
|
+
_value: TimeValue | InitialValue | None
|
|
1336
1349
|
proto: TimeInputProto = field(repr=False)
|
|
1337
1350
|
label: str
|
|
1338
1351
|
step: int
|
|
@@ -1389,7 +1402,7 @@ class TimeInput(Widget):
|
|
|
1389
1402
|
class DateTimeInput(Widget):
|
|
1390
1403
|
"""A representation of ``st.datetime_input``."""
|
|
1391
1404
|
|
|
1392
|
-
_value: DateTimeWidgetValue |
|
|
1405
|
+
_value: DateTimeWidgetValue | InitialValue | None
|
|
1393
1406
|
proto: DateTimeInputProto = field(repr=False)
|
|
1394
1407
|
label: str
|
|
1395
1408
|
format: str
|
streamlit/type_util.py
CHANGED
|
@@ -268,7 +268,7 @@ def _is_probably_plotly_dict(obj: object) -> TypeGuard[dict[str, Any]]:
|
|
|
268
268
|
if len(obj.keys()) == 0:
|
|
269
269
|
return False
|
|
270
270
|
|
|
271
|
-
if any(k not in
|
|
271
|
+
if any(k not in {"config", "data", "frames", "layout"} for k in obj):
|
|
272
272
|
return False
|
|
273
273
|
|
|
274
274
|
if any(_is_plotly_obj(v) for v in obj.values()):
|
|
@@ -472,7 +472,7 @@ def async_generator_to_sync(
|
|
|
472
472
|
try:
|
|
473
473
|
# Iterate over the async generator until it raises StopAsyncIteration
|
|
474
474
|
while True:
|
|
475
|
-
yield loop.run_until_complete(async_gen
|
|
475
|
+
yield loop.run_until_complete(anext(async_gen))
|
|
476
476
|
except StopAsyncIteration:
|
|
477
477
|
# The async generator has finished
|
|
478
478
|
pass
|
streamlit/url_util.py
CHANGED
|
@@ -87,9 +87,9 @@ def is_url(
|
|
|
87
87
|
if result.scheme not in allowed_schemas:
|
|
88
88
|
return False
|
|
89
89
|
|
|
90
|
-
if result.scheme in
|
|
90
|
+
if result.scheme in {"http", "https"}:
|
|
91
91
|
return bool(result.netloc)
|
|
92
|
-
if result.scheme in
|
|
92
|
+
if result.scheme in {"mailto", "data"}:
|
|
93
93
|
return bool(result.path)
|
|
94
94
|
|
|
95
95
|
except ValueError:
|
streamlit/user_info.py
CHANGED
|
@@ -30,10 +30,6 @@ from streamlit.auth_util import (
|
|
|
30
30
|
is_authlib_installed,
|
|
31
31
|
validate_auth_credentials,
|
|
32
32
|
)
|
|
33
|
-
from streamlit.deprecation_util import (
|
|
34
|
-
make_deprecated_name_warning,
|
|
35
|
-
show_deprecation_warning,
|
|
36
|
-
)
|
|
37
33
|
from streamlit.errors import StreamlitAPIException, StreamlitAuthError
|
|
38
34
|
from streamlit.proto.ForwardMsg_pb2 import ForwardMsg
|
|
39
35
|
from streamlit.runtime.metrics_util import gather_metrics
|
|
@@ -652,7 +648,7 @@ class UserInfoProxy(Mapping[str, str | bool | TokensProxy | None]):
|
|
|
652
648
|
}
|
|
653
649
|
"""
|
|
654
650
|
|
|
655
|
-
def __getitem__(self, key: str) -> str | bool |
|
|
651
|
+
def __getitem__(self, key: str) -> str | bool | TokensProxy | None:
|
|
656
652
|
if key == "tokens":
|
|
657
653
|
return self.tokens
|
|
658
654
|
try:
|
|
@@ -660,7 +656,7 @@ class UserInfoProxy(Mapping[str, str | bool | TokensProxy | None]):
|
|
|
660
656
|
except KeyError:
|
|
661
657
|
raise KeyError(f'st.user has no key "{key}".')
|
|
662
658
|
|
|
663
|
-
def __getattr__(self, key: str) -> str | bool |
|
|
659
|
+
def __getattr__(self, key: str) -> str | bool | TokensProxy | None:
|
|
664
660
|
if key == "tokens":
|
|
665
661
|
return self.tokens
|
|
666
662
|
try:
|
|
@@ -701,38 +697,3 @@ class UserInfoProxy(Mapping[str, str | bool | TokensProxy | None]):
|
|
|
701
697
|
"""Access exposed tokens via a dict-like object."""
|
|
702
698
|
user_info = _get_user_info()
|
|
703
699
|
return TokensProxy(cast("dict[str, str]", user_info.get("tokens", {})))
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
has_shown_experimental_user_warning = False
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
def maybe_show_deprecated_user_warning() -> None:
|
|
710
|
-
"""Show a deprecation warning for the experimental_user alias."""
|
|
711
|
-
global has_shown_experimental_user_warning # noqa: PLW0603
|
|
712
|
-
|
|
713
|
-
if not has_shown_experimental_user_warning:
|
|
714
|
-
has_shown_experimental_user_warning = True
|
|
715
|
-
show_deprecation_warning(
|
|
716
|
-
make_deprecated_name_warning(
|
|
717
|
-
"experimental_user",
|
|
718
|
-
"user",
|
|
719
|
-
"2025-11-06",
|
|
720
|
-
)
|
|
721
|
-
)
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
class DeprecatedUserInfoProxy(UserInfoProxy):
|
|
725
|
-
"""
|
|
726
|
-
A deprecated alias for UserInfoProxy.
|
|
727
|
-
|
|
728
|
-
This class is deprecated and will be removed in a future version of
|
|
729
|
-
Streamlit.
|
|
730
|
-
"""
|
|
731
|
-
|
|
732
|
-
def __getattribute__(self, name: str) -> Any:
|
|
733
|
-
maybe_show_deprecated_user_warning()
|
|
734
|
-
return super().__getattribute__(name)
|
|
735
|
-
|
|
736
|
-
def __getitem__(self, key: str) -> Any:
|
|
737
|
-
maybe_show_deprecated_user_warning()
|
|
738
|
-
return super().__getitem__(key)
|
streamlit/util.py
CHANGED
|
@@ -69,7 +69,20 @@ def _get_abs_folder_path(path: str) -> str:
|
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
class EventBasedPathWatcher:
|
|
72
|
-
"""Watches a single path on disk using watchdog.
|
|
72
|
+
"""Watches a single path on disk using watchdog.
|
|
73
|
+
|
|
74
|
+
Behavior differs based on whether watching a file or directory:
|
|
75
|
+
|
|
76
|
+
**File watching:** Detects content changes via MD5 hash comparison. The
|
|
77
|
+
callback receives the file path and is invoked when content changes. With
|
|
78
|
+
allow_nonexistent=True, also detects file creation.
|
|
79
|
+
|
|
80
|
+
**Directory watching:** Detects any file activity within the directory
|
|
81
|
+
(creation, deletion, modification). The callback receives the actual
|
|
82
|
+
changed file path (not the directory). Note that glob_pattern only affects
|
|
83
|
+
the initial state hash, not which events trigger callbacks - all file
|
|
84
|
+
events in the directory invoke the callback.
|
|
85
|
+
"""
|
|
73
86
|
|
|
74
87
|
@staticmethod
|
|
75
88
|
def close_all() -> None:
|
|
@@ -91,17 +104,22 @@ class EventBasedPathWatcher:
|
|
|
91
104
|
Parameters
|
|
92
105
|
----------
|
|
93
106
|
path : str
|
|
94
|
-
The path to watch.
|
|
107
|
+
The path to watch (file or directory).
|
|
95
108
|
on_changed : Callable[[str], None]
|
|
96
|
-
Callback
|
|
109
|
+
Callback invoked when changes are detected. For files, receives
|
|
110
|
+
the file path. For directories, receives the path of the actual
|
|
111
|
+
changed file within the directory.
|
|
97
112
|
glob_pattern : str or None
|
|
98
|
-
A glob pattern
|
|
99
|
-
|
|
100
|
-
directory
|
|
113
|
+
A glob pattern for initial state detection when watching a
|
|
114
|
+
directory (e.g., "*.py"). Note: This does NOT filter which file
|
|
115
|
+
events trigger the callback - all file events in the directory
|
|
116
|
+
will invoke the callback regardless of this pattern.
|
|
101
117
|
allow_nonexistent : bool
|
|
102
118
|
If True, the watcher will not raise an exception if the path does
|
|
103
119
|
not exist. This can be used to watch for the creation of a file or
|
|
104
|
-
directory at a given path.
|
|
120
|
+
directory at a given path. Note: The parent directory of the path
|
|
121
|
+
must exist for watching to work. If the parent doesn't exist, the
|
|
122
|
+
watcher is silently skipped.
|
|
105
123
|
"""
|
|
106
124
|
self._path = os.path.realpath(path)
|
|
107
125
|
self._on_changed = on_changed
|
|
@@ -189,6 +207,18 @@ class _MultiPathWatcher:
|
|
|
189
207
|
folder_handler, folder_path, recursive=True
|
|
190
208
|
)
|
|
191
209
|
self._folder_handlers[folder_path] = folder_handler
|
|
210
|
+
except FileNotFoundError:
|
|
211
|
+
# This happens when watching a non-existent file whose parent
|
|
212
|
+
# directory also doesn't exist (e.g., .streamlit/config.toml
|
|
213
|
+
# when .streamlit/ hasn't been created yet). This is expected
|
|
214
|
+
# and not an error - we just can't watch until the directory
|
|
215
|
+
# is created.
|
|
216
|
+
_LOGGER.debug(
|
|
217
|
+
"Cannot watch path %s: directory %s does not exist",
|
|
218
|
+
path,
|
|
219
|
+
folder_path,
|
|
220
|
+
)
|
|
221
|
+
return
|
|
192
222
|
except Exception as ex:
|
|
193
223
|
_LOGGER.warning(
|
|
194
224
|
"Failed to schedule watch observer for path %s",
|
|
@@ -66,7 +66,7 @@ def _is_watchdog_available() -> bool:
|
|
|
66
66
|
|
|
67
67
|
def report_watchdog_availability() -> None:
|
|
68
68
|
if (
|
|
69
|
-
config.get_option("server.fileWatcherType") not in
|
|
69
|
+
config.get_option("server.fileWatcherType") not in {"poll", "none"}
|
|
70
70
|
and not _is_watchdog_available()
|
|
71
71
|
):
|
|
72
72
|
msg = "\n $ xcode-select --install" if env_util.IS_DARWIN else ""
|
|
@@ -137,8 +137,37 @@ def watch_file(
|
|
|
137
137
|
path: str,
|
|
138
138
|
on_file_changed: Callable[[str], None],
|
|
139
139
|
watcher_type: str | None = None,
|
|
140
|
+
*, # keyword-only arguments:
|
|
141
|
+
allow_nonexistent: bool = False,
|
|
140
142
|
) -> bool:
|
|
141
|
-
|
|
143
|
+
"""Watch a file for changes.
|
|
144
|
+
|
|
145
|
+
The callback is invoked when the file's content changes (detected via MD5).
|
|
146
|
+
If allow_nonexistent is True, the watcher will also detect when the file
|
|
147
|
+
is created.
|
|
148
|
+
|
|
149
|
+
Parameters
|
|
150
|
+
----------
|
|
151
|
+
path
|
|
152
|
+
Path to the file to watch.
|
|
153
|
+
on_file_changed
|
|
154
|
+
Callback invoked with the file path when changes are detected.
|
|
155
|
+
watcher_type
|
|
156
|
+
Optional watcher type ('watchdog', 'poll', 'auto', or 'none').
|
|
157
|
+
allow_nonexistent
|
|
158
|
+
If True, watch for file creation even if the file doesn't exist yet.
|
|
159
|
+
Note: The file's parent directory must exist for watching to work.
|
|
160
|
+
If the parent directory doesn't exist, the watcher silently skips
|
|
161
|
+
watching (the file can't be created without its parent directory).
|
|
162
|
+
|
|
163
|
+
Returns
|
|
164
|
+
-------
|
|
165
|
+
bool
|
|
166
|
+
True if the watcher was successfully created.
|
|
167
|
+
"""
|
|
168
|
+
return _watch_path(
|
|
169
|
+
path, on_file_changed, watcher_type, allow_nonexistent=allow_nonexistent
|
|
170
|
+
)
|
|
142
171
|
|
|
143
172
|
|
|
144
173
|
def watch_dir(
|
|
@@ -149,6 +178,36 @@ def watch_dir(
|
|
|
149
178
|
glob_pattern: str | None = None,
|
|
150
179
|
allow_nonexistent: bool = False,
|
|
151
180
|
) -> bool:
|
|
181
|
+
"""Watch a directory for file changes.
|
|
182
|
+
|
|
183
|
+
The callback is invoked for any file activity within the directory,
|
|
184
|
+
including file creation, deletion, and content modifications. The callback
|
|
185
|
+
receives the path of the actual changed file (not the directory path).
|
|
186
|
+
|
|
187
|
+
Note: The glob_pattern parameter only affects the initial state detection
|
|
188
|
+
(which files are counted when determining if the directory changed). It does
|
|
189
|
+
NOT filter which file events trigger the callback - all file events in the
|
|
190
|
+
directory will invoke the callback regardless of glob_pattern.
|
|
191
|
+
|
|
192
|
+
Parameters
|
|
193
|
+
----------
|
|
194
|
+
path
|
|
195
|
+
Path to the directory to watch.
|
|
196
|
+
on_dir_changed
|
|
197
|
+
Callback invoked with the changed file path when changes are detected.
|
|
198
|
+
watcher_type
|
|
199
|
+
Optional watcher type ('watchdog', 'poll', 'auto', or 'none').
|
|
200
|
+
glob_pattern
|
|
201
|
+
Glob pattern for initial state detection (e.g., "*.py"). Does not
|
|
202
|
+
filter runtime events.
|
|
203
|
+
allow_nonexistent
|
|
204
|
+
If True, watch for directory creation even if it doesn't exist yet.
|
|
205
|
+
|
|
206
|
+
Returns
|
|
207
|
+
-------
|
|
208
|
+
bool
|
|
209
|
+
True if the watcher was successfully created.
|
|
210
|
+
"""
|
|
152
211
|
# Add a trailing slash to the path to ensure
|
|
153
212
|
# that its interpreted as a directory.
|
|
154
213
|
path = os.path.join(path, "")
|
streamlit/watcher/util.py
CHANGED
|
@@ -63,11 +63,19 @@ def calc_md5_with_blocking_retries(
|
|
|
63
63
|
# There's a race condition where sometimes file_path no longer exists when
|
|
64
64
|
# we try to read it (since the file is in the process of being written).
|
|
65
65
|
# So here we retry a few times using this loop. See issue #186.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
try:
|
|
67
|
+
content = _do_with_retries(
|
|
68
|
+
lambda: _get_file_content(path),
|
|
69
|
+
(FileNotFoundError, PermissionError),
|
|
70
|
+
path,
|
|
71
|
+
)
|
|
72
|
+
except StreamlitMaxRetriesError:
|
|
73
|
+
# If allow_nonexistent is True and the file was deleted between our
|
|
74
|
+
# exists check and the read, treat it as nonexistent instead of raising.
|
|
75
|
+
if allow_nonexistent:
|
|
76
|
+
content = path.encode("UTF-8")
|
|
77
|
+
else:
|
|
78
|
+
raise
|
|
71
79
|
|
|
72
80
|
return calc_md5(content)
|
|
73
81
|
|
|
@@ -91,11 +99,19 @@ def path_modification_time(path: str, allow_nonexistent: bool = False) -> float:
|
|
|
91
99
|
|
|
92
100
|
# Use retries to avoid race condition where file may be in the process of being
|
|
93
101
|
# modified.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
102
|
+
try:
|
|
103
|
+
return _do_with_retries(
|
|
104
|
+
lambda: os.stat(path).st_mtime,
|
|
105
|
+
(FileNotFoundError, PermissionError),
|
|
106
|
+
path,
|
|
107
|
+
)
|
|
108
|
+
except StreamlitMaxRetriesError:
|
|
109
|
+
# If allow_nonexistent is True and the file was deleted between our
|
|
110
|
+
# exists check and the stat call, return 0.0 instead of raising.
|
|
111
|
+
# This handles the race condition where a file is deleted while being watched.
|
|
112
|
+
if allow_nonexistent:
|
|
113
|
+
return 0.0
|
|
114
|
+
raise
|
|
99
115
|
|
|
100
116
|
|
|
101
117
|
def _get_file_content(file_path: str) -> bytes:
|
streamlit/web/bootstrap.py
CHANGED
|
@@ -139,7 +139,7 @@ def _on_server_start(server: Server) -> None:
|
|
|
139
139
|
if server_address_is_unix_socket():
|
|
140
140
|
# Don't open browser when server address is an unix socket
|
|
141
141
|
return
|
|
142
|
-
addr = config.get_option("server.address")
|
|
142
|
+
addr = server_util.get_display_address(config.get_option("server.address"))
|
|
143
143
|
else:
|
|
144
144
|
addr = "localhost"
|
|
145
145
|
|
|
@@ -246,8 +246,11 @@ def _print_url(is_running_hello: bool) -> None:
|
|
|
246
246
|
]
|
|
247
247
|
|
|
248
248
|
elif (
|
|
249
|
-
config.is_manually_set("server.address")
|
|
249
|
+
config.is_manually_set("server.address")
|
|
250
|
+
and not server_address_is_unix_socket()
|
|
251
|
+
and config.get_option("server.address") not in {"0.0.0.0", "::"} # noqa: S104
|
|
250
252
|
):
|
|
253
|
+
# Non-wildcard specific address - show single URL
|
|
251
254
|
named_urls = [
|
|
252
255
|
("URL", server_util.get_url(config.get_option("server.address"))),
|
|
253
256
|
]
|
|
@@ -325,8 +328,17 @@ def _install_config_watchers(flag_options: dict[str, Any]) -> None:
|
|
|
325
328
|
load_config_options(flag_options)
|
|
326
329
|
|
|
327
330
|
for filename in config.get_config_files("config.toml"):
|
|
328
|
-
if
|
|
329
|
-
|
|
331
|
+
# Watch each config file path directly, even if it doesn't exist yet.
|
|
332
|
+
# This allows detecting both file creation and subsequent modifications.
|
|
333
|
+
# We use the poll watcher because:
|
|
334
|
+
# 1. It handles non-existent paths gracefully, including when parent
|
|
335
|
+
# directories (like ~/.streamlit/) don't exist yet. The event-based
|
|
336
|
+
# watcher requires the parent directory to exist to schedule a watch.
|
|
337
|
+
# 2. Config files change rarely, so the polling overhead is negligible.
|
|
338
|
+
# 3. The 200ms poll interval latency is imperceptible for config reloads.
|
|
339
|
+
watch_file(
|
|
340
|
+
filename, on_config_changed, watcher_type="poll", allow_nonexistent=True
|
|
341
|
+
)
|
|
330
342
|
|
|
331
343
|
|
|
332
344
|
def run_asgi_app(
|
streamlit/web/cli.py
CHANGED
|
@@ -267,7 +267,7 @@ def _check_extension_or_raise(path_str: str) -> None:
|
|
|
267
267
|
|
|
268
268
|
|
|
269
269
|
def _get_command_line_as_string() -> str | None:
|
|
270
|
-
import subprocess
|
|
270
|
+
import subprocess # noqa: S404
|
|
271
271
|
|
|
272
272
|
parent = click.get_current_context().parent
|
|
273
273
|
if parent is None:
|
|
@@ -331,7 +331,6 @@ def _main_run(
|
|
|
331
331
|
@main.group("cache")
|
|
332
332
|
def cache() -> None:
|
|
333
333
|
"""Manage the Streamlit cache."""
|
|
334
|
-
pass
|
|
335
334
|
|
|
336
335
|
|
|
337
336
|
@cache.command("clear")
|
|
@@ -354,7 +353,6 @@ def cache_clear() -> None:
|
|
|
354
353
|
@main.group("config")
|
|
355
354
|
def config() -> None:
|
|
356
355
|
"""Manage Streamlit's config settings."""
|
|
357
|
-
pass
|
|
358
356
|
|
|
359
357
|
|
|
360
358
|
@config.command("show")
|
|
@@ -393,7 +391,6 @@ def test() -> None:
|
|
|
393
391
|
|
|
394
392
|
These commands are not included in the output of `streamlit help`.
|
|
395
393
|
"""
|
|
396
|
-
pass
|
|
397
394
|
|
|
398
395
|
|
|
399
396
|
@test.command("prog_name")
|
|
@@ -31,6 +31,7 @@ the source code without executing it.
|
|
|
31
31
|
from __future__ import annotations
|
|
32
32
|
|
|
33
33
|
import ast
|
|
34
|
+
import operator
|
|
34
35
|
from dataclasses import dataclass
|
|
35
36
|
from typing import TYPE_CHECKING, Final
|
|
36
37
|
|
|
@@ -404,7 +405,7 @@ def discover_asgi_app(
|
|
|
404
405
|
)
|
|
405
406
|
|
|
406
407
|
# Fall back to the first discovered app (by line number)
|
|
407
|
-
first_app = min(app_assignments.items(), key=
|
|
408
|
+
first_app = min(app_assignments.items(), key=operator.itemgetter(1))
|
|
408
409
|
_LOGGER.debug(
|
|
409
410
|
"Found ASGI app at %s:%s (fallback, line %d)",
|
|
410
411
|
module_str,
|