solara 1.29.1__py2.py3-none-any.whl → 1.30.1__py2.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 (286) hide show
  1. solara/__init__.py +5 -5
  2. solara/__main__.py +6 -2
  3. solara/autorouting.py +88 -56
  4. solara/cache.py +2 -2
  5. solara/checks.html +1 -1
  6. solara/components/__init__.py +1 -1
  7. solara/components/applayout.py +5 -5
  8. solara/components/button.py +4 -4
  9. solara/components/card.py +1 -2
  10. solara/components/component_vue.py +9 -8
  11. solara/components/cross_filter.py +6 -7
  12. solara/components/datatable.py +2 -3
  13. solara/components/figure_altair.py +1 -1
  14. solara/components/file_download.py +2 -2
  15. solara/components/file_drop.py +76 -33
  16. solara/components/file_drop.vue +24 -10
  17. solara/components/head.py +1 -2
  18. solara/components/head_tag.py +2 -3
  19. solara/components/image.py +1 -1
  20. solara/components/link.py +3 -4
  21. solara/components/meta.py +1 -1
  22. solara/components/misc.py +5 -9
  23. solara/datatypes.py +2 -2
  24. solara/hooks/use_reactive.py +2 -2
  25. solara/lab/components/confirmation_dialog.py +1 -1
  26. solara/lab/components/tabs.py +6 -6
  27. solara/reactive.py +1 -1
  28. solara/routing.py +9 -8
  29. solara/scope/__init__.py +3 -2
  30. solara/server/app.py +44 -1
  31. solara/server/assets/style.css +6 -0
  32. solara/server/esm.py +28 -4
  33. solara/server/kernel_context.py +78 -7
  34. solara/server/patch.py +3 -0
  35. solara/server/reload.py +2 -2
  36. solara/server/server.py +3 -3
  37. solara/server/settings.py +1 -0
  38. solara/server/starlette.py +66 -33
  39. solara/server/static/solara_bootstrap.py +1 -1
  40. solara/server/templates/solara.html.j2 +62 -42
  41. solara/tasks.py +1 -6
  42. solara/util.py +23 -1
  43. solara/website/assets/custom.css +56 -0
  44. solara/website/components/__init__.py +1 -0
  45. solara/website/components/algolia_api.vue +157 -0
  46. solara/website/components/docs.py +118 -0
  47. solara/website/components/header.py +20 -10
  48. solara/website/components/hero.py +1 -1
  49. solara/website/components/markdown.py +30 -0
  50. solara/website/pages/__init__.py +234 -20
  51. solara/website/pages/apps/jupyter-dashboard-1.py +1 -1
  52. solara/website/pages/apps/multipage/__init__.py +1 -1
  53. solara/website/pages/apps/multipage/page2.py +1 -1
  54. solara/website/pages/apps/scatter.py +21 -7
  55. solara/website/pages/changelog/__init__.py +8 -0
  56. solara/website/pages/{docs/content/95-changelog.md → changelog/changelog.md} +43 -2
  57. solara/website/pages/contact/__init__.py +8 -0
  58. solara/website/pages/documentation/__init__.py +184 -0
  59. solara/website/pages/{docs → documentation/advanced}/__init__.py +2 -2
  60. solara/website/pages/documentation/advanced/content/00-overview.md +1 -0
  61. solara/website/pages/{docs → documentation/advanced}/content/10-howto/00-overview.md +5 -0
  62. solara/website/pages/{docs/content/10-howto/20-multipage.md → documentation/advanced/content/10-howto/10-multipage.md} +9 -5
  63. solara/website/pages/{docs/content/10-howto/30-layout.md → documentation/advanced/content/10-howto/20-layout.md} +26 -21
  64. solara/website/pages/{docs/content/10-howto/50-testing.md → documentation/advanced/content/10-howto/30-testing.md} +5 -0
  65. solara/website/pages/{docs/content/10-howto/51-debugging.md → documentation/advanced/content/10-howto/31-debugging.md} +4 -1
  66. solara/website/pages/{docs/content/10-howto/80-embed.md → documentation/advanced/content/10-howto/40-embed.md} +7 -1
  67. solara/website/pages/{docs/content/10-howto/ipywidget_libraries.md → documentation/advanced/content/10-howto/50-ipywidget_libraries.md} +4 -0
  68. solara/website/pages/documentation/advanced/content/20-understanding/00-introduction.md +10 -0
  69. solara/website/pages/{docs → documentation/advanced}/content/20-understanding/05-ipywidgets.md +5 -0
  70. solara/website/pages/{docs → documentation/advanced}/content/20-understanding/06-ipyvuetify.md +5 -1
  71. solara/website/pages/{docs → documentation/advanced}/content/20-understanding/10-reacton.md +4 -0
  72. solara/website/pages/{docs → documentation/advanced}/content/20-understanding/12-reacton-basics.md +5 -1
  73. solara/website/pages/{docs → documentation/advanced}/content/20-understanding/15-anatomy.md +6 -2
  74. solara/website/pages/documentation/advanced/content/20-understanding/17-rules-of-hooks.md +7 -0
  75. solara/website/pages/{docs → documentation/advanced}/content/20-understanding/18-containers.md +9 -3
  76. solara/website/pages/{docs → documentation/advanced}/content/20-understanding/20-solara.md +5 -0
  77. solara/website/pages/{docs → documentation/advanced}/content/20-understanding/40-routing.md +13 -9
  78. solara/website/pages/{docs → documentation/advanced}/content/20-understanding/50-solara-server.md +6 -1
  79. solara/website/pages/{docs → documentation/advanced}/content/20-understanding/60-voila.md +5 -0
  80. solara/website/pages/{docs/content/50-enterprise → documentation/advanced/content/30-enterprise}/10-oauth.md +7 -3
  81. solara/website/pages/{docs/content/10-howto → documentation/advanced/content/40-development}/01-contribute.md +9 -5
  82. solara/website/pages/{docs/content/90-development → documentation/advanced/content/40-development}/10-setup.md +6 -2
  83. solara/website/pages/documentation/api/__init__.py +19 -0
  84. solara/website/pages/documentation/api/cross_filter/__init__.py +9 -0
  85. solara/website/pages/documentation/api/hooks/__init__.py +9 -0
  86. solara/website/pages/{api → documentation/api/hooks}/use_effect.md +3 -3
  87. solara/website/pages/{api → documentation/api/hooks}/use_effect.py +2 -1
  88. solara/website/pages/{api → documentation/api/hooks}/use_memo.md +2 -2
  89. solara/website/pages/{api → documentation/api/hooks}/use_memo.py +2 -1
  90. solara/website/pages/{api → documentation/api/hooks}/use_reactive.py +1 -2
  91. solara/website/pages/{api → documentation/api/hooks}/use_state.py +1 -2
  92. solara/website/pages/documentation/api/routing/__init__.py +9 -0
  93. solara/website/pages/{api → documentation/api/routing}/generate_routes.py +1 -2
  94. solara/website/pages/{api → documentation/api/routing}/generate_routes_directory.py +1 -2
  95. solara/website/pages/{api → documentation/api/routing}/use_route.py +2 -2
  96. solara/website/pages/{api → documentation/api/routing}/use_router.py +1 -2
  97. solara/website/pages/documentation/api/utilities/__init__.py +9 -0
  98. solara/website/pages/{api → documentation/api/utilities}/component_vue.py +1 -2
  99. solara/website/pages/{api → documentation/api/utilities}/computed.py +2 -2
  100. solara/website/pages/{api → documentation/api/utilities}/display.py +1 -2
  101. solara/website/pages/{api → documentation/api/utilities}/get_kernel_id.py +2 -2
  102. solara/website/pages/{api → documentation/api/utilities}/get_session_id.py +2 -2
  103. solara/website/pages/{api → documentation/api/utilities}/on_kernel_start.py +9 -3
  104. solara/website/pages/{api → documentation/api/utilities}/reactive.py +1 -2
  105. solara/website/pages/{api → documentation/api/utilities}/widget.py +2 -2
  106. solara/website/pages/documentation/components/__init__.py +12 -0
  107. solara/website/pages/documentation/components/advanced/__init__.py +9 -0
  108. solara/website/pages/documentation/components/data/__init__.py +9 -0
  109. solara/website/pages/documentation/components/enterprise/__init__.py +9 -0
  110. solara/website/pages/{api → documentation/components/enterprise}/avatar.py +1 -2
  111. solara/website/pages/{api → documentation/components/enterprise}/avatar_menu.py +1 -2
  112. solara/website/pages/documentation/components/input/__init__.py +9 -0
  113. solara/website/pages/{api → documentation/components/input}/checkbox.py +1 -2
  114. solara/website/pages/documentation/components/input/file_drop.py +75 -0
  115. solara/website/pages/{api → documentation/components/input}/input.py +1 -2
  116. solara/website/pages/{api → documentation/components/input}/select.py +1 -2
  117. solara/website/pages/{api → documentation/components/input}/slider.py +1 -2
  118. solara/website/pages/{api → documentation/components/input}/switch.py +1 -2
  119. solara/website/pages/{api → documentation/components/input}/togglebuttons.py +1 -2
  120. solara/website/pages/documentation/components/lab/__init__.py +9 -0
  121. solara/website/pages/{api → documentation/components/lab}/chat.py +2 -3
  122. solara/website/pages/{api → documentation/components/lab}/cookies_headers.py +1 -1
  123. solara/website/pages/{api → documentation/components/lab}/input_date.py +1 -2
  124. solara/website/pages/{api → documentation/components/lab}/menu.py +1 -2
  125. solara/website/pages/{api → documentation/components/lab}/task.py +1 -2
  126. solara/website/pages/{api → documentation/components/lab}/theming.py +1 -2
  127. solara/website/pages/{api → documentation/components/lab}/use_task.py +1 -2
  128. solara/website/pages/documentation/components/layout/__init__.py +9 -0
  129. solara/website/pages/{api → documentation/components/layout}/app_bar.py +1 -2
  130. solara/website/pages/{api → documentation/components/layout}/app_bar_title.py +1 -2
  131. solara/website/pages/{api → documentation/components/layout}/card.py +1 -2
  132. solara/website/pages/{api → documentation/components/layout}/card_actions.py +1 -2
  133. solara/website/pages/{api → documentation/components/layout}/griddraggable.py +1 -1
  134. solara/website/pages/{api → documentation/components/layout}/gridfixed.py +1 -1
  135. solara/website/pages/{api → documentation/components/layout}/hbox.py +1 -1
  136. solara/website/pages/{api → documentation/components/layout}/vbox.py +1 -1
  137. solara/website/pages/documentation/components/output/__init__.py +9 -0
  138. solara/website/pages/{api → documentation/components/output}/file_download.py +1 -2
  139. solara/website/pages/{api → documentation/components/output}/image.py +1 -2
  140. solara/website/pages/{api → documentation/components/output}/tooltip.py +1 -2
  141. solara/website/pages/documentation/components/page/__init__.py +9 -0
  142. solara/website/pages/documentation/components/status/__init__.py +9 -0
  143. solara/website/pages/{api → documentation/components/status}/error.py +3 -3
  144. solara/website/pages/{api → documentation/components/status}/info.py +3 -3
  145. solara/website/pages/{api → documentation/components/status}/progress.py +1 -2
  146. solara/website/pages/{api → documentation/components/status}/spinner.py +1 -2
  147. solara/website/pages/{api → documentation/components/status}/success.py +3 -3
  148. solara/website/pages/{api → documentation/components/status}/warning.py +3 -3
  149. solara/website/pages/documentation/components/viz/__init__.py +9 -0
  150. solara/website/pages/{api → documentation/components/viz}/plotly.py +1 -0
  151. solara/website/pages/{examples → documentation/examples}/__init__.py +3 -43
  152. solara/website/pages/{examples → documentation/examples}/general/custom_storage.py +1 -1
  153. solara/website/pages/{examples → documentation/examples}/general/deploy_model.py +3 -3
  154. solara/website/pages/{examples → documentation/examples}/general/login_oauth.py +1 -1
  155. solara/website/pages/{examples → documentation/examples}/general/vue_component.py +1 -2
  156. solara/website/pages/{examples → documentation/examples}/libraries/altair.py +2 -3
  157. solara/website/pages/{examples → documentation/examples}/libraries/ipyleaflet_advanced.py +1 -1
  158. solara/website/pages/{examples → documentation/examples}/utilities/countdown_timer.py +1 -1
  159. solara/website/pages/{examples → documentation/examples}/visualization/plotly.py +1 -2
  160. solara/website/pages/documentation/faq/__init__.py +12 -0
  161. solara/website/pages/{docs → documentation/faq}/content/99-faq.md +4 -1
  162. solara/website/pages/documentation/getting_started/__init__.py +9 -0
  163. solara/website/pages/{docs/content/03-quickstart.md → documentation/getting_started/content/00-quickstart.md} +7 -2
  164. solara/website/pages/{docs/content/00-introduction.md → documentation/getting_started/content/01-introduction.md} +20 -16
  165. solara/website/pages/{docs → documentation/getting_started}/content/02-installing.md +5 -1
  166. solara/website/pages/documentation/getting_started/content/04-tutorials/00-overview.md +14 -0
  167. solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/20-web-app.md +9 -4
  168. solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/30-ipywidgets.md +13 -9
  169. solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/40-streamlit.md +17 -12
  170. solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/50-dash.md +7 -3
  171. solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/_data_science.ipynb +5 -5
  172. solara/website/pages/documentation/getting_started/content/05-fundamentals/00-overview.md +11 -0
  173. solara/website/pages/{docs/content/07-fundamentals → documentation/getting_started/content/05-fundamentals}/10-components.md +7 -2
  174. solara/website/pages/{docs/content/07-fundamentals → documentation/getting_started/content/05-fundamentals}/50-state-management.md +8 -3
  175. solara/website/pages/documentation/getting_started/content/06-reference/00-overview.md +3 -0
  176. solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/40-static_files.md +4 -0
  177. solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/41-asset-files.md +5 -1
  178. solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/60-static-site-generation.md +5 -1
  179. solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/70-search.md +5 -1
  180. solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/80-reloading.md +7 -3
  181. solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/90-notebook-support.md +4 -1
  182. solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/95-caching.md +6 -1
  183. solara/website/pages/documentation/getting_started/content/07-deploying/00-overview.md +7 -0
  184. solara/website/pages/{docs/content/30-deploying → documentation/getting_started/content/07-deploying}/10-self-hosted.md +7 -3
  185. solara/website/pages/{docs/content/30-deploying → documentation/getting_started/content/07-deploying}/20-cloud-hosted.md +5 -1
  186. solara/website/pages/documentation/getting_started/content/80-what-is-lab.md +7 -0
  187. solara/website/pages/{docs → documentation/getting_started}/content/90-troubleshoot.md +4 -0
  188. solara/website/pages/showcase/__init__.py +1 -1
  189. solara/website/pages/showcase/domino_code_assist.py +1 -1
  190. solara/website/pages/showcase/solara_dev.py +1 -1
  191. solara/website/public/social/discord.svg +1 -0
  192. solara/website/public/social/github.svg +1 -0
  193. solara/website/public/social/twitter.svg +3 -0
  194. {solara-1.29.1.dist-info → solara-1.30.1.dist-info}/METADATA +8 -7
  195. solara-1.30.1.dist-info/RECORD +437 -0
  196. {solara-1.29.1.dist-info → solara-1.30.1.dist-info}/WHEEL +1 -1
  197. solara/website/pages/api/__init__.py +0 -292
  198. solara/website/pages/api/default_layout.py +0 -16
  199. solara/website/pages/api/file_drop.py +0 -36
  200. solara/website/pages/docs/content/04-tutorial/00-overview.md +0 -9
  201. solara/website/pages/docs/content/07-fundamentals/00-overview.md +0 -7
  202. solara/website/pages/docs/content/15-reference/00-overview.md +0 -6
  203. solara/website/pages/docs/content/20-understanding/00-introduction.md +0 -4
  204. solara/website/pages/docs/content/20-understanding/17-rules-of-hooks.md +0 -3
  205. solara/website/pages/docs/content/30-deploying/00-overview.md +0 -3
  206. solara/website/pages/docs/content/lab/00-what-is-lab.md +0 -3
  207. solara-1.29.1.dist-info/RECORD +0 -411
  208. /solara/website/pages/{docs/content/99-contact.md → contact/contact.md} +0 -0
  209. /solara/website/pages/{docs/content/50-enterprise → documentation/advanced/content/30-enterprise}/00-overview.md +0 -0
  210. /solara/website/pages/{docs/content/__init__.py → documentation/advanced/content/40-development/00-overview.md} +0 -0
  211. /solara/website/pages/{api → documentation/api/cross_filter}/cross_filter_dataframe.py +0 -0
  212. /solara/website/pages/{api → documentation/api/cross_filter}/cross_filter_report.py +0 -0
  213. /solara/website/pages/{api → documentation/api/cross_filter}/cross_filter_select.py +0 -0
  214. /solara/website/pages/{api → documentation/api/cross_filter}/cross_filter_slider.py +0 -0
  215. /solara/website/pages/{api → documentation/api/hooks}/use_cross_filter.py +0 -0
  216. /solara/website/pages/{api → documentation/api/hooks}/use_dark_effective.py +0 -0
  217. /solara/website/pages/{api → documentation/api/hooks}/use_exception.py +0 -0
  218. /solara/website/pages/{api → documentation/api/hooks}/use_previous.py +0 -0
  219. /solara/website/pages/{api → documentation/api/hooks}/use_state_or_update.py +0 -0
  220. /solara/website/pages/{api → documentation/api/hooks}/use_thread.md +0 -0
  221. /solara/website/pages/{api → documentation/api/hooks}/use_thread.py +0 -0
  222. /solara/website/pages/{api → documentation/api/hooks}/use_trait_observe.py +0 -0
  223. /solara/website/pages/{api → documentation/api/routing}/resolve_path.py +0 -0
  224. /solara/website/pages/{api → documentation/api/routing}/route.py +0 -0
  225. /solara/website/pages/{api → documentation/api/utilities}/memoize.py +0 -0
  226. /solara/website/pages/{api → documentation/components/advanced}/link.py +0 -0
  227. /solara/website/pages/{api → documentation/components/advanced}/meta.py +0 -0
  228. /solara/website/pages/{api → documentation/components/advanced}/style.py +0 -0
  229. /solara/website/pages/{api → documentation/components}/common.py +0 -0
  230. /solara/website/pages/{api → documentation/components/data}/dataframe.py +0 -0
  231. /solara/website/pages/{api → documentation/components/data}/pivot_table.py +0 -0
  232. /solara/website/pages/{api → documentation/components/input}/button.py +0 -0
  233. /solara/website/pages/{api → documentation/components/input}/file_browser.py +0 -0
  234. /solara/website/pages/{api → documentation/components/lab}/confirmation_dialog.py +0 -0
  235. /solara/website/pages/{api → documentation/components/lab}/tab.py +0 -0
  236. /solara/website/pages/{api → documentation/components/lab}/tabs.py +0 -0
  237. /solara/website/pages/{api → documentation/components/layout}/app_layout.py +0 -0
  238. /solara/website/pages/{api → documentation/components/layout}/column.py +0 -0
  239. /solara/website/pages/{api → documentation/components/layout}/columns.py +0 -0
  240. /solara/website/pages/{api → documentation/components/layout}/columns_responsive.py +0 -0
  241. /solara/website/pages/{api → documentation/components/layout}/row.py +0 -0
  242. /solara/website/pages/{api → documentation/components/layout}/sidebar.py +0 -0
  243. /solara/website/pages/{api → documentation/components/output}/html.py +0 -0
  244. /solara/website/pages/{api → documentation/components/output}/markdown.py +0 -0
  245. /solara/website/pages/{api → documentation/components/output}/markdown_editor.py +0 -0
  246. /solara/website/pages/{api → documentation/components/output}/sql_code.py +0 -0
  247. /solara/website/pages/{api → documentation/components/page}/head.py +0 -0
  248. /solara/website/pages/{api → documentation/components/page}/title.py +0 -0
  249. /solara/website/pages/{api → documentation/components/viz}/altair.py +0 -0
  250. /solara/website/pages/{api → documentation/components/viz}/echarts.py +0 -0
  251. /solara/website/pages/{api → documentation/components/viz}/matplotlib.py +0 -0
  252. /solara/website/pages/{api → documentation/components/viz}/plotly_express.py +0 -0
  253. /solara/website/pages/{examples → documentation/examples}/ai/__init__.py +0 -0
  254. /solara/website/pages/{examples → documentation/examples}/ai/chatbot.py +0 -0
  255. /solara/website/pages/{examples → documentation/examples}/ai/tokenizer.py +0 -0
  256. /solara/website/pages/{examples → documentation/examples}/basics/__init__.py +0 -0
  257. /solara/website/pages/{examples → documentation/examples}/basics/sine.py +0 -0
  258. /solara/website/pages/{examples → documentation/examples}/fullscreen/__init__.py +0 -0
  259. /solara/website/pages/{examples → documentation/examples}/fullscreen/authorization.py +0 -0
  260. /solara/website/pages/{examples → documentation/examples}/fullscreen/layout_demo.py +0 -0
  261. /solara/website/pages/{examples → documentation/examples}/fullscreen/multipage.py +0 -0
  262. /solara/website/pages/{examples → documentation/examples}/fullscreen/scatter.py +0 -0
  263. /solara/website/pages/{examples → documentation/examples}/fullscreen/scrolling.py +0 -0
  264. /solara/website/pages/{examples → documentation/examples}/fullscreen/tutorial_streamlit.py +0 -0
  265. /solara/website/pages/{examples → documentation/examples}/general/__init__.py +0 -0
  266. /solara/website/pages/{examples → documentation/examples}/general/live_update.py +0 -0
  267. /solara/website/pages/{examples → documentation/examples}/general/mycard.vue +0 -0
  268. /solara/website/pages/{examples → documentation/examples}/general/pokemon_search.py +0 -0
  269. /solara/website/pages/{examples → documentation/examples}/ipycanvas.py +0 -0
  270. /solara/website/pages/{examples → documentation/examples}/libraries/__init__.py +0 -0
  271. /solara/website/pages/{examples → documentation/examples}/libraries/bqplot.py +0 -0
  272. /solara/website/pages/{examples → documentation/examples}/libraries/ipyleaflet.py +0 -0
  273. /solara/website/pages/{examples → documentation/examples}/utilities/__init__.py +0 -0
  274. /solara/website/pages/{examples → documentation/examples}/utilities/calculator.py +0 -0
  275. /solara/website/pages/{examples → documentation/examples}/utilities/todo.py +0 -0
  276. /solara/website/pages/{examples → documentation/examples}/visualization/__init__.py +0 -0
  277. /solara/website/pages/{examples → documentation/examples}/visualization/annotator.py +0 -0
  278. /solara/website/pages/{examples → documentation/examples}/visualization/linked_views.py +0 -0
  279. /solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/10_data_science.py +0 -0
  280. /solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/60-jupyter-dashboard-part1.py +0 -0
  281. /solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/SF_crime_sample.csv.gz +0 -0
  282. /solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/_jupyter_dashboard_1.ipynb +0 -0
  283. {solara-1.29.1.data → solara-1.30.1.data}/data/etc/jupyter/jupyter_notebook_config.d/solara.json +0 -0
  284. {solara-1.29.1.data → solara-1.30.1.data}/data/etc/jupyter/jupyter_server_config.d/solara.json +0 -0
  285. {solara-1.29.1.dist-info → solara-1.30.1.dist-info}/entry_points.txt +0 -0
  286. {solara-1.29.1.dist-info → solara-1.30.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,6 @@
1
1
  import threading
2
2
  import typing
3
- from typing import Callable, Optional, cast
3
+ from typing import Callable, List, Optional, Union, cast
4
4
 
5
5
  import traitlets
6
6
  from ipyvue import Template
@@ -25,45 +25,26 @@ class FileDropZone(FileInput):
25
25
  template_file = (__file__, "file_drop.vue")
26
26
  items = traitlets.List(default_value=[]).tag(sync=True)
27
27
  label = traitlets.Unicode().tag(sync=True)
28
+ multiple = traitlets.Bool(True).tag(sync=True)
28
29
 
29
30
 
30
31
  @solara.component
31
- def FileDrop(
32
- label="Drop file here",
32
+ def _FileDrop(
33
+ label="Drop file(s) here",
33
34
  on_total_progress: Optional[Callable[[float], None]] = None,
34
- on_file: Optional[Callable[[FileInfo], None]] = None,
35
+ on_file: Optional[Callable[[Union[FileInfo, List[FileInfo]]], None]] = None,
35
36
  lazy: bool = True,
37
+ multiple: bool = False,
36
38
  ):
37
- """Region a user can drop a file into for file uploading.
38
-
39
- If lazy=True, no file content will be loaded into memory,
40
- nor will any data be transferred by default.
41
- A file object is passed to the `on_file` callback, and data will be transferred
42
- when needed.
43
-
44
- If lazy=False, the file content will be loaded into memory and passed to the `on_file` callback via the `.data` attribute.
45
-
46
- The on_file callback takes the following argument type:
47
- ```python
48
- class FileInfo(typing.TypedDict):
49
- name: str # file name
50
- size: int # file size in bytes
51
- file_obj: typing.BinaryIO
52
- data: Optional[bytes]: bytes # only present if lazy=False
53
- ```
54
-
55
-
56
- ## Arguments
57
- * `on_total_progress`: Will be called with the progress in % of the file upload.
58
- * `on_file`: Will be called with a `FileInfo` object, which contains the file `.name`, `.length` and a `.file_obj` object.
59
- * `lazy`: Whether to load the file content into memory or not. If `False`,
60
- the file content will be loaded into memory and passed to the `on_file` callback via the `.data` attribute.
39
+ """Generic implementation used by FileDrop and FileDropMultiple.
61
40
 
41
+ If multiple=True, multiple files can be uploaded.
62
42
  """
43
+
63
44
  file_info, set_file_info = solara.use_state(None)
64
45
  wired_files, set_wired_files = solara.use_state(cast(Optional[typing.List[FileInfo]], None))
65
46
 
66
- file_drop = FileDropZone.element(label=label, on_total_progress=on_total_progress, on_file_info=set_file_info) # type: ignore
47
+ file_drop = FileDropZone.element(label=label, on_total_progress=on_total_progress, on_file_info=set_file_info, multiple=multiple) # type: ignore
67
48
 
68
49
  def wire_files():
69
50
  if not file_info:
@@ -83,14 +64,76 @@ def FileDrop(
83
64
  if not wired_files:
84
65
  return
85
66
  if on_file:
86
- if not lazy:
87
- wired_files[0]["data"] = wired_files[0]["file_obj"].read()
67
+ for i in range(len(wired_files)):
68
+ if not lazy:
69
+ wired_files[i]["data"] = wired_files[i]["file_obj"].read()
70
+ else:
71
+ wired_files[i]["data"] = None
72
+ if multiple:
73
+ on_file(wired_files)
88
74
  else:
89
- wired_files[0]["data"] = None
90
- on_file(wired_files[0])
75
+ on_file(wired_files[0])
91
76
 
92
77
  result: solara.Result = hooks.use_thread(handle_file, [wired_files])
93
78
  if result.error:
94
79
  raise result.error
95
80
 
96
81
  return file_drop
82
+
83
+
84
+ @solara.component
85
+ def FileDrop(
86
+ label="Drop file here",
87
+ on_total_progress: Optional[Callable[[float], None]] = None,
88
+ on_file: Optional[Callable[[FileInfo], None]] = None,
89
+ lazy: bool = True,
90
+ ):
91
+ """Region a user can drop a file into for file uploading.
92
+
93
+ If lazy=True, no file content will be loaded into memory,
94
+ nor will any data be transferred by default.
95
+ If lazy=False, file content will be loaded into memory and passed to the `on_file` callback via the `FileInfo.data` attribute.
96
+
97
+
98
+ A file object is of the following argument type:
99
+ ```python
100
+ class FileInfo(typing.TypedDict):
101
+ name: str # file name
102
+ size: int # file size in bytes
103
+ file_obj: typing.BinaryIO
104
+ data: Optional[bytes]: bytes # only present if lazy=False
105
+ ```
106
+
107
+
108
+ ## Arguments
109
+ * `on_total_progress`: Will be called with the progress in % of the file upload.
110
+ * `on_file`: Will be called with a `FileInfo` object, which contains the file `.name`, `.length` and a `.file_obj` object.
111
+ * `lazy`: Whether to load the file contents into memory or not. If `False`,
112
+ the file contents will be loaded into memory via the `.data` attribute of file object(s).
113
+
114
+ """
115
+
116
+ return _FileDrop(label=label, on_total_progress=on_total_progress, on_file=on_file, lazy=lazy, multiple=False)
117
+
118
+
119
+ @solara.component
120
+ def FileDropMultiple(
121
+ label="Drop files here",
122
+ on_total_progress: Optional[Callable[[float], None]] = None,
123
+ on_file: Optional[Callable[[List[FileInfo]], None]] = None,
124
+ lazy: bool = True,
125
+ ):
126
+ """Region a user can drop multiple files into for file uploading.
127
+
128
+ Almost identical to `FileDrop` except that multiple files can be dropped and `on_file` is called
129
+ with a list of `FileInfo` objects.
130
+
131
+ ## Arguments
132
+ * `on_total_progress`: Will be called with the progress in % of the file(s) upload.
133
+ * `on_file`: Will be called with a `List[FileInfo]`.
134
+ Each `FileInfo` contains the file `.name`, `.length`, `.file_obj` object, and `.data` attributes.
135
+ * `lazy`: Whether to load the file contents into memory or not.
136
+
137
+ """
138
+
139
+ return _FileDrop(label=label, on_total_progress=on_total_progress, on_file=on_file, lazy=lazy, multiple=True)
@@ -1,6 +1,18 @@
1
1
  <template>
2
2
  <div ref="dropzone" class="solara-file-drop" effectAllowed="move">
3
- {{ (file_info && file_info.length === 1) ? file_info[0].name : label }}
3
+ <template v-if="file_info && file_info.length > 0">
4
+ <template v-if="multiple">
5
+ <div v-for="file in file_info">
6
+ {{ file.name }}
7
+ </div>
8
+ </template>
9
+ <template v-else>
10
+ {{ file_info[0].name }}
11
+ </template>
12
+ </template>
13
+ <template v-else>
14
+ {{ label }}
15
+ </template>
4
16
  </div>
5
17
  </template>
6
18
 
@@ -16,16 +28,17 @@ module.exports = {
16
28
  event.preventDefault();
17
29
  const items = await Promise.all([...event.dataTransfer.items]);
18
30
  const files = items.map(i => i.webkitGetAsEntry())
19
- const fileHolder = files.filter(f => f.isFile)[0]
20
- const file = await new Promise((rs, rj) => fileHolder.file(rs, rj))
31
+ const fileHolders = files.filter(f => f.isFile)
32
+ const nativeFilesPromises = fileHolders.map(fileHolder => new Promise((rs, rj) => fileHolder.file(rs, rj)))
33
+ const nativeFiles = await Promise.all(nativeFilesPromises)
21
34
 
22
- this.native_file_info = [file]
35
+ this.native_file_info = nativeFiles
23
36
  this.file_info = this.native_file_info.map(
24
- ({name, isFile, size}) => ({
25
- name,
26
- isFile,
27
- size,
28
- }));
37
+ ({name, size}) => ({
38
+ name,
39
+ isFile: true,
40
+ size,
41
+ }));
29
42
  });
30
43
  },
31
44
  methods: {
@@ -64,6 +77,7 @@ module.exports = {
64
77
  height: 100px;
65
78
  border: 1px dashed gray;
66
79
  margin: 8px 0;
67
- padding: 8px
80
+ padding: 8px;
81
+ overflow: auto;
68
82
  }
69
83
  </style>
solara/components/head.py CHANGED
@@ -1,7 +1,6 @@
1
1
  from typing import List
2
2
 
3
3
  import reacton
4
-
5
4
  import solara
6
5
 
7
6
 
@@ -9,7 +8,7 @@ import solara
9
8
  def Head(children: List[reacton.core.Element] = []):
10
9
  """A component that manager the "head" tag of the page to avoid duplicate tags, such as titles.
11
10
 
12
- Currently only supports the [title](/api/title) tag as child, e.g.:
11
+ Currently only supports the [title](/documentation/components/page/title) tag as child, e.g.:
13
12
 
14
13
  ```python
15
14
  import solara
@@ -2,9 +2,8 @@ from typing import Optional
2
2
 
3
3
  import ipyvuetify as vy
4
4
  import reacton.core
5
- import traitlets
6
-
7
5
  import solara
6
+ import traitlets
8
7
 
9
8
 
10
9
  class HeadTagWidget(vy.VuetifyTemplate):
@@ -19,7 +18,7 @@ class HeadTagWidget(vy.VuetifyTemplate):
19
18
  def HeadTag(tagname: str, key=None, attributes: Optional[dict] = None):
20
19
  """Add a child element to head element, or replace a meta tag with the same tagname and key.
21
20
 
22
- This component should be used inside a [Head](/api/head) component, e.g.:
21
+ This component should be used inside a [Head](/documentation/components/page/head) component, e.g.:
23
22
 
24
23
  ```python
25
24
  import solara
@@ -163,7 +163,7 @@ def Image(
163
163
  layout=layout,
164
164
  )
165
165
  elif solara.util.isinstanceof(image, "numpy:ndarray"):
166
- value = solara.util.numpy_to_image(image, format="png")
166
+ value = solara.util.numpy_to_image(image, format="png") # type: ignore
167
167
  return rw.Image(
168
168
  value=value,
169
169
  format="png",
solara/components/link.py CHANGED
@@ -2,7 +2,6 @@ from typing import Dict, List, Union
2
2
 
3
3
  import ipyvue as vue
4
4
  import reacton.ipyvue as vuer
5
-
6
5
  import solara
7
6
 
8
7
 
@@ -18,8 +17,8 @@ def Link(
18
17
 
19
18
  See also:
20
19
 
21
- * [Multipage](/docs/howto/multipage).
22
- * [Understanding Routing](/docs/understanding/routing).
20
+ * [Multipage](/documentation/advanced/howto/multipage).
21
+ * [Understanding Routing](/documentation/advanced/understanding/routing).
23
22
 
24
23
  Most common usage is in combination with a button, e.g.:
25
24
 
@@ -32,7 +31,7 @@ def Link(
32
31
  ## Arguments
33
32
 
34
33
  * path_or_route: the path or route to navigate to. Paths should be absolute, e.g. '/fruit/banana'.
35
- If a route is given, [`resolve_path`](/api/resolve_path)] will be used to resolve to the absolute path.
34
+ If a route is given, [`resolve_path`](/documentation/api/routing/resolve_path)] will be used to resolve to the absolute path.
36
35
  * children: the children of the link. If a child is clicked, the link will be followed.
37
36
  * nofollow: If True, the link will not be followed by web crawlers (such as google).
38
37
  * style: CSS styles to apply to the HTML link element. Either a string or a dictionary.
solara/components/meta.py CHANGED
@@ -9,7 +9,7 @@ from .head_tag import HeadTag
9
9
  def Meta(name: Optional[str] = None, property: Optional[str] = None, content: Optional[str] = None):
10
10
  """Add a meta tag to the head element, or replace a meta tag with the same name and or property.
11
11
 
12
- This component should be used inside a [Head](/api/head) component, e.g.:
12
+ This component should be used inside a [Head](/documentation/components/page/head) component, e.g.:
13
13
 
14
14
  ```python
15
15
  import solara
solara/components/misc.py CHANGED
@@ -3,7 +3,6 @@ from typing import Any, Callable, Dict, List, Union
3
3
 
4
4
  import reacton
5
5
  import reacton.ipyvuetify as v
6
-
7
6
  import solara
8
7
  import solara.widgets
9
8
  from solara.util import _combine_classes
@@ -159,7 +158,7 @@ def HBox(children=[], grow=True, align_items="stretch", classes: List[str] = [])
159
158
  def Row(children=[], gap="12px", justify="start", margin: int = 0, classes: List[str] = [], style: Union[str, Dict[str, str], None] = None):
160
159
  """Lays out children in a row, side by side, with the given gap between them.
161
160
 
162
- See also [Column](/api/column).
161
+ See also [Column](/documentation/components/layout/column).
163
162
 
164
163
  Example with three children side by side:
165
164
 
@@ -200,7 +199,7 @@ def Row(children=[], gap="12px", justify="start", margin: int = 0, classes: List
200
199
  def Column(children=[], gap="12px", align="stretch", margin: int = 0, classes: List[str] = [], style: Union[str, Dict[str, str], None] = None):
201
200
  """Lays out children in a column on top of each other, with the given gap between them.
202
201
 
203
- See also [Row](/api/row).
202
+ See also [Row](/documentation/components/layout/row).
204
203
 
205
204
  Example with three children on top of each other:
206
205
 
@@ -287,17 +286,14 @@ def FigurePlotly(
287
286
  "plotly_hover": on_hover,
288
287
  "plotly_unhover": on_unhover,
289
288
  "plotly_selected": on_selection,
290
- "plotly_deselect": on_deselect
289
+ "plotly_deselect": on_deselect,
291
290
  }
292
-
291
+
293
292
  callback = event_mapping.get(event_type)
294
293
  if callback:
295
294
  callback(data)
296
295
 
297
- fig_element = FigureWidget.element(
298
- on__js2py_pointsCallback=on_points_callback,
299
- on__js2py_relayout=on_relayout
300
- )
296
+ fig_element = FigureWidget.element(on__js2py_pointsCallback=on_points_callback, on__js2py_relayout=on_relayout)
301
297
 
302
298
  def update_data():
303
299
  fig_widget: FigureWidget = solara.get_widget(fig_element)
solara/datatypes.py CHANGED
@@ -114,8 +114,8 @@ class Route:
114
114
 
115
115
  ## See also
116
116
 
117
- * [Multipage](/docs/howto/multipage).
118
- * [Understanding Routing](/docs/understanding/routing).
117
+ * [Multipage](/documentation/advanced/howto/multipage).
118
+ * [Understanding Routing](/documentation/advanced/understanding/routing).
119
119
 
120
120
  """
121
121
 
@@ -13,7 +13,7 @@ def use_reactive(
13
13
 
14
14
  It is a useful alternative to `use_state` when you want to use a
15
15
  reactive variable for the component state.
16
- See also [our documentation on state management](/docs/fundamentals/state-management).
16
+ See also [our documentation on state management](/documentation/getting_started/fundamentals/state-management).
17
17
 
18
18
  If the variable passed is a reactive variable, it will be returned instead and no
19
19
  new reactive variable will be created. This is useful for implementing component
@@ -92,7 +92,7 @@ def use_reactive(
92
92
  except RuntimeError as e:
93
93
  raise RuntimeError(
94
94
  "use_reactive must be called from a component function, inside the render function.\n"
95
- "Do not call it top level, use [solara.reactive()](https://solara.dev/api/reactive) instead."
95
+ "Do not call it top level, use [solara.reactive()](https://solara.dev/documentation/api/utilities/reactive) instead."
96
96
  ) from e
97
97
  on_change_ref.current = on_change
98
98
 
@@ -60,7 +60,7 @@ def ConfirmationDialog(
60
60
  ):
61
61
  """A dialog used to confirm a user action.
62
62
 
63
- (*Note: [This component is experimental and its API may change in the future](/docs/lab).*)
63
+ (*Note: [This component is experimental and its API may change in the future](/documentation/getting_started/lab).*)
64
64
 
65
65
  By default, has a title, a text explaining the
66
66
  decision to be made, and two buttons "OK" and "Cancel".
@@ -17,9 +17,9 @@ def Tab(
17
17
  ):
18
18
  """An item in a Tabs component.
19
19
 
20
- (*Note: [This component is experimental and its API may change in the future](/docs/lab).*)
20
+ (*Note: [This component is experimental and its API may change in the future](/documentation/getting_started/lab).*)
21
21
 
22
- Should be a direct child of a [Tabs](/api/tabs).
22
+ Should be a direct child of a [Tabs](/documentation/components/lab/tabs).
23
23
 
24
24
  ## Arguments
25
25
  * `label`: The label of the tab.
@@ -62,12 +62,12 @@ def Tabs(
62
62
  ):
63
63
  """A tabbed container showing one tab at a time.
64
64
 
65
- (*Note: [This component is experimental and its API may change in the future](/docs/lab).*)
65
+ (*Note: [This component is experimental and its API may change in the future](/documentation/getting_started/lab).*)
66
66
 
67
- Note that if Tabs are used as a child of the [AppBar](/api/appbar) component, the tabs
67
+ Note that if Tabs are used as a child of the [AppBar](/documentation/components/layout/app_bar) component, the tabs
68
68
  will be placed under the app bar. See our [authorization app](/apps/authorization) for an example.
69
69
 
70
- If the children [Tab](/api/tab) elements are passed a `path_or_route` argument, the active tab
70
+ If the children [Tab](/documentation/components/lab/tab) elements are passed a `path_or_route` argument, the active tab
71
71
  will be based on the path of the current page.
72
72
 
73
73
 
@@ -89,7 +89,7 @@ def Tabs(
89
89
 
90
90
  ### Tabs with content
91
91
 
92
- This is usually only used when the tabs are placed in the [AppBar](/api/appbar) component.
92
+ This is usually only used when the tabs are placed in the [AppBar](/documentation/components/layout/app_bar) component.
93
93
 
94
94
  ```solara
95
95
  import solara
solara/reactive.py CHANGED
@@ -14,7 +14,7 @@ def reactive(value: T) -> Reactive[T]:
14
14
  Solara web applications. They provide an easy-to-use mechanism for keeping
15
15
  track of the changing state of data and for propagating those changes to
16
16
  the appropriate UI components. For managing local or component-specific
17
- state, consider using the [`solara.use_state()`](/api/use_state) function.
17
+ state, consider using the [`solara.use_state()`](/documentation/api/hooks/use_state) function.
18
18
 
19
19
 
20
20
  Reactive variables can be accessed using the `.value` attribute. To modify
solara/routing.py CHANGED
@@ -101,7 +101,7 @@ def use_route_level():
101
101
  def use_router() -> Router:
102
102
  """Returns the current router object.
103
103
 
104
- See also [Understanding Routing](/docs/understanding/routing).
104
+ See also [Understanding Routing](/documentation/advanced/understanding/routing).
105
105
 
106
106
  `use_router` returns the current router object. This is useful to build custom routing.
107
107
 
@@ -123,7 +123,7 @@ def use_router() -> Router:
123
123
  router = solara.use_router()
124
124
 
125
125
  def redirect():
126
- router.push(f"/api/use_route")
126
+ router.push(f"/documentation/api/routing/use_route")
127
127
 
128
128
  solara.Button("Navigate using an event", on_click=redirect)
129
129
  ```
@@ -138,7 +138,7 @@ def use_route(
138
138
  ) -> Tuple[Optional[solara.Route], List[solara.Route]]:
139
139
  """Returns (if found) the current route that matches the pathname, or None
140
140
 
141
- See also [Understanding Routing](/docs/understanding/routing).
141
+ See also [Understanding Routing](/documentation/advanced/understanding/routing).
142
142
 
143
143
  `use_route` returns (if found) the current route that matches the pathname, or None. It also returns all resolved routes of that level
144
144
  (i.e. all siblings and itself). This return tuple is useful to build custom navigation (e.g. using tabs or buttons).
@@ -166,9 +166,10 @@ def use_route(
166
166
 
167
167
  Note that all routes are relative, since a component does not know if it is embedded into a larger application, which may also do routing.
168
168
  Therefore you should never use the `route.path` for navigation since the route object has no knowledge of the full url
169
- (e.g. `/api/use_route/fruit/banana`) but only knows its small piece of the pathname (e.g. `banana`)
169
+ (e.g. `/documentation/api/routing/use_route/fruit/banana`) but only knows its small piece of the pathname (e.g. `banana`)
170
170
 
171
- Use [`resolve_path`](/api/resolve_path) to request the full url for navigation, or simply use the `Link` component that can do this for us.
171
+ Use [`resolve_path`](/documentation/api/routing/resolve_path) to request the full url for navigation,
172
+ or simply use the `Link` component that can do this for us.
172
173
 
173
174
  If the current route has children, any child component that calls `use_route` will return the matched route and its siblings of our children.
174
175
 
@@ -229,12 +230,12 @@ def resolve_path(path_or_route: Union[str, solara.Route], level=0) -> str:
229
230
 
230
231
  ## Arguments
231
232
 
232
- * path_or_route: a path string or a [`solara.Route`](/api/route) object to resolve.
233
+ * path_or_route: a path string or a [`solara.Route`](/documentation/api/routing/route) object to resolve.
233
234
 
234
235
  ## See also
235
236
 
236
- * [Multipage](/docs/howto/multipage).
237
- * [Understanding Routing](/docs/understanding/routing).
237
+ * [Multipage](/documentation/advanced/howto/multipage).
238
+ * [Understanding Routing](/documentation/advanced/understanding/routing).
238
239
 
239
240
 
240
241
  """
solara/scope/__init__.py CHANGED
@@ -50,7 +50,7 @@ def get_kernel_id(ipython_fallback=True) -> str:
50
50
  See [Understanding solara server](/docs/understanding/solara-server) for understanding the concept of virtual kernels
51
51
  and their lifetime.
52
52
 
53
- This unique ID can be useful to to implement storing state, scoped to a kernel. See [the scope example](/examples/general/scopes) for an example.
53
+ This unique ID can be useful to to implement storing state, scoped to a kernel. See [the scope example](/examples/general/custom_storage) for an example.
54
54
 
55
55
  If `ipython_fallback` is `True` (default), this function will also work in IPython notebooks, where it will return the IPython kernel id.
56
56
 
@@ -79,7 +79,8 @@ def get_session_id() -> str:
79
79
 
80
80
  See [Understanding solara server](/docs/understanding/solara-server#session) for more information about the Solara sessions.
81
81
 
82
- This unique ID can be useful to to implement storing state, scoped to a browser session. See [the scope example](/examples/general/scopes) for an example.
82
+ This unique ID can be useful to to implement storing state, scoped to a browser session. See [the scope example](/examples/general/custom_storage)
83
+ for an example.
83
84
  """
84
85
  import solara.server.kernel_context
85
86
 
solara/server/app.py CHANGED
@@ -1,3 +1,4 @@
1
+ import dataclasses
1
2
  import importlib.util
2
3
  import logging
3
4
  import os
@@ -5,6 +6,7 @@ import pickle
5
6
  import sys
6
7
  import threading
7
8
  import traceback
9
+ import warnings
8
10
  from enum import Enum
9
11
  from pathlib import Path
10
12
  from typing import Any, Dict, List, Optional, cast
@@ -14,6 +16,7 @@ import reacton
14
16
  from reacton.core import Element, render
15
17
 
16
18
  import solara
19
+ from solara.util import nested_get
17
20
 
18
21
  from . import kernel_context, patch, reload, settings
19
22
  from .kernel import Kernel
@@ -149,9 +152,24 @@ class AppScript:
149
152
  routes = solara.generate_routes(mod)
150
153
 
151
154
  app = solara.autorouting.RenderPage(self.app_name)
152
- if not hasattr(routes[0].module, self.app_name) and routes[0].children:
155
+
156
+ # when the root moduled defined routes, skip the enclosing route object
157
+ if len(routes) == 1 and routes[0].module and hasattr(routes[0].module, "routes"):
158
+ if routes[0].component:
159
+ warnings.warn(
160
+ f"{self.name} has a component defined, but you are also defining routes."
161
+ " To avoid confusing, consider renaming the {self.app_name} component."
162
+ )
153
163
  routes = routes[0].children
154
164
 
165
+ if self.app_name != "Page":
166
+ # if we specified the app name, we replace the component
167
+ if len(routes) > 1:
168
+ raise ValueError(f"App {self.name} has multiple routes, but a default app name was given: {self.app_name}")
169
+ assert len(routes) == 1
170
+ component = nested_get(routes[0].module.__dict__, self.app_name, None)
171
+ routes = [dataclasses.replace(routes[0], component=component)]
172
+
155
173
  if settings.ssg.build_path is None:
156
174
  settings.ssg.build_path = self.directory.parent.resolve() / "build"
157
175
 
@@ -231,6 +249,31 @@ class AppScript:
231
249
 
232
250
  solara.lab.toestand.ConnectionStore._type_counter.clear()
233
251
 
252
+ # we need to remove callbacks that are added in the app code
253
+ # which will be re-executed after the reload and we do not
254
+ # want to keep executing the old ones.
255
+ for kc in kernel_context._on_kernel_start_callbacks.copy():
256
+ callback, path, module, cleanup = kc
257
+ will_reload = False
258
+ if module is not None:
259
+ module_name = module.__name__
260
+ if module_name in reload.reloader.get_reload_module_names():
261
+ will_reload = True
262
+ elif path is not None:
263
+ if str(path.resolve()).startswith(str(self.directory)):
264
+ will_reload = True
265
+ else:
266
+ logger.warning(
267
+ "script %s is not in the same directory as the app %s but is using on_kernel_start, "
268
+ "this might lead to multiple entries, and might indicate a bug.",
269
+ path,
270
+ self.directory,
271
+ )
272
+
273
+ if will_reload:
274
+ logger.info("reload: Removing on_kernel_start callback: %s (since it will be added when reloaded)", callback)
275
+ cleanup()
276
+
234
277
  context_values = list(kernel_context.contexts.values())
235
278
  # save states into the context so the hot reload will
236
279
  # keep the same state
@@ -23,6 +23,12 @@ code::before {
23
23
  box-shadow: unset !important;
24
24
  }
25
25
 
26
+ .v-application--wrap .v-application--wrap {
27
+ /* disable min-height: 100vh set by vuetify for nested apps */
28
+ min-height: unset;
29
+ }
30
+
31
+
26
32
  div.highlight pre code {
27
33
  /* box-shadow: 0 2px 1px -1px rgb(0 0 0 / 20%), 0 1px 1px 0 rgb(0 0 0 / 14%), 0 1px 3px 0 rgb(0 0 0 / 12%); */
28
34
  /* border: 1px #333 solid; */
solara/server/esm.py CHANGED
@@ -2,15 +2,22 @@
2
2
  # in the future, we may want to move the esm features of ipyreact
3
3
  # into a separate package, and then we can import it unconditionally
4
4
  import logging
5
+ import threading
6
+ from collections import defaultdict
5
7
  from pathlib import Path
6
8
  from typing import Dict, List, Tuple, Union
7
9
 
8
10
  import ipyreact.importmap
9
11
  import ipyreact.module
10
12
 
13
+ from solara.server import kernel_context
14
+
11
15
  logger = logging.getLogger("solara.server.esm")
16
+ lock = threading.Lock()
12
17
 
13
18
  _modules: Dict[str, Tuple[Union[str, Path], List[str]]] = {}
19
+ _modules_added_per_kernel: Dict[str, Dict[str, ipyreact.module.Module]] = defaultdict(dict)
20
+ _import_map_per_kernel: Dict[str, ipyreact.importmap.ImportMap] = {}
14
21
 
15
22
 
16
23
  # in solara server, we'll monkey patch ipyreact.module with this
@@ -29,10 +36,20 @@ def get_module_names():
29
36
 
30
37
 
31
38
  def create_modules():
39
+ kernel_id = kernel_context.get_current_context().id
40
+ _modules_added = _modules_added_per_kernel[kernel_id]
32
41
  logger.info("define modules %s", _modules)
33
42
  widgets = {}
34
- for name, (module, dependencies) in _modules.items():
35
- widgets[name] = create_module(name, module, dependencies=dependencies)
43
+ with lock:
44
+ for name, (module, dependencies) in _modules.items():
45
+ if name not in _modules_added:
46
+ _modules_added[name] = create_module(name, module, dependencies=dependencies)
47
+ logger.info("create module %s %s %s", name, module, dependencies)
48
+ else:
49
+ _modules_added[name].code = module if not isinstance(module, Path) else module.read_text(encoding="utf8")
50
+ _modules_added[name].dependencies = dependencies
51
+ logger.info("update module %s %s %s", name, module, dependencies)
52
+ widgets[name] = _modules_added[name]
36
53
  return widgets
37
54
 
38
55
 
@@ -41,5 +58,12 @@ def create_module(name, module: Union[str, Path], dependencies: List[str]):
41
58
 
42
59
 
43
60
  def create_import_map():
44
- logger.info("create import map %s", ipyreact.importmap._effective_import_map)
45
- return ipyreact.importmap.ImportMap(import_map=ipyreact.importmap._effective_import_map)
61
+ kernel_id = kernel_context.get_current_context().id
62
+ with lock:
63
+ if kernel_id not in _import_map_per_kernel:
64
+ _import_map_per_kernel[kernel_id] = ipyreact.importmap.ImportMap(import_map=ipyreact.importmap._effective_import_map)
65
+ logger.info("create import map %s", ipyreact.importmap._effective_import_map)
66
+ else:
67
+ _import_map_per_kernel[kernel_id].import_map = ipyreact.importmap._effective_import_map
68
+ logger.info("update import map %s", ipyreact.importmap._effective_import_map)
69
+ return