streamlit-nightly 1.53.2.dev20260128__py3-none-any.whl → 1.53.2.dev20260202__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 (271) hide show
  1. streamlit/commands/execution_control.py +2 -2
  2. streamlit/config.py +3 -2
  3. streamlit/config_util.py +63 -8
  4. streamlit/delta_generator.py +2 -0
  5. streamlit/elements/deck_gl_json_chart.py +5 -1
  6. streamlit/elements/lib/built_in_chart_utils.py +24 -4
  7. streamlit/elements/lib/color_util.py +20 -1
  8. streamlit/elements/lib/column_types.py +6 -2
  9. streamlit/elements/lib/utils.py +6 -6
  10. streamlit/elements/markdown.py +0 -1
  11. streamlit/elements/metric.py +2 -1
  12. streamlit/elements/vega_charts.py +24 -4
  13. streamlit/elements/widgets/button_group.py +6 -276
  14. streamlit/elements/widgets/feedback.py +322 -0
  15. streamlit/elements/widgets/number_input.py +2 -1
  16. streamlit/elements/widgets/slider.py +2 -1
  17. streamlit/material_icon_names.py +1 -1
  18. streamlit/proto/Alert_pb2.py +2 -3
  19. streamlit/proto/AppPage_pb2.py +2 -3
  20. streamlit/proto/ArrowData_pb2.py +2 -3
  21. streamlit/proto/ArrowNamedDataSet_pb2.py +2 -3
  22. streamlit/proto/ArrowVegaLiteChart_pb2.py +2 -3
  23. streamlit/proto/Arrow_pb2.py +11 -18
  24. streamlit/proto/Arrow_pb2.pyi +1 -42
  25. streamlit/proto/AudioInput_pb2.py +5 -6
  26. streamlit/proto/AudioInput_pb2.pyi +3 -3
  27. streamlit/proto/Audio_pb2.py +3 -4
  28. streamlit/proto/AuthRedirect_pb2.py +2 -3
  29. streamlit/proto/AutoRerun_pb2.py +2 -3
  30. streamlit/proto/BackMsg_pb2.py +2 -3
  31. streamlit/proto/BackMsg_pb2.pyi +2 -10
  32. streamlit/proto/Balloons_pb2.py +2 -3
  33. streamlit/proto/Balloons_pb2.pyi +0 -2
  34. streamlit/proto/BidiComponent_pb2.py +2 -3
  35. streamlit/proto/Block_pb2.py +36 -43
  36. streamlit/proto/Block_pb2.pyi +3 -48
  37. streamlit/proto/ButtonGroup_pb2.py +11 -14
  38. streamlit/proto/ButtonGroup_pb2.pyi +6 -41
  39. streamlit/proto/ButtonLikeIconPosition_pb2.py +2 -3
  40. streamlit/proto/Button_pb2.py +3 -6
  41. streamlit/proto/Button_pb2.pyi +1 -18
  42. streamlit/proto/CameraInput_pb2.py +5 -6
  43. streamlit/proto/CameraInput_pb2.pyi +3 -3
  44. streamlit/proto/ChatInput_pb2.py +5 -8
  45. streamlit/proto/ChatInput_pb2.pyi +1 -17
  46. streamlit/proto/Checkbox_pb2.py +7 -8
  47. streamlit/proto/Checkbox_pb2.pyi +3 -3
  48. streamlit/proto/ClientState_pb2.py +2 -3
  49. streamlit/proto/Code_pb2.py +3 -6
  50. streamlit/proto/Code_pb2.pyi +1 -18
  51. streamlit/proto/ColorPicker_pb2.py +5 -6
  52. streamlit/proto/ColorPicker_pb2.pyi +3 -3
  53. streamlit/proto/Common_pb2.py +7 -8
  54. streamlit/proto/Common_pb2.pyi +2 -10
  55. streamlit/proto/Components_pb2.py +2 -3
  56. streamlit/proto/DateInput_pb2.py +5 -6
  57. streamlit/proto/DateInput_pb2.pyi +3 -3
  58. streamlit/proto/DateTimeInput_pb2.py +5 -6
  59. streamlit/proto/DateTimeInput_pb2.pyi +3 -3
  60. streamlit/proto/DeckGlJsonChart_pb2.py +5 -12
  61. streamlit/proto/DeckGlJsonChart_pb2.pyi +1 -54
  62. streamlit/proto/Delta_pb2.py +4 -6
  63. streamlit/proto/Delta_pb2.pyi +5 -14
  64. streamlit/proto/DocString_pb2.py +2 -3
  65. streamlit/proto/DownloadButton_pb2.py +2 -3
  66. streamlit/proto/Element_pb2.py +5 -8
  67. streamlit/proto/Element_pb2.pyi +9 -29
  68. streamlit/proto/Empty_pb2.py +2 -3
  69. streamlit/proto/Exception_pb2.py +2 -3
  70. streamlit/proto/Favicon_pb2.py +2 -3
  71. streamlit/proto/Feedback_pb2.py +28 -0
  72. streamlit/proto/Feedback_pb2.pyi +93 -0
  73. streamlit/proto/FileUploader_pb2.py +5 -6
  74. streamlit/proto/FileUploader_pb2.pyi +3 -3
  75. streamlit/proto/ForwardMsg_pb2.py +12 -18
  76. streamlit/proto/ForwardMsg_pb2.pyi +4 -48
  77. streamlit/proto/GapSize_pb2.py +2 -3
  78. streamlit/proto/GitInfo_pb2.py +2 -3
  79. streamlit/proto/GraphVizChart_pb2.py +3 -6
  80. streamlit/proto/GraphVizChart_pb2.pyi +1 -18
  81. streamlit/proto/Heading_pb2.py +2 -3
  82. streamlit/proto/HeightConfig_pb2.py +2 -3
  83. streamlit/proto/Html_pb2.py +2 -3
  84. streamlit/proto/IFrame_pb2.py +4 -11
  85. streamlit/proto/IFrame_pb2.pyi +1 -42
  86. streamlit/proto/Image_pb2.py +5 -8
  87. streamlit/proto/Image_pb2.pyi +2 -25
  88. streamlit/proto/Json_pb2.py +2 -3
  89. streamlit/proto/LabelVisibility_pb2.py +28 -0
  90. streamlit/proto/{LabelVisibilityMessage_pb2.pyi → LabelVisibility_pb2.pyi} +14 -14
  91. streamlit/proto/LinkButton_pb2.py +2 -3
  92. streamlit/proto/Logo_pb2.py +2 -3
  93. streamlit/proto/Markdown_pb2.py +5 -6
  94. streamlit/proto/Markdown_pb2.pyi +1 -5
  95. streamlit/proto/Metric_pb2.py +11 -12
  96. streamlit/proto/Metric_pb2.pyi +3 -3
  97. streamlit/proto/MetricsEvent_pb2.py +2 -3
  98. streamlit/proto/MultiSelect_pb2.py +5 -8
  99. streamlit/proto/MultiSelect_pb2.pyi +4 -14
  100. streamlit/proto/Navigation_pb2.py +2 -3
  101. streamlit/proto/NewSession_pb2.py +39 -28
  102. streamlit/proto/NewSession_pb2.pyi +44 -20
  103. streamlit/proto/NumberInput_pb2.py +7 -8
  104. streamlit/proto/NumberInput_pb2.pyi +3 -3
  105. streamlit/proto/PageConfig_pb2.py +2 -3
  106. streamlit/proto/PageInfo_pb2.py +2 -3
  107. streamlit/proto/PageLink_pb2.py +2 -3
  108. streamlit/proto/PageNotFound_pb2.py +2 -3
  109. streamlit/proto/PageProfile_pb2.py +2 -3
  110. streamlit/proto/ParentMessage_pb2.py +2 -3
  111. streamlit/proto/PlotlyChart_pb2.py +5 -10
  112. streamlit/proto/PlotlyChart_pb2.pyi +1 -58
  113. streamlit/proto/Progress_pb2.py +2 -3
  114. streamlit/proto/Radio_pb2.py +5 -8
  115. streamlit/proto/Radio_pb2.pyi +5 -22
  116. streamlit/proto/RootContainer_pb2.py +2 -3
  117. streamlit/proto/Selectbox_pb2.py +5 -8
  118. streamlit/proto/Selectbox_pb2.pyi +5 -25
  119. streamlit/proto/SessionEvent_pb2.py +2 -3
  120. streamlit/proto/SessionStatus_pb2.py +2 -3
  121. streamlit/proto/Skeleton_pb2.py +2 -3
  122. streamlit/proto/Slider_pb2.py +9 -10
  123. streamlit/proto/Slider_pb2.pyi +3 -3
  124. streamlit/proto/Snow_pb2.py +2 -3
  125. streamlit/proto/Snow_pb2.pyi +0 -2
  126. streamlit/proto/Space_pb2.py +2 -3
  127. streamlit/proto/Spinner_pb2.py +2 -3
  128. streamlit/proto/TextAlignmentConfig_pb2.py +2 -3
  129. streamlit/proto/TextArea_pb2.py +5 -8
  130. streamlit/proto/TextArea_pb2.pyi +4 -21
  131. streamlit/proto/TextInput_pb2.py +7 -8
  132. streamlit/proto/TextInput_pb2.pyi +3 -3
  133. streamlit/proto/Text_pb2.py +2 -3
  134. streamlit/proto/TimeInput_pb2.py +5 -6
  135. streamlit/proto/TimeInput_pb2.pyi +3 -3
  136. streamlit/proto/Toast_pb2.py +2 -3
  137. streamlit/proto/Transient_pb2.py +2 -3
  138. streamlit/proto/Video_pb2.py +3 -4
  139. streamlit/proto/WidgetStates_pb2.py +2 -3
  140. streamlit/proto/WidthConfig_pb2.py +2 -3
  141. streamlit/proto/openmetrics_data_model_pb2.py +2 -3
  142. streamlit/runtime/runtime.py +0 -4
  143. streamlit/static/index.html +2 -2
  144. streamlit/static/manifest.json +327 -312
  145. streamlit/static/static/css/{index.BUP6fTcR.css → index.C8MrxwGF.css} +1 -1
  146. streamlit/static/static/js/{ErrorOutline.esm.D71F8ziR.js → ErrorOutline.esm.CLuz0rSD.js} +1 -1
  147. streamlit/static/static/js/{FileDownload.esm.yTkppsJy.js → FileDownload.esm.CO68LcnZ.js} +1 -1
  148. streamlit/static/static/js/{FileHelper.hUOqtbwa.js → FileHelper.DAXgY6Ug.js} +1 -1
  149. streamlit/static/static/js/{FormClearHelper.DN8D_YXO.js → FormClearHelper.zxJ53Nym.js} +1 -1
  150. streamlit/static/static/js/{InputInstructions.DbssY6d4.js → InputInstructions.CCbg8esE.js} +1 -1
  151. streamlit/static/static/js/{Particles.BznyVdfo.js → Particles.BJw0A-zv.js} +1 -1
  152. streamlit/static/static/js/{ProgressBar.C5uBOtcx.js → ProgressBar.BrrHeAEh.js} +2 -2
  153. streamlit/static/static/js/{StreamlitSyntaxHighlighter.Nf1895x-.js → StreamlitSyntaxHighlighter.DvPLy3zk.js} +1 -1
  154. streamlit/static/static/js/{TableChart.esm.DHKzVs3a.js → TableChart.esm.BawvAi5p.js} +1 -1
  155. streamlit/static/static/js/{Toolbar.CQsWYXer.js → Toolbar.CbpscbNb.js} +1 -1
  156. streamlit/static/static/js/{WidgetLabelHelpIconInline.6xCU76OE.js → WidgetLabelHelpIconInline.DA4S2HFP.js} +1 -1
  157. streamlit/static/static/js/{base-input.Cs-E6S71.js → base-input.BKKl2eBF.js} +4 -4
  158. streamlit/static/static/js/{checkbox.OTGupu18.js → checkbox.CB43AKV4.js} +1 -1
  159. streamlit/static/static/js/{createDownloadLinkElement.DnBEQQbK.js → createDownloadLinkElement.jVwF96ey.js} +1 -1
  160. streamlit/static/static/js/data-grid-overlay-editor.CxQizSv7.js +1 -0
  161. streamlit/static/static/js/{downloader.K0GUNeuj.js → downloader.CKlIgsy5.js} +1 -1
  162. streamlit/static/static/js/{embed.o8HvK3mH.js → embed.BhRb_2n8.js} +1 -1
  163. streamlit/static/static/js/{es6.BHy5pqTP.js → es6.DPyfPmWm.js} +2 -2
  164. streamlit/static/static/js/formatNumber.DtfMnnPx.js +1 -0
  165. streamlit/static/static/js/{iconPosition.2YynQUxu.js → iconPosition.DkZAlu_k.js} +1 -1
  166. streamlit/static/static/js/{iframeResizer.contentWindow.D5h3hQuU.js → iframeResizer.contentWindow.l5sQWLra.js} +1 -1
  167. streamlit/static/static/js/{index.BZ-GJVxB.js → index.465nmxtO.js} +2 -2
  168. streamlit/static/static/js/{index.BPdmXoYW.js → index.6J1N4is3.js} +1 -1
  169. streamlit/static/static/js/{index.Bfo1cXfC.js → index.AZ9T4EqJ.js} +1 -1
  170. streamlit/static/static/js/{index.cfuZ69LI.js → index.BAK0CG1Q.js} +1 -1
  171. streamlit/static/static/js/{index.5zqfJ-in.js → index.BLD3tJ2C.js} +1 -1
  172. streamlit/static/static/js/{index.CxWzt6oi.js → index.BUlT_mOL.js} +3 -3
  173. streamlit/static/static/js/{index.DxGXuhh6.js → index.Bc_FQ4Wb.js} +1 -1
  174. streamlit/static/static/js/{index.DJfMW0Gy.js → index.BpCj2-sQ.js} +1 -1
  175. streamlit/static/static/js/{index.DLUSo6de.js → index.BzO83wKm.js} +1 -1
  176. streamlit/static/static/js/index.CAoX2tlo.js +2 -0
  177. streamlit/static/static/js/{index.CwtpGPHA.js → index.CBbYMKZp.js} +1 -1
  178. streamlit/static/static/js/{index.Bgf49D1Z.js → index.CL7it7tU.js} +222 -222
  179. streamlit/static/static/js/{index.DxfYCrPp.js → index.CL_icBXS.js} +1 -1
  180. streamlit/static/static/js/index.C_19KWNs.js +2 -0
  181. streamlit/static/static/js/index.CaU3Uv_L.js +1 -0
  182. streamlit/static/static/js/{index.Dh3PJIlq.js → index.Ccye_uLl.js} +1 -1
  183. streamlit/static/static/js/{index.C65jHNhe.js → index.CiS4giQ2.js} +1 -1
  184. streamlit/static/static/js/{index.hlAfdSqC.js → index.Ck64OQhV.js} +1 -1
  185. streamlit/static/static/js/{index.HmRK3HyC.js → index.CmbqbRMZ.js} +1 -1
  186. streamlit/static/static/js/{index.8MlRyIxN.js → index.CsEZTo6L.js} +2 -2
  187. streamlit/static/static/js/{index.DVRCyxMp.js → index.CsIENsKH.js} +1 -1
  188. streamlit/static/static/js/{index.Bqmx23jK.js → index.Cwg8nWw5.js} +1 -1
  189. streamlit/static/static/js/{index.CZf7Go1Z.js → index.D83azq2w.js} +1 -1
  190. streamlit/static/static/js/{index.CUkhn-vu.js → index.DDr-BLbJ.js} +1 -1
  191. streamlit/static/static/js/{index.iUV9rb8C.js → index.DKoJr0Se.js} +1 -1
  192. streamlit/static/static/js/{index.C2EoeVjP.js → index.DL_ywOgf.js} +2 -2
  193. streamlit/static/static/js/{index.q0ceUXt6.js → index.DR6V0uBJ.js} +1 -1
  194. streamlit/static/static/js/index.DZOGT9vX.js +1 -0
  195. streamlit/static/static/js/{index.BtuskCwg.js → index.Dac9Jib-.js} +1 -1
  196. streamlit/static/static/js/{index.6c-qDsD7.js → index.DiBeetOH.js} +1 -1
  197. streamlit/static/static/js/{index.DlgcEr0f.js → index.DjuMEZ6m.js} +1 -1
  198. streamlit/static/static/js/{index.DBPWUJsj.js → index.DmWUXdjc.js} +54 -54
  199. streamlit/static/static/js/{index.DL_yE83J.js → index.DpSc4e1j.js} +2 -2
  200. streamlit/static/static/js/{index.CgVv04GM.js → index.DzdPUxsx.js} +2 -2
  201. streamlit/static/static/js/{index.BtRWcqZV.js → index.FioS1Y9m.js} +1 -1
  202. streamlit/static/static/js/{index.BfMPq234.js → index.L9pOjBEn.js} +3 -3
  203. streamlit/static/static/js/{index.TjMWsKSH.js → index.LjqoQCm5.js} +3 -3
  204. streamlit/static/static/js/index.OizPL4jg.js +1 -0
  205. streamlit/static/static/js/{index.Cb03y5I8.js → index.Q2t_iBn0.js} +1 -1
  206. streamlit/static/static/js/index.WIMtx3m0.js +2 -0
  207. streamlit/static/static/js/index.Y9wPTZIf.js +1 -0
  208. streamlit/static/static/js/{index.COh5V_89.js → index.YutgmD9x.js} +2 -2
  209. streamlit/static/static/js/{index.BIqcOZ_u.js → index.b_f-McZ5.js} +1 -1
  210. streamlit/static/static/js/{index.BzTVI_BY.js → index.e6Ry6-Ft.js} +1 -1
  211. streamlit/static/static/js/index.hnu9U-5g.js +1 -0
  212. streamlit/static/static/js/{index.C6wyTXhz.js → index.rvbQETlC.js} +1 -1
  213. streamlit/static/static/js/index.w7yKy9fh.js +6 -0
  214. streamlit/static/static/js/{index.C7wst9Tm.js → index.y0h42OXL.js} +1 -1
  215. streamlit/static/static/js/{index.aCorc3Yt.js → index.y5HxPwg9.js} +48 -48
  216. streamlit/static/static/js/{index.CX0KdFyR.js → index.yHLCvUGh.js} +1 -1
  217. streamlit/static/static/js/{input.CXGIJ7D6.js → input.CAtwSQ27.js} +1 -1
  218. streamlit/static/static/js/{main.CCVkbuxC.js → main.VeMVx6VI.js} +1 -1
  219. streamlit/static/static/js/{memory.CNbnYs2A.js → memory.CxS_lIUn.js} +1 -1
  220. streamlit/static/static/js/number-overlay-editor.CoqRgZW_.js +9 -0
  221. streamlit/static/static/js/{pandasStylerUtils.CFSReOTm.js → pandasStylerUtils.DGWgd5sM.js} +1 -1
  222. streamlit/static/static/js/{sandbox.Bld0L3us.js → sandbox.2mSqEau0.js} +1 -1
  223. streamlit/static/static/js/sprintfjs.CsoVVZ9k.js +1 -0
  224. streamlit/static/static/js/{styled-components.BoUHK6TA.js → styled-components.BShfh7J8.js} +1 -1
  225. streamlit/static/static/js/{throttle.ByDFm7WV.js → throttle.Fq1DQK4p.js} +1 -1
  226. streamlit/static/static/js/{timepicker.CN6CUZEL.js → timepicker.DmyY-qtn.js} +1 -1
  227. streamlit/static/static/js/{toConsumableArray.DwMycSpg.js → toConsumableArray.Dvd9AIqV.js} +1 -1
  228. streamlit/static/static/js/uniqueId.CCajdEK8.js +1 -0
  229. streamlit/static/static/js/{useBasicWidgetState.Bg0ZMUt5.js → useBasicWidgetState.COLQ5AFB.js} +1 -1
  230. streamlit/static/static/js/{useIntlLocale.DgBUDcPA.js → useIntlLocale.kdIj0ego.js} +1 -1
  231. streamlit/static/static/js/{useTextInputAutoExpand.DDBezxks.js → useTextInputAutoExpand.DkuG1C1S.js} +1 -1
  232. streamlit/static/static/js/{useUpdateUiValue.Df1h6fXC.js → useUpdateUiValue.-UI_JsjT.js} +1 -1
  233. streamlit/static/static/js/{useWaveformController.DbWw5MEk.js → useWaveformController.C5PTwL6I.js} +1 -1
  234. streamlit/static/static/js/{withCalculatedWidth.YaK0HIIP.js → withCalculatedWidth.BREyS0pJ.js} +1 -1
  235. streamlit/static/static/js/{withFullScreenWrapper.CcWCKoY8.js → withFullScreenWrapper.DYDiEOrW.js} +1 -1
  236. streamlit/static/static/media/MaterialSymbols-Rounded.CnH1S47a.woff2 +0 -0
  237. streamlit/testing/v1/app_test.py +21 -5
  238. streamlit/testing/v1/element_tree.py +81 -4
  239. streamlit/web/server/server.py +0 -1
  240. streamlit/web/server/starlette/starlette_app.py +0 -1
  241. {streamlit_nightly-1.53.2.dev20260128.dist-info → streamlit_nightly-1.53.2.dev20260202.dist-info}/METADATA +10 -25
  242. {streamlit_nightly-1.53.2.dev20260128.dist-info → streamlit_nightly-1.53.2.dev20260202.dist-info}/RECORD +245 -252
  243. streamlit/proto/BokehChart_pb2.py +0 -27
  244. streamlit/proto/BokehChart_pb2.pyi +0 -56
  245. streamlit/proto/DataFrame_pb2.py +0 -56
  246. streamlit/proto/DataFrame_pb2.pyi +0 -430
  247. streamlit/proto/LabelVisibilityMessage_pb2.py +0 -29
  248. streamlit/proto/NamedDataSet_pb2.py +0 -28
  249. streamlit/proto/NamedDataSet_pb2.pyi +0 -67
  250. streamlit/proto/PagesChanged_pb2.py +0 -28
  251. streamlit/proto/PagesChanged_pb2.pyi +0 -55
  252. streamlit/proto/VegaLiteChart_pb2.py +0 -29
  253. streamlit/proto/VegaLiteChart_pb2.pyi +0 -79
  254. streamlit/static/static/js/data-grid-overlay-editor.COiiMi5r.js +0 -1
  255. streamlit/static/static/js/formatNumber.BK7h0k2z.js +0 -1
  256. streamlit/static/static/js/index.BBTKOM0z.js +0 -6
  257. streamlit/static/static/js/index.CSPY26T2.js +0 -1
  258. streamlit/static/static/js/index.CYhhEdja.js +0 -1
  259. streamlit/static/static/js/index.CdsyTabv.js +0 -1
  260. streamlit/static/static/js/index.CjRU8O1O.js +0 -2
  261. streamlit/static/static/js/index.Dc5-tFdw.js +0 -2
  262. streamlit/static/static/js/index.DcngUOyD.js +0 -2
  263. streamlit/static/static/js/index.VwDKazgt.js +0 -1
  264. streamlit/static/static/js/number-overlay-editor.CvI6wkld.js +0 -9
  265. streamlit/static/static/js/sprintf.DpPCfzXw.js +0 -1
  266. streamlit/static/static/js/uniqueId.DcCWa2cf.js +0 -1
  267. streamlit/static/static/media/MaterialSymbols-Rounded.C7IFxh57.woff2 +0 -0
  268. streamlit_nightly-1.53.2.dev20260128.data/scripts/streamlit.cmd +0 -16
  269. {streamlit_nightly-1.53.2.dev20260128.dist-info → streamlit_nightly-1.53.2.dev20260202.dist-info}/WHEEL +0 -0
  270. {streamlit_nightly-1.53.2.dev20260128.dist-info → streamlit_nightly-1.53.2.dev20260202.dist-info}/entry_points.txt +0 -0
  271. {streamlit_nightly-1.53.2.dev20260128.dist-info → streamlit_nightly-1.53.2.dev20260202.dist-info}/top_level.txt +0 -0
@@ -19,7 +19,6 @@ from dataclasses import dataclass, field
19
19
  from typing import (
20
20
  TYPE_CHECKING,
21
21
  Any,
22
- Final,
23
22
  Generic,
24
23
  Literal,
25
24
  TypeAlias,
@@ -28,7 +27,6 @@ from typing import (
28
27
  overload,
29
28
  )
30
29
 
31
- from streamlit import config
32
30
  from streamlit.elements.lib.form_utils import current_form_id
33
31
  from streamlit.elements.lib.layout_utils import (
34
32
  LayoutConfig,
@@ -76,20 +74,6 @@ if TYPE_CHECKING:
76
74
  T = TypeVar("T")
77
75
  V = TypeVar("V")
78
76
 
79
- _THUMB_ICONS: Final = (":material/thumb_up:", ":material/thumb_down:")
80
- _FACES_ICONS: Final = (
81
- ":material/sentiment_sad:",
82
- ":material/sentiment_dissatisfied:",
83
- ":material/sentiment_neutral:",
84
- ":material/sentiment_satisfied:",
85
- ":material/sentiment_very_satisfied:",
86
- )
87
- _NUMBER_STARS: Final = 5
88
- _STAR_ICON: Final = ":material/star:"
89
- # we don't have the filled-material icon library as a dependency. Hence, we have it here
90
- # in base64 format and send it over the wire as an image.
91
- _SELECTED_STAR_ICON: Final = ":material/star_filled:"
92
-
93
77
  SelectionMode: TypeAlias = Literal["single", "multi"]
94
78
 
95
79
 
@@ -185,35 +169,6 @@ class ButtonGroupSerde(Generic[T]):
185
169
  return self.serde.deserialize(ui_value)
186
170
 
187
171
 
188
- def get_mapped_options(
189
- feedback_option: Literal["thumbs", "faces", "stars"],
190
- ) -> tuple[list[ButtonGroupProto.Option], list[int]]:
191
- # options object understandable by the web app
192
- options: list[ButtonGroupProto.Option] = []
193
- # we use the option index in the webapp communication to
194
- # indicate which option is selected
195
- options_indices: list[int] = []
196
-
197
- if feedback_option == "thumbs":
198
- # reversing the index mapping to have thumbs up first (but still with the higher
199
- # index (=sentiment) in the list)
200
- options_indices = list(reversed(range(len(_THUMB_ICONS))))
201
- options = [ButtonGroupProto.Option(content_icon=icon) for icon in _THUMB_ICONS]
202
- elif feedback_option == "faces":
203
- options_indices = list(range(len(_FACES_ICONS)))
204
- options = [ButtonGroupProto.Option(content_icon=icon) for icon in _FACES_ICONS]
205
- elif feedback_option == "stars":
206
- options_indices = list(range(_NUMBER_STARS))
207
- options = [
208
- ButtonGroupProto.Option(
209
- content_icon=_STAR_ICON,
210
- selected_content_icon=_SELECTED_STAR_ICON,
211
- )
212
- ] * _NUMBER_STARS
213
-
214
- return options, options_indices
215
-
216
-
217
172
  def _build_proto(
218
173
  widget_id: str,
219
174
  formatted_options: Sequence[ButtonGroupProto.Option],
@@ -221,10 +176,7 @@ def _build_proto(
221
176
  disabled: bool,
222
177
  current_form_id: str,
223
178
  click_mode: ButtonGroupProto.ClickMode.ValueType,
224
- selection_visualization: ButtonGroupProto.SelectionVisualization.ValueType = (
225
- ButtonGroupProto.SelectionVisualization.ONLY_SELECTED
226
- ),
227
- style: Literal["borderless", "pills", "segmented_control"] = "pills",
179
+ style: Literal["pills", "segmented_control"] = "pills",
228
180
  label: str | None = None,
229
181
  label_visibility: LabelVisibility = "visible",
230
182
  help: str | None = None,
@@ -249,7 +201,6 @@ def _build_proto(
249
201
 
250
202
  for formatted_option in formatted_options:
251
203
  proto.options.append(formatted_option)
252
- proto.selection_visualization = selection_visualization
253
204
  return proto
254
205
 
255
206
 
@@ -263,219 +214,6 @@ def _maybe_raise_selection_mode_warning(selection_mode: SelectionMode) -> None:
263
214
 
264
215
 
265
216
  class ButtonGroupMixin:
266
- # These overloads are not documented in the docstring, at least not at this time, on
267
- # the theory that most people won't know what it means. And the Literals here are a
268
- # subclass of int anyway. Usually, we would make a type alias for
269
- # Literal["thumbs", "faces", "stars"]; but, in this case, we don't use it in too
270
- # many other places, and it's a more helpful autocomplete if we just enumerate the
271
- # values explicitly, so a decision has been made to keep it as not an alias.
272
- @overload
273
- def feedback(
274
- self,
275
- options: Literal["thumbs"] = ...,
276
- *,
277
- key: Key | None = None,
278
- default: int | None = None,
279
- disabled: bool = False,
280
- on_change: WidgetCallback | None = None,
281
- args: WidgetArgs | None = None,
282
- kwargs: WidgetKwargs | None = None,
283
- width: Width = "content",
284
- ) -> Literal[0, 1] | None: ...
285
- @overload
286
- def feedback(
287
- self,
288
- options: Literal["faces", "stars"] = ...,
289
- *,
290
- key: Key | None = None,
291
- default: int | None = None,
292
- disabled: bool = False,
293
- on_change: WidgetCallback | None = None,
294
- args: WidgetArgs | None = None,
295
- kwargs: WidgetKwargs | None = None,
296
- width: Width = "content",
297
- ) -> Literal[0, 1, 2, 3, 4] | None: ...
298
- @gather_metrics("feedback")
299
- def feedback(
300
- self,
301
- options: Literal["thumbs", "faces", "stars"] = "thumbs",
302
- *,
303
- key: Key | None = None,
304
- default: int | None = None,
305
- disabled: bool = False,
306
- on_change: WidgetCallback | None = None,
307
- args: WidgetArgs | None = None,
308
- kwargs: WidgetKwargs | None = None,
309
- width: Width = "content",
310
- ) -> int | None:
311
- """Display a feedback widget.
312
-
313
- A feedback widget is an icon-based button group available in three
314
- styles, as described in ``options``. It is commonly used in chat and AI
315
- apps to allow users to rate responses.
316
-
317
- Parameters
318
- ----------
319
- options : "thumbs", "faces", or "stars"
320
- The feedback options displayed to the user. ``options`` can be one
321
- of the following:
322
-
323
- - ``"thumbs"`` (default): Streamlit displays a thumb-up and
324
- thumb-down button group.
325
- - ``"faces"``: Streamlit displays a row of five buttons with
326
- facial expressions depicting increasing satisfaction from left to
327
- right.
328
- - ``"stars"``: Streamlit displays a row of star icons, allowing the
329
- user to select a rating from one to five stars.
330
-
331
- key : str or int
332
- An optional string or integer to use as the unique key for the widget.
333
- If this is omitted, a key will be generated for the widget
334
- based on its content. No two widgets may have the same key.
335
-
336
- default : int or None
337
- Default feedback value. This must be consistent with the feedback
338
- type in ``options``:
339
-
340
- - 0 or 1 if ``options="thumbs"``.
341
- - Between 0 and 4, inclusive, if ``options="faces"`` or
342
- ``options="stars"``.
343
-
344
- disabled : bool
345
- An optional boolean that disables the feedback widget if set
346
- to ``True``. The default is ``False``.
347
-
348
- on_change : callable
349
- An optional callback invoked when this feedback widget's value
350
- changes.
351
-
352
- args : list or tuple
353
- An optional list or tuple of args to pass to the callback.
354
-
355
- kwargs : dict
356
- An optional dict of kwargs to pass to the callback.
357
-
358
- width : "content", "stretch", or int
359
- The width of the feedback widget. This can be one of the following:
360
-
361
- - ``"content"`` (default): The width of the widget matches the
362
- width of its content, but doesn't exceed the width of the parent
363
- container.
364
- - ``"stretch"``: The width of the widget matches the width of the
365
- parent container.
366
- - An integer specifying the width in pixels: The widget has a
367
- fixed width. If the specified width is greater than the width of
368
- the parent container, the width of the widget matches the width
369
- of the parent container.
370
-
371
- Returns
372
- -------
373
- int or None
374
- An integer indicating the user's selection, where ``0`` is the
375
- lowest feedback. Higher values indicate more positive feedback.
376
- If no option was selected, the widget returns ``None``.
377
-
378
- - For ``options="thumbs"``, a return value of ``0`` indicates
379
- thumbs-down, and ``1`` indicates thumbs-up.
380
- - For ``options="faces"`` and ``options="stars"``, return values
381
- range from ``0`` (least satisfied) to ``4`` (most satisfied).
382
-
383
- Examples
384
- --------
385
- Display a feedback widget with stars, and show the selected sentiment:
386
-
387
- >>> import streamlit as st
388
- >>>
389
- >>> sentiment_mapping = ["one", "two", "three", "four", "five"]
390
- >>> selected = st.feedback("stars")
391
- >>> if selected is not None:
392
- >>> st.markdown(f"You selected {sentiment_mapping[selected]} star(s).")
393
-
394
- .. output::
395
- https://doc-feedback-stars.streamlit.app/
396
- height: 200px
397
-
398
- Display a feedback widget with thumbs, and show the selected sentiment:
399
-
400
- >>> import streamlit as st
401
- >>>
402
- >>> sentiment_mapping = [":material/thumb_down:", ":material/thumb_up:"]
403
- >>> selected = st.feedback("thumbs")
404
- >>> if selected is not None:
405
- >>> st.markdown(f"You selected: {sentiment_mapping[selected]}")
406
-
407
- .. output::
408
- https://doc-feedback-thumbs.streamlit.app/
409
- height: 200px
410
-
411
- """
412
-
413
- if options not in {"thumbs", "faces", "stars"}:
414
- raise StreamlitAPIException(
415
- "The options argument to st.feedback must be one of "
416
- "['thumbs', 'faces', 'stars']. "
417
- f"The argument passed was '{options}'."
418
- )
419
- transformed_options, options_indices = get_mapped_options(options)
420
-
421
- if default is not None and (default < 0 or default >= len(transformed_options)):
422
- raise StreamlitAPIException(
423
- f"The default value in '{options}' must be a number between 0 and {len(transformed_options) - 1}."
424
- f" The passed default value is {default}"
425
- )
426
-
427
- # Convert small pixel widths to "content" to prevent icon wrapping.
428
- # Calculate threshold based on theme.baseFontSize to be responsive to
429
- # custom themes. The calculation is based on icon buttons sized in rem:
430
- # - Button size: ~1.5rem (icon 1.25rem + padding 0.125rem x 2)
431
- # - Gap: 0.125rem between buttons
432
- # - thumbs: 2 buttons + 1 gap = 3.125rem
433
- # - faces/stars: 5 buttons + 4 gaps = 8rem
434
- base_font_size = config.get_option("theme.baseFontSize") or 16
435
- button_size_rem = 1.5
436
- gap_size_rem = 0.125
437
-
438
- if options == "thumbs":
439
- # 2 buttons + 1 gap
440
- min_width_rem = 2 * button_size_rem + gap_size_rem
441
- else:
442
- # 5 buttons + 4 gaps (faces or stars)
443
- min_width_rem = 5 * button_size_rem + 4 * gap_size_rem
444
-
445
- # Convert rem to pixels based on base font size, add 10% buffer
446
- min_width_threshold = int(min_width_rem * base_font_size * 1.1)
447
-
448
- if isinstance(width, int) and width < min_width_threshold:
449
- width = "content"
450
-
451
- _default: list[int] | None = (
452
- [options_indices[default]] if default is not None else None
453
- )
454
- serde = _SingleSelectSerde[int](options_indices, default_value=_default)
455
-
456
- selection_visualization = ButtonGroupProto.SelectionVisualization.ONLY_SELECTED
457
- if options == "stars":
458
- selection_visualization = (
459
- ButtonGroupProto.SelectionVisualization.ALL_UP_TO_SELECTED
460
- )
461
-
462
- sentiment = self._button_group(
463
- transformed_options,
464
- default=_default,
465
- key=key,
466
- selection_mode="single",
467
- disabled=disabled,
468
- deserializer=serde.deserialize,
469
- serializer=serde.serialize,
470
- on_change=on_change,
471
- args=args,
472
- kwargs=kwargs,
473
- selection_visualization=selection_visualization,
474
- style="borderless",
475
- width=width,
476
- )
477
- return sentiment.value
478
-
479
217
  @overload
480
218
  def pills(
481
219
  self,
@@ -1023,18 +761,13 @@ class ButtonGroupMixin:
1023
761
  default: list[int] | None = None,
1024
762
  selection_mode: SelectionMode = "single",
1025
763
  disabled: bool = False,
1026
- style: Literal[
1027
- "borderless", "pills", "segmented_control"
1028
- ] = "segmented_control",
764
+ style: Literal["pills", "segmented_control"] = "segmented_control",
1029
765
  format_func: Callable[[V], ButtonGroupProto.Option] | None = None,
1030
766
  deserializer: WidgetDeserializer[T],
1031
767
  serializer: WidgetSerializer[T],
1032
768
  on_change: WidgetCallback | None = None,
1033
769
  args: WidgetArgs | None = None,
1034
770
  kwargs: WidgetKwargs | None = None,
1035
- selection_visualization: ButtonGroupProto.SelectionVisualization.ValueType = (
1036
- ButtonGroupProto.SelectionVisualization.ONLY_SELECTED
1037
- ),
1038
771
  label: str | None = None,
1039
772
  label_visibility: LabelVisibility = "visible",
1040
773
  help: str | None = None,
@@ -1062,9 +795,9 @@ class ButtonGroupMixin:
1062
795
  "`selection_mode='single'`."
1063
796
  )
1064
797
 
1065
- if style not in {"borderless", "pills", "segmented_control"}:
798
+ if style not in {"pills", "segmented_control"}:
1066
799
  raise StreamlitAPIException(
1067
- "The style argument must be one of ['borderless', 'pills', 'segmented_control']. "
800
+ "The style argument must be one of ['pills', 'segmented_control']. "
1068
801
  f"The argument passed was '{style}'."
1069
802
  )
1070
803
 
@@ -1091,11 +824,9 @@ class ButtonGroupMixin:
1091
824
  )
1092
825
 
1093
826
  element_id = compute_and_register_element_id(
1094
- # The borderless style is used by st.feedback, but users expect to see
1095
- # "feedback" in errors
1096
- "feedback" if style == "borderless" else style,
827
+ style,
1097
828
  user_key=key,
1098
- # Treat the provided key as the main identity for segmented_control, pills and feedback,
829
+ # Treat the provided key as the main identity for segmented_control and pills,
1099
830
  # and only include kwargs that can invalidate the current selection.
1100
831
  # We whitelist the formatted options and the click mode (single vs multi).
1101
832
  key_as_main_identity={"options", "click_mode"},
@@ -1116,7 +847,6 @@ class ButtonGroupMixin:
1116
847
  disabled,
1117
848
  form_id,
1118
849
  click_mode=parsed_selection_mode,
1119
- selection_visualization=selection_visualization,
1120
850
  style=style,
1121
851
  label=label,
1122
852
  label_visibility=label_visibility,
@@ -0,0 +1,322 @@
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2026)
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 (
18
+ TYPE_CHECKING,
19
+ Final,
20
+ Literal,
21
+ cast,
22
+ overload,
23
+ )
24
+
25
+ from streamlit.elements.lib.form_utils import current_form_id
26
+ from streamlit.elements.lib.layout_utils import (
27
+ LayoutConfig,
28
+ Width,
29
+ validate_width,
30
+ )
31
+ from streamlit.elements.lib.policies import check_widget_policies
32
+ from streamlit.elements.lib.utils import (
33
+ Key,
34
+ compute_and_register_element_id,
35
+ save_for_app_testing,
36
+ to_key,
37
+ )
38
+ from streamlit.errors import StreamlitAPIException
39
+ from streamlit.proto.Feedback_pb2 import Feedback as FeedbackProto
40
+ from streamlit.runtime.metrics_util import gather_metrics
41
+ from streamlit.runtime.scriptrunner_utils.script_run_context import get_script_run_ctx
42
+ from streamlit.runtime.state import register_widget
43
+
44
+ if TYPE_CHECKING:
45
+ from streamlit.delta_generator import DeltaGenerator
46
+ from streamlit.runtime.state import (
47
+ WidgetArgs,
48
+ WidgetCallback,
49
+ WidgetKwargs,
50
+ )
51
+
52
+
53
+ # Number of options for each feedback type
54
+ _NUM_THUMBS_OPTIONS: Final = 2
55
+ _NUM_FACES_OPTIONS: Final = 5
56
+ _NUM_STARS_OPTIONS: Final = 5
57
+
58
+
59
+ def _get_num_options(feedback_type: Literal["thumbs", "faces", "stars"]) -> int:
60
+ """Get the number of options for the given feedback type."""
61
+ if feedback_type == "thumbs":
62
+ return _NUM_THUMBS_OPTIONS
63
+ if feedback_type == "faces":
64
+ return _NUM_FACES_OPTIONS
65
+ return _NUM_STARS_OPTIONS
66
+
67
+
68
+ def _feedback_type_to_proto(
69
+ feedback_type: Literal["thumbs", "faces", "stars"],
70
+ ) -> FeedbackProto.FeedbackType.ValueType:
71
+ """Convert a feedback type string to the proto enum value."""
72
+ if feedback_type == "thumbs":
73
+ return FeedbackProto.FeedbackType.THUMBS
74
+ if feedback_type == "faces":
75
+ return FeedbackProto.FeedbackType.FACES
76
+ return FeedbackProto.FeedbackType.STARS
77
+
78
+
79
+ class FeedbackSerde:
80
+ """Serializer/deserializer for feedback widget values.
81
+
82
+ Uses string as the wire format to distinguish three states:
83
+ - None (field not set): No UI interaction yet -> use default_value
84
+ - "" (empty string): User explicitly cleared -> return None
85
+ - "2" (string with value): User selected -> return int value
86
+
87
+ This allows clearing to work correctly even when a default is set.
88
+ The session state and return values are always int | None.
89
+ """
90
+
91
+ def __init__(self, default_value: int | None = None):
92
+ self.default_value = default_value
93
+
94
+ def serialize(self, value: int | None) -> str:
95
+ """Serialize int value to string for wire format."""
96
+ return "" if value is None else str(value)
97
+
98
+ def deserialize(self, ui_value: str | None) -> int | None:
99
+ """Deserialize string wire format back to int value."""
100
+ if ui_value is None:
101
+ return self.default_value # No UI interaction yet
102
+ if ui_value == "":
103
+ return None # User explicitly cleared
104
+ return int(ui_value) # User selected a value
105
+
106
+
107
+ class FeedbackMixin:
108
+ @overload
109
+ def feedback(
110
+ self,
111
+ options: Literal["thumbs"] = ...,
112
+ *,
113
+ key: Key | None = None,
114
+ default: int | None = None,
115
+ disabled: bool = False,
116
+ on_change: WidgetCallback | None = None,
117
+ args: WidgetArgs | None = None,
118
+ kwargs: WidgetKwargs | None = None,
119
+ width: Width = "content",
120
+ ) -> Literal[0, 1] | None: ...
121
+
122
+ @overload
123
+ def feedback(
124
+ self,
125
+ options: Literal["faces", "stars"] = ...,
126
+ *,
127
+ key: Key | None = None,
128
+ default: int | None = None,
129
+ disabled: bool = False,
130
+ on_change: WidgetCallback | None = None,
131
+ args: WidgetArgs | None = None,
132
+ kwargs: WidgetKwargs | None = None,
133
+ width: Width = "content",
134
+ ) -> Literal[0, 1, 2, 3, 4] | None: ...
135
+
136
+ @gather_metrics("feedback")
137
+ def feedback(
138
+ self,
139
+ options: Literal["thumbs", "faces", "stars"] = "thumbs",
140
+ *,
141
+ key: Key | None = None,
142
+ default: int | None = None,
143
+ disabled: bool = False,
144
+ on_change: WidgetCallback | None = None,
145
+ args: WidgetArgs | None = None,
146
+ kwargs: WidgetKwargs | None = None,
147
+ width: Width = "content",
148
+ ) -> int | None:
149
+ """Display a feedback widget.
150
+
151
+ A feedback widget is an icon-based button group available in three
152
+ styles, as described in ``options``. It is commonly used in chat and AI
153
+ apps to allow users to rate responses.
154
+
155
+ Parameters
156
+ ----------
157
+ options : "thumbs", "faces", or "stars"
158
+ The feedback options displayed to the user. ``options`` can be one
159
+ of the following:
160
+
161
+ - ``"thumbs"`` (default): Streamlit displays a thumb-up and
162
+ thumb-down button group.
163
+ - ``"faces"``: Streamlit displays a row of five buttons with
164
+ facial expressions depicting increasing satisfaction from left to
165
+ right.
166
+ - ``"stars"``: Streamlit displays a row of star icons, allowing the
167
+ user to select a rating from one to five stars.
168
+
169
+ key : str or int
170
+ An optional string or integer to use as the unique key for the widget.
171
+ If this is omitted, a key will be generated for the widget
172
+ based on its content. No two widgets may have the same key.
173
+
174
+ default : int or None
175
+ Default feedback value. This must be consistent with the feedback
176
+ type in ``options``:
177
+
178
+ - 0 or 1 if ``options="thumbs"``.
179
+ - Between 0 and 4, inclusive, if ``options="faces"`` or
180
+ ``options="stars"``.
181
+
182
+ disabled : bool
183
+ An optional boolean that disables the feedback widget if set
184
+ to ``True``. The default is ``False``.
185
+
186
+ on_change : callable
187
+ An optional callback invoked when this feedback widget's value
188
+ changes.
189
+
190
+ args : list or tuple
191
+ An optional list or tuple of args to pass to the callback.
192
+
193
+ kwargs : dict
194
+ An optional dict of kwargs to pass to the callback.
195
+
196
+ width : "content", "stretch", or int
197
+ The width of the feedback widget. This can be one of the following:
198
+
199
+ - ``"content"`` (default): The width of the widget matches the
200
+ width of its content, but doesn't exceed the width of the parent
201
+ container.
202
+ - ``"stretch"``: The width of the widget matches the width of the
203
+ parent container.
204
+ - An integer specifying the width in pixels: The widget has a
205
+ fixed width. If the specified width is greater than the width of
206
+ the parent container, the width of the widget matches the width
207
+ of the parent container.
208
+
209
+ Returns
210
+ -------
211
+ int or None
212
+ An integer indicating the user's selection, where ``0`` is the
213
+ lowest feedback. Higher values indicate more positive feedback.
214
+ If no option was selected, the widget returns ``None``.
215
+
216
+ - For ``options="thumbs"``, a return value of ``0`` indicates
217
+ thumbs-down, and ``1`` indicates thumbs-up.
218
+ - For ``options="faces"`` and ``options="stars"``, return values
219
+ range from ``0`` (least satisfied) to ``4`` (most satisfied).
220
+
221
+ Examples
222
+ --------
223
+ Display a feedback widget with stars, and show the selected sentiment:
224
+
225
+ >>> import streamlit as st
226
+ >>>
227
+ >>> sentiment_mapping = ["one", "two", "three", "four", "five"]
228
+ >>> selected = st.feedback("stars")
229
+ >>> if selected is not None:
230
+ >>> st.markdown(f"You selected {sentiment_mapping[selected]} star(s).")
231
+
232
+ .. output::
233
+ https://doc-feedback-stars.streamlit.app/
234
+ height: 200px
235
+
236
+ Display a feedback widget with thumbs, and show the selected sentiment:
237
+
238
+ >>> import streamlit as st
239
+ >>>
240
+ >>> sentiment_mapping = [":material/thumb_down:", ":material/thumb_up:"]
241
+ >>> selected = st.feedback("thumbs")
242
+ >>> if selected is not None:
243
+ >>> st.markdown(f"You selected: {sentiment_mapping[selected]}")
244
+
245
+ .. output::
246
+ https://doc-feedback-thumbs.streamlit.app/
247
+ height: 200px
248
+
249
+ """
250
+ if options not in {"thumbs", "faces", "stars"}:
251
+ raise StreamlitAPIException(
252
+ "The options argument to st.feedback must be one of "
253
+ "['thumbs', 'faces', 'stars']. "
254
+ f"The argument passed was '{options}'."
255
+ )
256
+
257
+ num_options = _get_num_options(options)
258
+
259
+ if default is not None and (default < 0 or default >= num_options):
260
+ raise StreamlitAPIException(
261
+ f"The default value in '{options}' must be a number between 0 and {num_options - 1}."
262
+ f" The passed default value is {default}"
263
+ )
264
+
265
+ key = to_key(key)
266
+ validate_width(width, allow_content=True)
267
+ layout_config = LayoutConfig(width=width)
268
+
269
+ check_widget_policies(self.dg, key, on_change, default_value=default)
270
+
271
+ ctx = get_script_run_ctx()
272
+ form_id = current_form_id(self.dg)
273
+
274
+ element_id = compute_and_register_element_id(
275
+ "feedback",
276
+ user_key=key,
277
+ key_as_main_identity={"options"},
278
+ dg=self.dg,
279
+ options=options,
280
+ default=default,
281
+ width=width,
282
+ )
283
+
284
+ # Build the proto
285
+ proto = FeedbackProto()
286
+ proto.id = element_id
287
+ proto.type = _feedback_type_to_proto(options)
288
+ proto.disabled = disabled
289
+ proto.form_id = form_id
290
+
291
+ if default is not None:
292
+ proto.default = default
293
+
294
+ serde = FeedbackSerde(default_value=default)
295
+
296
+ widget_state = register_widget(
297
+ proto.id,
298
+ on_change_handler=on_change,
299
+ args=args,
300
+ kwargs=kwargs,
301
+ deserializer=serde.deserialize,
302
+ serializer=serde.serialize,
303
+ ctx=ctx,
304
+ value_type="string_value",
305
+ )
306
+
307
+ if widget_state.value_changed:
308
+ if widget_state.value is not None:
309
+ proto.value = widget_state.value
310
+ proto.set_value = True
311
+
312
+ if ctx:
313
+ save_for_app_testing(ctx, element_id, None)
314
+
315
+ self.dg._enqueue("feedback", proto, layout_config=layout_config)
316
+
317
+ return widget_state.value
318
+
319
+ @property
320
+ def dg(self) -> DeltaGenerator:
321
+ """Get our DeltaGenerator."""
322
+ return cast("DeltaGenerator", self)
@@ -296,7 +296,8 @@ class NumberInputMixin:
296
296
  <https://github.com/alexei/sprintf.js?tab=readme-ov-file#format-specification>`_.
297
297
 
298
298
  For example, ``format="%0.1f"`` adjusts the displayed decimal
299
- precision to only show one digit after the decimal.
299
+ precision to only show one digit after the decimal. Use ``,`` for
300
+ thousand separators (e.g. ``"%,d"`` yields ``"1,234"``).
300
301
 
301
302
  key : str or int
302
303
  An optional string or integer to use as the unique key for the widget.