streamlit-nightly 1.45.2.dev20250602__py3-none-any.whl → 1.45.2.dev20250605__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 (97) hide show
  1. streamlit/config.py +56 -26
  2. streamlit/config_option.py +4 -2
  3. streamlit/elements/arrow.py +2 -4
  4. streamlit/elements/deck_gl_json_chart.py +1 -1
  5. streamlit/elements/heading.py +32 -0
  6. streamlit/elements/metric.py +11 -1
  7. streamlit/elements/plotly_chart.py +1 -1
  8. streamlit/elements/text.py +13 -1
  9. streamlit/elements/vega_charts.py +27 -2
  10. streamlit/elements/widgets/checkbox.py +23 -1
  11. streamlit/elements/widgets/color_picker.py +20 -1
  12. streamlit/file_util.py +14 -0
  13. streamlit/proto/ClientState_pb2.py +4 -4
  14. streamlit/proto/ClientState_pb2.pyi +7 -2
  15. streamlit/proto/NewSession_pb2.py +16 -16
  16. streamlit/proto/NewSession_pb2.pyi +7 -2
  17. streamlit/runtime/context.py +33 -1
  18. streamlit/static/index.html +1 -1
  19. streamlit/static/manifest.json +214 -214
  20. streamlit/static/static/js/{ErrorOutline.esm.D5NgCh5m.js → ErrorOutline.esm.tNLvotRM.js} +1 -1
  21. streamlit/static/static/js/{FileDownload.esm.NuObx1Tl.js → FileDownload.esm.C_4VXOrN.js} +1 -1
  22. streamlit/static/static/js/{FileHelper.BH7o4_P9.js → FileHelper.60q2rqnv.js} +1 -1
  23. streamlit/static/static/js/{FormClearHelper.BAxB8JFY.js → FormClearHelper.NJdi-ZLd.js} +1 -1
  24. streamlit/static/static/js/{Hooks.Bjt8oBOT.js → Hooks.CaYtegs-.js} +1 -1
  25. streamlit/static/static/js/{InputInstructions.BZZLp6t9.js → InputInstructions.WOkLURYg.js} +1 -1
  26. streamlit/static/static/js/{ProgressBar.BwRgGfI0.js → ProgressBar.C8qv5JmE.js} +1 -1
  27. streamlit/static/static/js/{RenderInPortalIfExists.C_c286r8.js → RenderInPortalIfExists.tUlEGlzo.js} +1 -1
  28. streamlit/static/static/js/{Toolbar.5tXjF_eX.js → Toolbar.CMYJPS_Q.js} +1 -1
  29. streamlit/static/static/js/{base-input.wfBDFTlB.js → base-input.DFrC09vO.js} +1 -1
  30. streamlit/static/static/js/{checkbox.D1GhNngU.js → checkbox.BnwpL_LT.js} +1 -1
  31. streamlit/static/static/js/{createSuper.CudEEVs2.js → createSuper.NiynzP3k.js} +1 -1
  32. streamlit/static/static/js/{data-grid-overlay-editor.DUTI_Rbg.js → data-grid-overlay-editor.CYEI83P5.js} +1 -1
  33. streamlit/static/static/js/{downloader.BwLpAoXN.js → downloader.9Wy4dgxg.js} +1 -1
  34. streamlit/static/static/js/{es6.B_HLnUb9.js → es6.JGDen1xD.js} +2 -2
  35. streamlit/static/static/js/{iframeResizer.contentWindow.BNcjOcan.js → iframeResizer.contentWindow.nFuzbEci.js} +1 -1
  36. streamlit/static/static/js/{index.DA7wmIe4.js → index.3h_mMb74.js} +1 -1
  37. streamlit/static/static/js/{index.jyqBAhvH.js → index.5Z_KRF3q.js} +1 -1
  38. streamlit/static/static/js/{index.D3wYqvI6.js → index.91OyW-Gr.js} +1 -1
  39. streamlit/static/static/js/{index.flyQEkeT.js → index.B7S_H7I8.js} +1 -1
  40. streamlit/static/static/js/{index.BJQPxOFd.js → index.BBDrA2tn.js} +1 -1
  41. streamlit/static/static/js/{index.B4PbMsBe.js → index.BFYl-rZt.js} +1 -1
  42. streamlit/static/static/js/{index.PR2mcvVR.js → index.BNZGa4XA.js} +1 -1
  43. streamlit/static/static/js/{index.D8i27llu.js → index.BRvAGIBI.js} +1 -1
  44. streamlit/static/static/js/{index.BkvVxUIW.js → index.BfYf3Wpe.js} +1 -1
  45. streamlit/static/static/js/{index.CxtkoiKl.js → index.Bj3saWDN.js} +1 -1
  46. streamlit/static/static/js/{index.Bj6uLiaA.js → index.BtrjwWEk.js} +1 -1
  47. streamlit/static/static/js/{index.Ca-9xoC2.js → index.C064I556.js} +1 -1
  48. streamlit/static/static/js/{index.CfzaRR6P.js → index.C37654wn.js} +1 -1
  49. streamlit/static/static/js/{index.df4uuSoD.js → index.C4zPKjq1.js} +1 -1
  50. streamlit/static/static/js/{index.BPGAPI9w.js → index.CBPqvzle.js} +1 -1
  51. streamlit/static/static/js/{index.CL0UH4WF.js → index.CIqU--y_.js} +1 -1
  52. streamlit/static/static/js/{index.Cf8KcH2X.js → index.CRliFDNK.js} +1 -1
  53. streamlit/static/static/js/{index.B0h616Th.js → index.CX-fCNEA.js} +1 -1
  54. streamlit/static/static/js/{index.NveskZ7j.js → index.C_U6DmFf.js} +1 -1
  55. streamlit/static/static/js/{index.BtpsTdHN.js → index.CgzkiPeB.js} +76 -76
  56. streamlit/static/static/js/{index.BGKVW2u9.js → index.Chouo5Qe.js} +1 -1
  57. streamlit/static/static/js/{index.DNR4wKJg.js → index.CsrXF9Cj.js} +1 -1
  58. streamlit/static/static/js/{index.sYLAHlH0.js → index.DM5GB-cu.js} +1 -1
  59. streamlit/static/static/js/{index.B3vWaIrN.js → index.DOOJNTFS.js} +1 -1
  60. streamlit/static/static/js/{index.CXP5ffxh.js → index.DOmrs5iq.js} +1 -1
  61. streamlit/static/static/js/{index.B0paBg5x.js → index.DQLD-hJX.js} +1 -1
  62. streamlit/static/static/js/{index.DIG9Mo9J.js → index.DdiZdMRx.js} +1 -1
  63. streamlit/static/static/js/{index.BOHEcsVb.js → index.DlPy-f9y.js} +1 -1
  64. streamlit/static/static/js/{index.CjRwuAdg.js → index.DsBk6ofW.js} +1 -1
  65. streamlit/static/static/js/{index.C1GNiWbH.js → index.DwohHcrt.js} +1 -1
  66. streamlit/static/static/js/{index.iF9jUtwl.js → index.FqXTo5A0.js} +1 -1
  67. streamlit/static/static/js/{index.3WJoJFGb.js → index.OVkzDHNi.js} +1 -1
  68. streamlit/static/static/js/{index.DNS8a-dx.js → index.fuysp8C5.js} +26 -26
  69. streamlit/static/static/js/{index.Cdd7Ri21.js → index.o3iaAZx6.js} +25 -25
  70. streamlit/static/static/js/{index.Bs0m0eUy.js → index.ttXUdWmg.js} +1 -1
  71. streamlit/static/static/js/{index.zq3LdRhj.js → index.yBwY5kh0.js} +1 -1
  72. streamlit/static/static/js/{index.CkOUlPYT.js → index.yUKivH8E.js} +1 -1
  73. streamlit/static/static/js/{input.CtZjQ6Pv.js → input.Bfr-6m-t.js} +1 -1
  74. streamlit/static/static/js/{memory.Ddl3R0up.js → memory.Dbw4SfUa.js} +1 -1
  75. streamlit/static/static/js/{mergeWith.D-TqDY0-.js → mergeWith.moXRXMIt.js} +1 -1
  76. streamlit/static/static/js/{number-overlay-editor.Dndf05Hx.js → number-overlay-editor.xIeRsh_h.js} +1 -1
  77. streamlit/static/static/js/{possibleConstructorReturn.DNEY6J9G.js → possibleConstructorReturn.BpF6cUQr.js} +1 -1
  78. streamlit/static/static/js/{sandbox.B_XDPkfx.js → sandbox.CD6NZtqo.js} +1 -1
  79. streamlit/static/static/js/{textarea.BZ9lTMhV.js → textarea.45gncRdV.js} +1 -1
  80. streamlit/static/static/js/{timepicker.D9UUwpRT.js → timepicker.T96XABz2.js} +1 -1
  81. streamlit/static/static/js/{toConsumableArray.DNJjbOUS.js → toConsumableArray.De5UEugE.js} +1 -1
  82. streamlit/static/static/js/{uniqueId.psBJ_tSg.js → uniqueId.sYiuwCOd.js} +1 -1
  83. streamlit/static/static/js/{useBasicWidgetState.CTpx4w-3.js → useBasicWidgetState.BdPzYDh_.js} +1 -1
  84. streamlit/static/static/js/{useOnInputChange.WKTDSC4O.js → useOnInputChange.B9Jrbmab.js} +1 -1
  85. streamlit/static/static/js/{withFullScreenWrapper.oyWCn2-3.js → withFullScreenWrapper.BJVHzNAl.js} +1 -1
  86. streamlit/util.py +24 -0
  87. streamlit/web/bootstrap.py +2 -2
  88. streamlit/web/cli.py +8 -5
  89. streamlit/web/server/server.py +0 -12
  90. streamlit/web/server/server_util.py +1 -1
  91. {streamlit_nightly-1.45.2.dev20250602.dist-info → streamlit_nightly-1.45.2.dev20250605.dist-info}/METADATA +1 -1
  92. {streamlit_nightly-1.45.2.dev20250602.dist-info → streamlit_nightly-1.45.2.dev20250605.dist-info}/RECORD +96 -97
  93. streamlit/elements/lib/event_utils.py +0 -39
  94. {streamlit_nightly-1.45.2.dev20250602.data → streamlit_nightly-1.45.2.dev20250605.data}/scripts/streamlit.cmd +0 -0
  95. {streamlit_nightly-1.45.2.dev20250602.dist-info → streamlit_nightly-1.45.2.dev20250605.dist-info}/WHEEL +0 -0
  96. {streamlit_nightly-1.45.2.dev20250602.dist-info → streamlit_nightly-1.45.2.dev20250605.dist-info}/entry_points.txt +0 -0
  97. {streamlit_nightly-1.45.2.dev20250602.dist-info → streamlit_nightly-1.45.2.dev20250605.dist-info}/top_level.txt +0 -0
streamlit/config.py CHANGED
@@ -54,6 +54,9 @@ _config_options_template: dict[str, ConfigOption] = OrderedDict()
54
54
  # Stores the current state of config options.
55
55
  _config_options: dict[str, ConfigOption] | None = None
56
56
 
57
+ # Stores the path to the main script. This is used to
58
+ # resolve config and secret files relative to the main script:
59
+ _main_script_path: str | None = None
57
60
 
58
61
  # Indicates that a config option was defined by the user.
59
62
  _USER_DEFINED: Final = "<user defined>"
@@ -243,6 +246,7 @@ def _create_option(
243
246
  replaced_by: str | None = None,
244
247
  type_: type = str,
245
248
  sensitive: bool = False,
249
+ multiple: bool = False,
246
250
  ) -> ConfigOption:
247
251
  '''Create a ConfigOption and store it globally in this module.
248
252
 
@@ -291,6 +295,7 @@ def _create_option(
291
295
  replaced_by=replaced_by,
292
296
  type_=type_,
293
297
  sensitive=sensitive,
298
+ multiple=multiple,
294
299
  )
295
300
  if option.section not in _section_descriptions:
296
301
  raise RuntimeError(
@@ -384,6 +389,7 @@ _create_option(
384
389
 
385
390
 
386
391
  @_create_option("global.developmentMode", visibility="hidden", type_=bool)
392
+ @util.memoize
387
393
  def _global_development_mode() -> bool:
388
394
  """Are we in development mode.
389
395
 
@@ -492,6 +498,7 @@ def _logger_message_format() -> str:
492
498
  type_=bool,
493
499
  scriptable=True,
494
500
  )
501
+ @util.memoize
495
502
  def _logger_enable_rich() -> bool:
496
503
  """
497
504
  Controls whether uncaught app exceptions are logged via the rich library.
@@ -677,6 +684,7 @@ _create_option(
677
684
  Note: This is a list of absolute paths.
678
685
  """,
679
686
  default_val=[],
687
+ multiple=True,
680
688
  )
681
689
 
682
690
  _create_option(
@@ -689,6 +697,7 @@ _create_option(
689
697
  Example: ['/home/user1/env', 'relative/path/to/folder']
690
698
  """,
691
699
  default_val=[],
700
+ multiple=True,
692
701
  )
693
702
 
694
703
  _create_option(
@@ -774,8 +783,6 @@ _create_option(
774
783
  "server.port",
775
784
  description="""
776
785
  The port where the server will listen for browser connections.
777
-
778
- Don't use port 3000 which is reserved for internal development.
779
786
  """,
780
787
  default_val=8501,
781
788
  type_=int,
@@ -831,6 +838,7 @@ _create_option(
831
838
  Example: ['http://example.com', 'https://streamlit.io']
832
839
  """,
833
840
  default_val=[],
841
+ multiple=True,
834
842
  )
835
843
 
836
844
  _create_option(
@@ -954,8 +962,7 @@ def _browser_server_port() -> int:
954
962
  - Open the browser automatically (part of `streamlit run`).
955
963
 
956
964
  This option is for advanced use cases. To change the port of your app, use
957
- `server.Port` instead. Don't use port 3000 which is reserved for internal
958
- development.
965
+ `server.Port` instead.
959
966
 
960
967
  Default: whatever value is set in server.port.
961
968
  """
@@ -1020,6 +1027,7 @@ _create_option(
1020
1027
  to pass the Mapbox API token.
1021
1028
  """,
1022
1029
  default_val="",
1030
+ type_=str,
1023
1031
  sensitive=True,
1024
1032
  deprecated=True,
1025
1033
  deprecation_text="""
@@ -1251,6 +1259,14 @@ _create_theme_options(
1251
1259
  """,
1252
1260
  )
1253
1261
 
1262
+ _create_theme_options(
1263
+ "dataframeBorderColor",
1264
+ categories=["theme", CustomThemeCategories.SIDEBAR],
1265
+ description="""
1266
+ The color of the border around dataframes and tables.
1267
+ """,
1268
+ )
1269
+
1254
1270
  _create_theme_options(
1255
1271
  "showWidgetBorder",
1256
1272
  categories=["theme", CustomThemeCategories.SIDEBAR],
@@ -1287,23 +1303,17 @@ _create_theme_options(
1287
1303
 
1288
1304
  _create_section("secrets", "Secrets configuration.")
1289
1305
 
1290
- _create_option(
1291
- "secrets.files",
1292
- description="""
1293
- List of locations where secrets are searched.
1294
1306
 
1295
- An entry can be a path to a TOML file or directory path where
1296
- Kubernetes style secrets are saved. Order is important, import is
1297
- first to last, so secrets in later files will take precedence over
1298
- earlier ones.
1299
- """,
1300
- default_val=[
1301
- # NOTE: The order here is important! Project-level secrets should overwrite
1302
- # global secrets.
1303
- file_util.get_streamlit_file_path("secrets.toml"),
1304
- file_util.get_project_streamlit_file_path("secrets.toml"),
1305
- ],
1306
- )
1307
+ @_create_option("secrets.files", multiple=True)
1308
+ def _secrets_files() -> list[str]:
1309
+ """List of locations where secrets are searched.
1310
+
1311
+ An entry can be a path to a TOML file or directory path where
1312
+ Kubernetes style secrets are saved. Order is important, import is
1313
+ first to last, so secrets in later files will take precedence over
1314
+ earlier ones.
1315
+ """
1316
+ return get_config_files("secrets.toml")
1307
1317
 
1308
1318
 
1309
1319
  def get_where_defined(key: str) -> str:
@@ -1553,10 +1563,30 @@ def _maybe_convert_to_number(v: Any) -> Any:
1553
1563
  # something.
1554
1564
  _on_config_parsed = Signal(doc="Emitted when the config file is parsed.")
1555
1565
 
1556
- CONFIG_FILENAMES = [
1557
- file_util.get_streamlit_file_path("config.toml"),
1558
- file_util.get_project_streamlit_file_path("config.toml"),
1559
- ]
1566
+
1567
+ def get_config_files(file_name: str) -> list[str]:
1568
+ """Return the list of config files (e.g. config.toml or secrets.toml) to be parsed.
1569
+
1570
+ Order is important, import is first to last, so options in later files
1571
+ will take precedence over earlier ones.
1572
+ """
1573
+ # script-level config files overwrite project-level config
1574
+ # files, which in turn overwrite global config files.
1575
+ config_files = [
1576
+ file_util.get_streamlit_file_path(file_name),
1577
+ file_util.get_project_streamlit_file_path(file_name),
1578
+ ]
1579
+
1580
+ if _main_script_path is not None:
1581
+ script_level_config = file_util.get_main_script_streamlit_file_path(
1582
+ _main_script_path, file_name
1583
+ )
1584
+ if script_level_config not in config_files:
1585
+ # We need to append the script-level config file to the list
1586
+ # so that it overwrites project & global level config files:
1587
+ config_files.append(script_level_config)
1588
+
1589
+ return config_files
1560
1590
 
1561
1591
 
1562
1592
  def get_config_options(
@@ -1607,7 +1637,7 @@ def get_config_options(
1607
1637
 
1608
1638
  # Values set in files later in the CONFIG_FILENAMES list overwrite those
1609
1639
  # set earlier.
1610
- for filename in CONFIG_FILENAMES:
1640
+ for filename in get_config_files("config.toml"):
1611
1641
  if not os.path.exists(filename):
1612
1642
  continue
1613
1643
 
@@ -1643,7 +1673,7 @@ def _check_conflicts() -> None:
1643
1673
  # When using the Node server, we must always connect to 8501 (this is
1644
1674
  # hard-coded in JS). Otherwise, the browser would decide what port to
1645
1675
  # connect to based on window.location.port, which in dev is going
1646
- # to be (3000)
1676
+ # to be 3000.
1647
1677
 
1648
1678
  # Import logger locally to prevent circular references
1649
1679
  from streamlit.logger import get_logger
@@ -108,6 +108,7 @@ class ConfigOption:
108
108
  replaced_by: str | None = None,
109
109
  type_: type = str,
110
110
  sensitive: bool = False,
111
+ multiple: bool = False,
111
112
  ) -> None:
112
113
  """Create a ConfigOption with the given name.
113
114
 
@@ -142,6 +143,8 @@ class ConfigOption:
142
143
  Useful to cast the config params sent by cmd option parameter.
143
144
  sensitive: bool
144
145
  Sensitive configuration options cannot be set by CLI parameter.
146
+ multiple: bool
147
+ Whether this config option can have multiple values.
145
148
  """
146
149
  # Parse out the section and name.
147
150
  self.key = key
@@ -183,8 +186,7 @@ class ConfigOption:
183
186
  self.where_defined = ConfigOption.DEFAULT_DEFINITION
184
187
  self.type = type_
185
188
  self.sensitive = sensitive
186
- # infer multiple values if the default value is a list or tuple
187
- self.multiple = isinstance(default_val, (list, tuple))
189
+ self.multiple = multiple
188
190
 
189
191
  if self.replaced_by:
190
192
  self.deprecated = True
@@ -37,7 +37,6 @@ from streamlit.elements.lib.column_config_utils import (
37
37
  process_config_mapping,
38
38
  update_column_config,
39
39
  )
40
- from streamlit.elements.lib.event_utils import AttributeDictionary
41
40
  from streamlit.elements.lib.form_utils import current_form_id
42
41
  from streamlit.elements.lib.pandas_styler_utils import marshall_styler
43
42
  from streamlit.elements.lib.policies import check_widget_policies
@@ -51,6 +50,7 @@ from streamlit.runtime.scriptrunner_utils.script_run_context import (
51
50
  get_script_run_ctx,
52
51
  )
53
52
  from streamlit.runtime.state import WidgetCallback, register_widget
53
+ from streamlit.util import AttributeDictionary
54
54
 
55
55
  if TYPE_CHECKING:
56
56
  from collections.abc import Hashable, Iterable
@@ -823,9 +823,7 @@ def _prep_data_for_add_rows(
823
823
  def _arrow_add_rows(
824
824
  dg: DeltaGenerator,
825
825
  data: Data = None,
826
- **kwargs: (
827
- DataFrame | npt.NDArray[Any] | Iterable[Any] | dict[Hashable, Any] | None
828
- ),
826
+ **kwargs: DataFrame | npt.NDArray[Any] | Iterable[Any] | dict[Hashable, Any] | None,
829
827
  ) -> DeltaGenerator | None:
830
828
  """Concatenate a dataframe to the bottom of the current one.
831
829
 
@@ -29,7 +29,6 @@ from typing import (
29
29
  from typing_extensions import TypeAlias
30
30
 
31
31
  from streamlit import config
32
- from streamlit.elements.lib.event_utils import AttributeDictionary
33
32
  from streamlit.elements.lib.form_utils import current_form_id
34
33
  from streamlit.elements.lib.policies import check_widget_policies
35
34
  from streamlit.elements.lib.utils import Key, compute_and_register_element_id, to_key
@@ -41,6 +40,7 @@ from streamlit.runtime.state import (
41
40
  WidgetCallback,
42
41
  register_widget,
43
42
  )
43
+ from streamlit.util import AttributeDictionary
44
44
 
45
45
  if TYPE_CHECKING:
46
46
  from collections.abc import Iterable, Mapping
@@ -19,6 +19,7 @@ from typing import TYPE_CHECKING, Literal, Union, cast
19
19
 
20
20
  from typing_extensions import TypeAlias
21
21
 
22
+ from streamlit.elements.lib.layout_utils import LayoutConfig, validate_width
22
23
  from streamlit.errors import StreamlitAPIException
23
24
  from streamlit.proto.Heading_pb2 import Heading as HeadingProto
24
25
  from streamlit.runtime.metrics_util import gather_metrics
@@ -26,6 +27,7 @@ from streamlit.string_util import clean_text
26
27
 
27
28
  if TYPE_CHECKING:
28
29
  from streamlit.delta_generator import DeltaGenerator
30
+ from streamlit.elements.lib.layout_utils import Width
29
31
  from streamlit.type_util import SupportsStr
30
32
 
31
33
 
@@ -48,6 +50,7 @@ class HeadingMixin:
48
50
  *, # keyword-only arguments:
49
51
  help: str | None = None,
50
52
  divider: Divider = False,
53
+ width: Width = "stretch",
51
54
  ) -> DeltaGenerator:
52
55
  """Display text in header formatting.
53
56
 
@@ -84,6 +87,11 @@ class HeadingMixin:
84
87
  the following: blue, green, orange, red, violet, gray/grey, or
85
88
  rainbow.
86
89
 
90
+ width : int or "stretch" or "content"
91
+ The width of the header. Can be an integer (pixels), "stretch" to
92
+ use the full width of the container, or "content" (default) to size
93
+ based on the content.
94
+
87
95
  Examples
88
96
  --------
89
97
  >>> import streamlit as st
@@ -101,6 +109,9 @@ class HeadingMixin:
101
109
  height: 600px
102
110
 
103
111
  """
112
+ validate_width(width, allow_content=True)
113
+ layout_config = LayoutConfig(width=width)
114
+
104
115
  return self.dg._enqueue(
105
116
  "heading",
106
117
  HeadingMixin._create_heading_proto(
@@ -110,6 +121,7 @@ class HeadingMixin:
110
121
  help=help,
111
122
  divider=divider,
112
123
  ),
124
+ layout_config=layout_config,
113
125
  )
114
126
 
115
127
  @gather_metrics("subheader")
@@ -120,6 +132,7 @@ class HeadingMixin:
120
132
  *, # keyword-only arguments:
121
133
  help: str | None = None,
122
134
  divider: Divider = False,
135
+ width: Width = "stretch",
123
136
  ) -> DeltaGenerator:
124
137
  """Display text in subheader formatting.
125
138
 
@@ -156,6 +169,11 @@ class HeadingMixin:
156
169
  the following: blue, green, orange, red, violet, gray/grey, or
157
170
  rainbow.
158
171
 
172
+ width : int or "stretch" or "content"
173
+ The width of the subheader. Can be an integer (pixels), "stretch" to
174
+ use the full width of the container, or "content" (default) to size
175
+ based on the content.
176
+
159
177
  Examples
160
178
  --------
161
179
  >>> import streamlit as st
@@ -173,6 +191,9 @@ class HeadingMixin:
173
191
  height: 500px
174
192
 
175
193
  """
194
+ validate_width(width, allow_content=True)
195
+ layout_config = LayoutConfig(width=width)
196
+
176
197
  return self.dg._enqueue(
177
198
  "heading",
178
199
  HeadingMixin._create_heading_proto(
@@ -182,6 +203,7 @@ class HeadingMixin:
182
203
  help=help,
183
204
  divider=divider,
184
205
  ),
206
+ layout_config=layout_config,
185
207
  )
186
208
 
187
209
  @gather_metrics("title")
@@ -191,6 +213,7 @@ class HeadingMixin:
191
213
  anchor: Anchor = None,
192
214
  *, # keyword-only arguments:
193
215
  help: str | None = None,
216
+ width: Width = "stretch",
194
217
  ) -> DeltaGenerator:
195
218
  """Display text in title formatting.
196
219
 
@@ -222,6 +245,11 @@ class HeadingMixin:
222
245
  including the Markdown directives described in the ``body``
223
246
  parameter of ``st.markdown``.
224
247
 
248
+ width : int or "stretch" or "content"
249
+ The width of the title. Can be an integer (pixels), "stretch" to
250
+ use the full width of the container, or "content" (default) to size
251
+ based on the content.
252
+
225
253
  Examples
226
254
  --------
227
255
  >>> import streamlit as st
@@ -234,11 +262,15 @@ class HeadingMixin:
234
262
  height: 220px
235
263
 
236
264
  """
265
+ validate_width(width, allow_content=True)
266
+ layout_config = LayoutConfig(width=width)
267
+
237
268
  return self.dg._enqueue(
238
269
  "heading",
239
270
  HeadingMixin._create_heading_proto(
240
271
  tag=HeadingProtoTag.TITLE_TAG, body=body, anchor=anchor, help=help
241
272
  ),
273
+ layout_config=layout_config,
242
274
  )
243
275
 
244
276
  @property
@@ -20,6 +20,7 @@ from typing import TYPE_CHECKING, Any, Literal, Union, cast
20
20
 
21
21
  from typing_extensions import TypeAlias
22
22
 
23
+ from streamlit.elements.lib.layout_utils import LayoutConfig, Width, validate_width
23
24
  from streamlit.elements.lib.policies import maybe_raise_label_warnings
24
25
  from streamlit.elements.lib.utils import (
25
26
  LabelVisibility,
@@ -58,6 +59,7 @@ class MetricMixin:
58
59
  help: str | None = None,
59
60
  label_visibility: LabelVisibility = "visible",
60
61
  border: bool = False,
62
+ width: Width = "stretch",
61
63
  ) -> DeltaGenerator:
62
64
  r"""Display a metric in big bold font, with an optional indicator of how the metric changed.
63
65
 
@@ -122,6 +124,11 @@ class MetricMixin:
122
124
  ``False`` (default), no border is shown. If this is ``True``, a
123
125
  border is shown.
124
126
 
127
+ width : int or "stretch" or "content"
128
+ The width of the metric. Can be either an integer (pixels), "stretch", or "content".
129
+ Defaults to "stretch". If "stretch", the metric will stretch to fill the available
130
+ space. If "content", the metric will adjust its width to fit its content.
131
+
125
132
  Examples
126
133
  --------
127
134
  **Example 1: Show a metric**
@@ -204,7 +211,10 @@ class MetricMixin:
204
211
  label_visibility
205
212
  )
206
213
 
207
- return self.dg._enqueue("metric", metric_proto)
214
+ validate_width(width, allow_content=True)
215
+ layout_config = LayoutConfig(width=width)
216
+
217
+ return self.dg._enqueue("metric", metric_proto, layout_config=layout_config)
208
218
 
209
219
  @property
210
220
  def dg(self) -> DeltaGenerator:
@@ -33,7 +33,6 @@ from typing_extensions import Required, TypeAlias
33
33
 
34
34
  from streamlit import type_util
35
35
  from streamlit.deprecation_util import show_deprecation_warning
36
- from streamlit.elements.lib.event_utils import AttributeDictionary
37
36
  from streamlit.elements.lib.form_utils import current_form_id
38
37
  from streamlit.elements.lib.policies import check_widget_policies
39
38
  from streamlit.elements.lib.streamlit_plotly_theme import (
@@ -45,6 +44,7 @@ from streamlit.proto.PlotlyChart_pb2 import PlotlyChart as PlotlyChartProto
45
44
  from streamlit.runtime.metrics_util import gather_metrics
46
45
  from streamlit.runtime.scriptrunner_utils.script_run_context import get_script_run_ctx
47
46
  from streamlit.runtime.state import WidgetCallback, register_widget
47
+ from streamlit.util import AttributeDictionary
48
48
 
49
49
  if TYPE_CHECKING:
50
50
  from collections.abc import Iterable
@@ -16,12 +16,14 @@ from __future__ import annotations
16
16
 
17
17
  from typing import TYPE_CHECKING, cast
18
18
 
19
+ from streamlit.elements.lib.layout_utils import LayoutConfig, validate_width
19
20
  from streamlit.proto.Text_pb2 import Text as TextProto
20
21
  from streamlit.runtime.metrics_util import gather_metrics
21
22
  from streamlit.string_util import clean_text
22
23
 
23
24
  if TYPE_CHECKING:
24
25
  from streamlit.delta_generator import DeltaGenerator
26
+ from streamlit.elements.lib.layout_utils import Width
25
27
  from streamlit.type_util import SupportsStr
26
28
 
27
29
 
@@ -32,6 +34,7 @@ class TextMixin:
32
34
  body: SupportsStr,
33
35
  *, # keyword-only arguments:
34
36
  help: str | None = None,
37
+ width: Width = "content",
35
38
  ) -> DeltaGenerator:
36
39
  r"""Write text without Markdown or HTML parsing.
37
40
 
@@ -53,6 +56,11 @@ class TextMixin:
53
56
  including the Markdown directives described in the ``body``
54
57
  parameter of ``st.markdown``.
55
58
 
59
+ width : int or "stretch" or "content"
60
+ The width of the text element. Can be an integer (pixels), "stretch" to
61
+ use the full width of the container, or "content" (default) to size
62
+ based on the content.
63
+
56
64
  Example
57
65
  -------
58
66
  >>> import streamlit as st
@@ -68,7 +76,11 @@ class TextMixin:
68
76
  text_proto.body = clean_text(body)
69
77
  if help:
70
78
  text_proto.help = help
71
- return self.dg._enqueue("text", text_proto)
79
+
80
+ validate_width(width, allow_content=True)
81
+ layout_config = LayoutConfig(width=width)
82
+
83
+ return self.dg._enqueue("text", text_proto, layout_config=layout_config)
72
84
 
73
85
  @property
74
86
  def dg(self) -> DeltaGenerator:
@@ -42,7 +42,6 @@ from streamlit.elements.lib.built_in_chart_utils import (
42
42
  generate_chart,
43
43
  maybe_raise_stack_warning,
44
44
  )
45
- from streamlit.elements.lib.event_utils import AttributeDictionary
46
45
  from streamlit.elements.lib.form_utils import current_form_id
47
46
  from streamlit.elements.lib.policies import check_widget_policies
48
47
  from streamlit.elements.lib.utils import Key, compute_and_register_element_id, to_key
@@ -53,7 +52,7 @@ from streamlit.proto.ArrowVegaLiteChart_pb2 import (
53
52
  from streamlit.runtime.metrics_util import gather_metrics
54
53
  from streamlit.runtime.scriptrunner_utils.script_run_context import get_script_run_ctx
55
54
  from streamlit.runtime.state import WidgetCallback, register_widget
56
- from streamlit.util import calc_md5
55
+ from streamlit.util import AttributeDictionary, calc_md5
57
56
 
58
57
  if TYPE_CHECKING:
59
58
  from collections.abc import Iterable, Sequence
@@ -250,6 +249,30 @@ class VegaLiteStateSerde:
250
249
  return json.dumps(selection_state, default=str)
251
250
 
252
251
 
252
+ def _patch_null_legend_titles(spec: VegaLiteSpec) -> None:
253
+ """Patches null legend titles in the 'color' channel of the spec.
254
+ This is a fix for the Vega-Lite bug where null legend titles
255
+ cause a wrong formatting of the chart as shown on the issue #9339.
256
+ """
257
+
258
+ encoding = spec.get("encoding")
259
+ if not isinstance(encoding, dict):
260
+ return
261
+
262
+ color_spec = encoding.get("color")
263
+ if not isinstance(color_spec, dict):
264
+ return
265
+
266
+ if "title" in color_spec and color_spec.get("title") is None:
267
+ # Patch legend title given null value directly in the encoding
268
+ color_spec["title"] = " "
269
+
270
+ legend = color_spec.get("legend")
271
+ if isinstance(legend, dict) and "title" in legend and legend.get("title") is None:
272
+ # Patch legend title given null value in the legend
273
+ legend["title"] = " "
274
+
275
+
253
276
  def _prepare_vega_lite_spec(
254
277
  spec: VegaLiteSpec,
255
278
  use_container_width: bool,
@@ -278,6 +301,8 @@ def _prepare_vega_lite_spec(
278
301
  else:
279
302
  spec["autosize"] = {"type": "fit", "contains": "padding"}
280
303
 
304
+ _patch_null_legend_titles(spec)
305
+
281
306
  return spec
282
307
 
283
308
 
@@ -19,6 +19,11 @@ from textwrap import dedent
19
19
  from typing import TYPE_CHECKING, cast
20
20
 
21
21
  from streamlit.elements.lib.form_utils import current_form_id
22
+ from streamlit.elements.lib.layout_utils import (
23
+ LayoutConfig,
24
+ Width,
25
+ validate_width,
26
+ )
22
27
  from streamlit.elements.lib.policies import (
23
28
  check_widget_policies,
24
29
  maybe_raise_label_warnings,
@@ -69,6 +74,7 @@ class CheckboxMixin:
69
74
  *, # keyword-only arguments:
70
75
  disabled: bool = False,
71
76
  label_visibility: LabelVisibility = "visible",
77
+ width: Width = "content",
72
78
  ) -> bool:
73
79
  r"""Display a checkbox widget.
74
80
 
@@ -133,6 +139,10 @@ class CheckboxMixin:
133
139
  label, which can help keep the widget aligned with other widgets.
134
140
  If this is ``"collapsed"``, Streamlit displays no label or spacer.
135
141
 
142
+ width : Width
143
+ The width of the widget. Can be an integer for pixel width,
144
+ or one of "content" or "stretch". Defaults to "content".
145
+
136
146
  Returns
137
147
  -------
138
148
  bool
@@ -165,6 +175,7 @@ class CheckboxMixin:
165
175
  label_visibility=label_visibility,
166
176
  type=CheckboxProto.StyleType.DEFAULT,
167
177
  ctx=ctx,
178
+ width=width,
168
179
  )
169
180
 
170
181
  @gather_metrics("toggle")
@@ -180,6 +191,7 @@ class CheckboxMixin:
180
191
  *, # keyword-only arguments:
181
192
  disabled: bool = False,
182
193
  label_visibility: LabelVisibility = "visible",
194
+ width: Width = "content",
183
195
  ) -> bool:
184
196
  r"""Display a toggle widget.
185
197
 
@@ -244,6 +256,10 @@ class CheckboxMixin:
244
256
  label, which can help keep the widget aligned with other widgets.
245
257
  If this is ``"collapsed"``, Streamlit displays no label or spacer.
246
258
 
259
+ width : Width
260
+ The width of the widget. Can be an integer for pixel width,
261
+ or one of "content" or "stretch". Defaults to "content".
262
+
247
263
  Returns
248
264
  -------
249
265
  bool
@@ -276,6 +292,7 @@ class CheckboxMixin:
276
292
  label_visibility=label_visibility,
277
293
  type=CheckboxProto.StyleType.TOGGLE,
278
294
  ctx=ctx,
295
+ width=width,
279
296
  )
280
297
 
281
298
  def _checkbox(
@@ -292,6 +309,7 @@ class CheckboxMixin:
292
309
  label_visibility: LabelVisibility = "visible",
293
310
  type: CheckboxProto.StyleType.ValueType = CheckboxProto.StyleType.DEFAULT,
294
311
  ctx: ScriptRunContext | None = None,
312
+ width: Width = "content",
295
313
  ) -> bool:
296
314
  key = to_key(key)
297
315
 
@@ -311,6 +329,7 @@ class CheckboxMixin:
311
329
  label=label,
312
330
  value=bool(value),
313
331
  help=help,
332
+ width=width,
314
333
  )
315
334
 
316
335
  checkbox_proto = CheckboxProto()
@@ -327,6 +346,9 @@ class CheckboxMixin:
327
346
  if help is not None:
328
347
  checkbox_proto.help = dedent(help)
329
348
 
349
+ validate_width(width, allow_content=True)
350
+ layout_config = LayoutConfig(width=width)
351
+
330
352
  serde = CheckboxSerde(value)
331
353
 
332
354
  checkbox_state = register_widget(
@@ -344,7 +366,7 @@ class CheckboxMixin:
344
366
  checkbox_proto.value = checkbox_state.value
345
367
  checkbox_proto.set_value = True
346
368
 
347
- self.dg._enqueue("checkbox", checkbox_proto)
369
+ self.dg._enqueue("checkbox", checkbox_proto, layout_config=layout_config)
348
370
  return checkbox_state.value
349
371
 
350
372
  @property