solara-ui 1.31.0__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.
- prefix/etc/jupyter/jupyter_notebook_config.d/solara.json +7 -0
- prefix/etc/jupyter/jupyter_server_config.d/solara.json +7 -0
- solara/__init__.py +124 -0
- solara/__main__.py +734 -0
- solara/alias.py +6 -0
- solara/autorouting.py +546 -0
- solara/cache.py +303 -0
- solara/checks.html +71 -0
- solara/checks.py +224 -0
- solara/comm.py +28 -0
- solara/components/__init__.py +59 -0
- solara/components/alert.py +155 -0
- solara/components/applayout.py +393 -0
- solara/components/button.py +85 -0
- solara/components/card.py +87 -0
- solara/components/checkbox.py +50 -0
- solara/components/code_highlight_css.py +11 -0
- solara/components/code_highlight_css.vue +63 -0
- solara/components/columns.py +159 -0
- solara/components/component_vue.py +110 -0
- solara/components/cross_filter.py +335 -0
- solara/components/dataframe.py +546 -0
- solara/components/datatable.py +221 -0
- solara/components/datatable.vue +175 -0
- solara/components/details.py +21 -0
- solara/components/download.vue +35 -0
- solara/components/echarts.py +75 -0
- solara/components/echarts.vue +128 -0
- solara/components/figure_altair.py +39 -0
- solara/components/file_browser.py +182 -0
- solara/components/file_download.py +199 -0
- solara/components/file_drop.py +139 -0
- solara/components/file_drop.vue +83 -0
- solara/components/file_list_widget.vue +78 -0
- solara/components/head.py +27 -0
- solara/components/head_tag.py +49 -0
- solara/components/head_tag.vue +60 -0
- solara/components/image.py +173 -0
- solara/components/input.py +436 -0
- solara/components/link.py +55 -0
- solara/components/markdown.py +378 -0
- solara/components/markdown_editor.py +25 -0
- solara/components/markdown_editor.vue +362 -0
- solara/components/matplotlib.py +74 -0
- solara/components/meta.py +47 -0
- solara/components/misc.py +333 -0
- solara/components/pivot_table.py +258 -0
- solara/components/pivot_table.vue +158 -0
- solara/components/progress.py +47 -0
- solara/components/select.py +182 -0
- solara/components/select.vue +27 -0
- solara/components/slider.py +442 -0
- solara/components/slider_date.vue +56 -0
- solara/components/spinner-solara.vue +105 -0
- solara/components/spinner.py +30 -0
- solara/components/sql_code.py +33 -0
- solara/components/sql_code.vue +128 -0
- solara/components/style.py +105 -0
- solara/components/switch.py +68 -0
- solara/components/tab_navigation.py +37 -0
- solara/components/title.py +90 -0
- solara/components/title.vue +38 -0
- solara/components/togglebuttons.py +200 -0
- solara/components/tooltip.py +61 -0
- solara/datatypes.py +143 -0
- solara/express.py +241 -0
- solara/hooks/__init__.py +4 -0
- solara/hooks/dataframe.py +99 -0
- solara/hooks/misc.py +263 -0
- solara/hooks/use_reactive.py +129 -0
- solara/hooks/use_thread.py +129 -0
- solara/kitchensink.py +8 -0
- solara/lab/__init__.py +34 -0
- solara/lab/components/__init__.py +6 -0
- solara/lab/components/chat.py +203 -0
- solara/lab/components/confirmation_dialog.py +163 -0
- solara/lab/components/cross_filter.py +7 -0
- solara/lab/components/input_date.py +298 -0
- solara/lab/components/menu.py +181 -0
- solara/lab/components/menu.vue +38 -0
- solara/lab/components/tabs.py +274 -0
- solara/lab/components/theming.py +98 -0
- solara/lab/components/theming.vue +72 -0
- solara/lab/hooks/__init__.py +0 -0
- solara/lab/hooks/dataframe.py +12 -0
- solara/lab/toestand.py +3 -0
- solara/lab/utils/__init__.py +2 -0
- solara/lab/utils/cookies.py +5 -0
- solara/lab/utils/dataframe.py +115 -0
- solara/lab/utils/headers.py +5 -0
- solara/layout.py +44 -0
- solara/lifecycle.py +46 -0
- solara/minisettings.py +133 -0
- solara/py.typed +0 -0
- solara/reactive.py +93 -0
- solara/routing.py +268 -0
- solara/scope/__init__.py +88 -0
- solara/scope/types.py +55 -0
- solara/server/__init__.py +0 -0
- solara/server/app.py +491 -0
- solara/server/assets/custom.css +1 -0
- solara/server/assets/custom.js +1 -0
- solara/server/assets/favicon.png +0 -0
- solara/server/assets/favicon.svg +5 -0
- solara/server/assets/style.css +1665 -0
- solara/server/assets/theme-dark.css +437 -0
- solara/server/assets/theme-light.css +420 -0
- solara/server/assets/theme.js +3 -0
- solara/server/cdn_helper.py +77 -0
- solara/server/esm.py +69 -0
- solara/server/fastapi.py +5 -0
- solara/server/flask.py +286 -0
- solara/server/jupyter/__init__.py +2 -0
- solara/server/jupyter/cdn_handler.py +28 -0
- solara/server/jupyter/server_extension.py +29 -0
- solara/server/jupytertools.py +46 -0
- solara/server/kernel.py +338 -0
- solara/server/kernel_context.py +357 -0
- solara/server/patch.py +552 -0
- solara/server/reload.py +242 -0
- solara/server/server.py +456 -0
- solara/server/settings.py +215 -0
- solara/server/shell.py +251 -0
- solara/server/starlette.py +601 -0
- solara/server/static/ansi.js +270 -0
- solara/server/static/highlight-dark.css +82 -0
- solara/server/static/highlight.css +43 -0
- solara/server/static/main-vuetify.js +260 -0
- solara/server/static/main.js +163 -0
- solara/server/static/solara_bootstrap.py +129 -0
- solara/server/static/sun.svg +23 -0
- solara/server/static/webworker.js +42 -0
- solara/server/telemetry.py +212 -0
- solara/server/templates/index.html.j2 +1 -0
- solara/server/templates/loader-plain.css +11 -0
- solara/server/templates/loader-plain.html +20 -0
- solara/server/templates/loader-solara.css +111 -0
- solara/server/templates/loader-solara.html +40 -0
- solara/server/templates/plain.html +82 -0
- solara/server/templates/solara.html.j2 +446 -0
- solara/server/threaded.py +75 -0
- solara/server/utils.py +30 -0
- solara/server/websocket.py +45 -0
- solara/settings.py +56 -0
- solara/tasks.py +837 -0
- solara/template/button.py +16 -0
- solara/template/markdown.py +42 -0
- solara/template/portal/.flake8 +6 -0
- solara/template/portal/.pre-commit-config.yaml +28 -0
- solara/template/portal/LICENSE +21 -0
- solara/template/portal/Procfile +7 -0
- solara/template/portal/mypy.ini +3 -0
- solara/template/portal/pyproject.toml +26 -0
- solara/template/portal/solara_portal/__init__.py +4 -0
- solara/template/portal/solara_portal/components/__init__.py +2 -0
- solara/template/portal/solara_portal/components/article.py +28 -0
- solara/template/portal/solara_portal/components/data.py +28 -0
- solara/template/portal/solara_portal/components/header.py +6 -0
- solara/template/portal/solara_portal/components/layout.py +6 -0
- solara/template/portal/solara_portal/content/articles/equis-in-vidi.md +85 -0
- solara/template/portal/solara_portal/content/articles/substiterat-vati.md +70 -0
- solara/template/portal/solara_portal/data.py +60 -0
- solara/template/portal/solara_portal/pages/__init__.py +67 -0
- solara/template/portal/solara_portal/pages/article/__init__.py +26 -0
- solara/template/portal/solara_portal/pages/tabular.py +29 -0
- solara/template/portal/solara_portal/pages/viz/__init__.py +70 -0
- solara/template/portal/solara_portal/pages/viz/overview.py +14 -0
- solara/test/__init__.py +0 -0
- solara/test/pytest_plugin.py +697 -0
- solara/toestand.py +772 -0
- solara/util.py +308 -0
- solara/website/__init__.py +0 -0
- solara/website/assets/custom.css +468 -0
- solara/website/assets/images/logo-small.png +0 -0
- solara/website/assets/images/logo.svg +17 -0
- solara/website/assets/images/logo_white.svg +50 -0
- solara/website/assets/theme.js +8 -0
- solara/website/components/__init__.py +5 -0
- solara/website/components/algolia.vue +24 -0
- solara/website/components/algolia_api.vue +187 -0
- solara/website/components/docs.py +118 -0
- solara/website/components/header.py +72 -0
- solara/website/components/hero.py +15 -0
- solara/website/components/mailchimp.py +12 -0
- solara/website/components/mailchimp.vue +47 -0
- solara/website/components/markdown.py +30 -0
- solara/website/components/notebook.py +171 -0
- solara/website/pages/__init__.py +575 -0
- solara/website/pages/apps/__init__.py +16 -0
- solara/website/pages/apps/authorization/__init__.py +119 -0
- solara/website/pages/apps/authorization/admin.py +12 -0
- solara/website/pages/apps/authorization/users.py +12 -0
- solara/website/pages/apps/jupyter-dashboard-1.py +116 -0
- solara/website/pages/apps/layout-demo.py +40 -0
- solara/website/pages/apps/multipage/__init__.py +38 -0
- solara/website/pages/apps/multipage/page1.py +26 -0
- solara/website/pages/apps/multipage/page2.py +34 -0
- solara/website/pages/apps/scatter.py +136 -0
- solara/website/pages/apps/scrolling.py +63 -0
- solara/website/pages/apps/tutorial-streamlit.py +18 -0
- solara/website/pages/changelog/__init__.py +8 -0
- solara/website/pages/changelog/changelog.md +204 -0
- solara/website/pages/contact/__init__.py +8 -0
- solara/website/pages/contact/contact.md +17 -0
- solara/website/pages/doc_use_download.py +85 -0
- solara/website/pages/documentation/__init__.py +184 -0
- solara/website/pages/documentation/advanced/__init__.py +9 -0
- solara/website/pages/documentation/advanced/content/00-overview.md +1 -0
- solara/website/pages/documentation/advanced/content/10-howto/00-overview.md +6 -0
- solara/website/pages/documentation/advanced/content/10-howto/10-multipage.md +196 -0
- solara/website/pages/documentation/advanced/content/10-howto/20-layout.md +125 -0
- solara/website/pages/documentation/advanced/content/10-howto/30-testing.md +162 -0
- solara/website/pages/documentation/advanced/content/10-howto/31-debugging.md +69 -0
- solara/website/pages/documentation/advanced/content/10-howto/40-embed.md +49 -0
- solara/website/pages/documentation/advanced/content/10-howto/50-ipywidget_libraries.md +124 -0
- solara/website/pages/documentation/advanced/content/15-reference/00-overview.md +3 -0
- solara/website/pages/documentation/advanced/content/15-reference/40-static_files.md +31 -0
- solara/website/pages/documentation/advanced/content/15-reference/41-asset-files.md +36 -0
- solara/website/pages/documentation/advanced/content/15-reference/60-static-site-generation.md +59 -0
- solara/website/pages/documentation/advanced/content/15-reference/70-search.md +34 -0
- solara/website/pages/documentation/advanced/content/15-reference/80-reloading.md +34 -0
- solara/website/pages/documentation/advanced/content/15-reference/90-notebook-support.md +7 -0
- solara/website/pages/documentation/advanced/content/15-reference/95-caching.md +148 -0
- solara/website/pages/documentation/advanced/content/20-understanding/00-introduction.md +10 -0
- solara/website/pages/documentation/advanced/content/20-understanding/05-ipywidgets.md +35 -0
- solara/website/pages/documentation/advanced/content/20-understanding/06-ipyvuetify.md +42 -0
- solara/website/pages/documentation/advanced/content/20-understanding/10-reacton.md +28 -0
- solara/website/pages/documentation/advanced/content/20-understanding/12-reacton-basics.md +108 -0
- solara/website/pages/documentation/advanced/content/20-understanding/15-anatomy.md +23 -0
- solara/website/pages/documentation/advanced/content/20-understanding/17-rules-of-hooks.md +7 -0
- solara/website/pages/documentation/advanced/content/20-understanding/18-containers.md +166 -0
- solara/website/pages/documentation/advanced/content/20-understanding/20-solara.md +18 -0
- solara/website/pages/documentation/advanced/content/20-understanding/40-routing.md +240 -0
- solara/website/pages/documentation/advanced/content/20-understanding/50-solara-server.md +97 -0
- solara/website/pages/documentation/advanced/content/20-understanding/60-voila.md +12 -0
- solara/website/pages/documentation/advanced/content/30-enterprise/00-overview.md +1 -0
- solara/website/pages/documentation/advanced/content/30-enterprise/10-oauth.md +171 -0
- solara/website/pages/documentation/advanced/content/40-development/00-overview.md +0 -0
- solara/website/pages/documentation/advanced/content/40-development/01-contribute.md +45 -0
- solara/website/pages/documentation/advanced/content/40-development/10-setup.md +76 -0
- solara/website/pages/documentation/api/__init__.py +19 -0
- solara/website/pages/documentation/api/cross_filter/__init__.py +9 -0
- solara/website/pages/documentation/api/cross_filter/cross_filter_dataframe.py +23 -0
- solara/website/pages/documentation/api/cross_filter/cross_filter_report.py +22 -0
- solara/website/pages/documentation/api/cross_filter/cross_filter_select.py +22 -0
- solara/website/pages/documentation/api/cross_filter/cross_filter_slider.py +22 -0
- solara/website/pages/documentation/api/hooks/__init__.py +9 -0
- solara/website/pages/documentation/api/hooks/use_cross_filter.py +25 -0
- solara/website/pages/documentation/api/hooks/use_dark_effective.py +12 -0
- solara/website/pages/documentation/api/hooks/use_effect.md +43 -0
- solara/website/pages/documentation/api/hooks/use_effect.py +9 -0
- solara/website/pages/documentation/api/hooks/use_exception.py +33 -0
- solara/website/pages/documentation/api/hooks/use_memo.md +16 -0
- solara/website/pages/documentation/api/hooks/use_memo.py +9 -0
- solara/website/pages/documentation/api/hooks/use_previous.py +33 -0
- solara/website/pages/documentation/api/hooks/use_reactive.py +16 -0
- solara/website/pages/documentation/api/hooks/use_state.py +10 -0
- solara/website/pages/documentation/api/hooks/use_state_or_update.py +69 -0
- solara/website/pages/documentation/api/hooks/use_thread.md +58 -0
- solara/website/pages/documentation/api/hooks/use_thread.py +44 -0
- solara/website/pages/documentation/api/hooks/use_trait_observe.py +12 -0
- solara/website/pages/documentation/api/routing/__init__.py +9 -0
- solara/website/pages/documentation/api/routing/generate_routes.py +10 -0
- solara/website/pages/documentation/api/routing/generate_routes_directory.py +10 -0
- solara/website/pages/documentation/api/routing/resolve_path.py +35 -0
- solara/website/pages/documentation/api/routing/route.py +31 -0
- solara/website/pages/documentation/api/routing/use_route.py +80 -0
- solara/website/pages/documentation/api/routing/use_router.py +16 -0
- solara/website/pages/documentation/api/utilities/__init__.py +9 -0
- solara/website/pages/documentation/api/utilities/component_vue.py +10 -0
- solara/website/pages/documentation/api/utilities/computed.py +16 -0
- solara/website/pages/documentation/api/utilities/display.py +16 -0
- solara/website/pages/documentation/api/utilities/get_kernel_id.py +16 -0
- solara/website/pages/documentation/api/utilities/get_session_id.py +16 -0
- solara/website/pages/documentation/api/utilities/memoize.py +35 -0
- solara/website/pages/documentation/api/utilities/on_kernel_start.py +27 -0
- solara/website/pages/documentation/api/utilities/reactive.py +16 -0
- solara/website/pages/documentation/api/utilities/widget.py +104 -0
- solara/website/pages/documentation/components/__init__.py +12 -0
- solara/website/pages/documentation/components/advanced/__init__.py +9 -0
- solara/website/pages/documentation/components/advanced/link.py +27 -0
- solara/website/pages/documentation/components/advanced/meta.py +20 -0
- solara/website/pages/documentation/components/advanced/style.py +45 -0
- solara/website/pages/documentation/components/common.py +9 -0
- solara/website/pages/documentation/components/data/__init__.py +9 -0
- solara/website/pages/documentation/components/data/dataframe.py +44 -0
- solara/website/pages/documentation/components/data/pivot_table.py +81 -0
- solara/website/pages/documentation/components/enterprise/__init__.py +9 -0
- solara/website/pages/documentation/components/enterprise/avatar.py +24 -0
- solara/website/pages/documentation/components/enterprise/avatar_menu.py +25 -0
- solara/website/pages/documentation/components/input/__init__.py +9 -0
- solara/website/pages/documentation/components/input/button.py +23 -0
- solara/website/pages/documentation/components/input/checkbox.py +10 -0
- solara/website/pages/documentation/components/input/file_browser.py +32 -0
- solara/website/pages/documentation/components/input/file_drop.py +76 -0
- solara/website/pages/documentation/components/input/input.py +19 -0
- solara/website/pages/documentation/components/input/select.py +22 -0
- solara/website/pages/documentation/components/input/slider.py +29 -0
- solara/website/pages/documentation/components/input/switch.py +10 -0
- solara/website/pages/documentation/components/input/togglebuttons.py +21 -0
- solara/website/pages/documentation/components/lab/__init__.py +9 -0
- solara/website/pages/documentation/components/lab/chat.py +109 -0
- solara/website/pages/documentation/components/lab/confirmation_dialog.py +55 -0
- solara/website/pages/documentation/components/lab/cookies_headers.py +48 -0
- solara/website/pages/documentation/components/lab/input_date.py +20 -0
- solara/website/pages/documentation/components/lab/menu.py +22 -0
- solara/website/pages/documentation/components/lab/tab.py +25 -0
- solara/website/pages/documentation/components/lab/tabs.py +45 -0
- solara/website/pages/documentation/components/lab/task.py +11 -0
- solara/website/pages/documentation/components/lab/theming.py +72 -0
- solara/website/pages/documentation/components/lab/use_task.py +11 -0
- solara/website/pages/documentation/components/layout/__init__.py +9 -0
- solara/website/pages/documentation/components/layout/app_bar.py +16 -0
- solara/website/pages/documentation/components/layout/app_bar_title.py +16 -0
- solara/website/pages/documentation/components/layout/app_layout.py +24 -0
- solara/website/pages/documentation/components/layout/card.py +15 -0
- solara/website/pages/documentation/components/layout/card_actions.py +16 -0
- solara/website/pages/documentation/components/layout/column.py +30 -0
- solara/website/pages/documentation/components/layout/columns.py +27 -0
- solara/website/pages/documentation/components/layout/columns_responsive.py +68 -0
- solara/website/pages/documentation/components/layout/griddraggable.py +62 -0
- solara/website/pages/documentation/components/layout/gridfixed.py +21 -0
- solara/website/pages/documentation/components/layout/hbox.py +18 -0
- solara/website/pages/documentation/components/layout/row.py +30 -0
- solara/website/pages/documentation/components/layout/sidebar.py +24 -0
- solara/website/pages/documentation/components/layout/vbox.py +19 -0
- solara/website/pages/documentation/components/output/__init__.py +9 -0
- solara/website/pages/documentation/components/output/file_download.py +11 -0
- solara/website/pages/documentation/components/output/html.py +21 -0
- solara/website/pages/documentation/components/output/image.py +11 -0
- solara/website/pages/documentation/components/output/markdown.py +57 -0
- solara/website/pages/documentation/components/output/markdown_editor.py +51 -0
- solara/website/pages/documentation/components/output/sql_code.py +85 -0
- solara/website/pages/documentation/components/output/tooltip.py +11 -0
- solara/website/pages/documentation/components/page/__init__.py +9 -0
- solara/website/pages/documentation/components/page/head.py +18 -0
- solara/website/pages/documentation/components/page/title.py +27 -0
- solara/website/pages/documentation/components/status/__init__.py +9 -0
- solara/website/pages/documentation/components/status/error.py +40 -0
- solara/website/pages/documentation/components/status/info.py +40 -0
- solara/website/pages/documentation/components/status/progress.py +10 -0
- solara/website/pages/documentation/components/status/spinner.py +11 -0
- solara/website/pages/documentation/components/status/success.py +40 -0
- solara/website/pages/documentation/components/status/warning.py +47 -0
- solara/website/pages/documentation/components/viz/__init__.py +9 -0
- solara/website/pages/documentation/components/viz/altair.py +42 -0
- solara/website/pages/documentation/components/viz/echarts.py +75 -0
- solara/website/pages/documentation/components/viz/matplotlib.py +30 -0
- solara/website/pages/documentation/components/viz/plotly.py +63 -0
- solara/website/pages/documentation/components/viz/plotly_express.py +41 -0
- solara/website/pages/documentation/examples/__init__.py +52 -0
- solara/website/pages/documentation/examples/ai/__init__.py +11 -0
- solara/website/pages/documentation/examples/ai/chatbot.py +95 -0
- solara/website/pages/documentation/examples/ai/tokenizer.py +107 -0
- solara/website/pages/documentation/examples/basics/__init__.py +10 -0
- solara/website/pages/documentation/examples/basics/sine.py +28 -0
- solara/website/pages/documentation/examples/fullscreen/__init__.py +10 -0
- solara/website/pages/documentation/examples/fullscreen/authorization.py +3 -0
- solara/website/pages/documentation/examples/fullscreen/layout_demo.py +3 -0
- solara/website/pages/documentation/examples/fullscreen/multipage.py +3 -0
- solara/website/pages/documentation/examples/fullscreen/scatter.py +3 -0
- solara/website/pages/documentation/examples/fullscreen/scrolling.py +3 -0
- solara/website/pages/documentation/examples/fullscreen/tutorial_streamlit.py +3 -0
- solara/website/pages/documentation/examples/general/__init__.py +10 -0
- solara/website/pages/documentation/examples/general/custom_storage.py +70 -0
- solara/website/pages/documentation/examples/general/deploy_model.py +115 -0
- solara/website/pages/documentation/examples/general/live_update.py +38 -0
- solara/website/pages/documentation/examples/general/login_oauth.py +81 -0
- solara/website/pages/documentation/examples/general/mycard.vue +58 -0
- solara/website/pages/documentation/examples/general/pokemon_search.py +51 -0
- solara/website/pages/documentation/examples/general/vue_component.py +50 -0
- solara/website/pages/documentation/examples/ipycanvas.py +49 -0
- solara/website/pages/documentation/examples/libraries/__init__.py +10 -0
- solara/website/pages/documentation/examples/libraries/altair.py +64 -0
- solara/website/pages/documentation/examples/libraries/bqplot.py +39 -0
- solara/website/pages/documentation/examples/libraries/ipyleaflet.py +33 -0
- solara/website/pages/documentation/examples/libraries/ipyleaflet_advanced.py +66 -0
- solara/website/pages/documentation/examples/utilities/__init__.py +10 -0
- solara/website/pages/documentation/examples/utilities/calculator.py +157 -0
- solara/website/pages/documentation/examples/utilities/countdown_timer.py +64 -0
- solara/website/pages/documentation/examples/utilities/todo.py +196 -0
- solara/website/pages/documentation/examples/visualization/__init__.py +6 -0
- solara/website/pages/documentation/examples/visualization/annotator.py +69 -0
- solara/website/pages/documentation/examples/visualization/linked_views.py +84 -0
- solara/website/pages/documentation/examples/visualization/plotly.py +44 -0
- solara/website/pages/documentation/faq/__init__.py +12 -0
- solara/website/pages/documentation/faq/content/99-faq.md +76 -0
- solara/website/pages/documentation/getting_started/__init__.py +9 -0
- solara/website/pages/documentation/getting_started/content/00-quickstart.md +89 -0
- solara/website/pages/documentation/getting_started/content/01-introduction.md +125 -0
- solara/website/pages/documentation/getting_started/content/02-installing.md +134 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/00-overview.md +14 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/10_data_science.py +13 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/20-web-app.md +89 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/30-ipywidgets.md +124 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/40-streamlit.md +146 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/50-dash.md +144 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/60-jupyter-dashboard-part1.py +64 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/SF_crime_sample.csv.gz +0 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/_data_science.ipynb +445 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/_jupyter_dashboard_1.ipynb +1000 -0
- solara/website/pages/documentation/getting_started/content/05-fundamentals/00-overview.md +11 -0
- solara/website/pages/documentation/getting_started/content/05-fundamentals/10-components.md +223 -0
- solara/website/pages/documentation/getting_started/content/05-fundamentals/50-state-management.md +88 -0
- solara/website/pages/documentation/getting_started/content/07-deploying/00-overview.md +7 -0
- solara/website/pages/documentation/getting_started/content/07-deploying/10-self-hosted.md +273 -0
- solara/website/pages/documentation/getting_started/content/07-deploying/20-cloud-hosted.md +80 -0
- solara/website/pages/documentation/getting_started/content/80-what-is-lab.md +7 -0
- solara/website/pages/documentation/getting_started/content/90-troubleshoot.md +26 -0
- solara/website/pages/docutils.py +38 -0
- solara/website/pages/showcase/__init__.py +105 -0
- solara/website/pages/showcase/domino_code_assist.py +60 -0
- solara/website/pages/showcase/planeto_tessa.py +19 -0
- solara/website/pages/showcase/solara_dev.py +54 -0
- solara/website/pages/showcase/solarathon_2023_team_2.py +22 -0
- solara/website/pages/showcase/solarathon_2023_team_4.py +22 -0
- solara/website/pages/showcase/solarathon_2023_team_5.py +23 -0
- solara/website/pages/showcase/solarathon_2023_team_6.py +34 -0
- solara/website/pages/showcase/wanderlust.py +27 -0
- solara/website/public/beach.jpeg +0 -0
- solara/website/public/logo.svg +6 -0
- solara/website/public/social/discord.svg +1 -0
- solara/website/public/social/github.svg +1 -0
- solara/website/public/social/twitter.svg +3 -0
- solara/website/public/success.html +25 -0
- solara/website/templates/index.html.j2 +117 -0
- solara/website/utils.py +51 -0
- solara/widgets/__init__.py +1 -0
- solara/widgets/vue/gridlayout.vue +110 -0
- solara/widgets/vue/html.vue +4 -0
- solara/widgets/vue/navigator.vue +104 -0
- solara/widgets/vue/vegalite.vue +115 -0
- solara/widgets/widgets.py +66 -0
- solara_ui-1.31.0.data/data/etc/jupyter/jupyter_notebook_config.d/solara.json +7 -0
- solara_ui-1.31.0.data/data/etc/jupyter/jupyter_server_config.d/solara.json +7 -0
- solara_ui-1.31.0.dist-info/METADATA +158 -0
- solara_ui-1.31.0.dist-info/RECORD +439 -0
- solara_ui-1.31.0.dist-info/WHEEL +5 -0
- solara_ui-1.31.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Understanding the basics of Reacton
|
|
3
|
+
description: Using your favorite component, we try to understand better how it works to improve your understanding of Reacton and, thus, how to use Solara.
|
|
4
|
+
---
|
|
5
|
+
# Reacton basic understanding
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Let us take our favorite component, and try to understand a bit better how it works to improve your understanding of Reacton and thus how to use Solara.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
```solara
|
|
12
|
+
import solara
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@solara.component
|
|
16
|
+
def ClickButton():
|
|
17
|
+
clicks, set_clicks = solara.use_state(0)
|
|
18
|
+
|
|
19
|
+
def my_click_hander():
|
|
20
|
+
set_clicks(clicks + 1)
|
|
21
|
+
|
|
22
|
+
return solara.Button(label=f"Clicked: {clicks}", on_click=my_click_hander)
|
|
23
|
+
|
|
24
|
+
Page = ClickButton
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
This example does a few things:
|
|
28
|
+
|
|
29
|
+
* Add state (`clicks`) to the component.
|
|
30
|
+
* Create a callback function (`my_click_handler`). When executed, it will increase ask Solara/Reacton to increase the `clicks` value on the next render, by calling `set_clicks`.
|
|
31
|
+
* Pass the callback function to the child component (`Button`).
|
|
32
|
+
* Set the `label` value on each render.
|
|
33
|
+
|
|
34
|
+
Now if a user clicks the button:
|
|
35
|
+
|
|
36
|
+
* The `my_click_hander` callback will be called by Solara/Reacton.
|
|
37
|
+
* Your code (the body of `my_click_hander`) will be executed and will ask Reacton to change the state of `clicks` by increasing the value by 1.
|
|
38
|
+
* Solara/Reacton got asked to change the of `clicks`, and will execute your render function (the body of the `ClickButton` component) once.
|
|
39
|
+
* Solara/Reacton will compare the return value of your component and will update the associated widget. In this case it will update the label to `"`Clicked: 1"`.
|
|
40
|
+
|
|
41
|
+
This pattern is very common, where a parent component (ClickButton) managed the state (the number of times clicked), and state changes (increasing the clicks by 1 on a button click).
|
|
42
|
+
|
|
43
|
+
## Graphically
|
|
44
|
+
|
|
45
|
+
Is the above text a bit dry? Maybe the annotated code will make it easier to understand for you.
|
|
46
|
+
|
|
47
|
+

|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
## Going deeper
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
Still not satisfied? Let's go through all the steps in more detail in this sequence diagram:
|
|
54
|
+
|
|
55
|
+
```mermaid
|
|
56
|
+
sequenceDiagram
|
|
57
|
+
actor You
|
|
58
|
+
participant Frontend as Frontend
|
|
59
|
+
participant ipywidgets as IPyWidgets
|
|
60
|
+
participant react as Reacton
|
|
61
|
+
participant component as ButtonClick
|
|
62
|
+
participant app as Solara
|
|
63
|
+
|
|
64
|
+
app->>component: el=ButtonClick()
|
|
65
|
+
app->>react: render(el)
|
|
66
|
+
|
|
67
|
+
activate react
|
|
68
|
+
react->>component: render()
|
|
69
|
+
component->>react: use_state(0)<br>(returns 0)
|
|
70
|
+
|
|
71
|
+
react->>react: reconsolidate()
|
|
72
|
+
react->>ipywidgets: create Button<br>description="Clicked: 0"
|
|
73
|
+
deactivate react
|
|
74
|
+
ipywidgets--)Frontend: create Button view
|
|
75
|
+
|
|
76
|
+
You->>Frontend: clicks button
|
|
77
|
+
|
|
78
|
+
Frontend--)ipywidgets: Button clicked
|
|
79
|
+
ipywidgets->>component: on_click
|
|
80
|
+
activate component
|
|
81
|
+
component->>react: set_clicks(1)
|
|
82
|
+
activate react
|
|
83
|
+
deactivate component
|
|
84
|
+
react->>+component: render()
|
|
85
|
+
component->>-react: use_state(0)<br> (now returns 1)
|
|
86
|
+
react->>react: reconsolidate()
|
|
87
|
+
react->>ipywidgets: update Button<br>description="Clicked 1"
|
|
88
|
+
deactivate react
|
|
89
|
+
ipywidgets--)Frontend: update Button<br>description="Clicked 1"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
In words
|
|
93
|
+
|
|
94
|
+
1. Solara create `el = ButtonClick()` from your component.
|
|
95
|
+
1. The `display(el)` triggers the call to Reacton.
|
|
96
|
+
1. The render call enters the render phase, which will call the function body (which we call render function) of the `ButtonClick` component.
|
|
97
|
+
1. Our ButtonClick render function calls [`use_state`](/documentation/api/hooks/use_state). Because this is our first render phase, this returns the initial value (0).
|
|
98
|
+
1. The ButtonClick render function returns a Button element (not a widget!) with `description="Clicked: 0 times"`.
|
|
99
|
+
1. The Reacton render call is done with the render phase, and enters the reconciliation phase, where it looks at the difference between the real widgets and the virtual widgets tree (represented by the Reacton elements). We find there is no previous widget associated with the virtual widget (or element) and decide to create a widget.
|
|
100
|
+
1. Asynchronously via the Jupyter protocol, a widget model and view are created and displayed to the user in the browser.
|
|
101
|
+
1. The user clicks on the button.
|
|
102
|
+
1. The `on_click` handler gets triggered on the Python side, inside of the `ButtonClick` component (called `my_click_handler`).
|
|
103
|
+
1. `my_click_handler` handler calls `set_clicks(1)` which triggers a re-render.
|
|
104
|
+
1. The render call enters the render phase, which calls the render function of `ButtonClick` for the second time.
|
|
105
|
+
1. Our ButtonClick render function calls [`reacton.use_state`](#use_state). Because this is our second render phase, this returns the last set value, which is 1.
|
|
106
|
+
1. The ButtonClick render function returns a new Button element (not a widget!) with the description `"Clicked: 1 times"`.
|
|
107
|
+
1. The Reacton render call is done with the render phase, and enters the reconciliation phase, where it looks at the difference between the real widgets and the virtual widgets tree (represented by the Reacton elements). We find there is a widget associated with the virtual widget (or element) and decide to update the changed attributes of the widget and set `description` to `"Clicked: 1 times"`.
|
|
108
|
+
1. Asynchronously via the Jupyter protocol, the widet model and view are being updated in the browser.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: The anatomy of Solara's functionality
|
|
3
|
+
description: Dive deep into how Solara uses the technologies it builds on.
|
|
4
|
+
---
|
|
5
|
+
# Anatomy
|
|
6
|
+
|
|
7
|
+
For communication, it is useful to speak the same language and use the same idiom.
|
|
8
|
+
As a reference, we provide this "anatomy" image of our favorite `ClickButton` component with a layout that we consider best practice.
|
|
9
|
+
|
|
10
|
+

|
|
11
|
+
|
|
12
|
+
## Explanations
|
|
13
|
+
|
|
14
|
+
* Import `solara` and you also get the `reacton` namespace with it (saves typing, and finding/remembering which hooks is in which packages)
|
|
15
|
+
* Add a `@solara.component` decorator to turn your function into a component.
|
|
16
|
+
* Start with `use_state` hooks and other hooks. This avoids issues with [conditional hooks](/documentation/advanced/understanding/rules-of-hooks) or hooks in loops.
|
|
17
|
+
* Data/state flows down (to children)
|
|
18
|
+
* Information (events, data) flows up from children via events and callbacks (`on_<some_event_name>=my_callback`).
|
|
19
|
+
* If you need multiple components, use a [parent container component](/documentation/components/layout/app_layout) as context manager. A good default name to give this context manager is `main`. Don't forget to return it in your render function!
|
|
20
|
+
* The body of your component (the function you wrote) is called the render function.
|
|
21
|
+
* In between the hooks as defining all your elements, you put your custom code, like checking variables, defining callbacks, and other logic.
|
|
22
|
+
* The only way for a component to cause itself to rerender is to have state (using `use_state`) and change it (calling the second return value with a different value).
|
|
23
|
+
* Reacton is declarative. Your render function gets executed after every state change (and thus should be relatively cheap to execute). Reacton will find out the changes for you and will add, remove and update the associated widgets for you. This means you can easily do conditional rendering (creating an element in an if statement or in a loop).
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Using Solara with containers to build complex UIs
|
|
3
|
+
description: Solara allows for using python with statements to populate your UIs in a structured manner. This makes it easy to compose different components to build cohesive
|
|
4
|
+
wholes.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Laying out components with containers
|
|
8
|
+
|
|
9
|
+
## Introduction
|
|
10
|
+
Some components, such as our favorite `ClickButton` only add logic on top of an existing component:
|
|
11
|
+
|
|
12
|
+
```solara
|
|
13
|
+
import solara
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@solara.component
|
|
17
|
+
def ClickButton():
|
|
18
|
+
clicks, set_clicks = solara.use_state(0)
|
|
19
|
+
|
|
20
|
+
def my_click_hander():
|
|
21
|
+
set_clicks(clicks + 1)
|
|
22
|
+
|
|
23
|
+
# We return a single component
|
|
24
|
+
return solara.Button(label=f"Clicked: {clicks}", on_click=my_click_hander)
|
|
25
|
+
|
|
26
|
+
Page = ClickButton
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## Containers with children
|
|
31
|
+
However, more sophisticated components will add multiple components together into a container component, let's take a look at an example.
|
|
32
|
+
|
|
33
|
+
```solara
|
|
34
|
+
import solara
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@solara.component
|
|
38
|
+
def FancyClickButton():
|
|
39
|
+
clicks, set_clicks = solara.use_state(0)
|
|
40
|
+
|
|
41
|
+
def increase_count():
|
|
42
|
+
set_clicks(clicks + 1)
|
|
43
|
+
|
|
44
|
+
def reset_count():
|
|
45
|
+
set_clicks(0)
|
|
46
|
+
|
|
47
|
+
button_increase = solara.Button(label=f"Clicked: {clicks}", on_click=increase_count)
|
|
48
|
+
button_reset = solara.Button(label="Reset", on_click=reset_count)
|
|
49
|
+
|
|
50
|
+
return solara.Row(children=[button_increase,
|
|
51
|
+
button_reset,
|
|
52
|
+
])
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
Page = FancyClickButton
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
Here we use an [HBox](/documentation/components/layout/hbox) to lay out two child components horizontally.
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
## Cleaner way to add children to containers
|
|
64
|
+
|
|
65
|
+
Because using container components is so common, we created a more convenient way to pass children to components and made your code look neater and more structured as well (avoiding nested lists).
|
|
66
|
+
|
|
67
|
+
```solara
|
|
68
|
+
import solara
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@solara.component
|
|
72
|
+
def FancyClickButton():
|
|
73
|
+
clicks, set_clicks = solara.use_state(0)
|
|
74
|
+
|
|
75
|
+
def increase_count():
|
|
76
|
+
set_clicks(clicks + 1)
|
|
77
|
+
|
|
78
|
+
def reset_count():
|
|
79
|
+
set_clicks(0)
|
|
80
|
+
|
|
81
|
+
with solara.Row() as main:
|
|
82
|
+
solara.Button(label=f"Clicked: {clicks}", on_click=increase_count)
|
|
83
|
+
solara.Button(label="Reset", on_click=reset_count)
|
|
84
|
+
return main
|
|
85
|
+
|
|
86
|
+
Page = FancyClickButton
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Here we are using the top-level Row as a context manager (with the name `main`). All child elements created within this context are automatically added as a child.
|
|
91
|
+
|
|
92
|
+
### About Context managers
|
|
93
|
+
|
|
94
|
+
Context managers are a Python language feature, there are two ways to use them:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
|
|
98
|
+
with some_anonymous_context_manager():
|
|
99
|
+
print("some code")
|
|
100
|
+
|
|
101
|
+
with some_named_context_manager() as this_is_my_name:
|
|
102
|
+
print("some other code")
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Where the last example assigns the context manager to a variable. In Solara we only need to do that to the top context manager, since we need to return that in our [render function](/documentation/advanced/understanding/anatomy).
|
|
106
|
+
|
|
107
|
+
All Reacton or Solara components return elements that can be used as context managers. Context managers allow for code to be executed before and after your code block inside of the context manager. This allows us to capture all elements created inside of the context manager. If you want to know more about context managers, consult the Python documentation since this is not Solara specific.
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
### Nested layout
|
|
111
|
+
|
|
112
|
+
Adding children using context managers work at any level, you can nest Rows and Columns [or any other containers](/api#components).
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
```solara
|
|
116
|
+
import solara
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@solara.component
|
|
120
|
+
def FancyClickButton():
|
|
121
|
+
clicks, set_clicks = solara.use_state(0)
|
|
122
|
+
|
|
123
|
+
def increase_count():
|
|
124
|
+
set_clicks(clicks + 1)
|
|
125
|
+
|
|
126
|
+
def reset_count():
|
|
127
|
+
set_clicks(0)
|
|
128
|
+
|
|
129
|
+
with solara.Column() as main:
|
|
130
|
+
with solara.Row():
|
|
131
|
+
solara.Button(label=f"Clicked: {clicks}", on_click=increase_count)
|
|
132
|
+
solara.Button(label="Reset", on_click=reset_count)
|
|
133
|
+
solara.Text("I am a child of a VBox")
|
|
134
|
+
return main
|
|
135
|
+
|
|
136
|
+
Page = FancyClickButton
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
## Automatic containers
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
Instead of returning the main container, Solara also allows you to not have a return value (or return `None`).
|
|
145
|
+
If that is the case, Solara will look at what elements you created. If you created one, that element will be taken
|
|
146
|
+
as a return value instead. If you make more than one element, those elements will be automatically wrapped by
|
|
147
|
+
a [Column](/documentation/components/layout/column). The only benefit of returning an element, is that we can infer the correct return type,
|
|
148
|
+
which can be useful for testing purposes. Users should probably never return an element, but use the automatic
|
|
149
|
+
container feature.
|
|
150
|
+
|
|
151
|
+
```solara
|
|
152
|
+
import solara
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@solara.component
|
|
156
|
+
def ClickButton():
|
|
157
|
+
clicks, set_clicks = solara.use_state(0)
|
|
158
|
+
|
|
159
|
+
def my_click_hander():
|
|
160
|
+
set_clicks(clicks + 1)
|
|
161
|
+
|
|
162
|
+
solara.Button(label=f"Clicked: {clicks}", on_click=my_click_hander)
|
|
163
|
+
solara.Button(label=f"Reset", on_click=lambda: set_clicks(0))
|
|
164
|
+
|
|
165
|
+
Page = ClickButton
|
|
166
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Understanding different parts of Solara
|
|
3
|
+
description: Solara is made of two main components, the bulk of the Solara package with UI elements that can be used together with Jupyter, and Solara server, which
|
|
4
|
+
allows for these UI elements to be used in standalone apps and dashboards
|
|
5
|
+
---
|
|
6
|
+
# Solara
|
|
7
|
+
|
|
8
|
+
Solara combines [ipywidgets](./ipywidgets), [reacton](./reacton) and puts it into a opinionated framework to make web apps with a focus on data (data apps for short).
|
|
9
|
+
|
|
10
|
+
Solara consists of two main parts
|
|
11
|
+
|
|
12
|
+
## Solara-ui
|
|
13
|
+
|
|
14
|
+
A set of opinionated react components and hooks that allow you to build web apps and data apps faster.
|
|
15
|
+
|
|
16
|
+
## Solara-server
|
|
17
|
+
|
|
18
|
+
A web framework for deploying web apps or data apps in production. Opinionated about pages and routing.
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Understanding routing in multi-page solara apps
|
|
3
|
+
description: Dive into Solara's routing, which provides a powerful way to compose multiple pages or dashboards into an integrated app.
|
|
4
|
+
---
|
|
5
|
+
# Routing
|
|
6
|
+
|
|
7
|
+
Routing takes care of linking a web address (more specifically the [pathname](https://developer.mozilla.org/en-US/docs/Web/API/Location), e.g. "/docs/basics/solara") to a state of the UI,
|
|
8
|
+
usually in the form of what appears as pages to a user.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
Routing includes navigation (hitting the back and forward button of your browser) without causing a page reload.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## Automatic routing
|
|
15
|
+
|
|
16
|
+
Setting up routing can be repetitive, and therefore Solara comes also with a more opinionated method for setting up routing automatically.
|
|
17
|
+
|
|
18
|
+
### Based on a directory
|
|
19
|
+
|
|
20
|
+
Using [`generate_routes_directory(path: Path) -> List[solara.Route]:`](/documentation/api/routing/generate_routes_directory) we can request Solara to give us a list of
|
|
21
|
+
routes by scanning a directory for Python scripts, Notebooks and Markdown files.
|
|
22
|
+
|
|
23
|
+
This function is being used by Solara if you run solara server with a directory as argument name, as used in [our Multi Page guide](/documentation/advanced/howto/multipage). More details can be found there.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Based on a Python package
|
|
27
|
+
|
|
28
|
+
Similar to the previous section [`generate_routes(module: ModuleType) -> List[solara.Route]:`](/documentation/api/routing/generate_routes) will return a list of routes by scanning a Python package or module for `Page` components, or `app` elements. Again, more information can be found at [our Multi Page guide](/documentation/advanced/howto/multipage).
|
|
29
|
+
|
|
30
|
+
## Manually defining routes
|
|
31
|
+
|
|
32
|
+
In Solara, we set up routing by defining a list of `solara.Route` objects, where each route can have another list of routes, its children, forming
|
|
33
|
+
a tree of routes. We assign this list to the `routes` variable in your main app script or module.
|
|
34
|
+
|
|
35
|
+
### Defining route components
|
|
36
|
+
|
|
37
|
+
If no `Page` component is found in your main script or module, Solara will assume you have either set the `component` or the `module` argument of the `solara.Route` object.
|
|
38
|
+
|
|
39
|
+
For example
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
import solara
|
|
43
|
+
from solara.website.pages.examples.utilities import calculator
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@solara.component
|
|
47
|
+
def Home():
|
|
48
|
+
solara.Markdown("Home")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@solara.component
|
|
52
|
+
def About():
|
|
53
|
+
solara.Markdown("About")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
routes = [
|
|
57
|
+
solara.Route(path="/", component=Home, label="Home"),
|
|
58
|
+
# the calculator module should have a Page component
|
|
59
|
+
solara.Route(path="calculator", module=calculator, label="Calculator"),
|
|
60
|
+
solara.Route(path="about", component=About, label="About"),
|
|
61
|
+
]
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Defining route components
|
|
65
|
+
|
|
66
|
+
If you do define a `Page` component, you are fully responsible for how routing is done, but we recommend using [use_route](/documentation/api/routing/use_route).
|
|
67
|
+
|
|
68
|
+
An example route definition could be something like this:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
import solara as sol
|
|
72
|
+
|
|
73
|
+
routes = [
|
|
74
|
+
# route level == 0
|
|
75
|
+
solara.Route(path="/"), # matches empty path ''
|
|
76
|
+
solara.Route(
|
|
77
|
+
# route level == 1
|
|
78
|
+
path="docs", # matches '/docs'
|
|
79
|
+
children=[
|
|
80
|
+
# route level == 2
|
|
81
|
+
solara.Route(path="basics", children=[ # matches '/docs/basics'
|
|
82
|
+
# route level == 3
|
|
83
|
+
solara.Route(path="react"), # matches '/docs/basics/react'
|
|
84
|
+
solara.Route(path="ipywidgets"), # matches '/docs/basics/ipywidgets'
|
|
85
|
+
solara.Route(path="solara"), # matches '/docs/basics/solara'
|
|
86
|
+
]),
|
|
87
|
+
solara.Route(path="advanced") # matches '/docs/advanced'
|
|
88
|
+
],
|
|
89
|
+
),
|
|
90
|
+
solara.Route(
|
|
91
|
+
path="blog",
|
|
92
|
+
# route level == 1
|
|
93
|
+
children=[
|
|
94
|
+
solara.Route(path="/"), # matches '/blog'
|
|
95
|
+
solara.Route(path="foo"), # matches '/blog/foo'
|
|
96
|
+
solara.Route(path="bar"), # matches '/blog/bar'
|
|
97
|
+
],
|
|
98
|
+
),
|
|
99
|
+
solara.Route(path="contact") # matches '/contact'
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
# Lets assume our pathname is `/docs/basics/react`,
|
|
103
|
+
@solara.component
|
|
104
|
+
def Page():
|
|
105
|
+
level = solara.use_route_level() # returns 0
|
|
106
|
+
route_current, routes_current_level = solara.use_routes()
|
|
107
|
+
# route_current is routes[1], i.e. solara.Route(path="docs", children=[...])
|
|
108
|
+
# routes_current_level is [routes[0], routes[1], routes[2], routes[3]], i.e.:
|
|
109
|
+
# [solara.Route(path="/"), solara.Route(path="docs", children=[...]),
|
|
110
|
+
# solara.Route(path="blog", children=[...]), solara.Route(path="contact")]
|
|
111
|
+
if route_current is None: # no matching route
|
|
112
|
+
return solara.Error("oops, page not found")
|
|
113
|
+
else:
|
|
114
|
+
# we could render some top level navigation here based on route_current_level and route_current
|
|
115
|
+
return MyFirstLevelChildComponent()
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Routes are matched by splitting the pathname around the slash ("/") and matching each part to the routes. The level of the depth into the tree is what we call the `route_level`, which starts at 0, the top level.
|
|
119
|
+
Each call to `use_route` will return the current route (if there is a match to the current path) and the list of siblings including itself.
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
Now the `MyFirstLevelChildComponent` component is responsible for rendering the second level navigation:
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
@solara.component
|
|
126
|
+
def MyFirstLevelChildComponent():
|
|
127
|
+
level = solara.use_route_level() # returns 1
|
|
128
|
+
route_current, routes_current_level = solara.use_routes()
|
|
129
|
+
# route_current is routes[1].children[0], i.e. solara.Route(path="basics", children=[...])
|
|
130
|
+
# routes_current_level is [routes[1].children[0], routes[1].children[1]], i.e.:
|
|
131
|
+
# [solara.Route(path="basics", children=[...]), solara.Route(path="advanced")]
|
|
132
|
+
if route_current is None: # no matching route
|
|
133
|
+
return solara.Error("oops, page not found")
|
|
134
|
+
else:
|
|
135
|
+
# we could render some mid level navigation here based on route_current_level and route_current
|
|
136
|
+
return MySecondLevelChildComponent()
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
And the `MySecondLevelChildComponent` component is responsible for rendering the third level navigation:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
@solara.component
|
|
144
|
+
def MySecondLevelChildComponent():
|
|
145
|
+
level = solara.use_route_level() # returns 2
|
|
146
|
+
route_current, routes_current_level = solara.use_routes()
|
|
147
|
+
# route_current is routes[1].children[0].children[0], i.e. solara.Route(path="react")
|
|
148
|
+
# routes_current_level is [routes[1].children[0].children[0], routes[1].children[0].children[1], routes[1].children[0].children[2]], i.e.
|
|
149
|
+
# [solara.Route(path="react"), solara.Route(path="ipywidgets"), solara.Route(path="solara")]
|
|
150
|
+
if route_current is None: # no matching route
|
|
151
|
+
return solara.Error("oops, page not found")
|
|
152
|
+
else:
|
|
153
|
+
# we could render some mid level navigation here based on route_current_level and route_current
|
|
154
|
+
if route_current.path == "react":
|
|
155
|
+
return DocsBasicReact() # render the actual content
|
|
156
|
+
elif route_current.path == "ipywidgets":
|
|
157
|
+
return DocsBasicIpyWidgets()
|
|
158
|
+
elif route_current.path == "solara":
|
|
159
|
+
return DocsBasicSolara()
|
|
160
|
+
else:
|
|
161
|
+
return solara.Error("oops, not possible!")
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
From this code, we can see we are free how we transform the routes into the state of the UI (i.e. which components are rendered).
|
|
166
|
+
|
|
167
|
+
## Adding data
|
|
168
|
+
|
|
169
|
+
Often, your render logic needs some extra data on what to display. For instance, you may want to dynamically render tabs based on the routes,
|
|
170
|
+
which requires you to have a label, and know which component to add.
|
|
171
|
+
For this purposed we added `label: str` and the `component' attributes, so you can defines routes likes:
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
routes = [
|
|
175
|
+
solara.Route("/", component=Home, label="What is Solara ☀️?"),
|
|
176
|
+
solara.Route("docs", component=docs.App, label="Docs", children=docs.routes),
|
|
177
|
+
solara.Route(
|
|
178
|
+
"demo",
|
|
179
|
+
component=demo.Demo,
|
|
180
|
+
children=demo.routes,
|
|
181
|
+
label="Demo",
|
|
182
|
+
),
|
|
183
|
+
...
|
|
184
|
+
]
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
In the case where you did not specify a `Page` component, label is used for the [Title](/documentation/components/page/title) component.
|
|
188
|
+
|
|
189
|
+
If you need to store more data in the route, you are free to put whatever you want in the `data` attribute, see also [Route](/documentation/api/routing/route).
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
## Linking
|
|
194
|
+
|
|
195
|
+
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.
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
Therefore you should never use the `route.path` for navigation since the route object has no knowledge of the full url
|
|
199
|
+
(e.g. `/docs/basics/ipywigets`) but only knows its small piece of the pathname (e.g. `ipywidgets`)
|
|
200
|
+
|
|
201
|
+
Using [`resolve_path`](/documentation/api/routing/resolve_path) we can request the full url for navigation.
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
def resolve_path(path_or_route: Union[str, solara.Route], level=0) -> str:
|
|
205
|
+
...
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
We can pass this full URL to the [`solara.Link`](/documentation/components/advanced/link) component, e.g. like:
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
@solara.component
|
|
212
|
+
def LinkToIpywidgets():
|
|
213
|
+
route_ipywidgets = routes.children[1].children[0].children[1]
|
|
214
|
+
# route_ipywidgets.path == "ipywidgets"
|
|
215
|
+
path = solara.resolve_path(route_ipywidgets)
|
|
216
|
+
# path == '/docs/basics/ipywidgets
|
|
217
|
+
with solara.Link(path) as main:
|
|
218
|
+
solara.Button("read about ipywidgets")
|
|
219
|
+
return main
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Fully manual routing
|
|
223
|
+
|
|
224
|
+
If you want to do routing fully manually, you can use the [`solara.use_router`](/documentation/api/routing/use_router) hook, and use the `.path` attribute.
|
|
225
|
+
|
|
226
|
+
```python
|
|
227
|
+
import solara
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
@solara.component
|
|
231
|
+
def Page():
|
|
232
|
+
router = solara.use_router()
|
|
233
|
+
path = router.path
|
|
234
|
+
parts = path.split("/")
|
|
235
|
+
solara.Markdown(f"Path = {path!r}, and split up into {parts!r}")
|
|
236
|
+
# now you can do anything with path or parts.
|
|
237
|
+
# e.g.
|
|
238
|
+
# if parts[0] == "docs":
|
|
239
|
+
# solara.Markdown("You are in the docs section")
|
|
240
|
+
```
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Understanding the way Solara server works
|
|
3
|
+
description: Solara server enables running ipywidgets-based applications as standalone dashboards and apps, allowing multiple "Virtual kernels" to share
|
|
4
|
+
the same process for better performance and scalability.
|
|
5
|
+
---
|
|
6
|
+
# Solara server
|
|
7
|
+
|
|
8
|
+
The solara server enables running ipywidgets based applications without a real Jupyter kernel, allowing multiple "Virtual kernels" to share the same process for better performance and scalability.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
To install the solara server, run:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
$ pip install "solara-server[starlette]"
|
|
16
|
+
$ # pip install solara # to get all solara packages
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
See [our installation guide](/documentation/getting_started/installing) for more information.
|
|
20
|
+
|
|
21
|
+
## WebSocket in Solara
|
|
22
|
+
Solara uses a WebSocket to transmit state and updates directly from the server to the browser. This ensures that the state remains centralized on the server, facilitating state transitions server-side and enabling live updates to be pushed directly to the browser.
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## Virtual Kernels
|
|
26
|
+
Normally when a browser page connects to a Solara server, a virtual kernel is created and is assigned a unique identifier termed a "Kernel ID." Should a WebSocket disconnection occur, Solara attempts to re-establish the connection, sending the Kernel ID during this process. If the server recognizes this ID (and the requested kernel hasn't expired) the Solara app resumes operations seamlessly.
|
|
27
|
+
|
|
28
|
+
### Virtual kernel lifecycle
|
|
29
|
+
Closing a browser page will directly shut the virtual kernel down (if this page was the last known page to the Solara server). This ensures that active closing of pages will directly clean up any memory usage on the server side for this kernel.
|
|
30
|
+
|
|
31
|
+
However, when the websocket between the web page and the server disconnects, the server keeps the kernel alive for 24 hours after the closure of the last WebSocket connection. The duration is customizable through the `SOLARA_KERNEL_CULL_TIMEOUT` environment variable. This feature is particularly handy in scenarios where devices like computers hibernate, leading to WebSocket disconnections. Upon awakening and subsequent WebSocket reconnection, the Solara app picks up right where it left off.
|
|
32
|
+
|
|
33
|
+
To optimize memory usage or address specific needs, one might opt for a shorter expiration duration. For instance, setting `SOLARA_KERNEL_CULL_TIMEOUT=1m` will cause sessions to expire after just 1 minute. Other possible options are `2d` (2 days), `3h` (3 hours), `30s` (30 seconds), etc. If no units are given, seconds are assumed.
|
|
34
|
+
|
|
35
|
+
### Maximum number of kernels connected
|
|
36
|
+
|
|
37
|
+
Each virtual kernel runs in its own thread, this ensures that one particular user (actually browser page) cannot block the execution of another virtual kernel. However, each thread consumes a bit of resources. If you want to limit the number of kernels, this can be done by setting the `SOLARA_KERNELS_MAX_COUNT` environment variable. The default is unlimited (empty string), but you can set it to any number you like. If the limit is reached, the server will refuse new connections until a kernel is closed.
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
## Handling Multiple Workers
|
|
41
|
+
In setups with multiple workers, it's possible for a page to reconnect to a different worker than its original. This would result in a loss of the virtual kernel (since it lives on a different worker), prompting the Solara app to initiate a fresh start. To prevent this scenario, a sticky session configuration is recommended, ensuring consistent client-worker connections. Utilizing a load balancer, such as [nginx](https://www.nginx.com/), can achieve this.
|
|
42
|
+
|
|
43
|
+
If you have questions about setting this up, or require assistance, please [contact us](https://solara.dev/docs/contact).
|
|
44
|
+
|
|
45
|
+
## Sessions
|
|
46
|
+
|
|
47
|
+
Solara uses a browser cookie (named `solara-session-id`) to store a unique session id. This session id is available via [get_session_id()](https://solara.dev/api/get_session_id) and is the same for all
|
|
48
|
+
browser pages. This can be used to store state that outlives a page refresh.
|
|
49
|
+
|
|
50
|
+
We recommend storing the state in an external database, especially in the case of multiple workers/nodes. If you want to store state associated to a session in-memory, make sure to set up sticky sessions.
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
## Readiness check
|
|
56
|
+
|
|
57
|
+
To check if the server is ready to accept request, the `/readyz` endpoint is added, and should return a 200 HTTP status code, e.g.:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
$ curl http://localhost:8765/readyz
|
|
61
|
+
curl -I localhost:8765
|
|
62
|
+
|
|
63
|
+
HTTP/1.1 200 OK
|
|
64
|
+
...
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Live resource information
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
To check resource usage of the server (CPU, memory, etc.), the `/resourcez` endpoint is added, and should return a 200 HTTP status code and include
|
|
71
|
+
various resource information, like threads created and running, number of virtual kernels, etc. in JSON format. To get also memory and cpu usage, you can include
|
|
72
|
+
the `?verbose` query parameter, e.g.:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
$ curl http://localhost:8765/resourcez\?verbose
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The JSON format may be subject to change.
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
## Production mode
|
|
83
|
+
|
|
84
|
+
By default, solara runs in development mode. This means, it will:
|
|
85
|
+
|
|
86
|
+
* Automatically [reload your project files](/documentation/advanced/reference/reloading) by watching files on the filesystemn
|
|
87
|
+
* Load debug version of the CSS files and JavaScript files for improved error messages (which leads to larger asset files).
|
|
88
|
+
|
|
89
|
+
To disabled all of these option, pass the `--production` flag, or set the environment variable `SOLARA_MODE=production`.
|
|
90
|
+
|
|
91
|
+
## Telemetry
|
|
92
|
+
|
|
93
|
+
Solara uses Mixpanel to collect usage of the solara server. We track when a server is started, stopped and a daily report of the number of unique users and connections made. To opt out of mixpanel telemetry, either:
|
|
94
|
+
|
|
95
|
+
* Set the environmental variable `SOLARA_TELEMETRY_MIXPANEL_ENABLE` to `False`.
|
|
96
|
+
* Install [python-dotenv](https://pypi.org/project/python-dotenv/) and put `SOLARA_TELEMETRY_MIXPANEL_ENABLE=False` in a `.env` file.
|
|
97
|
+
* Run in auto restart mode (e.g. using `$ solara run sol.py --auto-restart`)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Working together with Voilà
|
|
3
|
+
description: Voilà, together with `voila-vuetify` is an alternative to Solara server. The most significant difference is that Voilà will start one kernel/process
|
|
4
|
+
per page request, while Solara server is more scalable.
|
|
5
|
+
---
|
|
6
|
+
# Voilà
|
|
7
|
+
|
|
8
|
+
[Voilà](https://voila.readthedocs.io/) allows you to convert a Jupyter Notebook into an interactive dashboard that allows you to share your work with others.
|
|
9
|
+
|
|
10
|
+
Voilà is Jupyter notebook focused, meaning that it will render all output from your notebook. Using [`voila-vuetify`](https://github.com/voila-dashboards/voila-vuetify) Voilà allows for a more app like experience, showing only the output you want.
|
|
11
|
+
|
|
12
|
+
Voilà, together with `voila-vuetify` is an alternative to [Solara server](./solara-server). The most significant difference is that Voilà will start one kernel/process per page request, while [Solara server](./solara-server) can serve many more users from a single process. Sharing the same process means Solara apps can share memory among users (e.g. a large dataset), which will usually lead to better performance and less resource usage.
|