streamlit 1.49.1__py3-none-any.whl → 1.51.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.
Files changed (259) hide show
  1. streamlit/__init__.py +4 -1
  2. streamlit/column_config.py +2 -0
  3. streamlit/commands/navigation.py +7 -7
  4. streamlit/commands/page_config.py +4 -6
  5. streamlit/components/v1/custom_component.py +17 -42
  6. streamlit/components/v2/__init__.py +458 -0
  7. streamlit/components/v2/bidi_component/__init__.py +20 -0
  8. streamlit/components/v2/bidi_component/constants.py +29 -0
  9. streamlit/components/v2/bidi_component/main.py +386 -0
  10. streamlit/components/v2/bidi_component/serialization.py +265 -0
  11. streamlit/components/v2/bidi_component/state.py +92 -0
  12. streamlit/components/v2/component_definition_resolver.py +143 -0
  13. streamlit/components/v2/component_file_watcher.py +403 -0
  14. streamlit/components/v2/component_manager.py +431 -0
  15. streamlit/components/v2/component_manifest_handler.py +122 -0
  16. streamlit/components/v2/component_path_utils.py +245 -0
  17. streamlit/components/v2/component_registry.py +409 -0
  18. streamlit/components/v2/get_bidi_component_manager.py +51 -0
  19. streamlit/components/v2/manifest_scanner.py +615 -0
  20. streamlit/components/v2/presentation.py +198 -0
  21. streamlit/components/v2/types.py +324 -0
  22. streamlit/config.py +741 -32
  23. streamlit/config_option.py +4 -1
  24. streamlit/config_util.py +650 -1
  25. streamlit/connections/base_connection.py +4 -2
  26. streamlit/dataframe_util.py +18 -10
  27. streamlit/delta_generator.py +8 -7
  28. streamlit/delta_generator_singletons.py +3 -1
  29. streamlit/deprecation_util.py +17 -6
  30. streamlit/elements/arrow.py +90 -42
  31. streamlit/elements/deck_gl_json_chart.py +98 -39
  32. streamlit/elements/dialog_decorator.py +2 -1
  33. streamlit/elements/exception.py +3 -1
  34. streamlit/elements/form.py +6 -6
  35. streamlit/elements/graphviz_chart.py +24 -9
  36. streamlit/elements/heading.py +3 -5
  37. streamlit/elements/iframe.py +0 -2
  38. streamlit/elements/image.py +12 -13
  39. streamlit/elements/layouts.py +89 -22
  40. streamlit/elements/lib/built_in_chart_utils.py +95 -31
  41. streamlit/elements/lib/color_util.py +8 -18
  42. streamlit/elements/lib/column_config_utils.py +9 -8
  43. streamlit/elements/lib/column_types.py +595 -148
  44. streamlit/elements/lib/dialog.py +3 -2
  45. streamlit/elements/lib/image_utils.py +3 -5
  46. streamlit/elements/lib/layout_utils.py +50 -13
  47. streamlit/elements/lib/mutable_status_container.py +2 -2
  48. streamlit/elements/lib/options_selector_utils.py +2 -2
  49. streamlit/elements/lib/pandas_styler_utils.py +30 -14
  50. streamlit/elements/lib/utils.py +21 -9
  51. streamlit/elements/map.py +81 -40
  52. streamlit/elements/media.py +7 -7
  53. streamlit/elements/metric.py +11 -35
  54. streamlit/elements/pdf.py +2 -4
  55. streamlit/elements/plotly_chart.py +142 -26
  56. streamlit/elements/progress.py +2 -4
  57. streamlit/elements/pyplot.py +6 -6
  58. streamlit/elements/space.py +113 -0
  59. streamlit/elements/vega_charts.py +400 -143
  60. streamlit/elements/widgets/audio_input.py +52 -4
  61. streamlit/elements/widgets/button.py +29 -29
  62. streamlit/elements/widgets/button_group.py +33 -6
  63. streamlit/elements/widgets/camera_input.py +3 -4
  64. streamlit/elements/widgets/chat.py +7 -0
  65. streamlit/elements/widgets/checkbox.py +1 -0
  66. streamlit/elements/widgets/color_picker.py +1 -0
  67. streamlit/elements/widgets/data_editor.py +34 -29
  68. streamlit/elements/widgets/file_uploader.py +6 -10
  69. streamlit/elements/widgets/multiselect.py +14 -3
  70. streamlit/elements/widgets/number_input.py +5 -4
  71. streamlit/elements/widgets/radio.py +10 -2
  72. streamlit/elements/widgets/select_slider.py +8 -4
  73. streamlit/elements/widgets/selectbox.py +9 -2
  74. streamlit/elements/widgets/slider.py +38 -41
  75. streamlit/elements/widgets/text_widgets.py +6 -0
  76. streamlit/elements/widgets/time_widgets.py +15 -12
  77. streamlit/elements/write.py +28 -23
  78. streamlit/emojis.py +1 -1
  79. streamlit/errors.py +115 -0
  80. streamlit/git_util.py +65 -43
  81. streamlit/hello/hello.py +8 -0
  82. streamlit/hello/utils.py +2 -1
  83. streamlit/material_icon_names.py +1 -1
  84. streamlit/navigation/page.py +4 -1
  85. streamlit/proto/ArrowData_pb2.py +27 -0
  86. streamlit/proto/ArrowData_pb2.pyi +46 -0
  87. streamlit/proto/Arrow_pb2.py +10 -8
  88. streamlit/proto/Arrow_pb2.pyi +31 -2
  89. streamlit/proto/AudioInput_pb2.py +2 -2
  90. streamlit/proto/AudioInput_pb2.pyi +6 -2
  91. streamlit/proto/BidiComponent_pb2.py +34 -0
  92. streamlit/proto/BidiComponent_pb2.pyi +153 -0
  93. streamlit/proto/Block_pb2.py +11 -11
  94. streamlit/proto/Block_pb2.pyi +9 -1
  95. streamlit/proto/DeckGlJsonChart_pb2.py +10 -4
  96. streamlit/proto/DeckGlJsonChart_pb2.pyi +9 -3
  97. streamlit/proto/Element_pb2.py +5 -3
  98. streamlit/proto/Element_pb2.pyi +14 -4
  99. streamlit/proto/HeightConfig_pb2.py +2 -2
  100. streamlit/proto/HeightConfig_pb2.pyi +6 -3
  101. streamlit/proto/NewSession_pb2.py +18 -16
  102. streamlit/proto/NewSession_pb2.pyi +158 -6
  103. streamlit/proto/PlotlyChart_pb2.py +8 -6
  104. streamlit/proto/PlotlyChart_pb2.pyi +3 -1
  105. streamlit/proto/Space_pb2.py +27 -0
  106. streamlit/proto/Space_pb2.pyi +42 -0
  107. streamlit/proto/WidgetStates_pb2.py +2 -2
  108. streamlit/proto/WidgetStates_pb2.pyi +13 -3
  109. streamlit/proto/WidthConfig_pb2.py +2 -2
  110. streamlit/proto/WidthConfig_pb2.pyi +6 -3
  111. streamlit/runtime/app_session.py +45 -6
  112. streamlit/runtime/caching/cache_data_api.py +4 -4
  113. streamlit/runtime/caching/cache_errors.py +4 -1
  114. streamlit/runtime/caching/cache_resource_api.py +3 -2
  115. streamlit/runtime/caching/cache_utils.py +2 -1
  116. streamlit/runtime/caching/cached_message_replay.py +3 -3
  117. streamlit/runtime/caching/hashing.py +3 -4
  118. streamlit/runtime/caching/legacy_cache_api.py +2 -1
  119. streamlit/runtime/connection_factory.py +1 -3
  120. streamlit/runtime/forward_msg_queue.py +4 -1
  121. streamlit/runtime/fragment.py +2 -1
  122. streamlit/runtime/memory_media_file_storage.py +1 -1
  123. streamlit/runtime/metrics_util.py +6 -2
  124. streamlit/runtime/runtime.py +14 -0
  125. streamlit/runtime/scriptrunner/exec_code.py +2 -1
  126. streamlit/runtime/scriptrunner/script_runner.py +2 -2
  127. streamlit/runtime/scriptrunner_utils/script_run_context.py +3 -6
  128. streamlit/runtime/secrets.py +2 -4
  129. streamlit/runtime/session_manager.py +3 -1
  130. streamlit/runtime/state/common.py +30 -5
  131. streamlit/runtime/state/presentation.py +85 -0
  132. streamlit/runtime/state/safe_session_state.py +2 -2
  133. streamlit/runtime/state/session_state.py +220 -16
  134. streamlit/runtime/state/widgets.py +19 -3
  135. streamlit/runtime/theme_util.py +148 -0
  136. streamlit/runtime/websocket_session_manager.py +3 -1
  137. streamlit/source_util.py +2 -2
  138. streamlit/static/index.html +2 -2
  139. streamlit/static/manifest.json +244 -227
  140. streamlit/static/static/css/{index.C8X8rNzw.css → index.BpABIXK9.css} +1 -1
  141. streamlit/static/static/css/index.DgR7E2CV.css +1 -0
  142. streamlit/static/static/js/{ErrorOutline.esm.DcGrhbBP.js → ErrorOutline.esm.YoJdlW1p.js} +1 -1
  143. streamlit/static/static/js/{FileDownload.esm.DgBvV6Pq.js → FileDownload.esm.Ddx8VEYy.js} +1 -1
  144. streamlit/static/static/js/{FileHelper.M6AAaeuA.js → FileHelper.90EtOmj9.js} +1 -1
  145. streamlit/static/static/js/{FormClearHelper.DHh1GFzm.js → FormClearHelper.BB1Km6eP.js} +1 -1
  146. streamlit/static/static/js/InputInstructions.jhH15PqV.js +1 -0
  147. streamlit/static/static/js/{Particles.DDVT-6Qc.js → Particles.DUsputn1.js} +1 -1
  148. streamlit/static/static/js/{ProgressBar.BEY0cXXV.js → ProgressBar.DLY8H6nE.js} +2 -2
  149. streamlit/static/static/js/Toolbar.D8nHCkuz.js +1 -0
  150. streamlit/static/static/js/{base-input.CK3UVGp1.js → base-input.CJGiNqed.js} +3 -3
  151. streamlit/static/static/js/{checkbox.D8W881TL.js → checkbox.Cpdd482O.js} +1 -1
  152. streamlit/static/static/js/{createSuper.B6W-Dh9S.js → createSuper.CuQIogbW.js} +1 -1
  153. streamlit/static/static/js/data-grid-overlay-editor.2Ufgxc6y.js +1 -0
  154. streamlit/static/static/js/{downloader.DiKpuU_S.js → downloader.CN0K7xlu.js} +1 -1
  155. streamlit/static/static/js/{es6.B8zRNPZ-.js → es6.BJcsVXQ0.js} +2 -2
  156. streamlit/static/static/js/{iframeResizer.contentWindow.DIewJmmh.js → iframeResizer.contentWindow.XzUvQqcZ.js} +1 -1
  157. streamlit/static/static/js/index.B1ZQh4P1.js +1 -0
  158. streamlit/static/static/js/index.BKstZk0M.js +27 -0
  159. streamlit/static/static/js/{index.Bte_9Lyq.js → index.BMcFsUee.js} +1 -1
  160. streamlit/static/static/js/{index.qhs54UAB.js → index.BR-IdcTb.js} +1 -1
  161. streamlit/static/static/js/{index.CejBxbg1.js → index.B_dWA3vd.js} +1 -1
  162. streamlit/static/static/js/{index.D5naqx-J.js → index.BgnZEMVh.js} +1 -1
  163. streamlit/static/static/js/{index.C7fRKRs4.js → index.BohqXifI.js} +1 -1
  164. streamlit/static/static/js/{index.cnnXF7xQ.js → index.Br5nxKNj.js} +1 -1
  165. streamlit/static/static/js/index.BrIKVbNc.js +3 -0
  166. streamlit/static/static/js/index.BtWUPzle.js +1 -0
  167. streamlit/static/static/js/index.C0RLraek.js +1 -0
  168. streamlit/static/static/js/{index.CP5TD2z1.js → index.CAIjskgG.js} +1 -1
  169. streamlit/static/static/js/{index.CD8HuT3N.js → index.CAj-7vWz.js} +135 -162
  170. streamlit/static/static/js/{index.DtYN2x4k.js → index.CMtEit2O.js} +1 -1
  171. streamlit/static/static/js/index.CkRlykEE.js +12 -0
  172. streamlit/static/static/js/{index.Ts_0SdB9.js → index.CmN3FXfI.js} +2 -2
  173. streamlit/static/static/js/{index.BnEpvLEz.js → index.CwbFI1_-.js} +1 -1
  174. streamlit/static/static/js/{index.CcJf6BCU.js → index.CxIUUfab.js} +27 -27
  175. streamlit/static/static/js/index.D2KPNy7e.js +1 -0
  176. streamlit/static/static/js/{index.Ch7MBCx0.js → index.D3GPA5k4.js} +47 -47
  177. streamlit/static/static/js/{index.ho6NIXGl.js → index.DGAh7DMq.js} +1 -1
  178. streamlit/static/static/js/index.DKb_NvmG.js +197 -0
  179. streamlit/static/static/js/{index.CvYYtxD_.js → index.DMqgUYKq.js} +1 -1
  180. streamlit/static/static/js/{index.zecpGxtj.js → index.DOFlg3dS.js} +1 -1
  181. streamlit/static/static/js/{index.B9mjBcgE.js → index.DPUXkcQL.js} +1 -1
  182. streamlit/static/static/js/index.DX1xY89g.js +1 -0
  183. streamlit/static/static/js/index.DYATBCsq.js +2 -0
  184. streamlit/static/static/js/{index.D2-atlaQ.js → index.DaSmGJ76.js} +3 -3
  185. streamlit/static/static/js/index.Dd7bMeLP.js +1 -0
  186. streamlit/static/static/js/{index.4eF4NxG2.js → index.DjmmgI5U.js} +1 -1
  187. streamlit/static/static/js/index.Dq56CyM2.js +1 -0
  188. streamlit/static/static/js/index.DuiXaS5_.js +7 -0
  189. streamlit/static/static/js/index.DvFidMLe.js +2 -0
  190. streamlit/static/static/js/{index.452cqrrL.js → index.DwkhC5Pc.js} +1 -1
  191. streamlit/static/static/js/{index.Dk4C7X3i.js → index.Q-3sFn1v.js} +1 -1
  192. streamlit/static/static/js/{index.CjXWwH-y.js → index.QJ5QO9sJ.js} +1 -1
  193. streamlit/static/static/js/{index.B6U8LQo3.js → index.VwTaeety.js} +1 -1
  194. streamlit/static/static/js/index.YOqQbeX8.js +1 -0
  195. streamlit/static/static/js/{input.nzVJphXi.js → input.D4MN_FzN.js} +1 -1
  196. streamlit/static/static/js/{memory.CjCgTQz3.js → memory.DrZjtdGT.js} +1 -1
  197. streamlit/static/static/js/{number-overlay-editor.DaRFzZEO.js → number-overlay-editor.DRwAw1In.js} +1 -1
  198. streamlit/static/static/js/{possibleConstructorReturn.DgiPnZ9N.js → possibleConstructorReturn.exeeJQEP.js} +1 -1
  199. streamlit/static/static/js/record.B-tDciZb.js +1 -0
  200. streamlit/static/static/js/{sandbox.mithfq7Z.js → sandbox.ClO3IuUr.js} +1 -1
  201. streamlit/static/static/js/{timepicker.Dbl5KFh6.js → timepicker.DAhu-vcF.js} +4 -4
  202. streamlit/static/static/js/{toConsumableArray.D-Dx88BQ.js → toConsumableArray.DNbljYEC.js} +1 -1
  203. streamlit/static/static/js/{uniqueId.Bh26R_3S.js → uniqueId.oG4Gvj1v.js} +1 -1
  204. streamlit/static/static/js/{useBasicWidgetState.DeK-QJpD.js → useBasicWidgetState.D6sOH6oI.js} +1 -1
  205. streamlit/static/static/js/{useTextInputAutoExpand.4iAdLWD-.js → useTextInputAutoExpand.4u3_GcuN.js} +2 -2
  206. streamlit/static/static/js/{useUpdateUiValue.CmT7_nJN.js → useUpdateUiValue.F2R3eTeR.js} +1 -1
  207. streamlit/static/static/js/wavesurfer.esm.vI8Eid4k.js +73 -0
  208. streamlit/static/static/js/withFullScreenWrapper.zothJIsI.js +1 -0
  209. streamlit/static/static/media/MaterialSymbols-Rounded.C7IFxh57.woff2 +0 -0
  210. streamlit/string_util.py +56 -1
  211. streamlit/testing/v1/app_test.py +2 -2
  212. streamlit/testing/v1/element_tree.py +23 -9
  213. streamlit/testing/v1/util.py +2 -2
  214. streamlit/type_util.py +3 -4
  215. streamlit/url_util.py +1 -3
  216. streamlit/user_info.py +1 -2
  217. streamlit/util.py +3 -1
  218. streamlit/watcher/event_based_path_watcher.py +23 -12
  219. streamlit/watcher/local_sources_watcher.py +11 -1
  220. streamlit/watcher/path_watcher.py +9 -6
  221. streamlit/watcher/polling_path_watcher.py +4 -1
  222. streamlit/watcher/util.py +2 -2
  223. streamlit/web/bootstrap.py +0 -31
  224. streamlit/web/cli.py +51 -22
  225. streamlit/web/server/bidi_component_request_handler.py +193 -0
  226. streamlit/web/server/component_file_utils.py +97 -0
  227. streamlit/web/server/component_request_handler.py +8 -21
  228. streamlit/web/server/oidc_mixin.py +3 -1
  229. streamlit/web/server/routes.py +18 -5
  230. streamlit/web/server/server.py +10 -0
  231. streamlit/web/server/server_util.py +3 -1
  232. streamlit/web/server/upload_file_request_handler.py +3 -1
  233. {streamlit-1.49.1.dist-info → streamlit-1.51.0.dist-info}/METADATA +4 -5
  234. {streamlit-1.49.1.dist-info → streamlit-1.51.0.dist-info}/RECORD +238 -209
  235. streamlit/static/static/css/index.COe1010n.css +0 -1
  236. streamlit/static/static/js/Hooks.DGu1od_L.js +0 -1
  237. streamlit/static/static/js/InputInstructions.z6sVgyYt.js +0 -1
  238. streamlit/static/static/js/Toolbar.DSnK1fUh.js +0 -1
  239. streamlit/static/static/js/data-grid-overlay-editor.DRTHOydk.js +0 -1
  240. streamlit/static/static/js/index.BXYmrqnf.js +0 -1
  241. streamlit/static/static/js/index.B_8AnktO.js +0 -1
  242. streamlit/static/static/js/index.Bl7zGQSh.js +0 -7
  243. streamlit/static/static/js/index.BnJIOYn9.js +0 -73
  244. streamlit/static/static/js/index.C1HcTl5K.js +0 -1
  245. streamlit/static/static/js/index.C7lSmSOP.js +0 -1
  246. streamlit/static/static/js/index.C_tmcx4B.js +0 -1
  247. streamlit/static/static/js/index.D3K5nOu9.js +0 -197
  248. streamlit/static/static/js/index.DkKT3LUI.js +0 -1
  249. streamlit/static/static/js/index.MTPPBDHk.js +0 -2
  250. streamlit/static/static/js/index.pqW9AMJD.js +0 -3
  251. streamlit/static/static/js/index.urHgTgMQ.js +0 -12
  252. streamlit/static/static/js/index.wzkv_11M.js +0 -1
  253. streamlit/static/static/js/index.yF5AncHY.js +0 -1
  254. streamlit/static/static/js/withFullScreenWrapper.DLp1ENGm.js +0 -1
  255. streamlit/static/static/media/MaterialSymbols-Rounded.CBxVaFdk.woff2 +0 -0
  256. {streamlit-1.49.1.data → streamlit-1.51.0.data}/scripts/streamlit.cmd +0 -0
  257. {streamlit-1.49.1.dist-info → streamlit-1.51.0.dist-info}/WHEEL +0 -0
  258. {streamlit-1.49.1.dist-info → streamlit-1.51.0.dist-info}/entry_points.txt +0 -0
  259. {streamlit-1.49.1.dist-info → streamlit-1.51.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,198 @@
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ from typing import TYPE_CHECKING, Any, TypedDict, cast
18
+
19
+ from typing_extensions import Self
20
+
21
+ from streamlit.errors import StreamlitAPIException
22
+ from streamlit.logger import get_logger
23
+ from streamlit.runtime.scriptrunner import get_script_run_ctx
24
+
25
+ if TYPE_CHECKING:
26
+ from streamlit.runtime.state import SessionState
27
+ from streamlit.runtime.state.common import WidgetValuePresenter
28
+
29
+
30
+ _LOGGER = get_logger(__name__)
31
+
32
+
33
+ class _TriggerPayload(TypedDict, total=False):
34
+ event: str
35
+ value: object
36
+
37
+
38
+ def make_bidi_component_presenter(
39
+ aggregator_id: str,
40
+ component_id: str | None = None,
41
+ allowed_state_keys: set[str] | None = None,
42
+ ) -> WidgetValuePresenter:
43
+ """Return a presenter that merges trigger events into CCv2 state.
44
+
45
+ This function returns a callable that takes a component's persistent state
46
+ value and the current `SessionState` instance, and returns the user-visible
47
+ value that should appear in `st.session_state`.
48
+
49
+ The presenter is side-effect-free and does not mutate stored state or
50
+ callback behavior. It is intended to be attached to the persistent state
51
+ widget via the generic `presenter` hook.
52
+
53
+ Parameters
54
+ ----------
55
+ aggregator_id
56
+ The ID of the trigger aggregator widget that holds the event payloads.
57
+
58
+ Returns
59
+ -------
60
+ WidgetValuePresenter
61
+ A callable that merges the trigger event values into the component's
62
+ base state for presentation in `st.session_state`.
63
+
64
+ """
65
+
66
+ def _present(base_value: object, session_state: SessionState) -> object:
67
+ def _check_modification(k: str) -> None:
68
+ ctx = get_script_run_ctx()
69
+ if ctx is not None and component_id is not None:
70
+ user_key = session_state._key_id_mapper.get_key_from_id(component_id)
71
+ if (
72
+ component_id in ctx.widget_ids_this_run
73
+ or user_key in ctx.form_ids_this_run
74
+ ):
75
+ raise StreamlitAPIException(
76
+ f"`st.session_state.{user_key}.{k}` cannot be modified after the component"
77
+ f" with key `{user_key}` is instantiated."
78
+ )
79
+
80
+ # Base state must be a flat mapping; otherwise, present as-is.
81
+ base_map: dict[str, object] | None = None
82
+ if isinstance(base_value, dict):
83
+ base_map = cast("dict[str, object]", base_value)
84
+
85
+ if base_map is not None:
86
+ # Read the trigger aggregator payloads if present
87
+ try:
88
+ agg_meta = session_state._new_widget_state.widget_metadata.get(
89
+ aggregator_id
90
+ )
91
+ if agg_meta is None or agg_meta.value_type != "json_trigger_value":
92
+ return base_value
93
+
94
+ try:
95
+ agg_payloads_obj = session_state._new_widget_state[aggregator_id]
96
+ except KeyError:
97
+ agg_payloads_obj = None
98
+
99
+ payloads_list: list[_TriggerPayload] | None
100
+ if agg_payloads_obj is None:
101
+ payloads_list = None
102
+ elif isinstance(agg_payloads_obj, list):
103
+ # Filter and cast to the expected payload type shape
104
+ payloads_list = [
105
+ cast("_TriggerPayload", p)
106
+ for p in agg_payloads_obj
107
+ if isinstance(p, dict)
108
+ ]
109
+ elif isinstance(agg_payloads_obj, dict):
110
+ payloads_list = [cast("_TriggerPayload", agg_payloads_obj)]
111
+ else:
112
+ payloads_list = None
113
+
114
+ event_to_val: dict[str, object] = {}
115
+ if payloads_list is not None:
116
+ for payload in payloads_list:
117
+ ev = payload.get("event")
118
+ if isinstance(ev, str):
119
+ event_to_val[ev] = payload.get("value")
120
+
121
+ # Merge triggers into a flat view: triggers first, then base
122
+ flat: dict[str, object] = dict(event_to_val)
123
+ flat.update(base_map)
124
+
125
+ # Return a write-through dict that updates the underlying
126
+ # component state when users assign nested keys via
127
+ # st.session_state[component_user_key][name] = value. Using a
128
+ # dict subclass ensures pretty-printing and JSON serialization
129
+ # behave as expected for st.write and logs.
130
+ class _WriteThrough(dict[str, object]):
131
+ def __init__(self, data: dict[str, object]) -> None:
132
+ super().__init__(data)
133
+
134
+ def __getattr__(self, name: str) -> object:
135
+ return self.get(name)
136
+
137
+ def __setattr__(self, name: str, value: object) -> None:
138
+ if name.startswith(("__", "_")):
139
+ return super().__setattr__(name, value)
140
+ self[name] = value
141
+ return None
142
+
143
+ def __deepcopy__(self, memo: dict[int, Any]) -> Self:
144
+ # This object is a proxy to the real state. Don't copy it.
145
+ memo[id(self)] = self
146
+ return self
147
+
148
+ def __setitem__(self, k: str, v: object) -> None:
149
+ _check_modification(k)
150
+
151
+ if (
152
+ allowed_state_keys is not None
153
+ and k not in allowed_state_keys
154
+ ):
155
+ # Silently ignore invalid keys to match permissive session_state semantics
156
+ return
157
+
158
+ # Update the underlying stored base state and this dict
159
+ super().__setitem__(k, v)
160
+ try:
161
+ # Store back to session state's widget store as a flat mapping
162
+ ss = session_state
163
+ # Directly set the value in the new widget state store
164
+ if component_id is not None:
165
+ ss._new_widget_state.set_from_value(
166
+ component_id, dict(self)
167
+ )
168
+ except Exception as e:
169
+ _LOGGER.debug("Failed to persist CCv2 state update: %s", e)
170
+
171
+ def __delitem__(self, k: str) -> None:
172
+ _check_modification(k)
173
+
174
+ super().__delitem__(k)
175
+ try:
176
+ ss = session_state
177
+ if component_id is not None:
178
+ ss._new_widget_state.set_from_value(
179
+ component_id, dict(self)
180
+ )
181
+ except Exception as e:
182
+ _LOGGER.debug(
183
+ "Failed to persist CCv2 state deletion: %s", e
184
+ )
185
+
186
+ return _WriteThrough(flat)
187
+ except Exception as e:
188
+ # On any error, fall back to the base value
189
+ _LOGGER.debug(
190
+ "Failed to merge trigger events into component state: %s",
191
+ e,
192
+ exc_info=e,
193
+ )
194
+ return base_value
195
+
196
+ return base_value
197
+
198
+ return _present
@@ -0,0 +1,324 @@
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Shared typing utilities for the `st.components.v2` API.
16
+
17
+ This module exposes common, user-facing argument types and callable
18
+ signatures used by the bidirectional component API. Import these types to
19
+ annotate code that constructs kwargs dictionaries for components, or when
20
+ authoring wrappers/utilities around `st.components.v2.component`.
21
+
22
+ The goal is to keep the public argument surface documented in one place and
23
+ reusable across both the user-facing factory in `components/v2/__init__.py`
24
+ and the internal implementation in `components/v2/bidi_component/main.py`.
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ from typing import TYPE_CHECKING, Any, Protocol
30
+
31
+ if TYPE_CHECKING:
32
+ from streamlit.components.v2.bidi_component.state import BidiComponentResult
33
+ from streamlit.elements.lib.layout_utils import Height, Width
34
+ from streamlit.runtime.state.common import WidgetCallback
35
+
36
+
37
+ # Individual argument type aliases to make reuse ergonomic across modules.
38
+ BidiComponentKey = str | None
39
+ BidiComponentData = Any | None
40
+ BidiComponentDefaults = dict[str, Any] | None
41
+ ComponentIsolateStyles = bool
42
+
43
+
44
+ class BidiComponentCallable(Protocol):
45
+ '''Signature of the mounting command returned by ``st.components.v2.component``.
46
+
47
+ This callable mounts a bidirectional component in a Streamlit app and
48
+ returns a ``BidiComponentResult`` object that exposes the component's
49
+ state and trigger values.
50
+
51
+ For published components, this callable is often wrapped in a user-friendly
52
+ command with typed parameters and declared defaults.
53
+
54
+ Parameters
55
+ ----------
56
+ key : str or None
57
+ An optional string to use as the unique key for the
58
+ component instance. If this is omitted, an internal key is generated
59
+ for the component instance based on its mounting parameters. No two
60
+ Streamlit elements may have the same key.
61
+
62
+ When a key is defined, the component's state is available in Session
63
+ State via the key.
64
+
65
+ .. note::
66
+ If you want to access this key in your component's frontend, you
67
+ must pass it explicitly within the ``data`` parameter. The ``key``
68
+ parameter in ``BidiComponentCallable`` is not the same as the
69
+ ``key`` property in ``ComponentArgs`` in the component's frontend
70
+ code.
71
+
72
+ The frontend key is automatically generated to be unique among all
73
+ instances of all components and to avoid collisions with classes
74
+ and IDs in the app's DOM.
75
+
76
+ data : Any or None
77
+ Data to pass to the component. This can be one of the following:
78
+
79
+ - A JSON-serializable object, like ``Dict[str, str | int]`` or
80
+ ``List[str]``.
81
+ - An Arrow-serializable object, like ``pandas.DataFrame``.
82
+ - Raw bytes.
83
+ - A dictionary of JSON-serializable and Arrow-serializable objects.
84
+ The dictionary's keys must be Python primitives.
85
+
86
+ Because this data is sent to the frontend, it must be serializable by
87
+ one of the supported serialization methods (JSON, Arrow, or raw bytes).
88
+ You can't pass arbitrary Python objects. Arrow-serialization is only
89
+ supported at the top level of the ``data`` parameter or one level deep
90
+ in a dictionary. Raw bytes are only supported at the top level.
91
+
92
+ default : dict[str, Any] or None
93
+ Default state values for the component. Each key in the dictionary must
94
+ correspond to a valid state attribute with an ``on_<key>_change``
95
+ callback. This callback can be empty, but must be included as a
96
+ parameter when the component is mounted.
97
+
98
+ Trigger values do not support manual defaults. All trigger and state
99
+ values defined by an associated callback are initialized to ``None`` by
100
+ default.
101
+
102
+ width : "stretch", "content", or int
103
+ Width of the component. This can be one of the following:
104
+
105
+ - ``"stretch"`` (default): The component is wrapped in a ``<div>`` with
106
+ CSS style ``width: 100%;``.
107
+ - ``"content"``: The component is wrapped in a ``<div>`` with CSS
108
+ style ``width: fit-content;``.
109
+ - An integer specifying the width in pixels: The component is wrapped
110
+ in a ``<div>`` with the specified pixel width.
111
+
112
+ You are responsible for ensuring the component's inner HTML content is
113
+ responsive to the ``<div>`` wrapper.
114
+
115
+ height : "content", "stretch", or int
116
+ Height of the component. This can be one of the following:
117
+
118
+ - ``"content"`` (default): The component is wrapped in a ``<div>`` with
119
+ CSS style ``height: auto;``.
120
+ - ``"stretch"``: The component is wrapped in a ``<div>`` with CSS
121
+ style ``height: 100%;``.
122
+ - An integer specifying the height in pixels: The component is wrapped
123
+ in a ``<div>`` with the specified pixel height. If the component
124
+ content is larger than the specified height, scrolling is enabled.
125
+
126
+ .. note::
127
+ Use scrolling containers sparingly. If you use scrolling
128
+ containers, avoid heights that exceed 500 pixels. Otherwise,
129
+ the scroll surface of the container might cover the majority of
130
+ the screen on mobile devices, which makes it hard to scroll the
131
+ rest of the app.
132
+
133
+ If you want to disable scrolling for a fixed-height component,
134
+ include an inner ``<div>`` wrapper in your component's HTML to
135
+ control the overflow behavior.
136
+
137
+ You are responsible for ensuring the component's inner HTML content is
138
+ responsive to the ``<div>`` wrapper.
139
+
140
+ isolate_styles : bool
141
+ Whether to sandbox the component styles in a shadow root. If this is
142
+ ``True`` (default), the component's HTML is mounted inside a shadow DOM
143
+ and, in your component's JavaScript, ``parentElement`` returns a
144
+ ``ShadowRoot``. If this is ``False``, the component's HTML is mounted
145
+ directly into the app's DOM tree, and ``parentElement`` returns a
146
+ regular ``HTMLElement``.
147
+
148
+ **callbacks : Callable or None
149
+ Callbacks with the naming pattern ``on_<key>_change`` for each state and
150
+ trigger key. For example, if your component has a state key of
151
+ ``"value"`` and a trigger key of ``"click"``, its callbacks can include
152
+ ``on_value_change`` and ``on_click_change``.
153
+
154
+ Only names that follow this pattern are recognized. Custom components
155
+ don't currently support callbacks with arguments.
156
+
157
+ Callbacks are required for any state values defined in the ``default``
158
+ parameter. Otherwise, a callback is optional. To ensure your
159
+ component's result always returns the expected attributes, you can pass
160
+ empty callbacks like ``lambda: None``.
161
+
162
+ Returns
163
+ -------
164
+ BidiComponentResult
165
+ Component state object that exposes state and trigger values.
166
+
167
+ Examples
168
+ --------
169
+ **Example 1: Create a bidirectional text input component**
170
+
171
+ If you assign a key to a mounted instance of a component, you can feed its
172
+ state back into the component through the ``data`` parameter. This allows
173
+ you to both read and write state values from Session State. The following
174
+ example has a user-friendly wrapper around the mounting command to provide
175
+ typed parameters and a clean end-user API. A couple buttons demonstrate
176
+ programmatic updates to the component's state.
177
+
178
+ .. code-block:: python
179
+
180
+ import streamlit as st
181
+
182
+ HTML = """
183
+ <label style='padding-right: 1em;' for='txt'>Enter text</label>
184
+ <input id='txt' type='text' />
185
+ """
186
+
187
+ JS = """
188
+ export default function(component) {
189
+ const { setStateValue, parentElement, data } = component;
190
+
191
+ const label = parentElement.querySelector('label');
192
+ label.innerText = data.label;
193
+
194
+ const input = parentElement.querySelector('input');
195
+ if (input.value !== data.value) {
196
+ input.value = data.value ?? '';
197
+ };
198
+
199
+ input.onkeydown = (e) => {
200
+ if (e.key === 'Enter') {
201
+ setStateValue('value', e.target.value);
202
+ }
203
+ };
204
+
205
+ input.onblur = (e) => {
206
+ setStateValue('value', e.target.value);
207
+ };
208
+ }
209
+ """
210
+
211
+ my_component = st.components.v2.component(
212
+ "my_text_input",
213
+ html=HTML,
214
+ js=JS,
215
+ )
216
+
217
+
218
+ def my_component_wrapper(
219
+ label, *, default="", key=None, on_change=lambda: None
220
+ ):
221
+ component_state = st.session_state.get(key, {})
222
+ value = component_state.get("value", default)
223
+ data = {"label": label, "value": value}
224
+ result = my_component(
225
+ data=data,
226
+ default={"value": value},
227
+ key=key,
228
+ on_value_change=on_change,
229
+ )
230
+ return result
231
+
232
+
233
+ st.title("My custom component")
234
+
235
+ if st.button("Hello World"):
236
+ st.session_state["my_text_input_instance"]["value"] = "Hello World"
237
+ if st.button("Clear text"):
238
+ st.session_state["my_text_input_instance"]["value"] = ""
239
+ result = my_component_wrapper(
240
+ "Enter something",
241
+ default="I love Streamlit!",
242
+ key="my_text_input_instance",
243
+ )
244
+
245
+ st.write("Result:", result)
246
+ st.write("Session state:", st.session_state)
247
+
248
+ .. output ::
249
+ https://doc-components-text-input.streamlit.app/
250
+ height: 600px
251
+
252
+ **Example 2: Add Tailwind CSS to a component**
253
+
254
+ You can use the ``isolate_styles`` parameter to disable shadow DOM
255
+ isolation and apply global styles like Tailwind CSS to your component. The
256
+ following example creates a simple button styled with Tailwind CSS. This
257
+ example also demonstrates using different keys to mount multiple instances
258
+ of the same component in one app.
259
+
260
+ .. code-block:: python
261
+
262
+ import streamlit as st
263
+
264
+ with open("tailwind.js", "r") as f:
265
+ TAILWIND_SCRIPT = f.read()
266
+
267
+ HTML = """
268
+ <button class="bg-blue-500 hover:bg-blue-700 text-white py-1 px-3 rounded">
269
+ Click me!
270
+ </button>
271
+ """
272
+ JS = (
273
+ TAILWIND_SCRIPT
274
+ + """
275
+ export default function(component) {
276
+ const { setTriggerValue, parentElement } = component;
277
+ const button = parentElement.querySelector('button');
278
+ button.onclick = () => {
279
+ setTriggerValue('clicked', true);
280
+ };
281
+ }
282
+ """
283
+ )
284
+ my_component = st.components.v2.component(
285
+ "my_tailwind_button",
286
+ html=HTML,
287
+ js=JS,
288
+ )
289
+ result_1 = my_component(
290
+ isolate_styles=False, on_clicked_change=lambda: None, key="one"
291
+ )
292
+ result_1
293
+
294
+ result_2 = my_component(
295
+ isolate_styles=False, on_clicked_change=lambda: None, key="two"
296
+ )
297
+ result_2
298
+
299
+ .. output ::
300
+ https://doc-components-tailwind-button.streamlit.app/
301
+ height: 350px
302
+
303
+ '''
304
+
305
+ def __call__(
306
+ self,
307
+ *,
308
+ key: BidiComponentKey = None,
309
+ data: BidiComponentData = None,
310
+ default: BidiComponentDefaults = None,
311
+ width: Width = "stretch",
312
+ height: Height = "content",
313
+ isolate_styles: ComponentIsolateStyles = True,
314
+ **on_callbacks: WidgetCallback | None,
315
+ ) -> BidiComponentResult: ...
316
+
317
+
318
+ __all__ = [
319
+ "BidiComponentCallable",
320
+ "BidiComponentData",
321
+ "BidiComponentDefaults",
322
+ "BidiComponentKey",
323
+ "ComponentIsolateStyles",
324
+ ]